User:David.humphrey/Audio Data API 2: Difference between revisions

Line 85: Line 85:
===== Complete Example: Visualizing Audio Spectrum =====
===== Complete Example: Visualizing Audio Spectrum =====


'''NOTE: This example is not updated to the new API.'''
This example calculates and displays FFT spectrum data for the playing audio:
 
This example uses the native FFT data from <code>mozSpectrum</code> to display the frequency spectrum in a canvas:


[[File:fft.png]]
[[File:fft.png]]
Line 94: Line 92:
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<html>
   <head>    
   <head>
     <title>JavaScript Spectrum Example</title>
     <title>JavaScript Spectrum Example</title>
   </head>
   </head>
   <body>
   <body>
     <audio src="song.ogg"  
     <audio src="song.ogg"
           controls="true"
           controls="true"
          onloadedmetadata="loadedMetadata(event);"
           onaudiowritten="audioWritten(event);"
           onaudiowritten="audioWritten(event);"
           style="width: 512px;">
           style="width: 512px;">
     </audio>
     </audio>
   
     <div><canvas id="fft" width="512" height="200"></canvas></div>
     <div><canvas id="fft" width="512" height="200"></canvas></div>
   
 
     <script>
     <script>
      var spectrum;
       var canvas = document.getElementById('fft'),
     
          ctx = canvas.getContext('2d'),
       var canvas = document.getElementById('fft');
          channels,
      var ctx = canvas.getContext('2d');
          rate;
 
      function loadedMetadata(event) {
        channels = event.mozChannels;
        rate = event.mozRate;
      }


       function audioWritten(event) {
       function audioWritten(event) {
         spectrum = event.mozSpectrum;
         var fb = event.mozFrameBuffer,
       
            fft = new FFT(fb.length / channels, rate),
         var specSize = spectrum.length, magnitude;
            signal = new Float32Array(fb.length / channels),
          
            magnitude;
 
         for (var i = 0, fbl = fb.length / 2; i < fbl; i++ ) {
          // Assuming interlaced stereo channels, need to split and merge into a stero-mix mono signal
          signal[i] = (fb[2*i] + fb[2*i+1]) / 2;
         }
 
        fft.forward(signal);
 
         // Clear the canvas before drawing spectrum
         // Clear the canvas before drawing spectrum
         ctx.clearRect(0,0, canvas.width, canvas.height);
         ctx.clearRect(0,0, canvas.width, canvas.height);
       
 
         for ( var i = 0; i < specSize; i++ ) {
         for (var i = 0; i < fft.spectrum.length; i++ ) {
           magnitude = spectrum.item(i) * 4000; // multiply spectrum by a zoom value
           magnitude = fft.spectrum[i] * 4000; // multiply spectrum by a zoom value
         
 
           // Draw rectangle bars for each frequency bin
           // Draw rectangle bars for each frequency bin
           ctx.fillRect(i * 4, canvas.height, 3, -magnitude);
           ctx.fillRect(i * 4, canvas.height, 3, -magnitude);
         }
         }
       }
       }
      // FFT from dsp.js, see below
      var FFT = function(bufferSize, sampleRate) {
        this.bufferSize  = bufferSize;
        this.sampleRate  = sampleRate;
        this.spectrum    = new Float32Array(bufferSize/2);
        this.real        = new Float32Array(bufferSize);
        this.imag        = new Float32Array(bufferSize);
        this.reverseTable = new Uint32Array(bufferSize);
        this.sinTable    = new Float32Array(bufferSize);
        this.cosTable    = new Float32Array(bufferSize);
        var limit = 1,
            bit = bufferSize >> 1;
        while ( limit < bufferSize ) {
          for ( var i = 0; i < limit; i++ ) {
            this.reverseTable[i + limit] = this.reverseTable[i] + bit;
          }
          limit = limit << 1;
          bit = bit >> 1;
        }
        for ( var i = 0; i < bufferSize; i++ ) {
          this.sinTable[i] = Math.sin(-Math.PI/i);
          this.cosTable[i] = Math.cos(-Math.PI/i);
        }
      };
      FFT.prototype.forward = function(buffer) {
        var bufferSize  = this.bufferSize,
            cosTable    = this.cosTable,
            sinTable    = this.sinTable,
            reverseTable = this.reverseTable,
            real        = this.real,
            imag        = this.imag,
            spectrum    = this.spectrum;
        if ( bufferSize % 2 !== 0 ) {
          throw "Invalid buffer size, must be a power of 2.";
        }
        if ( bufferSize !== buffer.length ) {
          throw "Supplied buffer is not the same size as defined FFT. FFT Size: " +
                bufferSize + " Buffer Size: " + buffer.length;
        }
        for ( var i = 0; i < bufferSize; i++ ) {
          real[i] = buffer[reverseTable[i]];
          imag[i] = 0;
        }
        var halfSize = 1,
            phaseShiftStepReal,
            phaseShiftStepImag,
            currentPhaseShiftReal,
            currentPhaseShiftImag,
            off,
            tr,
            ti,
            tmpReal,
            i;
        while ( halfSize < bufferSize ) {
          phaseShiftStepReal = cosTable[halfSize];
          phaseShiftStepImag = sinTable[halfSize];
          currentPhaseShiftReal = 1.0;
          currentPhaseShiftImag = 0.0;
          for ( var fftStep = 0; fftStep < halfSize; fftStep++ ) {
            i = fftStep;
            while ( i < bufferSize ) {
              off = i + halfSize;
              tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
              ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
              real[off] = real[i] - tr;
              imag[off] = imag[i] - ti;
              real[i] += tr;
              imag[i] += ti;
              i += halfSize << 1;
            }
            tmpReal = currentPhaseShiftReal;
            currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
            currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
          }
          halfSize = halfSize << 1;
}
        i = bufferSize/2;
        while(i--) {
          spectrum[i] = 2 * Math.sqrt(real[i] * real[i] + imag[i] * imag[i]) / bufferSize;
}
      };
     </script>
     </script>
   </body>
   </body>
Confirmed users
656

edits