class PCM16CaptureProcessor extends AudioWorkletProcessor { constructor(options) { super(); this.targetRate = Number(options.processorOptions?.targetSampleRate || 16000); this.frameSamples = Number(options.processorOptions?.frameSamples || 320); this.ratio = sampleRate / this.targetRate; this.source = []; this.position = 0; this.output = []; } process(inputs) { const channel = inputs[0]?.[0]; if (!channel?.length) return true; for (const sample of channel) this.source.push(sample); while (this.position + this.ratio <= this.source.length) { const start = Math.floor(this.position); const end = Math.max(start + 1, Math.floor(this.position + this.ratio)); let sum = 0; for (let index = start; index < end && index < this.source.length; index += 1) sum += this.source[index]; this.output.push(sum / Math.max(1, end - start)); this.position += this.ratio; if (this.output.length >= this.frameSamples) this.flush(); } const consumed = Math.floor(this.position); if (consumed > 0) { this.source.splice(0, consumed); this.position -= consumed; } return true; } flush() { const frame = this.output.splice(0, this.frameSamples); const pcm = new Int16Array(frame.length); let energy = 0; for (let index = 0; index < frame.length; index += 1) { const sample = Math.max(-1, Math.min(1, frame[index])); energy += sample * sample; pcm[index] = sample < 0 ? Math.round(sample * 32768) : Math.round(sample * 32767); } const volume = Math.min(100, Math.round(Math.sqrt(energy / Math.max(1, frame.length)) * 280)); this.port.postMessage({ type: "pcm", bytes: pcm.buffer, volume }, [pcm.buffer]); } } registerProcessor("pcm16-capture", PCM16CaptureProcessor);