Demo-Frontend-Voice-Agent / src /audio /AudioRecorder.ts
ishaq101's picture
fix chunk empty audio
6078ab4
const TARGET_SAMPLE_RATE = 16000;
const CHUNK_SAMPLES = 3200; // 100ms at 16kHz
export class AudioRecorder {
private context: AudioContext | null = null;
private stream: MediaStream | null = null;
private workletNode: AudioWorkletNode | null = null;
private accumulator: Float32Array = new Float32Array(0);
micLevel = 0;
async start(onChunk: (pcm: ArrayBuffer) => void): Promise<void> {
this.stream = await navigator.mediaDevices.getUserMedia({
audio: {
channelCount: 1,
echoCancellation: true,
noiseSuppression: true,
},
});
this.context = new AudioContext({ sampleRate: TARGET_SAMPLE_RATE });
await this.context.resume();
await this.context.audioWorklet.addModule(
new URL("./RecorderWorkletProcessor.js", import.meta.url).href
);
const source = this.context.createMediaStreamSource(this.stream);
this.workletNode = new AudioWorkletNode(this.context, "recorder-processor");
this.workletNode.port.onmessage = (e: MessageEvent<Float32Array>) => {
const input = e.data;
const combined = new Float32Array(this.accumulator.length + input.length);
combined.set(this.accumulator);
combined.set(input, this.accumulator.length);
this.accumulator = combined;
while (this.accumulator.length >= CHUNK_SAMPLES) {
const chunk = this.accumulator.slice(0, CHUNK_SAMPLES);
this.accumulator = this.accumulator.slice(CHUNK_SAMPLES);
// RMS for barge-in detection
let sumSq = 0;
for (let i = 0; i < chunk.length; i++) sumSq += chunk[i] * chunk[i];
this.micLevel = Math.sqrt(sumSq / chunk.length) * 32767;
// Convert float32 → int16 PCM
const int16 = new Int16Array(chunk.length);
for (let i = 0; i < chunk.length; i++) {
const s = Math.max(-1, Math.min(1, chunk[i]));
int16[i] = s < 0 ? s * 32768 : s * 32767;
}
onChunk(int16.buffer);
}
};
source.connect(this.workletNode);
}
stop(): void {
this.workletNode?.disconnect();
this.workletNode = null;
this.stream?.getTracks().forEach((t) => t.stop());
this.stream = null;
this.context?.close();
this.context = null;
this.accumulator = new Float32Array(0);
this.micLevel = 0;
}
}