UNPKG

ten-vad-lib

Version:

A JavaScript library for Ten VAD (Voice Activity Detection) based on WebAssembly

3 lines (2 loc) 6.33 kB
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).TenVADLib={})}(this,function(e){"use strict";class t{constructor(){this.module=null,this.loadingPromise=null}static getInstance(){return t.instance||(t.instance=new t),t.instance}async loadModule(e={}){return this.module?this.module:(this.loadingPromise||(this.loadingPromise=this.loadModuleInternal(e)),this.loadingPromise)}async loadModuleInternal(e={}){const t=e.wasmPath||this.getDefaultWasmPath(),o=e.jsPath||this.getDefaultJsPath();try{window.createVADModule||await this.loadScript(o);let e=0;const s=100;for(;!window.createVADModule&&e<s;)await new Promise(e=>setTimeout(e,100)),e++;if(!window.createVADModule)throw new Error("createVADModule not found. Please ensure ten_vad.js is loaded correctly.");const i=await fetch(t);if(!i.ok)throw new Error(`Failed to load WASM file from ${t}`);const n=await i.arrayBuffer(),r=await window.createVADModule({wasmBinary:n,locateFile:e=>e.endsWith(".wasm")?t:e,noInitialRun:!1,noExitRuntime:!0});return this.addHelperFunctions(r),this.module=r,r}catch(e){throw this.loadingPromise=null,e}}getDefaultWasmPath(){if("undefined"!=typeof window){const e=document.currentScript||document.querySelector('script[src*="ten-vad"]');if(e){const t=e.getAttribute("src");if(t){return`${t.substring(0,t.lastIndexOf("/"))}/wasm/ten_vad.wasm`}}}return"/wasm/ten_vad.wasm"}getDefaultJsPath(){if("undefined"!=typeof window){const e=document.currentScript||document.querySelector('script[src*="ten-vad"]');if(e){const t=e.getAttribute("src");if(t){return`${t.substring(0,t.lastIndexOf("/"))}/wasm/ten_vad.js`}}}return"/wasm/ten_vad.js"}async loadScript(e){return new Promise((t,o)=>{const s=document.createElement("script");s.src=e,s.onload=()=>t(),s.onerror=()=>o(new Error(`Failed to load script: ${e}`)),document.head.appendChild(s)})}addHelperFunctions(e){e.getValue||(e.getValue=function(t,o){switch(o){case"i32":return e.HEAP32[t>>2];case"float":return e.HEAPF32[t>>2];default:throw new Error(`Unsupported type: ${o}`)}}),e.UTF8ToString||(e.UTF8ToString=function(t){if(!t)return"";let o="",s=t;for(;e.HEAPU8[s];)o+=String.fromCharCode(e.HEAPU8[s++]);return o})}getModule(){return this.module}reset(){this.module=null,this.loadingPromise=null}}class o{constructor(e,t,o){this.isDestroyed=!1,this.module=e,this.hopSize=t,this.voiceThreshold=o,this.vadHandlePtr=e._malloc(4);const s=e._ten_vad_create(this.vadHandlePtr,t,o);if(0!==s)throw new Error(`VAD creation failed with code: ${s}`);this.vadHandle=e.getValue(this.vadHandlePtr,"i32")}async processFrame(e){if(this.isDestroyed)throw new Error("VAD instance has been destroyed");if(e.length!==this.hopSize)throw new Error(`Expected ${this.hopSize} samples, got ${e.length}`);const t=this.module._malloc(2*this.hopSize),o=this.module._malloc(4),s=this.module._malloc(4);try{this.module.HEAP16.set(e,t/2);const i=this.module._ten_vad_process(this.vadHandle,t,this.hopSize,o,s);if(0!==i)throw new Error(`Frame processing failed with code: ${i}`);const n=this.module.getValue(o,"float"),r=this.module.getValue(s,"i32");return{probability:n,isVoice:1===r}}finally{this.module._free(t),this.module._free(o),this.module._free(s)}}reset(){if(this.isDestroyed)throw new Error("VAD instance has been destroyed");this.destroy(),this.isDestroyed=!1,this.vadHandlePtr=this.module._malloc(4);const e=this.module._ten_vad_create(this.vadHandlePtr,this.hopSize,this.voiceThreshold);if(0!==e)throw new Error(`VAD recreation failed with code: ${e}`);this.vadHandle=this.module.getValue(this.vadHandlePtr,"i32")}destroy(){this.isDestroyed||(this.vadHandlePtr&&(this.module._ten_vad_destroy(this.vadHandlePtr),this.module._free(this.vadHandlePtr),this.vadHandlePtr=0),this.isDestroyed=!0)}getVersion(){try{const e=this.module._ten_vad_get_version();return e?this.module.UTF8ToString(e):"Unknown Version"}catch(e){return console.error("Failed to get VAD version:",e),"Error Getting Version"}}}const s={hopSize:256,voiceThreshold:.5,wasmPath:"/wasm/ten_vad.wasm",jsPath:"/wasm/ten_vad.js",minSpeechDuration:100,maxSilenceDuration:500};class i{constructor(e={}){this.options={...s,...e},this.moduleLoader=t.getInstance()}static async new(e={}){const t=new i(e);return await t.moduleLoader.loadModule({wasmPath:t.options.wasmPath,jsPath:t.options.jsPath}),t}async*run(e,t){const s=await this.moduleLoader.loadModule({wasmPath:this.options.wasmPath,jsPath:this.options.jsPath}),i=new o(s,this.options.hopSize,this.options.voiceThreshold);try{const o=this.preprocessAudio(e,t),s=this.options.hopSize,n=Math.floor(o.length/s);let r=null;for(let e=0;e<n;e++){const n=e*s,a=n+s,l=o.slice(n,a),d=await i.processFrame(l),h=e*s/t*1e3;if(d.isVoice){r||(r={start:h,end:h,audio:new Float32Array(0),probabilities:[]}),r.end=h,r.probabilities.push(d.probability);const e=new Float32Array(l.length);for(let t=0;t<l.length;t++)e[t]=l[t]/32768;const t=new Float32Array(r.audio.length+e.length);t.set(r.audio),t.set(e,r.audio.length),r.audio=t}else if(r){if(r.end-r.start>=this.options.minSpeechDuration){const e=r.probabilities.reduce((e,t)=>e+t,0)/r.probabilities.length;yield{audio:r.audio,start:r.start,end:r.end,probability:e}}r=null}}if(r){if(r.end-r.start>=this.options.minSpeechDuration){const e=r.probabilities.reduce((e,t)=>e+t,0)/r.probabilities.length;yield{audio:r.audio,start:r.start,end:r.end,probability:e}}}}finally{i.destroy()}}async process(e,t){const o=Date.now(),s=[];for await(const o of this.run(e,t))s.push(o);const i=Date.now()-o,n=i/(e.length/t*1e3),r=Math.floor(e.length/this.options.hopSize),a=s.reduce((e,t)=>e+Math.floor((t.end-t.start)/16),0);return{speechSegments:s,statistics:{totalFrames:r,voiceFrames:a,voicePercentage:a/r*100,processingTime:i,realTimeFactor:n}}}preprocessAudio(e,t){const o=new Int16Array(e.length);for(let t=0;t<e.length;t++){const s=Math.max(-1,Math.min(1,e[t]));o[t]=Math.round(32767*s)}return 16e3!==t?this.resample(o,t,16e3):o}resample(e,t,o){if(t===o)return e;const s=t/o,i=Math.round(e.length/s),n=new Int16Array(i);for(let t=0;t<i;t++){const o=Math.floor(t*s);o<e.length&&(n[t]=e[o])}return n}}e.NonRealTimeTenVAD=i,e.TenVAD=i,e.VADInstance=o,e.VADModuleLoader=t,e.defaultTenVADOptions=s}); //# sourceMappingURL=index.min.js.map