class VoiceCaptureProcessor extends AudioWorkletProcessor { constructor() { super(); this.pending = []; this.pendingLength = 0; this.flushSamples = Math.max(1024, Math.floor(sampleRate * 0.25)); this.port.onmessage = (event) => { if (event.data && event.data.type === "flush") { this.flush(); } }; } process(inputs) { const input = inputs[0]; const channel = input && input[0]; if (!channel || channel.length === 0) return true; const pcm = new Int16Array(channel.length); for (let i = 0; i < channel.length; i += 1) { const sample = Math.max(-1, Math.min(1, channel[i])); pcm[i] = sample < 0 ? sample * 0x8000 : sample * 0x7fff; } this.pending.push(pcm); this.pendingLength += pcm.length; if (this.pendingLength >= this.flushSamples) { this.flush(); } return true; } flush() { if (this.pendingLength <= 0) return; const merged = new Int16Array(this.pendingLength); let offset = 0; for (const chunk of this.pending) { merged.set(chunk, offset); offset += chunk.length; } this.pending = []; this.pendingLength = 0; this.port.postMessage( { type: "chunk", sampleRate, channels: 1, sampleWidth: 2, pcm: merged.buffer, }, [merged.buffer], ); } } registerProcessor("voice-capture-processor", VoiceCaptureProcessor);