One of the big issues I have with the whole energy buffer idea is that it makes it sound like you have to punch a whole array of data back and forth with arraycopies.
To remove all of array stuff you could simply track a playhead moving over the buffer erasing the oldest averages. No array operations necessary.
So my trouble at the moment is that I have an energy average buffer now - it seems to work a lot better than thresholds (responds nicely to Jackson and his Computer Band), but I haven't done it the GameDev fashion, so I'm still registering beats when it's quiet. (The fact is, I don't understand the equations, neither mine nor ddf's code is executing 44100 times a second, I compromised and compared one frame to an average of 40). Here's my code. I'm gonna take a brain break and then see if I can pull the essentials out of ddf's code.
Code:
//import processing.opengl.*;
import krister.Ess.*;
FFT fft;
AudioInput in;
int bufferSize;
BeatZone2 bd;
float [] bdFill;
void setup(){
size(532, 230);//,OPENGL);
Ess.start(this);
bufferSize = 1024;
in = new AudioInput(bufferSize);
fft = new FFT(bufferSize);
fft.equalizer(true);
// set up our FFT normalization/dampening
float minLimit=.005;
float maxLimit=.05;
float myDamp=.1f;
int numAverages=32;
fft.limits(minLimit,maxLimit);
fft.damp(myDamp);
fft.averages(numAverages);
bdFill = new float[]{
255.0, 255.0, 255.0 };
// set BeatDetect
bd = new BeatZone2(fft, in, 3, in.size, in.sampleRate);
bd.setZone(0, 0, 100);
bd.setZone(1, 100, 400);
bd.setZone(2, 400, 511);
in.start();
}
// Maximum fft 1.0
// Minimum fft 0.0
// After init to Ess FFT demo that is
void draw(){
background(242, 240, 174);
// Beat Detect
noStroke();
fill(131,104,81);
for(int i=0; i<bufferSize/2; i++) {
rect(10+i,10,1,fft.spectrum[i]*200);
}
stroke(0);
for(int i = 0; i < bd.zoneE.length; i++){
bdFill[i] = bd.beat[i] ? 255.0 : bdFill[i] > 0.0 ? bdFill[i] - 5 : 0.0;
fill(color(250, 186, 10, bdFill[i]));
rect(10+bd.zoneStart[i], 10, bd.zoneEnd[i] - bd.zoneStart[i], 200);
line(10+bd.zoneStart[i], bd.zoneE[i]*200, 10 + bd.zoneEnd[i], bd.zoneE[i]*200);
}
}
public void audioInputData(AudioInput theInput) {
fft.getSpectrum(in);
bd.read();
}
public void stop() {
Ess.stop();
super.stop();
}
class BeatZone2{
FFT fft;
AudioInput in;
float [] zoneE;
int [] zoneStart;
int [] zoneEnd;
float [][] zoneBuffer;
boolean [] beat;
float decay = 0.009;
int playhead = 0;
int bufferLength = 40;
BeatZone2(FFT fft, AudioInput in, int numZone, int bins, float rate){
this.fft = fft;
this.in = in;
zoneE = new float[numZone];
zoneStart = new int[numZone];
zoneEnd = new int[numZone];
beat = new boolean[numZone];
zoneBuffer = new float[numZone][];
}
void read(){
for(int i = 0; i < zoneE.length; i++){
beat[i] = false;
zoneE[i] = getZoneE(i);
if(zoneE[i] > getBufferAve(i)){
beat[i] = true;
}
loadBuffer(i, zoneE[i]);
}
playhead = (playhead + 1) % bufferLength;
}
void setZone(int num, int start, int end){
zoneStart[num] = start;
zoneEnd[num] = end;
zoneBuffer[num] = new float[bufferLength];
}
float getZoneE(int num){
float e = 0.0;
for(int i = zoneStart[num]; i < zoneEnd[num]; i++){
e += fft.spectrum[i];
}
return e / (zoneEnd[num] - zoneStart[num]);
}
void loadBuffer(int num, float val){
zoneBuffer[num][playhead] = val;
}
float getBufferAve(int num){
float a = 0.0;
for(int i = 0; i < zoneBuffer[num].length; i++){
a += zoneBuffer[num][i];
}
return a / zoneBuffer[num].length;
}
}