Skip to content

Instantly share code, notes, and snippets.

@duncanbeevers
Created January 7, 2010 19:20
Show Gist options
  • Save duncanbeevers/271479 to your computer and use it in GitHub Desktop.
Save duncanbeevers/271479 to your computer and use it in GitHub Desktop.
var spectrum = [];
var signal = [];
var peak = [];
var bufferSize = 2048;
var calculateDFT = function(buffer, sampleRate) {
var complexValues = [];
for ( var k = 0; k < buffer.length/2; k ++ ) {
var real = 0.0;
var imag = 0.0;
var binFrequency = k * (sampleRate / buffer.length);
for ( var n = 0; n < buffer.length; n++ ) {
var step = n * binFrequency / sampleRate;
real += Math.cos(2*Math.PI * step) * signal[n];
imag += Math.sin(2*Math.PI * step) * signal[n];
}
complexValues[k] = {real: real, imag: imag};
}
return complexValues;
}
var calculateFFT = function(real, imag) {
var bufferSize = real.length;
if ( bufferSize == 1 ) {
return [{real: real[0], imag: imag[0]}];
}
if ( bufferSize % 2 != 0 ) throw "Invalid buffer size, must be a power of 2";
var even_real = [];
var even_imag = [];
var odd_real = [];
var odd_imag = [];
// break up signal into odd and even parts
for ( var k = 0; k < bufferSize/2; k++ ) {
if ( typeof(imag) == 'undefined' ) {
// this is a non complex signal
even_real[k] = real[2*k];
even_imag[k] = 0.0;
odd_real[k] = real[2*k+1];
odd_imag[k] = 0.0;
} else {
even_real[k] = real[2*k];
even_imag[k] = imag[2*k];
odd_real[k] = real[2*k+1];
odd_imag[k] = imag[2*k+1];
}
}
var q = calculateFFT(even_real, even_imag);
var r = calculateFFT(odd_real, odd_imag);
complexValues = [];
for ( var k = 0; k < bufferSize/2; k++ ) {
var kth = -2 * k * Math.PI / bufferSize;
var wk = {real: Math.cos(kth), imag: Math.sin(kth)};
complexValues[k] = {
real: q[k].real + (wk.real*r[k].real - wk.imag*r[k].imag),
imag: q[k].imag + (wk.real*r[k].imag + wk.imag*r[k].real)
};
complexValues[k + bufferSize/2] = {
real: q[k].real - (wk.real*r[k].real - wk.imag*r[k].imag),
imag: q[k].imag - (wk.real*r[k].imag + wk.imag*r[k].real)
};
}
return complexValues;
}
var buildReverseTable = function(bufferSize) {
ReverseTable = new Array(bufferSize);
ReverseTable[0] = 0;
var limit = 1;
var bit = bufferSize >> 1;
while ( limit < bufferSize ) {
for ( var i = 0; i < limit; i++ ) {
ReverseTable[i + limit] = ReverseTable[i] + bit;
}
limit = limit << 1;
bit = bit >> 1;
}
}
var buildTrigTables = function(bufferSize) {
SinLUT = new Array(bufferSize);
CosLUT = new Array(bufferSize);
for ( var i = 0; i < bufferSize; i++ ) {
SinLUT[i] = Math.sin(-Math.PI/i);
CosLUT[i] = Math.cos(-Math.PI/i);
}
}
buildReverseTable(2048);
buildTrigTables(2048);
var calculateFFT2 = function(buffer) {
if ( bufferSize % 2 != 0 ) throw "Invalid buffer size, must be a power of 2";
var complexValues = Array(buffer.length);
for ( var i = 0; i < buffer.length; i++ ) {
complexValues[i] = {real: buffer[ReverseTable[i]], imag: 0.0};
}
var halfSize = 1;
while ( halfSize < buffer.length ) {
var phaseShiftStepReal = CosLUT[halfSize];
var phaseShiftStepImag = SinLUT[halfSize];
var currentPhaseShiftReal = 1.0;
var currentPhaseShiftImag = 0.0;
for ( var fftStep = 0; fftStep < halfSize; fftStep++ ) {
var i = fftStep;
while ( i < buffer.length ) {
var off = i + halfSize;
var tr = (currentPhaseShiftReal * complexValues[off].real) - (currentPhaseShiftImag * complexValues[off].imag);
var ti = (currentPhaseShiftReal * complexValues[off].imag) + (currentPhaseShiftImag * complexValues[off].real);
complexValues[off].real = complexValues[i].real - tr;
complexValues[off].imag = complexValues[i].imag - ti;
complexValues[i].real += tr;
complexValues[i].imag += ti;
i += halfSize << 1;
}
var tmpReal = currentPhaseShiftReal;
currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
}
halfSize = halfSize << 1;
}
return complexValues;
}
function audioWritten(event) {
signal = event.mozFrameBuffer;
spectrum = event.mozSpectrum;
// calculate spectrum from complex values
for ( var i = 0; i < spectrum.length; i++ ) {
if ( typeof(peak[i]) == 'undefined' || peak[i] < spectrum.item(i) ) {
peak[i] = spectrum.item(i);
} else {
peak[i] *= 0.99; // peak slowly falls until a new peak is found
}
}
/*
var d = event.mozAudioData;
//console.log("Frame Length=" + d.length + " [" + d.item(0) + ", " + d.item(1) + ", " + d.item(2) + ", " + d.item(3) + ", ...]");
if ( d.length == 4096 ) {
// Problem: The audioWritten buffer should be 4096 samples, 2048 per channel.
// FFT running in js can't process 2048 samples quick enough and it gets quite laggy
// so we only look at the first 1024 samples and scrap the rest.
for ( var i = 0; i < d.length/2; i++ ) {
// Assuming interlaced stereo channels, need to split and merge into a stero-mix mono signal
signal[i] = (d.item(2*i) + d.item(2*i+1)) / 2;
}
var complexValues = calculateFFT2(signal);
// calculate spectrum from complex values
for ( var i = 0; i < signal.length/2; i++ ) {
spectrum[i] = Math.sqrt(Math.pow(complexValues[i].real, 2) + Math.pow(complexValues[i].imag, 2));
spectrum[i] *= -1 * Math.log((signal.length/2 - i) * (0.5/signal.length/2)); // equalize, attenuates low freqs and boosts highs
//spectrum[i] *= 2;
if ( typeof(peak[i]) == 'undefined' || peak[i] < spectrum[i] ) {
peak[i] = spectrum[i];
} else {
peak[i] *= 0.99; // peak slowly falls until a new peak is found
}
}
}
*/
//console.log("FrameBuffer Length=" + fb.length + " [" + fb.item(0) + ", " + fb.item(1) + ", " + fb.item(2) + ", " + fb.item(3) + ", ...]");
//console.log("Spectrum Length=" + s.length + " [" + s.item(0) + ", " + s.item(1) + ", " + s.item(2) + ", " + s.item(3) + ", ...]");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment