Add WebAssembly for VAD (#1281)
This commit is contained in:
253
wasm/vad/sherpa-onnx-vad.js
Normal file
253
wasm/vad/sherpa-onnx-vad.js
Normal file
@@ -0,0 +1,253 @@
|
||||
function freeConfig(config, Module) {
|
||||
if ('buffer' in config) {
|
||||
Module._free(config.buffer);
|
||||
}
|
||||
|
||||
if ('sileroVad' in config) {
|
||||
freeConfig(config.sileroVad, Module)
|
||||
}
|
||||
|
||||
|
||||
Module._free(config.ptr);
|
||||
}
|
||||
|
||||
// The user should free the returned pointers
|
||||
function initSherpaOnnxSileroVadModelConfig(config, Module) {
|
||||
const modelLen = Module.lengthBytesUTF8(config.model || '') + 1;
|
||||
|
||||
const n = modelLen;
|
||||
|
||||
const buffer = Module._malloc(n);
|
||||
|
||||
const len = 5 * 4;
|
||||
const ptr = Module._malloc(len);
|
||||
|
||||
Module.stringToUTF8(config.model || '', buffer, modelLen);
|
||||
|
||||
offset = 0;
|
||||
Module.setValue(ptr, buffer, 'i8*');
|
||||
offset += 4;
|
||||
|
||||
Module.setValue(ptr + offset, config.threshold || 0.5, 'float');
|
||||
offset += 4;
|
||||
|
||||
Module.setValue(ptr + offset, config.minSilenceDuration || 0.5, 'float');
|
||||
offset += 4;
|
||||
|
||||
Module.setValue(ptr + offset, config.minSpeechDuration || 0.25, 'float');
|
||||
offset += 4;
|
||||
|
||||
Module.setValue(ptr + offset, config.windowSize || 512, 'i32');
|
||||
offset += 4;
|
||||
|
||||
return {
|
||||
buffer: buffer, ptr: ptr, len: len,
|
||||
}
|
||||
}
|
||||
|
||||
function initSherpaOnnxVadModelConfig(config, Module) {
|
||||
if (!('sileroVad' in config)) {
|
||||
config.sileroVad = {
|
||||
model: '',
|
||||
threshold: 0.50,
|
||||
minSilenceDuration: 0.50,
|
||||
minSpeechDuration: 0.25,
|
||||
windowSize: 512,
|
||||
};
|
||||
}
|
||||
|
||||
const sileroVad =
|
||||
initSherpaOnnxSileroVadModelConfig(config.sileroVad, Module);
|
||||
|
||||
const len = sileroVad.len + 4 * 4;
|
||||
const ptr = Module._malloc(len);
|
||||
|
||||
const providerLen = Module.lengthBytesUTF8(config.provider || 'cpu') + 1;
|
||||
const buffer = Module._malloc(providerLen);
|
||||
Module.stringToUTF8(config.provider || 'cpu', buffer, providerLen);
|
||||
|
||||
let offset = 0;
|
||||
Module._CopyHeap(sileroVad.ptr, sileroVad.len, ptr + offset);
|
||||
offset += sileroVad.len;
|
||||
|
||||
Module.setValue(ptr + offset, config.sampleRate || 16000, 'i32');
|
||||
offset += 4;
|
||||
|
||||
Module.setValue(ptr + offset, config.numThreads || 1, 'i32');
|
||||
offset += 4;
|
||||
|
||||
Module.setValue(ptr + offset, buffer, 'i8*'); // provider
|
||||
offset += 4;
|
||||
|
||||
Module.setValue(ptr + offset, config.debug || 0, 'i32');
|
||||
offset += 4;
|
||||
|
||||
return {
|
||||
buffer: buffer, ptr: ptr, len: len, sileroVad: sileroVad,
|
||||
}
|
||||
}
|
||||
|
||||
function createVad(Module, myConfig) {
|
||||
const sileroVad = {
|
||||
model: './silero_vad.onnx',
|
||||
threshold: 0.50,
|
||||
minSilenceDuration: 0.50,
|
||||
minSpeechDuration: 0.25,
|
||||
windowSize: 512,
|
||||
};
|
||||
|
||||
let config = {
|
||||
sileroVad: sileroVad,
|
||||
sampleRate: 16000,
|
||||
numThreads: 1,
|
||||
provider: 'cpu',
|
||||
debug: 1,
|
||||
bufferSizeInSeconds: 30,
|
||||
};
|
||||
|
||||
if (myConfig) {
|
||||
config = myConfig;
|
||||
}
|
||||
|
||||
return new Vad(config, Module);
|
||||
}
|
||||
|
||||
|
||||
class CircularBuffer {
|
||||
constructor(capacity, Module) {
|
||||
this.handle = Module._SherpaOnnxCreateCircularBuffer(capacity);
|
||||
this.Module = Module;
|
||||
}
|
||||
|
||||
free() {
|
||||
this.Module._SherpaOnnxDestroyCircularBuffer(this.handle);
|
||||
this.handle = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @param samples {Float32Array}
|
||||
*/
|
||||
push(samples) {
|
||||
const pointer =
|
||||
this.Module._malloc(samples.length * samples.BYTES_PER_ELEMENT);
|
||||
this.Module.HEAPF32.set(samples, pointer / samples.BYTES_PER_ELEMENT);
|
||||
this.Module._SherpaOnnxCircularBufferPush(
|
||||
this.handle, pointer, samples.length);
|
||||
this.Module._free(pointer);
|
||||
}
|
||||
|
||||
get(startIndex, n) {
|
||||
const p =
|
||||
this.Module._SherpaOnnxCircularBufferGet(this.handle, startIndex, n);
|
||||
|
||||
const samplesPtr = p / 4;
|
||||
const samples = new Float32Array(n);
|
||||
for (let i = 0; i < n; i++) {
|
||||
samples[i] = this.Module.HEAPF32[samplesPtr + i];
|
||||
}
|
||||
|
||||
this.Module._SherpaOnnxCircularBufferFree(p);
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
pop(n) {
|
||||
this.Module._SherpaOnnxCircularBufferPop(this.handle, n);
|
||||
}
|
||||
|
||||
size() {
|
||||
return this.Module._SherpaOnnxCircularBufferSize(this.handle);
|
||||
}
|
||||
|
||||
head() {
|
||||
return this.Module._SherpaOnnxCircularBufferHead(this.handle);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.Module._SherpaOnnxCircularBufferReset(this.handle);
|
||||
}
|
||||
}
|
||||
|
||||
class Vad {
|
||||
constructor(configObj, Module) {
|
||||
this.config = configObj;
|
||||
const config = initSherpaOnnxVadModelConfig(configObj, Module);
|
||||
Module._MyPrint(config.ptr);
|
||||
const handle = Module._SherpaOnnxCreateVoiceActivityDetector(
|
||||
config.ptr, configObj.bufferSizeInSeconds || 30);
|
||||
freeConfig(config, Module);
|
||||
|
||||
this.handle = handle;
|
||||
this.Module = Module;
|
||||
}
|
||||
|
||||
free() {
|
||||
this.Module._SherpaOnnxDestroyVoiceActivityDetector(this.handle);
|
||||
this.handle = 0
|
||||
}
|
||||
|
||||
// samples is a float32 array
|
||||
acceptWaveform(samples) {
|
||||
const pointer =
|
||||
this.Module._malloc(samples.length * samples.BYTES_PER_ELEMENT);
|
||||
this.Module.HEAPF32.set(samples, pointer / samples.BYTES_PER_ELEMENT);
|
||||
this.Module._SherpaOnnxVoiceActivityDetectorAcceptWaveform(
|
||||
this.handle, pointer, samples.length);
|
||||
this.Module._free(pointer);
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return this.Module._SherpaOnnxVoiceActivityDetectorEmpty(this.handle) == 1;
|
||||
}
|
||||
|
||||
isDetected() {
|
||||
return this.Module._SherpaOnnxVoiceActivityDetectorDetected(this.handle) ==
|
||||
1;
|
||||
}
|
||||
|
||||
pop() {
|
||||
this.Module._SherpaOnnxVoiceActivityDetectorPop(this.handle);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.Module._SherpaOnnxVoiceActivityDetectorClear(this.handle);
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
samples: a 1-d float32 array,
|
||||
start: an int32
|
||||
}
|
||||
*/
|
||||
front() {
|
||||
const h = this.Module._SherpaOnnxVoiceActivityDetectorFront(this.handle);
|
||||
|
||||
const start = this.Module.HEAP32[h / 4];
|
||||
const samplesPtr = this.Module.HEAP32[h / 4 + 1] / 4;
|
||||
const numSamples = this.Module.HEAP32[h / 4 + 2];
|
||||
|
||||
const samples = new Float32Array(numSamples);
|
||||
for (let i = 0; i < numSamples; i++) {
|
||||
samples[i] = this.Module.HEAPF32[samplesPtr + i];
|
||||
}
|
||||
|
||||
this.Module._SherpaOnnxDestroySpeechSegment(h);
|
||||
return {samples: samples, start: start};
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.Module._SherpaOnnxVoiceActivityDetectorReset(this.handle);
|
||||
}
|
||||
|
||||
flush() {
|
||||
this.Module._SherpaOnnxVoiceActivityDetectorFlush(this.handle);
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof process == 'object' && typeof process.versions == 'object' &&
|
||||
typeof process.versions.node == 'string') {
|
||||
module.exports = {
|
||||
createVad,
|
||||
CircularBuffer,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user