audio-inspect
Version:
Lightweight yet powerful audio analysis library
2 lines (1 loc) • 7.91 kB
JavaScript
var f=class e extends Error{name="AudioInspectError";code;cause;details;constructor(t,s,i,n){super(s,{cause:i}),this.code=t,this.cause=i,this.details=n,Error.captureStackTrace&&Error.captureStackTrace(this,e)}};function L(e){return Array.isArray(e)}function P(e,t="mix"){if(e.numberOfChannels<=0)throw new f("INVALID_INPUT","No channels available");let s=Array.from({length:e.numberOfChannels},(r,c)=>c),i;if(t==="mix")i=s;else{if(t==="all")throw new f("INVALID_INPUT",'Channel selector "all" is not supported for scalar results; use "mix" or a single channel index');if(L(t)){if(t.length===0)throw new f("INVALID_INPUT","Channel selection array cannot be empty");if(t.length>1)throw new f("INVALID_INPUT",'Multi-channel selector arrays are not supported for scalar results; use "mix" or a single channel index');i=t.slice()}else if(typeof t=="number"){if(!Number.isInteger(t))throw new f("INVALID_INPUT",`Invalid channel number: ${t}`);i=[t]}else throw new f("INVALID_INPUT",`Invalid channel selector: ${String(t)}`)}for(let r of i){if(r<0||r>=e.numberOfChannels)throw new f("INVALID_INPUT",`Invalid channel number: ${r}`);if(!e.channelData[r])throw new f("INVALID_INPUT",`Channel ${r} data does not exist`)}if(i.length===1){let r=i[0];return e.channelData[r]}let n=new Float32Array(e.length);for(let r=0;r<e.length;r++){let c=0;for(let o of i){let a=e.channelData[o];a&&r<a.length&&(c+=a[r])}n[r]=c/i.length}return n}function I(e,t=1,s=-1/0){return!Number.isFinite(e)||!Number.isFinite(t)||e<=0||t<=0?s:20*Math.log10(e/t)}var C=new Map;function q(e){if(e<=0||!isFinite(e))throw new f("INVALID_INPUT","Sample rate must be a positive finite value");if(e<8e3||e>384e3)throw new f("UNSUPPORTED_FORMAT",`Sample rate ${e} Hz is not supported`)}function B(e){q(e);let t=C.get(e);if(t)return t;let s=20.598997,i=107.65265,n=737.86223,r=12194.217,c=1.9997,o=[],a=2*e*Math.tan(Math.PI*s/e),p=2*e*Math.tan(Math.PI*i/e),h=2*e*Math.tan(Math.PI*n/e),d=2*e*Math.tan(Math.PI*r/e),u=Math.pow(10,c/20);{let m=a,l=m*m,b=Math.SQRT2,A=4*e*e+2*b*m*e+l,x=2*(l-4*e*e)/A,w=(4*e*e-2*b*m*e+l)/A;o.push({b0:4*e*e*u/A,b1:-8*e*e*u/A,b2:4*e*e*u/A,a0:1,a1:x,a2:w})}{let m=p,l=2*e+m,b=(m-2*e)/l;o.push({b0:2*e/l,b1:-2*e/l,b2:0,a0:1,a1:b,a2:0})}{let m=h,l=2*e+m,b=(m-2*e)/l;o.push({b0:2*e/l,b1:-2*e/l,b2:0,a0:1,a1:b,a2:0})}{let m=d,l=m*m,b=Math.SQRT2,A=4*e*e+2*b*m*e+l,x=2*(l-4*e*e)/A,w=(4*e*e-2*b*m*e+l)/A;o.push({b0:l/A,b1:2*l/A,b2:l/A,a0:1,a1:x,a2:w})}let y=1/V(o,1e3,e).magnitude;return o.forEach(m=>{m.b0*=y,m.b1*=y,m.b2*=y}),C.set(e,o),o}function V(e,t,s){let i=2*Math.PI*t/s,n=1,r=0;for(let a of e){let p=Math.cos(i),h=Math.sin(i),d=Math.cos(2*i),u=Math.sin(2*i),g=a.b0+a.b1*p+a.b2*d,y=-a.b1*h-a.b2*u,m=a.a0+a.a1*p+a.a2*d,l=-a.a1*h-a.a2*u,b=m*m+l*l;if(b===0)throw new f("INVALID_INPUT","Division by zero occurred while calculating frequency response");let A=(g*m+y*l)/b,x=(y*m-g*l)/b,w=n*A-r*x,D=n*x+r*A;n=w,r=D}let c=Math.sqrt(n*n+r*r),o=Math.atan2(r,n);return{magnitude:c,phase:o}}function U(e,t,s={x1:0,x2:0,y1:0,y2:0}){let{x1:i,x2:n,y1:r,y2:c}=s;for(let o=0;o<e.length;o++){let a=e[o],p=t.b0*a+t.b1*i+t.b2*n-t.a1*r-t.a2*c;e[o]=p,n=i,i=a,c=r,r=p}s.x1=i,s.x2=n,s.y1=r,s.y2=c}function v(e,t){let s=B(t),i=e.slice();for(let n of s)U(i,n);return i}function W(e){if(![2,4,8].includes(e))throw new f("INVALID_INPUT","Oversampling factor must be 2, 4, or 8. Sinc interpolation quality is not guaranteed for other values.")}function O(e,t={}){if(e.length===0)return 0;let{factor:s=4,interpolation:i="cubic"}=t;W(s);let n=0;if(s<=1||e.length===1){for(let c=0;c<e.length;c++){let o=Math.abs(e[c]);o>n&&(n=o)}return n}if(i==="sinc"){let c=a=>{if(Math.abs(a)<1e-10)return 1;let p=Math.PI*a;return Math.abs(a)<3?3*Math.sin(p/3)*Math.sin(p)/(p*p):0},o=(e.length-1)*s+1;for(let a=0;a<o;a++){let p=a/s,h=0;for(let u=-3;u<=3;u++){let g=Math.floor(p)+u;g>=0&&g<e.length&&(h+=e[g]*c(p-g))}let d=Math.abs(h);d>n&&(n=d)}return n}for(let c=0;c<e.length-1;c++){let o=e[c],a=e[c+1],p=o,h=a;i==="cubic"&&(p=e[Math.max(0,c-1)],h=e[Math.min(e.length-1,c+2)]);for(let d=0;d<s;d++){let u=d/s,g=i==="linear"?o+(a-o)*u:.5*(2*o+(-p+a)*u+(2*p-5*o+4*a-h)*u*u+(-p+3*o-3*a+h)*u*u*u),y=Math.abs(g);y>n&&(n=y)}}let r=Math.abs(e[e.length-1]);return r>n&&(n=r),n}var Y=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])]);var S=-1/0;function T(e,t={}){if(typeof t!="object"||t===null||Array.isArray(t))throw new f("INVALID_INPUT","RMS options must be an object");let s={channel:t.channel??"mix",asDB:t.asDB??!1,reference:t.reference??1,truePeak:t.truePeak??!1,oversamplingFactor:t.oversamplingFactor??4,interpolation:t.interpolation??"cubic"},i=P(e,s.channel);if(i.length===0)return s.asDB?S:0;let n=0,r=0;for(let o=0;o<i.length;o++){let a=i[o];Number.isFinite(a)&&(n+=a*a,r++)}if(r===0)return s.asDB?S:0;let c=Math.sqrt(n/r);return s.asDB?I(c,s.reference):c}function N(e,t={}){let s={channel:t.channel??"mix",asDB:t.asDB??!1,reference:t.reference??1,truePeak:t.truePeak??!1,oversamplingFactor:t.oversamplingFactor??4,interpolation:t.interpolation??"cubic"},i=P(e,s.channel);if(i.length===0)return s.asDB?S:0;let n;if(s.truePeak)n=O(i,{factor:s.oversamplingFactor,interpolation:s.interpolation});else{n=0;for(let r=0;r<i.length;r++)n=Math.max(n,Math.abs(i[r]))}return s.asDB?I(n,s.reference):n}function R(e,t,s="simple"){if(e.length===0)return{peak:0,rms:0,cfDb:-1/0,cfLinear:0};let n=e;s==="weighted"&&(n=v(e,t));let r=0,c=0;for(let h=0;h<n.length;h++){let d=n[h],u=Math.abs(d);r=Math.max(r,u),c+=d*d}if(n.length===0)return{peak:0,rms:0,cfDb:-1/0,cfLinear:0};let o=Math.sqrt(c/n.length);if(r<1e-10&&o<1e-10)return{peak:0,rms:0,cfDb:-1/0,cfLinear:0};if(o<1e-10)return{peak:r,rms:o,cfDb:-1/0,cfLinear:0};let a=r/o,p=I(a,1);return{peak:r,rms:o,cfDb:p,cfLinear:a}}function xe(e,t={}){let{channel:i="mix",windowSize:n,hopSize:r,method:c="simple"}=t,o,a;if(c==="weighted"){let u=P(e,i),g=v(u,e.sampleRate),y=0,m=0;for(let l=0;l<g.length;l++){let b=g[l],A=Math.abs(b);y=Math.max(y,A),m+=b*b}o=y,a=g.length>0?Math.sqrt(m/g.length):0}else{let u={channel:i,asDB:!1};o=N(e,u),a=T(e,u)}let p,h;o<1e-10&&a<1e-10||a<1e-10?(p=0,h=-1/0):(p=o/a,h=I(p,1));let d;if(typeof n=="number"&&typeof r=="number"){if(n<=0||r<=0)throw new f("INVALID_INPUT","windowSize and hopSize must be positive values");r>n&&console.warn("[audio-inspect] hopSize is larger than windowSize, so gaps will occur between analysis windows");let u=Math.floor(n*e.sampleRate),g=Math.floor(r*e.sampleRate);if(u===0||g===0)throw new f("INVALID_INPUT","Window size is too small for the sample rate");let y=P(e,i),m=y.length;if(m<u){let l=R(y,e.sampleRate,c);d={times:new Float32Array([e.duration/2]),values:new Float32Array([l.cfDb]),valuesLinear:new Float32Array([l.cfLinear]),peaks:new Float32Array([l.peak]),rmsValues:new Float32Array([l.rms])}}else{let l=Math.floor((m-u)/g)+1,b=new Float32Array(l),A=new Float32Array(l),x=new Float32Array(l),w=new Float32Array(l),D=new Float32Array(l);for(let F=0;F<l;F++){let k=F*g,_=Math.min(k+u,m),E=y.subarray(k,_),M=R(E,e.sampleRate,c);b[F]=(k+u/2)/e.sampleRate,A[F]=M.cfDb,x[F]=M.cfLinear,w[F]=M.peak,D[F]=M.rms}d={times:b,values:A,valuesLinear:x,peaks:w,rmsValues:D}}}return{crestFactor:h,crestFactorLinear:p,peak:o,rms:a,timeVarying:d}}export{xe as getCrestFactor};