@volley/recognition-client-sdk 0.1.423 → 0.1.621
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.bundled.d.ts +237 -7
- package/dist/index.bundled.d.ts +346 -10
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +248 -12
- package/dist/index.js.map +4 -4
- package/dist/recog-client-sdk.browser.js +236 -11
- package/dist/recog-client-sdk.browser.js.map +4 -4
- package/dist/recognition-client.d.ts +32 -1
- package/dist/recognition-client.d.ts.map +1 -1
- package/dist/recognition-client.types.d.ts +20 -0
- package/dist/recognition-client.types.d.ts.map +1 -1
- package/dist/simplified-vgf-recognition-client.d.ts +17 -0
- package/dist/simplified-vgf-recognition-client.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +2 -0
- package/src/recognition-client.ts +160 -5
- package/src/recognition-client.types.ts +23 -0
- package/src/simplified-vgf-recognition-client.integration.spec.ts +15 -3
- package/src/simplified-vgf-recognition-client.ts +30 -3
- package/src/utils/audio-ring-buffer.spec.ts +335 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio Ring Buffer Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { AudioRingBuffer } from './audio-ring-buffer.js';
|
|
6
|
+
|
|
7
|
+
describe('AudioRingBuffer', () => {
|
|
8
|
+
describe('constructor', () => {
|
|
9
|
+
it('should initialize with correct buffer size', () => {
|
|
10
|
+
const buffer = new AudioRingBuffer({
|
|
11
|
+
maxBufferDurationSec: 10,
|
|
12
|
+
chunksPerSecond: 100
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
expect(buffer.isEmpty()).toBe(true);
|
|
16
|
+
expect(buffer.getBufferedCount()).toBe(0);
|
|
17
|
+
expect(buffer.isOverflowing()).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should accept logger function', () => {
|
|
21
|
+
const logger = jest.fn();
|
|
22
|
+
const buffer = new AudioRingBuffer({
|
|
23
|
+
maxBufferDurationSec: 1,
|
|
24
|
+
chunksPerSecond: 10,
|
|
25
|
+
logger
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(buffer).toBeDefined();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('write', () => {
|
|
33
|
+
it('should write ArrayBuffer data', () => {
|
|
34
|
+
const buffer = new AudioRingBuffer({
|
|
35
|
+
maxBufferDurationSec: 1,
|
|
36
|
+
chunksPerSecond: 10
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const data = new ArrayBuffer(100);
|
|
40
|
+
buffer.write(data);
|
|
41
|
+
|
|
42
|
+
expect(buffer.isEmpty()).toBe(false);
|
|
43
|
+
expect(buffer.getBufferedCount()).toBe(1);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should write ArrayBufferView data', () => {
|
|
47
|
+
const buffer = new AudioRingBuffer({
|
|
48
|
+
maxBufferDurationSec: 1,
|
|
49
|
+
chunksPerSecond: 10
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const data = new Uint8Array(100);
|
|
53
|
+
buffer.write(data);
|
|
54
|
+
|
|
55
|
+
expect(buffer.isEmpty()).toBe(false);
|
|
56
|
+
expect(buffer.getBufferedCount()).toBe(1);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should track total buffered bytes', () => {
|
|
60
|
+
const buffer = new AudioRingBuffer({
|
|
61
|
+
maxBufferDurationSec: 1,
|
|
62
|
+
chunksPerSecond: 10
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
buffer.write(new ArrayBuffer(100));
|
|
66
|
+
buffer.write(new ArrayBuffer(200));
|
|
67
|
+
|
|
68
|
+
const stats = buffer.getStats();
|
|
69
|
+
expect(stats.totalBufferedBytes).toBe(300);
|
|
70
|
+
expect(stats.chunksBuffered).toBe(2);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should handle multiple writes', () => {
|
|
74
|
+
const buffer = new AudioRingBuffer({
|
|
75
|
+
maxBufferDurationSec: 1,
|
|
76
|
+
chunksPerSecond: 100
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
for (let i = 0; i < 50; i++) {
|
|
80
|
+
buffer.write(new ArrayBuffer(10));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
expect(buffer.getBufferedCount()).toBe(50);
|
|
84
|
+
expect(buffer.isOverflowing()).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('overflow detection', () => {
|
|
89
|
+
it('should detect overflow when buffer is full', () => {
|
|
90
|
+
const buffer = new AudioRingBuffer({
|
|
91
|
+
maxBufferDurationSec: 1,
|
|
92
|
+
chunksPerSecond: 5 // Small buffer of 5 chunks
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Write more than buffer can hold
|
|
96
|
+
for (let i = 0; i < 10; i++) {
|
|
97
|
+
buffer.write(new ArrayBuffer(10));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
expect(buffer.isOverflowing()).toBe(true);
|
|
101
|
+
const stats = buffer.getStats();
|
|
102
|
+
expect(stats.hasWrapped).toBe(true);
|
|
103
|
+
expect(stats.overflowCount).toBeGreaterThan(0);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should call logger on overflow', () => {
|
|
107
|
+
const logger = jest.fn();
|
|
108
|
+
const buffer = new AudioRingBuffer({
|
|
109
|
+
maxBufferDurationSec: 1,
|
|
110
|
+
chunksPerSecond: 3, // Small buffer
|
|
111
|
+
logger
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Fill buffer to overflow
|
|
115
|
+
for (let i = 0; i < 10; i++) {
|
|
116
|
+
buffer.write(new ArrayBuffer(10));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Logger should have been called with overflow message
|
|
120
|
+
expect(logger).toHaveBeenCalledWith(
|
|
121
|
+
'debug',
|
|
122
|
+
expect.stringContaining('overflow'),
|
|
123
|
+
expect.any(Object)
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('read', () => {
|
|
129
|
+
it('should return null when buffer is empty', () => {
|
|
130
|
+
const buffer = new AudioRingBuffer({
|
|
131
|
+
maxBufferDurationSec: 1,
|
|
132
|
+
chunksPerSecond: 10
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
expect(buffer.read()).toBeNull();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should return data in FIFO order', () => {
|
|
139
|
+
const buffer = new AudioRingBuffer({
|
|
140
|
+
maxBufferDurationSec: 1,
|
|
141
|
+
chunksPerSecond: 10
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const data1 = new Uint8Array([1, 2, 3]);
|
|
145
|
+
const data2 = new Uint8Array([4, 5, 6]);
|
|
146
|
+
|
|
147
|
+
buffer.write(data1);
|
|
148
|
+
buffer.write(data2);
|
|
149
|
+
|
|
150
|
+
const chunk1 = buffer.read();
|
|
151
|
+
expect(chunk1).not.toBeNull();
|
|
152
|
+
expect(chunk1!.data).toBe(data1);
|
|
153
|
+
|
|
154
|
+
const chunk2 = buffer.read();
|
|
155
|
+
expect(chunk2).not.toBeNull();
|
|
156
|
+
expect(chunk2!.data).toBe(data2);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should decrement buffered count after read', () => {
|
|
160
|
+
const buffer = new AudioRingBuffer({
|
|
161
|
+
maxBufferDurationSec: 1,
|
|
162
|
+
chunksPerSecond: 10
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
buffer.write(new ArrayBuffer(10));
|
|
166
|
+
buffer.write(new ArrayBuffer(10));
|
|
167
|
+
|
|
168
|
+
expect(buffer.getBufferedCount()).toBe(2);
|
|
169
|
+
|
|
170
|
+
buffer.read();
|
|
171
|
+
expect(buffer.getBufferedCount()).toBe(1);
|
|
172
|
+
|
|
173
|
+
buffer.read();
|
|
174
|
+
expect(buffer.getBufferedCount()).toBe(0);
|
|
175
|
+
expect(buffer.isEmpty()).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('readAll', () => {
|
|
180
|
+
it('should return empty array when buffer is empty', () => {
|
|
181
|
+
const buffer = new AudioRingBuffer({
|
|
182
|
+
maxBufferDurationSec: 1,
|
|
183
|
+
chunksPerSecond: 10
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
expect(buffer.readAll()).toEqual([]);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should return all chunks without removing them', () => {
|
|
190
|
+
const buffer = new AudioRingBuffer({
|
|
191
|
+
maxBufferDurationSec: 1,
|
|
192
|
+
chunksPerSecond: 10
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
buffer.write(new ArrayBuffer(10));
|
|
196
|
+
buffer.write(new ArrayBuffer(20));
|
|
197
|
+
buffer.write(new ArrayBuffer(30));
|
|
198
|
+
|
|
199
|
+
const chunks = buffer.readAll();
|
|
200
|
+
expect(chunks).toHaveLength(3);
|
|
201
|
+
|
|
202
|
+
// Should still have all chunks
|
|
203
|
+
expect(buffer.getBufferedCount()).toBe(3);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('flush', () => {
|
|
208
|
+
it('should return all chunks and clear buffer', () => {
|
|
209
|
+
const buffer = new AudioRingBuffer({
|
|
210
|
+
maxBufferDurationSec: 1,
|
|
211
|
+
chunksPerSecond: 10
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
buffer.write(new ArrayBuffer(10));
|
|
215
|
+
buffer.write(new ArrayBuffer(20));
|
|
216
|
+
|
|
217
|
+
const chunks = buffer.flush();
|
|
218
|
+
expect(chunks).toHaveLength(2);
|
|
219
|
+
expect(buffer.isEmpty()).toBe(true);
|
|
220
|
+
expect(buffer.getBufferedCount()).toBe(0);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('clear', () => {
|
|
225
|
+
it('should reset all state', () => {
|
|
226
|
+
const buffer = new AudioRingBuffer({
|
|
227
|
+
maxBufferDurationSec: 1,
|
|
228
|
+
chunksPerSecond: 5
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Fill and overflow
|
|
232
|
+
for (let i = 0; i < 10; i++) {
|
|
233
|
+
buffer.write(new ArrayBuffer(100));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
expect(buffer.isOverflowing()).toBe(true);
|
|
237
|
+
|
|
238
|
+
buffer.clear();
|
|
239
|
+
|
|
240
|
+
expect(buffer.isEmpty()).toBe(true);
|
|
241
|
+
expect(buffer.getBufferedCount()).toBe(0);
|
|
242
|
+
expect(buffer.isOverflowing()).toBe(false);
|
|
243
|
+
|
|
244
|
+
const stats = buffer.getStats();
|
|
245
|
+
expect(stats.chunksBuffered).toBe(0);
|
|
246
|
+
expect(stats.totalBufferedBytes).toBe(0);
|
|
247
|
+
expect(stats.overflowCount).toBe(0);
|
|
248
|
+
expect(stats.hasWrapped).toBe(false);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should call logger when clearing', () => {
|
|
252
|
+
const logger = jest.fn();
|
|
253
|
+
const buffer = new AudioRingBuffer({
|
|
254
|
+
maxBufferDurationSec: 1,
|
|
255
|
+
chunksPerSecond: 10,
|
|
256
|
+
logger
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
buffer.write(new ArrayBuffer(10));
|
|
260
|
+
buffer.clear();
|
|
261
|
+
|
|
262
|
+
expect(logger).toHaveBeenCalledWith(
|
|
263
|
+
'debug',
|
|
264
|
+
expect.stringContaining('cleared')
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe('getStats', () => {
|
|
270
|
+
it('should return correct statistics', () => {
|
|
271
|
+
const buffer = new AudioRingBuffer({
|
|
272
|
+
maxBufferDurationSec: 1,
|
|
273
|
+
chunksPerSecond: 10
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
buffer.write(new ArrayBuffer(100));
|
|
277
|
+
buffer.write(new ArrayBuffer(200));
|
|
278
|
+
|
|
279
|
+
const stats = buffer.getStats();
|
|
280
|
+
|
|
281
|
+
expect(stats.chunksBuffered).toBe(2);
|
|
282
|
+
expect(stats.currentBufferedChunks).toBe(2);
|
|
283
|
+
expect(stats.totalBufferedBytes).toBe(300);
|
|
284
|
+
expect(stats.hasWrapped).toBe(false);
|
|
285
|
+
expect(stats.overflowCount).toBe(0);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
describe('getBufferedCount with wraparound', () => {
|
|
290
|
+
it('should correctly count after partial read and more writes', () => {
|
|
291
|
+
const buffer = new AudioRingBuffer({
|
|
292
|
+
maxBufferDurationSec: 1,
|
|
293
|
+
chunksPerSecond: 10 // Buffer size of 10
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Write 5 chunks
|
|
297
|
+
for (let i = 0; i < 5; i++) {
|
|
298
|
+
buffer.write(new ArrayBuffer(10));
|
|
299
|
+
}
|
|
300
|
+
expect(buffer.getBufferedCount()).toBe(5);
|
|
301
|
+
|
|
302
|
+
// Read 3 chunks
|
|
303
|
+
buffer.read();
|
|
304
|
+
buffer.read();
|
|
305
|
+
buffer.read();
|
|
306
|
+
expect(buffer.getBufferedCount()).toBe(2);
|
|
307
|
+
|
|
308
|
+
// Write 4 more (wraps around in buffer)
|
|
309
|
+
for (let i = 0; i < 4; i++) {
|
|
310
|
+
buffer.write(new ArrayBuffer(10));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Should have 2 + 4 = 6 chunks
|
|
314
|
+
expect(buffer.getBufferedCount()).toBe(6);
|
|
315
|
+
expect(buffer.isOverflowing()).toBe(false);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should handle buffer overflow correctly', () => {
|
|
319
|
+
const buffer = new AudioRingBuffer({
|
|
320
|
+
maxBufferDurationSec: 1,
|
|
321
|
+
chunksPerSecond: 3 // Buffer size of 3
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Write 5 chunks (more than buffer can hold)
|
|
325
|
+
for (let i = 0; i < 5; i++) {
|
|
326
|
+
buffer.write(new ArrayBuffer(10));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Buffer should be full but not exceed capacity
|
|
330
|
+
// Ring buffer drops oldest when full
|
|
331
|
+
expect(buffer.getBufferedCount()).toBeLessThanOrEqual(3);
|
|
332
|
+
expect(buffer.isOverflowing()).toBe(true);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
});
|