|
function freeConfig(config, Module) { |
|
if ('buffer' in config) { |
|
Module._free(config.buffer); |
|
} |
|
|
|
if ('sileroVad' in config) { |
|
freeConfig(config.sileroVad, Module) |
|
} |
|
|
|
|
|
Module._free(config.ptr); |
|
} |
|
|
|
|
|
function initSherpaOnnxSileroVadModelConfig(config, Module) { |
|
const modelLen = Module.lengthBytesUTF8(config.model || '') + 1; |
|
|
|
const n = modelLen; |
|
|
|
const buffer = Module._malloc(n); |
|
|
|
const len = 6 * 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; |
|
|
|
Module.setValue(ptr + offset, config.maxSpeechDuration || 20, 'float'); |
|
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, |
|
maxSpeechDuration: 20, |
|
}; |
|
} |
|
|
|
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*'); |
|
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, |
|
maxSpeechDuration: 20, |
|
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 |
|
} |
|
|
|
|
|
|
|
|
|
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); |
|
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 |
|
} |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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, |
|
}; |
|
} |
|
|