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>
40 lines
1.1 KiB
JavaScript
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);
|