57 lines
1.9 KiB
JavaScript
57 lines
1.9 KiB
JavaScript
/* Stateful box-filter resampling: phase and integration survive render quanta. */
|
|
class TranscriptionPCM extends AudioWorkletProcessor {
|
|
constructor() {
|
|
super();
|
|
this.ratio = sampleRate / 16000;
|
|
this.weight = 0;
|
|
this.sum = 0;
|
|
this.samples = [];
|
|
this.stopped = false;
|
|
this.port.onmessage = ({ data }) => {
|
|
if (data.type === 'flush') {
|
|
this.stopped = true;
|
|
if (this.weight > 0) this.samples.push(this.sum / this.weight);
|
|
this.emit();
|
|
this.port.postMessage({ type: 'flushed' });
|
|
}
|
|
};
|
|
}
|
|
emit() {
|
|
if (!this.samples.length) return;
|
|
const buffer = new ArrayBuffer(this.samples.length * 2);
|
|
const view = new DataView(buffer);
|
|
let energy = 0;
|
|
this.samples.forEach((value, index) => {
|
|
const sample = Math.max(-1, Math.min(1, value));
|
|
view.setInt16(index * 2, Math.round(sample * (sample < 0 ? 32768 : 32767)), true);
|
|
energy += sample * sample;
|
|
});
|
|
this.port.postMessage({ type: 'pcm', buffer, level: Math.sqrt(energy / this.samples.length) }, [buffer]);
|
|
this.samples = [];
|
|
}
|
|
process(inputs) {
|
|
if (this.stopped) return false;
|
|
const channels = inputs[0];
|
|
if (!channels?.length) return true;
|
|
for (let i = 0; i < channels[0].length; i++) {
|
|
let value = 0;
|
|
for (const channel of channels) value += channel[i] / channels.length;
|
|
let remaining = 1;
|
|
while (remaining > 1e-9) {
|
|
const take = Math.min(remaining, this.ratio - this.weight);
|
|
this.sum += value * take;
|
|
this.weight += take;
|
|
remaining -= take;
|
|
if (this.weight >= this.ratio - 1e-9) {
|
|
this.samples.push(this.sum / this.ratio);
|
|
this.sum = 0;
|
|
this.weight = 0;
|
|
if (this.samples.length >= 2400) this.emit();
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
registerProcessor('transcription-pcm', TranscriptionPCM);
|