@volley/recognition-client-sdk-node22 0.1.424
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/README.md +344 -0
- package/dist/browser.bundled.d.ts +1280 -0
- package/dist/browser.d.ts +10 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/config-builder.d.ts +134 -0
- package/dist/config-builder.d.ts.map +1 -0
- package/dist/errors.d.ts +41 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/factory.d.ts +36 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/index.bundled.d.ts +2572 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10199 -0
- package/dist/index.js.map +7 -0
- package/dist/recog-client-sdk.browser.d.ts +10 -0
- package/dist/recog-client-sdk.browser.d.ts.map +1 -0
- package/dist/recog-client-sdk.browser.js +5746 -0
- package/dist/recog-client-sdk.browser.js.map +7 -0
- package/dist/recognition-client.d.ts +128 -0
- package/dist/recognition-client.d.ts.map +1 -0
- package/dist/recognition-client.types.d.ts +271 -0
- package/dist/recognition-client.types.d.ts.map +1 -0
- package/dist/simplified-vgf-recognition-client.d.ts +178 -0
- package/dist/simplified-vgf-recognition-client.d.ts.map +1 -0
- package/dist/utils/audio-ring-buffer.d.ts +69 -0
- package/dist/utils/audio-ring-buffer.d.ts.map +1 -0
- package/dist/utils/message-handler.d.ts +45 -0
- package/dist/utils/message-handler.d.ts.map +1 -0
- package/dist/utils/url-builder.d.ts +28 -0
- package/dist/utils/url-builder.d.ts.map +1 -0
- package/dist/vgf-recognition-mapper.d.ts +66 -0
- package/dist/vgf-recognition-mapper.d.ts.map +1 -0
- package/dist/vgf-recognition-state.d.ts +91 -0
- package/dist/vgf-recognition-state.d.ts.map +1 -0
- package/package.json +74 -0
- package/src/browser.ts +24 -0
- package/src/config-builder.spec.ts +265 -0
- package/src/config-builder.ts +240 -0
- package/src/errors.ts +84 -0
- package/src/factory.spec.ts +215 -0
- package/src/factory.ts +47 -0
- package/src/index.ts +127 -0
- package/src/recognition-client.spec.ts +889 -0
- package/src/recognition-client.ts +844 -0
- package/src/recognition-client.types.ts +338 -0
- package/src/simplified-vgf-recognition-client.integration.spec.ts +718 -0
- package/src/simplified-vgf-recognition-client.spec.ts +1525 -0
- package/src/simplified-vgf-recognition-client.ts +524 -0
- package/src/utils/audio-ring-buffer.spec.ts +335 -0
- package/src/utils/audio-ring-buffer.ts +170 -0
- package/src/utils/message-handler.spec.ts +311 -0
- package/src/utils/message-handler.ts +131 -0
- package/src/utils/url-builder.spec.ts +252 -0
- package/src/utils/url-builder.ts +92 -0
- package/src/vgf-recognition-mapper.spec.ts +78 -0
- package/src/vgf-recognition-mapper.ts +232 -0
- package/src/vgf-recognition-state.ts +102 -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
|
+
});
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio Ring Buffer
|
|
3
|
+
* Manages circular buffer for audio data with overflow detection
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface BufferedAudio {
|
|
7
|
+
data: ArrayBuffer | ArrayBufferView;
|
|
8
|
+
timestamp: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface AudioRingBufferConfig {
|
|
12
|
+
maxBufferDurationSec: number;
|
|
13
|
+
chunksPerSecond: number;
|
|
14
|
+
logger?: (level: 'debug' | 'info' | 'warn' | 'error', message: string, data?: any) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class AudioRingBuffer {
|
|
18
|
+
private buffer: BufferedAudio[] = [];
|
|
19
|
+
private bufferSize: number;
|
|
20
|
+
private writeIndex = 0;
|
|
21
|
+
private readIndex = 0;
|
|
22
|
+
private hasWrapped = false;
|
|
23
|
+
private totalBufferedBytes = 0;
|
|
24
|
+
private overflowCount = 0;
|
|
25
|
+
private chunksBuffered = 0;
|
|
26
|
+
private logger?: (level: 'debug' | 'info' | 'warn' | 'error', message: string, data?: any) => void;
|
|
27
|
+
|
|
28
|
+
constructor(config: AudioRingBufferConfig) {
|
|
29
|
+
this.bufferSize = config.maxBufferDurationSec * config.chunksPerSecond;
|
|
30
|
+
this.buffer = new Array(this.bufferSize);
|
|
31
|
+
if (config.logger) {
|
|
32
|
+
this.logger = config.logger;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Write audio chunk to ring buffer with overflow detection
|
|
38
|
+
*/
|
|
39
|
+
write(audioData: ArrayBuffer | ArrayBufferView): void {
|
|
40
|
+
const bytes = ArrayBuffer.isView(audioData) ? audioData.byteLength : audioData.byteLength;
|
|
41
|
+
|
|
42
|
+
// Write to current position
|
|
43
|
+
this.buffer[this.writeIndex] = {
|
|
44
|
+
data: audioData,
|
|
45
|
+
timestamp: Date.now()
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Advance write pointer
|
|
49
|
+
const nextWriteIndex = (this.writeIndex + 1) % this.bufferSize;
|
|
50
|
+
|
|
51
|
+
// Detect overflow: write caught up to read
|
|
52
|
+
if (nextWriteIndex === this.readIndex && this.writeIndex !== this.readIndex) {
|
|
53
|
+
this.hasWrapped = true;
|
|
54
|
+
this.overflowCount++;
|
|
55
|
+
|
|
56
|
+
// Log buffer overflow event
|
|
57
|
+
if (this.logger) {
|
|
58
|
+
this.logger('debug', '[RecogSDK] Buffer overflow detected', {
|
|
59
|
+
bufferSize: this.bufferSize,
|
|
60
|
+
totalOverflows: this.overflowCount,
|
|
61
|
+
droppedChunk: this.buffer[this.readIndex]?.timestamp
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Move read pointer forward to make room (drop oldest)
|
|
66
|
+
this.readIndex = (this.readIndex + 1) % this.bufferSize;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this.writeIndex = nextWriteIndex;
|
|
70
|
+
this.chunksBuffered++;
|
|
71
|
+
this.totalBufferedBytes += bytes;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Read next chunk from buffer
|
|
76
|
+
*/
|
|
77
|
+
read(): BufferedAudio | null {
|
|
78
|
+
if (this.isEmpty()) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const chunk = this.buffer[this.readIndex];
|
|
83
|
+
this.readIndex = (this.readIndex + 1) % this.bufferSize;
|
|
84
|
+
return chunk || null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Read all buffered chunks without removing them
|
|
89
|
+
*/
|
|
90
|
+
readAll(): BufferedAudio[] {
|
|
91
|
+
const chunks: BufferedAudio[] = [];
|
|
92
|
+
let index = this.readIndex;
|
|
93
|
+
|
|
94
|
+
while (index !== this.writeIndex) {
|
|
95
|
+
const chunk = this.buffer[index];
|
|
96
|
+
if (chunk) {
|
|
97
|
+
chunks.push(chunk);
|
|
98
|
+
}
|
|
99
|
+
index = (index + 1) % this.bufferSize;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return chunks;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Flush all buffered data and advance read pointer
|
|
107
|
+
*/
|
|
108
|
+
flush(): BufferedAudio[] {
|
|
109
|
+
const chunks = this.readAll();
|
|
110
|
+
this.readIndex = this.writeIndex;
|
|
111
|
+
return chunks;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get count of buffered chunks
|
|
116
|
+
*/
|
|
117
|
+
getBufferedCount(): number {
|
|
118
|
+
if (this.writeIndex >= this.readIndex) {
|
|
119
|
+
return this.writeIndex - this.readIndex;
|
|
120
|
+
} else {
|
|
121
|
+
// Wrapped around
|
|
122
|
+
return this.bufferSize - this.readIndex + this.writeIndex;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Check if buffer is empty
|
|
128
|
+
*/
|
|
129
|
+
isEmpty(): boolean {
|
|
130
|
+
return this.readIndex === this.writeIndex;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Check if buffer has overflowed
|
|
135
|
+
*/
|
|
136
|
+
isOverflowing(): boolean {
|
|
137
|
+
return this.hasWrapped;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Clear the buffer and reset all counters
|
|
142
|
+
* Frees memory by releasing all stored audio chunks
|
|
143
|
+
*/
|
|
144
|
+
clear(): void {
|
|
145
|
+
this.buffer = [];
|
|
146
|
+
this.writeIndex = 0;
|
|
147
|
+
this.readIndex = 0;
|
|
148
|
+
this.hasWrapped = false;
|
|
149
|
+
this.overflowCount = 0;
|
|
150
|
+
this.chunksBuffered = 0;
|
|
151
|
+
this.totalBufferedBytes = 0;
|
|
152
|
+
|
|
153
|
+
if (this.logger) {
|
|
154
|
+
this.logger('debug', '[RecogSDK] Audio buffer cleared');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get buffer statistics
|
|
160
|
+
*/
|
|
161
|
+
getStats() {
|
|
162
|
+
return {
|
|
163
|
+
chunksBuffered: this.chunksBuffered,
|
|
164
|
+
currentBufferedChunks: this.getBufferedCount(),
|
|
165
|
+
overflowCount: this.overflowCount,
|
|
166
|
+
hasWrapped: this.hasWrapped,
|
|
167
|
+
totalBufferedBytes: this.totalBufferedBytes
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|