web-voice-detection
Version:
A WebAssembly-powered Voice Activity Detection library for the browser.
86 lines (72 loc) • 2.19 kB
text/typescript
// TypeScript code
import { log } from "./logging";
type ResamplerOptions = {
nativeSampleRate: number;
targetSampleRate: number;
targetFrameSize: number;
};
export class Resampler {
inputBuffer: Array<number>;
constructor(public options: ResamplerOptions) {
if (options.nativeSampleRate < 16000) {
log.error(
"nativeSampleRate is too low. Should have 16000 = targetSampleRate <= nativeSampleRate",
);
}
this.inputBuffer = [];
}
process = (audioFrame: Float32Array): Float32Array[] => {
const outputFrames: Array<Float32Array> = [];
this.fillInputBuffer(audioFrame);
while (this.hasEnoughDataForFrame()) {
const outputFrame = this.generateOutputFrame();
outputFrames.push(outputFrame);
}
return outputFrames;
};
*stream(this: Resampler, audioFrame: Float32Array): Generator<Float32Array> {
this.fillInputBuffer(audioFrame);
while (this.hasEnoughDataForFrame()) {
const outputFrame = this.generateOutputFrame();
yield outputFrame;
}
}
private fillInputBuffer(audioFrame: Float32Array) {
for (const sample of audioFrame) {
this.inputBuffer.push(sample);
}
}
private hasEnoughDataForFrame(): boolean {
return (
(this.inputBuffer.length * this.options.targetSampleRate) / this.options.nativeSampleRate >=
this.options.targetFrameSize
);
}
private generateOutputFrame(): Float32Array {
const outputFrame = new Float32Array(this.options.targetFrameSize);
let outputIndex = 0;
let inputIndex = 0;
while (outputIndex < this.options.targetFrameSize) {
let sum = 0;
let num = 0;
while (
inputIndex <
Math.min(
this.inputBuffer.length,
((outputIndex + 1) * this.options.nativeSampleRate) / this.options.targetSampleRate,
)
) {
const value = this.inputBuffer[inputIndex];
if (value !== undefined) {
sum += value;
num++;
}
inputIndex++;
}
outputFrame[outputIndex] = sum / num;
outputIndex++;
}
this.inputBuffer = this.inputBuffer.slice(inputIndex);
return outputFrame;
}
}