Created
January 7, 2010 19:20
-
-
Save duncanbeevers/271479 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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