audio-inspect
Version:
Lightweight yet powerful audio analysis library
2 lines (1 loc) • 10.8 kB
JavaScript
"use strict";var E=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var Q=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var $=(e,n)=>{for(var t in n)E(e,t,{get:n[t],enumerable:!0})},Y=(e,n,t,a)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of Q(n))!X.call(e,o)&&o!==t&&E(e,o,{get:()=>n[o],enumerable:!(a=H(n,o))||a.enumerable});return e};var Z=e=>Y(E({},"__esModule",{value:!0}),e);var fe={};$(fe,{getKWeightingCoeffs:()=>me,getLUFS:()=>he,getLUFSRealtime:()=>G});module.exports=Z(fe);var g=class e extends Error{name="AudioInspectError";code;cause;details;constructor(n,t,a,o){super(t,{cause:a}),this.code=n,this.cause=a,this.details=o,Error.captureStackTrace&&Error.captureStackTrace(this,e)}};function J(e){if(![2,4,8].includes(e))throw new g("INVALID_INPUT","Oversampling factor must be 2, 4, or 8. Sinc interpolation quality is not guaranteed for other values.")}function q(e,n={}){if(e.length===0)return 0;let{factor:t=4,interpolation:a="cubic"}=n;J(t);let o=0;if(t<=1||e.length===1){for(let r=0;r<e.length;r++){let l=Math.abs(e[r]);l>o&&(o=l)}return o}if(a==="sinc"){let r=i=>{if(Math.abs(i)<1e-10)return 1;let u=Math.PI*i;return Math.abs(i)<3?3*Math.sin(u/3)*Math.sin(u)/(u*u):0},l=(e.length-1)*t+1;for(let i=0;i<l;i++){let u=i/t,c=0;for(let m=-3;m<=3;m++){let f=Math.floor(u)+m;f>=0&&f<e.length&&(c+=e[f]*r(u-f))}let h=Math.abs(c);h>o&&(o=h)}return o}for(let r=0;r<e.length-1;r++){let l=e[r],i=e[r+1],u=l,c=i;a==="cubic"&&(u=e[Math.max(0,r-1)],c=e[Math.min(e.length-1,r+2)]);for(let h=0;h<t;h++){let m=h/t,f=a==="linear"?l+(i-l)*m:.5*(2*l+(-u+i)*m+(2*u-5*l+4*i-c)*m*m+(-u+3*l-3*i+c)*m*m*m),d=Math.abs(f);d>o&&(o=d)}}let s=Math.abs(e[e.length-1]);return s>o&&(o=s),o}var z=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 ee(e){if(e!==2&&e!==4)throw new g("INVALID_INPUT","BS.1770 true peak supports oversampling factor 2 or 4")}function te(e){let n=0;for(let t=0;t<e.length;t++){let a=Math.abs(e[t]);a>n&&(n=a)}return n}function ne(e,n){let t=z[0];if(!t||t.length===0)return 0;let a=t.length,o=new Float64Array(a),s=0,r=0;for(let l=0;l<e.length;l++){o[s]=e[l];for(let i of n){let u=z[i];if(!u)continue;let c=0;for(let m=0;m<a;m++){let f=s-m;f<0&&(f+=a),c+=o[f]*u[m]}let h=Math.abs(c);h>r&&(r=h)}s=(s+1)%a}return r}function V(e,n={}){if(e.length===0)return 0;let t=n.factor??4;ee(t);let a=te(e),s=ne(e,t===2?[0,2]:[0,1,2,3]);return s>a&&(a=s),a}function _(e,n=1,t=-1/0){return!Number.isFinite(e)||!Number.isFinite(n)||e<=0||n<=0?t:20*Math.log10(e/n)}function w(e,n=1,t=-1/0){return!Number.isFinite(e)||!Number.isFinite(n)||e<=0||n<=0?t:10*Math.log10(e/n)}var W=new Map;function re(e){if(!isFinite(e)||e<=0)throw new g("INVALID_INPUT","Sample rate must be a positive finite value");if(e<8e3||e>384e3)throw new g("UNSUPPORTED_FORMAT",`Sample rate ${e} Hz is not supported`)}function oe(e){re(e);let n=W.get(e);if(n)return n;let t=[];if(e===48e3)t.push({b0:1.53512485958697,b1:-2.69169618940638,b2:1.19839281085285,a0:1,a1:-1.69065929318241,a2:.73248077421585}),t.push({b0:1,b1:-2,b2:1,a0:1,a1:-1.99004745483398,a2:.99007225036621});else{let a=1681.974450955532,o=3.99984385397,s=.7071752369554193,r=Math.tan(Math.PI*a/e),l=Math.pow(10,o/20),i=Math.pow(l,.499666774155),u=1+r/s+r*r;t.push({b0:(l+i*r/s+r*r)/u,b1:2*(r*r-l)/u,b2:(l-i*r/s+r*r)/u,a0:1,a1:2*(r*r-1)/u,a2:(1-r/s+r*r)/u});let c=38.13547087613982,h=.5003270373253953,m=Math.tan(Math.PI*c/e),f=1+m/h+m*m;t.push({b0:1,b1:-2,b2:1,a0:1,a1:2*(m*m-1)/f,a2:(1-m/h+m*m)/f});let y=1/ae(t,997,e).magnitude;t[0]&&(t[0].b0*=y,t[0].b1*=y,t[0].b2*=y)}return W.set(e,t),t}function ae(e,n,t){let a=2*Math.PI*n/t,o=1,s=0;for(let i of e){let u=Math.cos(a),c=Math.sin(a),h=Math.cos(2*a),m=Math.sin(2*a),f=i.b0+i.b1*u+i.b2*h,d=-i.b1*c-i.b2*m,y=i.a0+i.a1*u+i.a2*h,k=-i.a1*c-i.a2*m,F=y*y+k*k;if(F===0)throw new g("INVALID_INPUT","Division by zero occurred while calculating frequency response");let I=(f*y+d*k)/F,M=(d*y-f*k)/F,O=o*I-s*M,R=o*M+s*I;o=O,s=R}let r=Math.sqrt(o*o+s*s),l=Math.atan2(s,o);return{magnitude:r,phase:l}}function v(e){return oe(e)}var ie=-70,se=10,L=400,j=.75,ue=3e3,le=400;function K(e,n,t,a,o){if(o<=0)return;let{x1:s,x2:r,y1:l,y2:i}=a;for(let u=0;u<o;u++){let c=e[u],h=t.b0*c+t.b1*s+t.b2*r-t.a1*l-t.a2*i;n[u]=h,r=s,s=c,i=l,l=h}a.x1=s,a.x2=r,a.y1=l,a.y2=i}function ce(e,n=e.length){let t=0,a=Math.min(n,e.length);if(a===0)return-1/0;for(let o=0;o<a;o++){let s=e[o];if(!s||s.length===0)continue;let r=0;for(let i=0;i<s.length;i++){let u=s[i];r+=u*u}if(s.length===0)continue;t+=1*(r/s.length)}return-.691+w(Math.max(1e-15,t),1)}function D(e){if(e.length===0)return-1/0;let n=e.reduce((t,a)=>t+Math.pow(10,(a+.691)/10),0);return-.691+w(n/e.length,1)}function me(e){return v(e)}function he(e,n={}){let{channelMode:t=e.numberOfChannels>=2?"stereo":"mono",gated:a=!0,calculateShortTerm:o=!1,calculateMomentary:s=!1,collectSeries:r=!1,calculateLoudnessRange:l=!1,calculateTruePeak:i=!1,truePeakMethod:u="bs1770",truePeakOversamplingFactor:c=4,truePeakInterpolation:h="sinc"}=n;if(e.numberOfChannels===0)throw new g("INVALID_INPUT","No processable channels available");let m=G(e.sampleRate,{channelMode:t,gated:a,maxDurationMs:e.duration*1e3+5e3}),f=[];if(t==="mono"){let b=e.channelData[0];b&&f.push(b)}else{let b=e.channelData[0],p=e.channelData[1];b&&f.push(b),p&&f.push(p)}if(f.length===0)throw new g("INVALID_INPUT","No processable channels available");let d=[],y=f[0].length,k=L*(1-j),F=Math.max(1,Math.floor(k/1e3*e.sampleRate));for(let b=0;b<y;b+=F){let p=Math.min(b+F,y),A=f.map(T=>T.subarray(b,p));d.push(A)}let I=r===!0||r==="both"||r==="shortTerm",M=r===!0||r==="both"||r==="momentary",O=l,R=[],N=[],U=[],B=[],S;for(let b=0;b<d.length;b++){let p=d[b];S=m.process(p);let T=Math.min((b+1)*F,y)/e.sampleRate;if((I||M)&&R.push(T),M){let P=Number.isFinite(S.momentary)?S.momentary:-1/0;N.push(P)}if(I){let P=Number.isFinite(S.shortTerm)?S.shortTerm:-1/0;U.push(P)}O&&B.push(S.shortTerm)}let x={integrated:S?.integrated??-1/0};if(o&&(x.shortTerm=S?.shortTerm??-1/0),s&&(x.momentary=S?.momentary??-1/0),l&&B.length>0){let b=B.filter(p=>p>-70&&isFinite(p)).sort((p,A)=>p-A);if(b.length>0){let p=Math.floor(b.length*.1),A=Math.floor(b.length*.95),T=b[p]??-1/0,P=b[A]??-1/0;x.loudnessRange=P-T,x.statistics={percentile10:T,percentile95:P}}}if((I||M)&&(x.series={times:Float32Array.from(R),...I?{shortTerm:Float32Array.from(U)}:{},...M?{momentary:Float32Array.from(N)}:{}}),i){if(u==="bs1770"&&c===8)throw new g("INVALID_INPUT","truePeakOversamplingFactor=8 is unsupported for truePeakMethod='bs1770'; use factor 2 or 4, or switch to interSamplePeak");x.truePeak=f.map(b=>{if(u==="interSamplePeak"){let A=q(b,{factor:c,interpolation:h});return _(A,1)}let p=V(b,{factor:c===2?2:4});return _(p,1)})}return x}var C=class{sampleRate;channelMode;blockSize;hopSize;blockBuffer=[];maxBlocks;currentSamples=[];filterStage1Scratch=[];filterStage2Scratch=[];sampleCount=0;biquadStates=[];totalSamplesProcessed=0;gated;constructor(n,t="stereo",a=3e4,o=!0){if(this.sampleRate=n,this.channelMode=t,this.gated=o,this.blockSize=Math.floor(L/1e3*n),this.blockSize===0)throw new g("INVALID_INPUT",`Sample rate ${n} Hz is too low for realtime LUFS processing. Minimum required sample rate is ${Math.ceil(1e3/L)} Hz.`);this.hopSize=Math.floor(this.blockSize*(1-j)),this.maxBlocks=Math.ceil(a/(this.hopSize/n*1e3));let s=t==="stereo"?2:1;for(let r=0;r<s;r++)this.currentSamples.push(new Float32Array(this.blockSize)),this.filterStage1Scratch.push(new Float32Array(this.blockSize)),this.filterStage2Scratch.push(new Float32Array(this.blockSize)),this.biquadStates.push([{x1:0,x2:0,y1:0,y2:0},{x1:0,x2:0,y1:0,y2:0}])}ensureFilterScratchCapacity(n,t){for(let a=0;a<t;a++){let o=this.filterStage1Scratch[a],s=this.filterStage2Scratch[a];(!o||o.length<n)&&(this.filterStage1Scratch[a]=new Float32Array(n)),(!s||s.length<n)&&(this.filterStage2Scratch[a]=new Float32Array(n))}}process(n){let t=this.channelMode==="stereo"?Math.min(n.length,2):1,a=v(this.sampleRate),o=n[0]?.length||0;if(o===0)return this.calculateCurrentLUFS();this.ensureFilterScratchCapacity(o,t);for(let r=0;r<t;r++){let l=n[r],i=this.filterStage1Scratch[r],u=this.filterStage2Scratch[r];if(!(!i||!u)){if(!l){u.fill(0,0,o);continue}K(l,i,a[0],this.biquadStates[r][0],o),K(i,u,a[1],this.biquadStates[r][1],o)}}let s=0;for(;s<o;){let r=this.blockSize-this.sampleCount,l=Math.min(o-s,r);for(let i=0;i<t;i++){let u=this.filterStage2Scratch[i],c=this.currentSamples[i];u&&c.set(u.subarray(s,s+l),this.sampleCount)}if(this.sampleCount+=l,s+=l,this.sampleCount>=this.blockSize){let i=ce(this.currentSamples,t);if(isFinite(i)){let u=this.totalSamplesProcessed+this.blockSize/2;this.blockBuffer.push([i,u]),this.blockBuffer.length>this.maxBlocks&&this.blockBuffer.shift()}for(let u=0;u<t;u++)this.currentSamples[u].copyWithin(0,this.hopSize);this.sampleCount=this.blockSize-this.hopSize,this.totalSamplesProcessed+=this.hopSize}}return this.calculateCurrentLUFS()}calculateCurrentLUFS(){if(this.blockBuffer.length===0)return{integrated:-1/0,momentary:-1/0,shortTerm:-1/0};let n=this.totalSamplesProcessed+this.sampleCount,t=le/1e3*this.sampleRate,a=ue/1e3*this.sampleRate,o=this.blockBuffer.filter(([c,h])=>n-h<=t).map(([c])=>c),s=this.blockBuffer.filter(([c,h])=>n-h<=a).map(([c])=>c),r=-1/0,l=this.blockBuffer.map(([c])=>c).filter(c=>isFinite(c));if(!this.gated)r=D(l);else if(l.length>0){let c=l.filter(h=>h>=ie);if(c.length>0){let m=D(c)-se,f=c.filter(d=>d>=m);r=D(f)}}let i=-1/0;if(o.length>0){let c=o.reduce((h,m)=>h+Math.pow(10,(m+.691)/10),0);i=-.691+w(c/o.length,1)}let u=-1/0;if(s.length>0){let c=s.reduce((h,m)=>h+Math.pow(10,(m+.691)/10),0);u=-.691+w(c/s.length,1)}return{integrated:r,momentary:i,shortTerm:u}}reset(){this.blockBuffer=[],this.sampleCount=0,this.totalSamplesProcessed=0;for(let n=0;n<this.currentSamples.length;n++){this.currentSamples[n].fill(0);for(let t=0;t<this.biquadStates[n].length;t++)this.biquadStates[n][t]={x1:0,x2:0,y1:0,y2:0}}}getBufferSize(){return this.blockBuffer.length}};function G(e,n={}){let{channelMode:t="stereo",maxDurationMs:a=3e4,gated:o=!0}=n;return new C(e,t,a,o)}