Non-Real Time Analysis and Rendering
This is a “utility” post dealing with Non-Real Time analysis, which I hope can help someone who has struggled with this issues before.
Here’s the setting. You have a nice sketch in Processing which reacts to an external input, say music or mouse position, and want to render it to show the world how fun and joyful the time spent while coding might be. You then realize that using the function saveFrame() creates problems: each single frame takes too long to save to disk, and everything goes horribly out of sync. In this case it is convenient to have a sketch that retrieves the data needed frame by frame, say the frequency spectrum of a piece of audio. One can later load this data, and render it via saveFrame(), knowing that when the frames are reassembled at the prescribed framerate, everything will be in sync.
The following code does exactly that. It uses a class called Saver, which in this case keeps track of the frequency spectrum. Under the more than reasonable assumption that the Fast Fourier Transform done by the Minim library is computed in less than 1/30 of seconds, the data you’ll retrieve for each frame will be in sync with the audio. Then you can go to your sketch which visualizes this data, load the value saved in the .txt file, and use it anywhere you would use the array of frequencies, say. It should be immediate to adapt the piece of code to your need. To save to disk you have to press any key
import ddf.minim.analysis.*;
Minim minim;
AudioPlayer song;
FFT fft;
String songPath = "InsertPathToSongHere";
Saver mySaver;
boolean saved = false;
boolean pressed = false;
void setup() {
size(200, 200);
background(0);
minim = new Minim(this);
song = minim.loadFile(songPath, 1024);
frameRate(30);
song.play();
fft = new FFT(song.bufferSize(), song.sampleRate());
mySaver = new Saver(fft.specSize(), "data/analysis.txt");
}
void draw() {
background(0);
fft.forward(song.mix);
for (int i = 0; i < fft.specSize(); i++) {
float a = fft.getBand(i);
mySaver.setElement(a);
}
mySaver.update();
if (pressed & !saved) {
mySaver.write();
saved = true;
}
}
void keyPressed() {
pressed = true;
}
//Define the Saver class
class Saver {
int buffSize;
String pathTosave;
ArrayList data;
int arraylength;
Saver(int _buffSize, String _pathTosave) {
buffSize = _buffSize;
pathTosave = _pathTosave;
arraylength = 0;
data = new ArrayList();
}
void setElement(float a) {
data.add(a);
}
void update() {
arraylength++;
}
void write() {
String[] dataString = new String[arraylength];
int index;
for (int i = 0; i < dataString.length; i++) {
String temp = "";
for (int j = 0; j < buffSize; j++) {
index = i * buffSize + j;
if ( j == buffSize - 1){
temp += (float) data.get(index);
} else {
temp += (float) data.get(index) + " ";
}
}
dataString[i] = temp;
}
saveStrings(pathTosave, dataString);
println("Data saved!");
}
}
This technique was inspired by something I’ve read somewhere, I will try to retrieve it and point to it here. 😉