The Fast Fourrier Transform analysis.
var audio = new maximJs.maxiAudio();
audio.init();
var fft = new maximJs.maxiFFT();
var mfcc = new maximJs.maxiMFCC();
var oct = new maximJs.maxiFFTOctaveAnalyzer();
var nAverages = 12;
// for storing fft values
/*
required as passing a vector from one class
to another isn't currently working
*/
var magnitudes = new Module.VectorFloat();
var magnitudesDB = new Module.VectorFloat();
// will store mfcc output
var mfccs = new Module.VectorDouble();
var osc = new maximJs.maxiOsc();
// if you want to play a sample...
// var samplePlayer = new maximJs.maxiSample();
// audio.loadSample("./audio/beat2.wav", samplePlayer);
var fftSize = 1024;
var sampleRate = 44100;
var magMult = 6;
var fftDraw = {
barsBottom: 600,
barsLeft: 50,
barsSize:1,
magMult: 6
};
var mfccDraw = {
barsBottom: 350,
barsLeft: 350,
barsSize:10,
magMult: 24
};
var octDraw = {
barsBottom: 150,
barsLeft: 350,
barsSize:10,
magMult: 0.5
};
var amtAvgs = 15;
var pitchHist = [];
window.onload = setup;
function setup(){
fft.setup(fftSize, 512, 256);
mfcc.setup(512, 42, 13, 20, 20000, sampleRate);
oct.setup(sampleRate, fftSize/2, nAverages);
for(var i = 0; i < 13; ++i){
mfccs.push_back(0);
}
for(var i = 0; i < fftSize/2; ++i){
magnitudes.push_back(0);
}
// This gets the window in the browser
var canvas=document.getElementById("myCanvas");
// This creates a 2d drawing context in it.
context=canvas.getContext("2d");
//finally, we return setInterval, which wants a function to call,
//and a period in milliseconds to wait between calling it.
return setInterval(draw, 40);
}
audio.play = function(){
// create wave for fft
var wave = osc.square(220);
// or use a sample
// var wave = samplePlayer.isReady() ? samplePlayer.play() : 0.0;
// process wave
if(fft.process(wave)){
fft.magsToDB();
// // sorry, this is not nice
Module.vectorTools.clearVectorFloat(magnitudes);
Module.vectorTools.clearVectorFloat(magnitudesDB);
for(var i = 0; i < fftSize/2; ++i){
magnitudes.push_back(fft.getMagnitude(i));
magnitudesDB.push_back(fft.getMagnitudeDB(i));
}
// // pass magnitudes to mfcc and store in mfccs vector
mfcc.mfcc(magnitudes, mfccs);
oct.calculate(magnitudesDB);
}
this.output = wave;
}
//The stuff inside the script tag is very similar to processing code.
function draw() {
//This is basically the same as any other 2D processing draw routine.
//clear the screen
context.clearRect(0,0,700,700);
// fft
context.fillStyle="#FF0000";
context.font = "30px Arial";
context.fillText("FFT",70,200);
for(var i=0; i < fftSize / 2; i++) {
context.beginPath();
context.rect(
fftDraw.barsLeft + i,
fftDraw.barsBottom,
fftDraw.barsSize,
-(fft.getMagnitude(i) * fftDraw.magMult));
context.fill();
context.closePath();
}
// mfcc
context.fillStyle="rgba(0, 220, 0, 0.5)"
context.font = "30px Arial";
context.fillText("MFCC",350,250);
for(var i=0; i < 13; i++) {
context.beginPath();
context.rect(
mfccDraw.barsLeft + i*mfccDraw.barsSize,
mfccDraw.barsBottom,
mfccDraw.barsSize,
(mfccs.get(i) * mfccDraw.magMult));
context.fill();
context.closePath();
}
// octave analyser
var j = 0;
for(var i=0; i < amtAvgs; ++i) {
pitchHist[i] = 0;
}
for (var i = 0; i < oct.nAverages; ++i) {
pitchHist[j] += oct.getAverage(i);
j++;
j = j % amtAvgs;
}
context.fillStyle="rgba(0, 0, 255, 0.5)"
context.font = "30px Arial";
context.fillText("OCT",350,50);
for(var i=0; i < amtAvgs; i++) {
context.beginPath();
context.rect(
octDraw.barsLeft + i*(octDraw.barsSize*1.5),
octDraw.barsBottom,
octDraw.barsSize,
-(pitchHist[i] * octDraw.magMult));
context.fill();
context.closePath();
}
}