Files
wz-phone/crates/wzp-web/static/audio-processor.js
Siavash Sameni 12b6f30f9b feat: room-based calls + AudioWorklet for capture and playback
Rooms:
- URL-based: open /myroom to join a room
- Two clients in same room get bridged through relay
- Input field for room name, also supports URL path and hash
- Each room creates independent relay connections

AudioWorklet (replaces deprecated ScriptProcessorNode):
- capture-processor.js: accumulates mic samples, sends 960-sample frames
- playback-processor.js: pull-based output with 200ms buffer cap
- Falls back to ScriptProcessor if AudioWorklet unavailable
- Eliminates drift: worklet runs on audio thread, not main thread

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 20:16:06 +04:00

40 lines
1.1 KiB
JavaScript

// AudioWorklet processor for capturing microphone audio.
// Accumulates samples and posts 960-sample (20ms @ 48kHz) frames to the main thread.
class CaptureProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.buffer = new Float32Array(0);
}
process(inputs, outputs, parameters) {
const input = inputs[0];
if (!input || !input[0]) return true;
const samples = input[0]; // Float32Array, typically 128 samples
// Accumulate
const newBuf = new Float32Array(this.buffer.length + samples.length);
newBuf.set(this.buffer);
newBuf.set(samples, this.buffer.length);
this.buffer = newBuf;
// Send complete 960-sample frames
while (this.buffer.length >= 960) {
const frame = this.buffer.slice(0, 960);
this.buffer = this.buffer.slice(960);
// Convert to Int16
const pcm = new Int16Array(960);
for (let i = 0; i < 960; i++) {
pcm[i] = Math.max(-32768, Math.min(32767, Math.round(frame[i] * 32767)));
}
this.port.postMessage(pcm.buffer, [pcm.buffer]);
}
return true;
}
}
registerProcessor('capture-processor', CaptureProcessor);