audio-inspect
Version:
Lightweight yet powerful audio analysis library
2 lines (1 loc) • 5.24 kB
JavaScript
"use strict";var O=Object.create;var h=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var N=Object.getPrototypeOf,B=Object.prototype.hasOwnProperty;var U=(n,e)=>{for(var t in e)h(n,t,{get:e[t],enumerable:!0})},z=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of S(e))!B.call(n,i)&&i!==t&&h(n,i,{get:()=>e[i],enumerable:!(r=D(e,i))||r.enumerable});return n};var C=(n,e,t)=>(t=n!=null?O(N(n)):{},z(e||!n||!n.__esModule?h(t,"default",{value:n,enumerable:!0}):t,n)),M=n=>z(h({},"__esModule",{value:!0}),n);var _={};U(_,{FFTProviderFactory:()=>A,NativeFFTProvider:()=>F});module.exports=M(_);var o=class n extends Error{name="AudioInspectError";code;cause;details;constructor(e,t,r,i){super(t,{cause:r}),this.code=e,this.cause=r,this.details=i,Error.captureStackTrace&&Error.captureStackTrace(this,n)}};function E(n,e){let t=new Float32Array(n/2+1),r=e/n;for(let i=0;i<t.length;i++)t[i]=i*r;return t}var y=class{constructor(e,t,r=!1){this.size=e;this.sampleRate=t;this.enableProfiling=r;this.complexInput=new Float32Array(this.size*2),this.frequencyBins=E(this.size,this.sampleRate),this.initializationPromise=this.initializeWebFFT()}fftInstance=null;initializationPromise=null;complexInput;frequencyBins;get name(){return"WebFFT"}async waitForInitialization(){this.initializationPromise&&await this.initializationPromise}async initializeWebFFT(){try{let t=(await import("webfft")).default;this.fftInstance=new t(this.size),this.enableProfiling&&this.fftInstance?.profile&&await this.fftInstance.profile()}catch(e){throw new o("UNSUPPORTED_FORMAT",`Failed to initialize WebFFT: ${e instanceof Error?e.message:String(e)}`)}}async fft(e){if(this.initializationPromise&&await this.initializationPromise,!this.fftInstance)throw new o("UNSUPPORTED_FORMAT","WebFFT initialization failed");if(e.length!==this.size)throw new o("INVALID_INPUT",`Invalid input size. Expected: ${this.size}, Actual: ${e.length}`);for(let s=0;s<this.size;s++)this.complexInput[s*2]=e[s],this.complexInput[s*2+1]=0;let t=this.fftInstance.fft(this.complexInput),r=new Float32Array(this.size/2+1),i=new Float32Array(this.size/2+1);for(let s=0;s<r.length;s++){let u=t[s*2],a=t[s*2+1];r[s]=Math.sqrt(u*u+a*a),i[s]=Math.atan2(a,u)}return{complex:t,magnitude:r,phase:i,frequencies:this.frequencyBins}}async profile(){if(this.initializationPromise&&await this.initializationPromise,!this.fftInstance||!this.fftInstance.profile)throw new o("UNSUPPORTED_FORMAT","WebFFT is not initialized");await this.fftInstance.profile()}dispose(){if(this.fftInstance)try{this.fftInstance.dispose&&this.fftInstance.dispose()}catch(e){console.warn("FFT instance disposal failed:",e)}finally{this.fftInstance=null,this.initializationPromise=null}}},F=class{constructor(e,t){this.size=e;this.sampleRate=t;if(!this.isPowerOfTwo(e))throw new o("INVALID_INPUT","FFT size must be a power of two");this.precomputeTables()}bitReversalTable;twiddleFactorsReal;twiddleFactorsImag;scratchReal;scratchImag;frequencyBins;get name(){return"Native FFT (Cooley-Tukey)"}isPowerOfTwo(e){return e>0&&(e&e-1)===0}precomputeTables(){this.bitReversalTable=new Uint32Array(this.size);let e=Math.log2(this.size);for(let r=0;r<this.size;r++){let i=0;for(let s=0;s<e;s++)i=i<<1|r>>s&1;this.bitReversalTable[r]=i}let t=this.size/2;this.twiddleFactorsReal=new Float32Array(t),this.twiddleFactorsImag=new Float32Array(t);for(let r=0;r<t;r++){let i=-2*Math.PI*r/this.size;this.twiddleFactorsReal[r]=Math.cos(i),this.twiddleFactorsImag[r]=Math.sin(i)}this.scratchReal=new Float32Array(this.size),this.scratchImag=new Float32Array(this.size),this.frequencyBins=E(this.size,this.sampleRate)}fft(e){if(e.length!==this.size)throw new o("INVALID_INPUT",`Invalid input size. Expected: ${this.size}, Actual: ${e.length}`);let t=this.scratchReal,r=this.scratchImag;for(let a=0;a<this.size;a++){let l=this.bitReversalTable[a];t[l]=e[a],r[l]=0}for(let a=1;a<this.size;a*=2){let l=a*2,c=this.size/l;for(let p=0;p<this.size;p+=l)for(let m=0;m<a;m++){let b=m*c,w=this.twiddleFactorsReal[b],R=this.twiddleFactorsImag[b],d=p+m,f=p+m+a,I=t[d],T=r[d],g=t[f],P=r[f],v=g*w-P*R,x=g*R+P*w;t[d]=I+v,r[d]=T+x,t[f]=I-v,r[f]=T-x}}let i=new Float32Array(this.size*2),s=new Float32Array(this.size/2+1),u=new Float32Array(this.size/2+1);for(let a=0;a<this.size;a++)if(i[a*2]=t[a],i[a*2+1]=r[a],a<=this.size/2){let l=t[a],c=r[a];s[a]=Math.sqrt(l*l+c*c),u[a]=Math.atan2(c,l)}return{complex:i,magnitude:s,phase:u,frequencies:this.frequencyBins}}dispose(){this.scratchReal=new Float32Array(0),this.scratchImag=new Float32Array(0),this.bitReversalTable=new Uint32Array(0),this.twiddleFactorsReal=new Float32Array(0),this.twiddleFactorsImag=new Float32Array(0),this.frequencyBins=new Float32Array(0)}},A=class{static async createProvider(e){switch(e.type){case"webfft":{let t=new y(e.fftSize,e.sampleRate,e.enableProfiling);return await t.waitForInitialization(),t}case"native":return new F(e.fftSize,e.sampleRate);case"custom":if(!e.customProvider)throw new o("INVALID_INPUT","Custom provider is not specified");return e.customProvider;default:{let t=e.type;throw new o("UNSUPPORTED_FORMAT",`Unsupported FFT provider: ${String(t)}`)}}}static getAvailableProviders(){return["webfft","native"]}};