This example calculates and displays Fast Fourier Transform (FFT) spectrum data for the playing audio. The function handling the loadedMetaData event stores the metadata of the audio element in global variables; the function for the MozAudioAvailable event does an FFT of the samples and displays them in a canvas.
NOTE: You can use the AudioNode
called AnalyserNode
to perform real-time FFT analysis on an audio stream, rather than the code shown below.
<!DOCTYPE html> <html> <head> <title>JavaScript Spectrum Example</title> </head> <body> <audio id="audio-element" src="song.ogg" controls="true" style="width: 512px;"> </audio> <div><canvas id="fft" width="512" height="200"></canvas></div> <script> var canvas = document.getElementById('fft'), ctx = canvas.getContext('2d'), channels, rate, frameBufferLength, fft; function loadedMetadata() { channels = audio.mozChannels; rate = audio.mozSampleRate; frameBufferLength = audio.mozFrameBufferLength; fft = new FFT(frameBufferLength / channels, rate); } function audioAvailable(event) { var fb = event.frameBuffer, t = event.time, /* unused, but it's there */ signal = new Float32Array(fb.length / channels), magnitude; for (var i = 0, fbl = frameBufferLength / 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 ctx.clearRect(0,0, canvas.width, canvas.height); for (var i = 0; i < fft.spectrum.length; i++ ) { // multiply spectrum by a zoom value magnitude = fft.spectrum[i] * 4000; // Draw rectangle bars for each frequency bin ctx.fillRect(i * 4, canvas.height, 3, -magnitude); } } var audio = document.getElementById('audio-element'); audio.addEventListener('MozAudioAvailable', audioAvailable, false); audio.addEventListener('loadedmetadata', loadedMetadata, false); // 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 !== 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> </body> </html>
Document Tags and Contributors
Last updated by:
Sloppierkitty7,