| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| const TARGET_SR = 16_000; |
| const CHUNK_SAMPLES = 320; |
|
|
| class AudioCaptureProcessor extends AudioWorkletProcessor { |
| constructor () { |
| super(); |
| this._buf = []; |
| this._active = false; |
|
|
| this.port.onmessage = ({ data }) => { |
| if (data.type === 'start') this._active = true; |
| if (data.type === 'stop') this._active = false; |
| }; |
| } |
|
|
| process (inputs) { |
| if (!this._active) return true; |
|
|
| const channel = inputs[0]?.[0]; |
| if (!channel || channel.length === 0) return true; |
|
|
| |
| const ratio = sampleRate / TARGET_SR; |
| const outCount = Math.floor(channel.length / ratio); |
|
|
| for (let i = 0; i < outCount; i++) { |
| const srcStart = i * ratio; |
| const srcEnd = srcStart + ratio; |
| let sum = 0; |
| let n = 0; |
| for (let j = Math.floor(srcStart); j < Math.min(Math.ceil(srcEnd), channel.length); j++) { |
| sum += channel[j]; |
| n++; |
| } |
| this._buf.push(n > 0 ? sum / n : 0); |
| } |
|
|
| |
| while (this._buf.length >= CHUNK_SAMPLES) { |
| const slice = this._buf.splice(0, CHUNK_SAMPLES); |
| const int16 = new Int16Array(CHUNK_SAMPLES); |
| for (let i = 0; i < CHUNK_SAMPLES; i++) { |
| int16[i] = Math.max(-32_768, Math.min(32_767, Math.round(slice[i] * 32_767))); |
| } |
| this.port.postMessage({ type: 'audio', pcm: int16.buffer }, [int16.buffer]); |
| } |
|
|
| return true; |
| } |
| } |
|
|
| registerProcessor('audio-capture-processor', AudioCaptureProcessor); |
|
|