Skip to content

Tutorials: fft analysis with audiolib.js

kindohm edited this page May 2, 2012 · 1 revision

Given the following HTML:

<p><button id="playButton">play</button></p>
<canvas id="canvas" width="600" height="200" style="background: #eee;"></canvas>

The following code generates sound from an oscillator with some compounded frequency modulation. This produces some interesting data to run through the FFT (rather than a plain sine wave). This code then pushes audio samples to an FFT and outputs the FFT spectrum to an HTML5 canvas:

(function () {

	// vars
	var device, 
		osc, 
		lfo1,
		lfo2,
		lfo3,
		lfo4,
		fft,
		fps = 30,
		channelCount = 2,
		width,
		height,
		context,
		playing = false;

	// This is the function that actually fills the
	// audio buffer with samples. In this case it
	// does crazy stuff with LFO's to produce
	// some interesting samples for the FFT.
	function audioCallback (buffer, channels) {

		if (!playing) return;

		var i, n, sample, bufferLength = buffer.length;

		for (i = 0; i < bufferLength; i += channels) {

			lfo1.generate();
			lfo2.generate();
			lfo3.generate();
			lfo4.generate();

			lfo2.fm = lfo1.getMix();
			lfo3.fm = lfo2.getMix();
			lfo4.fm = lfo3.getMix();

			osc.fm = lfo4.getMix();
			osc.generate();

			sample = osc.getMix();

			fft.pushSample(sample);

			for (n = 0; n < channelCount; n++){
				buffer[i + n] = sample;
			}
		}
	}

	window.addEventListener('load', function() {

		var canvas, button;

		// set up audio device and oscillators
		device = audioLib.AudioDevice(audioCallback, channelCount);
		osc = audioLib.Oscillator(device.sampleRate, 600);
		osc.waveShape = 'triangle';
		lfo1 = audioLib.Oscillator(device.sampleRate, 0.1);
		lfo2 = audioLib.Oscillator(device.sampleRate, 10.3);		
		lfo3 = audioLib.Oscillator(device.sampleRate, 0.7);
		lfo4 = audioLib.Oscillator(device.sampleRate, 6.2);		

		// create the FFT
		fft = audioLib.FFT(device.sampleRate, 4096);

		// set up UI and FFT canvas
		canvas = document.getElementById('canvas');
		context = canvas.getContext('2d');
		width = canvas.width;
		height = canvas.height;
		gradient = context.createLinearGradient(0, 0, 0, height);
		gradient.addColorStop(0, "#ff0000");
		gradient.addColorStop(0.6, "#ff0000");
		gradient.addColorStop(1, "#0000ff");	
		context.fillStyle = gradient;
		context.lineWidth = 1;

		button = document.getElementById('playButton');
		button.onclick = function () {
			playing = !playing;
			button.innerHTML = playing ? 'pause' : 'play';
		};

	});

	// This function actually draws the FFT spectrum
	// on the HTML5 canvas.
	function drawFFT () {

		var length, count;
		length = fft.spectrum.length / 8;

		context.clearRect(0, 0, width, height);
		context.beginPath();
		context.moveTo(0, height);

		for (count = 0; count < length; count++) {	
			context.lineTo(count / length * width, 
				fft.spectrum[count] * -height * 2 + height);
		}

		context.moveTo(width,0);
		context.closePath();
		context.fill();
		context.stroke();	
	}

	// Use sink.js (built in to audiolib.js) to
	// call drawFFT at the given frame rate.
	Sink.doInterval(function(){ 
                if (playing) drawFFT();
   	}, 1000/fps);	

})();