Skip to content
This repository was archived by the owner on Jan 7, 2025. It is now read-only.

Commit 169aa62

Browse files
authored
Merge pull request #1 from speechly/fix/segment-modification-race
Optional energy-based noise gate to control listening on/off "hands free".
2 parents 3bec516 + d484328 commit 169aa62

File tree

14 files changed

+2674
-139
lines changed

14 files changed

+2674
-139
lines changed

speechly-client-net-standard-2.0/Speechly/SLUClient/Segment.cs

Lines changed: 67 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -34,99 +34,114 @@ public string ToString(
3434
string confirmationMark
3535
) {
3636
var sb = new StringBuilder();
37-
var entityIds = new string[words.Length];
38-
foreach (KeyValuePair<string, Entity> entity in entities) {
39-
for (var i = entity.Value.startPosition; i < entity.Value.endPosition; i++) {
40-
entityIds[i] = entity.Key;
37+
38+
lock(this) {
39+
var entityIds = new string[words.Length];
40+
foreach (KeyValuePair<string, Entity> entity in entities) {
41+
for (var i = entity.Value.startPosition; i < entity.Value.endPosition; i++) {
42+
entityIds[i] = entity.Key;
43+
}
4144
}
42-
}
4345

44-
sb.Append(intentTag(this.intent.intent));
45-
46-
bool firstWord = sb.Length == 0;
47-
string lastEntityId = null;
46+
sb.Append(intentTag(this.intent.intent));
47+
48+
bool firstWord = sb.Length == 0;
49+
string lastEntityId = null;
4850

49-
for (int i = 0; i < words.Length; i++) {
50-
var word = words[i];
51-
if (word == null) continue;
51+
for (int i = 0; i < words.Length; i++) {
52+
var word = words[i];
53+
if (word == null) continue;
5254

5355

54-
if (entityIds[i] != lastEntityId) {
55-
if (lastEntityId != null) {
56-
var entity = entities[lastEntityId];
56+
if (entityIds[i] != lastEntityId) {
57+
if (lastEntityId != null) {
58+
var entity = entities[lastEntityId];
59+
if (!firstWord) sb.Append(" ");
60+
sb.Append(entityTag(entity.value, entity.type));
61+
firstWord = false;
62+
}
63+
}
64+
65+
if (entityIds[i] == null) {
5766
if (!firstWord) sb.Append(" ");
58-
sb.Append(entityTag(entity.value, entity.type));
67+
sb.Append(word.word);
5968
firstWord = false;
6069
}
70+
71+
lastEntityId = entityIds[i];
6172
}
6273

63-
if (entityIds[i] == null) {
74+
if (lastEntityId != null) {
6475
if (!firstWord) sb.Append(" ");
65-
sb.Append(word.word);
66-
firstWord = false;
76+
var entity = entities[lastEntityId];
77+
sb.Append(entityTag(entity.value, entity.type));
6778
}
6879

69-
lastEntityId = entityIds[i];
70-
}
71-
72-
if (lastEntityId != null) {
73-
if (!firstWord) sb.Append(" ");
74-
var entity = entities[lastEntityId];
75-
sb.Append(entityTag(entity.value, entity.type));
80+
if (this.isFinal) sb.Append(confirmationMark);
7681
}
7782

78-
if (this.isFinal) sb.Append(confirmationMark);
79-
8083
return sb.ToString();
8184
}
8285

8386
public void UpdateTranscript(Word word) {
84-
if (word.index >= words.Length) {
85-
Array.Resize(ref words, word.index + 1);
87+
lock(this) {
88+
if (word.index >= words.Length) {
89+
Array.Resize(ref words, word.index + 1);
90+
}
91+
words[word.index] = word;
8692
}
87-
words[word.index] = word;
8893
}
8994

9095
public void UpdateTranscript(Word[] words) {
91-
foreach(Word w in words) UpdateTranscript(w);
96+
lock(this) {
97+
foreach(Word w in words) UpdateTranscript(w);
98+
}
9299
}
93100

94101
public void UpdateEntity(Entity entity) {
95-
entities[EntityMapKey(entity)] = entity;
102+
lock(this) {
103+
entities[EntityMapKey(entity)] = entity;
104+
}
96105
}
97106

98107
public void UpdateEntity(Entity[] entities) {
99-
foreach(Entity entity in entities) UpdateEntity(entity);
108+
lock(this) {
109+
foreach(Entity entity in entities) UpdateEntity(entity);
110+
}
100111
}
101112

102113
public void UpdateIntent(string intent, bool isFinal) {
103-
this.intent.intent = intent;
104-
this.intent.isFinal = isFinal;
114+
lock(this) {
115+
this.intent.intent = intent;
116+
this.intent.isFinal = isFinal;
117+
}
105118
}
106119

107120
public void EndSegment() {
108-
// Filter away any entities which were not finalized.
109-
foreach (KeyValuePair<string, Entity> entity in entities) {
110-
if (!entity.Value.isFinal) {
111-
this.entities.Remove(entity.Key);
121+
lock(this) {
122+
// Filter away any entities which were not finalized.
123+
foreach (KeyValuePair<string, Entity> entity in entities) {
124+
if (!entity.Value.isFinal) {
125+
this.entities.Remove(entity.Key);
126+
}
112127
}
113-
}
114128

115-
// Filter away any transcripts which were not finalized. Keep indices intact.
116-
for (int i = 0; i < words.Length; i++) {
117-
if (words[i] != null) {
118-
if (!words[i].isFinal) {
119-
words[i] = null;
129+
// Filter away any transcripts which were not finalized. Keep indices intact.
130+
for (int i = 0; i < words.Length; i++) {
131+
if (words[i] != null) {
132+
if (!words[i].isFinal) {
133+
words[i] = null;
134+
}
120135
}
121136
}
122-
}
123137

124-
if (!this.intent.isFinal) {
125-
this.intent.intent = "";
126-
this.intent.isFinal = true;
138+
if (!this.intent.isFinal) {
139+
this.intent.intent = "";
140+
this.intent.isFinal = true;
141+
}
142+
// Mark as final.
143+
this.isFinal = true;
127144
}
128-
// Mark as final.
129-
this.isFinal = true;
130145
}
131146

132147
private string EntityMapKey(Entity e) {

speechly-client-net-standard-2.0/Speechly/SLUClient/SpeechlyClient.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
namespace Speechly.SLUClient {
99

1010
public class SpeechlyClient {
11-
public static bool DEBUG_SAVE_AUDIO = false;
11+
public static bool DEBUG_SAVE_AUDIO = true;
1212

1313
public delegate void SegmentChangeDelegate(Segment segment);
1414
public delegate void StateChangeDelegate(ClientState state);
@@ -27,6 +27,7 @@ public class SpeechlyClient {
2727
public StateChangeDelegate OnStateChange = (ClientState state) => {};
2828

2929
public bool IsListening { get; private set; } = false;
30+
public int SamplesSent { get; private set; } = 0;
3031
public ClientState State { get; private set; } = ClientState.Disconnected;
3132
// Optional message queue should messages be run in the main thread
3233
private ConcurrentQueue<string> messageQueue = new ConcurrentQueue<string>();
@@ -122,6 +123,7 @@ public async Task<string> StartContext(string appId = null) {
122123
if (DEBUG_SAVE_AUDIO) {
123124
debugAudioStream = new FileStream($"utterance_{contextId}.raw", FileMode.CreateNew, FileAccess.Write, FileShare.None);
124125
}
126+
SamplesSent = 0;
125127
SetState(ClientState.Recording);
126128
return contextId;
127129
} catch (Exception e) {
@@ -143,6 +145,7 @@ public async Task SendAudio(Stream fileStream) {
143145
if (DEBUG_SAVE_AUDIO) {
144146
debugAudioStream.Write(b, 0, bytesRead);
145147
}
148+
SamplesSent += bytesRead / 2;
146149
await wsClient.SendBytes(new ArraySegment<byte>(b, 0, bytesRead));
147150
}
148151
}
@@ -166,6 +169,7 @@ public async Task SendAudio(float[] floats, int start = 0, int end = -1) {
166169
debugAudioStream.Write(buf, 0, buf.Length);
167170
}
168171

172+
SamplesSent += bufSize;
169173
await wsClient.SendBytes(new ArraySegment<byte>(buf));
170174
}
171175

@@ -245,8 +249,9 @@ private void OnResponse(string msgString)
245249
break;
246250
}
247251
}
248-
} catch (Exception e) {
249-
throw new Exception($"Ouch. {e.GetType()} while handling message with content: {msgString}");
252+
} catch {
253+
Logger.LogError($"Error while handling message with content: {msgString}");
254+
throw;
250255
}
251256
}
252257

speechly-client.unitypackage

8.72 KB
Binary file not shown.

0 commit comments

Comments
 (0)