UNPKG

audio-inspect

Version:

Lightweight yet powerful audio analysis library

2 lines (1 loc) 10.7 kB
var d=class e extends Error{name="AudioInspectError";code;cause;details;constructor(n,t,a,i){super(t,{cause:a}),this.code=n,this.cause=a,this.details=i,Error.captureStackTrace&&Error.captureStackTrace(this,e)}};function y(){if(typeof performance<"u"&&performance.now)return performance.now();if(typeof process<"u"&&process.hrtime){let[e,n]=process.hrtime();return e*1e3+n/1e6}return Date.now()}function E(e){return Array.isArray(e)}function I(e,n="mix"){if(e.numberOfChannels<=0)throw new d("INVALID_INPUT","No channels available");let t=Array.from({length:e.numberOfChannels},(r,o)=>o),a;if(n==="mix")a=t;else{if(n==="all")throw new d("INVALID_INPUT",'Channel selector "all" is not supported for scalar results; use "mix" or a single channel index');if(E(n)){if(n.length===0)throw new d("INVALID_INPUT","Channel selection array cannot be empty");if(n.length>1)throw new d("INVALID_INPUT",'Multi-channel selector arrays are not supported for scalar results; use "mix" or a single channel index');a=n.slice()}else if(typeof n=="number"){if(!Number.isInteger(n))throw new d("INVALID_INPUT",`Invalid channel number: ${n}`);a=[n]}else throw new d("INVALID_INPUT",`Invalid channel selector: ${String(n)}`)}for(let r of a){if(r<0||r>=e.numberOfChannels)throw new d("INVALID_INPUT",`Invalid channel number: ${r}`);if(!e.channelData[r])throw new d("INVALID_INPUT",`Channel ${r} data does not exist`)}if(a.length===1){let r=a[0];return e.channelData[r]}let i=new Float32Array(e.length);for(let r=0;r<e.length;r++){let o=0;for(let s of a){let l=e.channelData[s];l&&r<l.length&&(o+=l[r])}i[r]=o/a.length}return i}function M(e,n,t){return n<0||n>=e.length?t:e[n]??t}function k(e,n=1,t=-1/0){return!Number.isFinite(e)||!Number.isFinite(n)||e<=0||n<=0?t:20*Math.log10(e/n)}function _(e){if(![2,4,8].includes(e))throw new d("INVALID_INPUT","Oversampling factor must be 2, 4, or 8. Sinc interpolation quality is not guaranteed for other values.")}function S(e,n={}){if(e.length===0)return 0;let{factor:t=4,interpolation:a="cubic"}=n;_(t);let i=0;if(t<=1||e.length===1){for(let o=0;o<e.length;o++){let s=Math.abs(e[o]);s>i&&(i=s)}return i}if(a==="sinc"){let o=l=>{if(Math.abs(l)<1e-10)return 1;let m=Math.PI*l;return Math.abs(l)<3?3*Math.sin(m/3)*Math.sin(m)/(m*m):0},s=(e.length-1)*t+1;for(let l=0;l<s;l++){let m=l/t,u=0;for(let p=-3;p<=3;p++){let f=Math.floor(m)+p;f>=0&&f<e.length&&(u+=e[f]*o(m-f))}let h=Math.abs(u);h>i&&(i=h)}return i}for(let o=0;o<e.length-1;o++){let s=e[o],l=e[o+1],m=s,u=l;a==="cubic"&&(m=e[Math.max(0,o-1)],u=e[Math.min(e.length-1,o+2)]);for(let h=0;h<t;h++){let p=h/t,f=a==="linear"?s+(l-s)*p:.5*(2*s+(-m+l)*p+(2*m-5*s+4*l-u)*p*p+(-m+3*s-3*l+u)*p*p*p),b=Math.abs(f);b>i&&(i=b)}}let r=Math.abs(e[e.length-1]);return r>i&&(i=r),i}var j=Object.freeze([Object.freeze([.001708984375,.010986328125,-.0196533203125,.033203125,-.0594482421875,.1373291015625,.97216796875,-.102294921875,.047607421875,-.026611328125,.014892578125,-.00830078125]),Object.freeze([-.0291748046875,.029296875,-.0517578125,.089111328125,-.16650390625,.465087890625,.77978515625,-.2003173828125,.1015625,-.0582275390625,.0330810546875,-.0189208984375]),Object.freeze([-.0189208984375,.0330810546875,-.0582275390625,.1015625,-.2003173828125,.77978515625,.465087890625,-.16650390625,.089111328125,-.0517578125,.029296875,-.0291748046875]),Object.freeze([-.00830078125,.014892578125,-.026611328125,.047607421875,-.102294921875,.97216796875,.1373291015625,-.0594482421875,.033203125,-.0196533203125,.010986328125,.001708984375])]);function O(e,n,t=!1){let a=[],i=e.length;if(i<3)return a;for(let r=1;r<i-1;r++){let o=Math.abs(e[r]),s=Math.abs(e[r-1]),l=Math.abs(e[r+1]);if(o>s&&o>l&&o>n){let m={position:r,amplitude:o};t&&(m.prominence=L(e,r,o)),a.push(m)}}return a}function L(e,n,t){let a=t;for(let r=n-1;r>=0;r--){let o=Math.abs(e[r]);if(o>t)break;a=Math.min(a,o)}let i=t;for(let r=n+1;r<e.length;r++){let o=Math.abs(e[r]);if(o>t)break;i=Math.min(i,o)}return t-Math.max(a,i)}function te(e,n={}){let{count:t=100,threshold:a=.1,channel:i="mix",minDistance:r=Math.floor(e.sampleRate/100)}=n;if(t<=0)throw new d("INVALID_INPUT","Peak count must be a positive integer");if(a<0||a>1)throw new d("INVALID_INPUT","Threshold must be in the range [0, 1]");let o=I(e,i);if(o.length===0)return{peaks:[],maxAmplitude:0,averageAmplitude:0};let s=O(o,a);if(s.length===0)return{peaks:[],maxAmplitude:0,averageAmplitude:0};s.sort((p,f)=>f.amplitude-p.amplitude);let l=[],m=[];for(let p of s){if(l.length>=t)break;let f=p.position-r,b=p.position+r;m.some(([x,P])=>!(b<x||f>P))||(l.push({position:p.position,time:p.position/e.sampleRate,amplitude:p.amplitude}),m.push([f,b]))}l.sort((p,f)=>p.position-f.position);let u=s.length>0?s[0]?.amplitude??0:0,h=s.length>0?s.reduce((p,f)=>p+f.amplitude,0)/s.length:0;return{peaks:l,maxAmplitude:u,averageAmplitude:h}}var w=-1/0;function ne(e,n={}){if(typeof n!="object"||n===null||Array.isArray(n))throw new d("INVALID_INPUT","RMS options must be an object");let t={channel:n.channel??"mix",asDB:n.asDB??!1,reference:n.reference??1,truePeak:n.truePeak??!1,oversamplingFactor:n.oversamplingFactor??4,interpolation:n.interpolation??"cubic"},a=I(e,t.channel);if(a.length===0)return t.asDB?w:0;let i=0,r=0;for(let s=0;s<a.length;s++){let l=a[s];Number.isFinite(l)&&(i+=l*l,r++)}if(r===0)return t.asDB?w:0;let o=Math.sqrt(i/r);return t.asDB?k(o,t.reference):o}function re(e,n={}){let t={channel:n.channel??"mix",asDB:n.asDB??!1,reference:n.reference??1,truePeak:n.truePeak??!1,oversamplingFactor:n.oversamplingFactor??4,interpolation:n.interpolation??"cubic"},a=I(e,t.channel);if(a.length===0)return t.asDB?w:0;let i;if(t.truePeak)i=S(a,{factor:t.oversamplingFactor,interpolation:t.interpolation});else{i=0;for(let r=0;r<a.length;r++)i=Math.max(i,Math.abs(a[r]))}return t.asDB?k(i,t.reference):i}function ae(e,n="mix"){let t=I(e,n);if(t.length<2)return 0;let a=0;for(let i=1;i<t.length;i++){let r=t[i-1],o=t[i];(r>=0&&o<0||r<0&&o>=0)&&a++}return a/(t.length-1)}function oe(e,n={}){let{framesPerSecond:t=60,channel:a="mix",method:i="rms"}=n,r=I(e,a),o=Math.ceil(e.duration*t),s=e.length>0?e.length:o>0?1:0,l=Math.min(o,s),m=l>0?Math.max(1,Math.floor(e.length/l)):0,u=[],h=0,p=0;for(let b=0;b<l;b++){let A=b*m,x=Math.min(A+m,r.length);if(x<=A){let R=u.length>0?M(u,u.length-1,{time:0,amplitude:0}).amplitude:0;u.push({time:(A+m/2)/e.sampleRate,amplitude:R});continue}let P=r.subarray(A,x),c;switch(i){case"peak":c=N(P);break;case"average":c=C(P);break;case"rms":default:c=T(P);break}let g=(A+(x-A)/2)/e.sampleRate;u.push({time:g,amplitude:c}),h=Math.max(h,c),p+=c}let f=l>0?p/l:0;return{waveform:u,maxAmplitude:h,averageAmplitude:f,frameCount:l,samplesPerFrame:m}}function T(e){if(e.length===0)return 0;let n=0;for(let t=0;t<e.length;t++){let a=e[t];n+=a*a}return Math.sqrt(n/e.length)}function N(e){let n=0;for(let t=0;t<e.length;t++){let a=Math.abs(e[t]);n=Math.max(n,a)}return n}function C(e){if(e.length===0)return 0;let n=0;for(let t=0;t<e.length;t++)n+=Math.abs(e[t]);return n/e.length}function ie(e,n={}){let t=y(),{framesPerSecond:a=60,channel:i="mix",method:r="rms",onProgress:o}=n;o?.(0,"Starting waveform analysis");let s=I(e,i),l=Math.ceil(e.duration*a),m=e.length>0?e.length:l>0?1:0,u=Math.min(l,m),h=u>0?Math.max(1,Math.floor(e.length/u)):0;o?.(25,"Frame configuration complete");let p=new Float32Array(u),f=new Float32Array(u),b=0,A=0;o?.(50,"Starting amplitude calculation");for(let c=0;c<u;c++){let g=c*h,R=Math.min(g+h,s.length);if(R<=g){let D=c>0?p[c-1]:0;p[c]=D,f[c]=(g+h/2)/e.sampleRate;continue}let v=s.subarray(g,R),F;switch(r){case"peak":F=N(v);break;case"average":F=C(v);break;case"rms":default:F=T(v);break}if(p[c]=F,f[c]=(g+(R-g)/2)/e.sampleRate,b=Math.max(b,F),A+=F,c%Math.max(1,Math.floor(u/20))===0){let D=50+c/u*45;o?.(Math.round(D),`Processing frame ${c+1}/${u}`)}}let x=u>0?A/u:0,P=y()-t;return o?.(100,"Processing complete"),{amplitudes:p,timestamps:f,frameCount:u,samplesPerFrame:h,framesPerSecond:a,maxAmplitude:b,averageAmplitude:x,sampleRate:e.sampleRate,duration:e.duration,processingTime:P}}function se(e,n={}){let t=y(),{count:a=100,threshold:i=.1,channel:r="mix",minDistance:o=Math.floor(e.sampleRate/100),onProgress:s}=n;if(s?.(0,"Starting peaks analysis"),a<=0)throw new d("INVALID_INPUT","Peak count must be a positive integer");if(i<0||i>1)throw new d("INVALID_INPUT","Threshold must be in the range [0, 1]");let l=I(e,r);if(l.length===0)return{positions:new Float32Array(0),amplitudes:new Float32Array(0),times:new Float32Array(0),maxAmplitude:0,averageAmplitude:0,count:0,sampleRate:e.sampleRate,duration:e.duration,processingTime:y()-t};s?.(25,"Detecting peak candidates");let m=O(l,i);if(m.length===0)return{positions:new Float32Array(0),amplitudes:new Float32Array(0),times:new Float32Array(0),maxAmplitude:0,averageAmplitude:0,count:0,sampleRate:e.sampleRate,duration:e.duration,processingTime:y()-t};s?.(50,"Sorting peaks"),m.sort((c,g)=>g.amplitude-c.amplitude),s?.(75,"Selecting peaks");let u=[],h=[];for(let c of m){if(u.length>=a)break;let g=c.position-o,R=c.position+o;h.some(([F,D])=>!(R<F||g>D))||(u.push({position:c.position,time:c.position/e.sampleRate,amplitude:c.amplitude}),h.push([g,R]))}u.sort((c,g)=>c.position-g.position);let p=new Float32Array(u.length),f=new Float32Array(u.length),b=new Float32Array(u.length);for(let c=0;c<u.length;c++){let g=u[c];g&&(p[c]=g.position,f[c]=g.amplitude,b[c]=g.time)}let A=m.length>0?m[0]?.amplitude??0:0,x=m.length>0?m.reduce((c,g)=>c+g.amplitude,0)/m.length:0,P=y()-t;return s?.(100,"Processing complete"),{positions:p,amplitudes:f,times:b,maxAmplitude:A,averageAmplitude:x,count:u.length,sampleRate:e.sampleRate,duration:e.duration,processingTime:P}}function le(e,n={}){let t=y(),{channel:a="mix",asDB:i=!1,reference:r=1,onProgress:o}=n,s=typeof a=="number"?a:-1;o?.(0,"Starting RMS analysis");let l=I(e,a);if(l.length===0)return{value:i?-1/0:0,channel:s,sampleRate:e.sampleRate,duration:e.duration,processingTime:y()-t};o?.(50,"Computing RMS value");let m=0,u=0;for(let f=0;f<l.length;f++){let b=l[f];Number.isFinite(b)&&(m+=b*b,u++)}if(u===0){let f=i?-1/0:0,b=y()-t;return o?.(100,"Processing complete"),i?{value:f,channel:s,sampleRate:e.sampleRate,duration:e.duration,processingTime:b}:{value:f,valueDB:-1/0,channel:s,sampleRate:e.sampleRate,duration:e.duration,processingTime:b}}let h=Math.sqrt(m/u),p=y()-t;return o?.(100,"Processing complete"),i?{value:k(h,r),channel:s,sampleRate:e.sampleRate,duration:e.duration,processingTime:p}:{value:h,valueDB:k(h,r),channel:s,sampleRate:e.sampleRate,duration:e.duration,processingTime:p}}export{re as getPeak,re as getPeakAmplitude,te as getPeaks,se as getPeaksAnalysis,ne as getRMS,le as getRMSAnalysis,oe as getWaveform,ie as getWaveformAnalysis,ae as getZeroCrossing};