// Microphone capture worklet: resamples the context rate to 48 kHz mono and // posts 20 ms frames (960 Float32 samples) together with their RMS level. const OUT_RATE = 48000; const FRAME_SAMPLES = 960; class Ts6MicCapture extends AudioWorkletProcessor { constructor() { super(); this.ratio = sampleRate / OUT_RATE; // input samples per output sample this.t = 0; // fractional read position; -1 refers to `last` this.last = 0; this.frame = new Float32Array(FRAME_SAMPLES); this.offset = 0; } push(sample) { this.frame[this.offset++] = sample; if (this.offset < FRAME_SAMPLES) return; let sum = 0; for (let i = 0; i < FRAME_SAMPLES; i++) sum += this.frame[i] * this.frame[i]; const frame = this.frame; this.frame = new Float32Array(FRAME_SAMPLES); this.offset = 0; this.port.postMessage({ samples: frame, rms: Math.sqrt(sum / FRAME_SAMPLES) }, [frame.buffer]); } process(inputs, outputs) { const output = outputs[0] && outputs[0][0]; if (output) output.fill(0); const channels = inputs[0]; if (!channels || channels.length === 0) return true; // Downmix to mono. let input = channels[0]; if (channels.length > 1) { input = new Float32Array(input.length); for (const ch of channels) for (let i = 0; i < input.length; i++) input[i] += ch[i] / channels.length; } if (this.ratio === 1) { for (let i = 0; i < input.length; i++) this.push(input[i]); return true; } // Linear interpolation resampler. const len = input.length; while (this.t < len - 1) { const i0 = Math.floor(this.t); const frac = this.t - i0; const a = i0 < 0 ? this.last : input[i0]; const b = input[i0 + 1]; this.push(a + (b - a) * frac); this.t += this.ratio; } this.t -= len; this.last = input[len - 1]; return true; } } registerProcessor("ts6-mic-capture", Ts6MicCapture);