Search code examples
javascripttypescriptweb-audio-api

How to process a wav file with an AnalyzerNode in Typescript?


I'm trying to calculate the Fast Fourier transform of a wav file in Typescript.

I was thinking about using a dedicated npm package for this, like fft.js, but then I noticed that the standard AnalyzerNode has a built-in FFT feature. However, I've only seen AnalyzerNode used to process real-time audio streams.

How would you send it data loaded from a generic wav file, either loaded from the filesystem or from any other source (e.g. wav-decoder), and not the microphone?


Solution

  • First, note that AnalyserNode only gives you the magnitude of the FFT; the phase part is not included. However, if that works for you, then you can use an OfflineAudioContext and suspend(time) to get the FFT (magnitude). Something like:

    // Let f be the file that has the wav file.
    // c is used only so we can run decodeAudioData.  There are other ways to 
    // do this.
    let c = new AudioContext();
    let b = await fetch(f)
      .then(response => response.arrayBuffer())
      .then(buffer => c.decodeAudioData(buffer));
    
    let oac = new OfflineAudioContext({
      numberOfChannels: b.numberOfChannels,
      length: b.length,
      sampleRate: b.sampleRate});
    
    let a = new AnalyserNode(oac, {fftSize: 1024});
    
    // Suspend the context every fftSize frames so we can get the FFT of 
    // the previous fftSize frames.
    for (let k = a.fftSize; k < b.length; k += a.fftSize) {
      oac.suspend(k / b.sampleRate)
        .then(() => {
          a.getFloatFrequencyData(fftData);
          // Copy fftData out or do something with it
          });
        .then(() => oac.resume());
    }
    
    // We can ignore the buffer that startRendering would return from the
    // resolved promise.
    await oac.startRendering();