UNPKG

audio-inspect

Version:

Lightweight yet powerful audio analysis library

2 lines (1 loc) 5.54 kB
var c=class e extends Error{name="AudioInspectError";code;cause;details;constructor(s,l,n,i){super(l,{cause:n}),this.code=s,this.cause=n,this.details=i,Error.captureStackTrace&&Error.captureStackTrace(this,e)}};function U(e){return Array.isArray(e)}function M(e,s="mix"){if(e.numberOfChannels<=0)throw new c("INVALID_INPUT","No channels available");let l=Array.from({length:e.numberOfChannels},(r,o)=>o),n;if(s==="mix")n=l;else{if(s==="all")throw new c("INVALID_INPUT",'Channel selector "all" is not supported for scalar results; use "mix" or a single channel index');if(U(s)){if(s.length===0)throw new c("INVALID_INPUT","Channel selection array cannot be empty");if(s.length>1)throw new c("INVALID_INPUT",'Multi-channel selector arrays are not supported for scalar results; use "mix" or a single channel index');n=s.slice()}else if(typeof s=="number"){if(!Number.isInteger(s))throw new c("INVALID_INPUT",`Invalid channel number: ${s}`);n=[s]}else throw new c("INVALID_INPUT",`Invalid channel selector: ${String(s)}`)}for(let r of n){if(r<0||r>=e.numberOfChannels)throw new c("INVALID_INPUT",`Invalid channel number: ${r}`);if(!e.channelData[r])throw new c("INVALID_INPUT",`Channel ${r} data does not exist`)}if(n.length===1){let r=n[0];return e.channelData[r]}let i=new Float32Array(e.length);for(let r=0;r<e.length;r++){let o=0;for(let t of n){let a=e.channelData[t];a&&r<a.length&&(o+=a[r])}i[r]=o/n.length}return i}function C(e,s=1,l=-1/0){return!Number.isFinite(e)||!Number.isFinite(s)||e<=0||s<=0?l:10*Math.log10(e/s)}function w(e,s){let{samples:l,frameSize:n,hopSize:i,sampleRate:r,padEnd:o=!0}=e;if(!Number.isInteger(n)||n<=0)throw new c("INVALID_INPUT","frameSize must be a positive integer");if(!Number.isInteger(i)||i<=0)throw new c("INVALID_INPUT","hopSize must be a positive integer");if(!Number.isFinite(r)||r<=0)throw new c("INVALID_INPUT","sampleRate must be positive");if(l.length===0)return 0;let t=new Float32Array(n),a=0,u=0;for(;u<l.length||a===0&&o;){let p=Math.min(u+n,l.length),h=Math.max(0,p-u);if(h<n&&!o&&a>0||(t.fill(0),h>0&&t.set(l.subarray(u,p),0),s({frame:t,frameIndex:a,startSample:u,timeSec:(u+n/2)/r}),a+=1,u+=i,!o&&u+n>l.length)||h<n&&o)break}return a}function B(e,s=.97){let l=new Float32Array(e.length);if(e.length===0)return l;l[0]=e[0];for(let n=1;n<e.length;n++)l[n]=e[n]-s*e[n-1];return l}function q(e,s,l,n,i=!1){let r=[],o=[];return e.length===0?{energies:new Float32Array(0),times:new Float32Array(0)}:(w({samples:e,frameSize:s,hopSize:l,sampleRate:n,padEnd:!0},({frame:t,timeSec:a})=>{let u=0;for(let p=0;p<t.length;p++){let h=t[p];u+=h*h}u/=t.length,r.push(i?C(u,1,-100):u),o.push(a)}),{energies:new Float32Array(r),times:new Float32Array(o)})}function W(e,s,l,n=!0){let i=[];return e.length===0?new Float32Array(0):(w({samples:e,frameSize:s,hopSize:l,sampleRate:1,padEnd:!0},({frame:r})=>{let o=0,t=Math.sign(r[0]??0);for(let a=1;a<r.length;a++){let u=Math.sign(r[a]??0);t!==u&&t!==0&&u!==0&&(o+=1),t=u}i.push(n?o/Math.max(1,r.length-1):o)}),new Float32Array(i))}function $(e,s,l,n=10){let i=new Float32Array(e.length),r=0,o=Math.min(n,e.length);for(let t=0;t<o;t++)r+=e[t];r=o>0?r/o:0;for(let t=0;t<e.length;t++){let a=e[t];if(t===0)i[t]=r*l;else{let u=i[t-1];u!==void 0&&a<u&&(r=s*r+(1-s)*a),i[t]=r*l}}return i}function G(e,s=5){let l=new Float32Array(e.length),n=Math.floor(s/2);for(let i=0;i<e.length;i++){let r=Math.max(0,i-n),o=Math.min(e.length,i+n+1),t=[];for(let a=r;a<o;a++)t.push(e[a]);if(t.sort((a,u)=>a-u),t.length>0){let a=Math.floor(t.length/2);l[i]=t[a]}else l[i]=0}return l}function K(e,s,l=.5,n=.1,i=.3){let r=[],o=null;for(let t=0;t<e.length;t++){let a=e[t],u=s[t],p=a>=l;if(!o)o={start:u,end:u,type:p?"speech":"silence",confidence:Math.abs(a-.5)*2};else if(p&&o.type==="speech"||!p&&o.type==="silence"){o.end=u;let h=Math.abs(a-.5)*2;o.confidence=Math.max(o.confidence||0,h)}else r.push(o),o={start:u,end:u,type:p?"speech":"silence",confidence:Math.abs(a-.5)*2}}return o&&r.push(o),Q(r,n,i)}function Q(e,s,l){if(e.length===0)return[];let n=[],i=0;for(;i<e.length;){let r=e[i];if(!r){i++;continue}let o=r.end-r.start;if(r.type==="speech"&&o>=s||r.type==="silence"&&o>=l)n.push(r),i++;else{if(n.length>0&&i+1<e.length){let t=n[n.length-1],a=e[i+1];if(t&&a&&t.type===a.type){t.end=a.end,i+=2;continue}}if(n.length>0){let t=n[n.length-1];t&&(t.end=r.end)}i++}}return n}function te(e,s={}){let{channel:l="mix",frameSizeMs:n=30,hopSizeMs:i=10,method:r="combined",energyThreshold:o=.02,zcrThresholdLow:t=.05,zcrThresholdHigh:a=.15,adaptiveAlpha:u=.99,noiseFactor:p=3,minSilenceDurationMs:h=300,minSpeechDurationMs:z=100,preEmphasis:O=!0,smoothing:P=!0}=s,d=M(e,l);O&&(d=B(d));let S=e.sampleRate,I=Math.floor(n/1e3*S),x=Math.floor(i/1e3*S);if(I===0||x===0)return{segments:[],speechRatio:0};let{energies:b,times:R}=q(d,I,x,S,!1),y=W(d,I,x,!0);if(b.length===0)return{segments:[],speechRatio:0};let A=new Float32Array(b.length);switch(r){case"energy":{for(let m=0;m<b.length;m++){let f=b[m];A[m]=f>o?1:0}break}case"zcr":{for(let m=0;m<y.length;m++){let f=y[m];A[m]=f>t&&f<a?1:0}break}case"combined":{for(let m=0;m<b.length;m++){let f=b[m],g=y[m],F=f>o?1:0,D=g>t&&g<a?1:0;A[m]=(F+D)/2}break}case"adaptive":{let m=$(b,u,p);for(let f=0;f<b.length;f++){let g=b[f],F=y[f],D=m[f],k=g>D?1:0,v=F>t&&F<a?.5:0;A[f]=Math.min(1,k+v)}break}}let E=P?G(A,5):A,V=z/1e3,_=h/1e3,T=K(E,R,.5,V,_),N=0;for(let m of T)m.type==="speech"&&(N+=m.end-m.start);let L=e.duration>0?Math.min(1,N/e.duration):0;return{segments:T,speechRatio:L,features:{energies:b,zcrs:y,decisions:E,times:R}}}export{te as getVAD};