I'm writing a program that talks with Wekinator, but I'm having a problem with sending the feature names over OSC that I'm fairly certain is a problem with chuck.
At the bottom of the code (see bottom of email) is the relevant method, sendOscFeatureNames(). When I run wekinator and get to the training screen, I run this code. The instant it sends the feature names to Wekinator (i.e., the instant it is run), an exception gets raised:
"
Exception in thread "Thread-4" java.lang.ArrayIndexOutOfBoundsException: 1536
at com.illposed.osc.utility.OSCByteArrayToJavaConverter.lengthOfCurrentS
tring(Unknown Source)
at com.illposed.osc.utility.OSCByteArrayToJavaConverter.readString(Unkno
wn Source)
at com.illposed.osc.utility.OSCByteArrayToJavaConverter.readArgument(Unk
nown Source)
at com.illposed.osc.utility.OSCByteArrayToJavaConverter.convertMessage(U
nknown Source)
at com.illposed.osc.utility.OSCByteArrayToJavaConverter.convert(Unknown
Source)
at com.illposed.osc.OSCPortIn.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
"
This exception is not thrown when I replace numDataPoints[i] with a constant (like 5).
Here is the entire .ck file. You can use it just like you would any OSC feature extractor with Wekinator.
//Extracts the following for different numDataPoints
//0 - centroidAvg
//1 - centroidStdDev
//2 - centroidMin
//3 - centroidMax
//4 - rmsAvg
//5 - rmsStdDev
//6 - rmsMin
//7 - rmsMax
//8 - fft bin with highest fval (highest over all windows)
//...
//whenever an amplitude > threshold is detected from input, computed over
//numDataPoints 128-sample ffts that overlap by 64-samples
//
"127.0.0.1" => string hostname;
OscSend xmit;
xmit.setHost( hostname, 6448 );
OscSend xmit2;
xmit2.setHost( hostname, 6448 );
//Custom objects
adc => FFT f =^ RMS rms => blackhole;
f =^ Centroid centroid => blackhole;
f =^ Flux flux => blackhole;
f =^ RollOff rolloff => blackhole;
UAnaBlob b;
//for storing the results from each window
float rmsArr[50];
float cent[50];
float highestFFTBin[50]; //bin with highest fft fval
float highestFFTVal[50]; //the actual value of the bin
//Set up bin stuff
128 => int FFT_SIZE;
FFT_SIZE => f.size;
Windowing.hamming(64) => f.window;
1::second / 1::samp => float SR;
SR/FFT_SIZE => float bin_width;
//constants
.5 => float threshold;
0 => int peakDetected; //flag if peak has been detected
now => time lastPeakTime;
100::samp => dur peakWindow;
10::samp => dur peakPollRate;
[1,5,10,15,20,25,30,35,40,45,50] @=> int numDataPoints[]; //list of numDataPoints values to try
1 => float rmsMultiplier; //so it isn't out of wek's range (this actually doesn't matter, leave it at 1)
0 => int currentlyAnalyzing; //flag if currently analyzing i.e. don't detect peak
//Run the peak detector in parallel to set the peakDetected flag
spork ~peakDetector();
spork ~sendOscFeatureNames();
//Extract features and send via osc when peak detected.
while (true) {
if (peakDetected) {
<<<"Peak detected! Analyzing">>>;
analyzeAndSend();
}
.1::second => now;
}
//When peak detected, compute min, max, avg, and std. dev for centroid and rms
//and send
fun void analyzeAndSend() {
1 => currentlyAnalyzing;
xmit.startMsg( "/oscCustomFeatures", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
//Get all float and rms values for the maximum numDataPoints
getRmsCentroidAndFFT();
for (0 => int i; i < 11; i++) {
analyzeNumDataPoints(numDataPoints[i]) @=> float result[];
for (0 => int j; j < 9; j++) {
result[j] => xmit.addFloat;
}
}
<<<"done">>>;
0 => currentlyAnalyzing;
}
//populates rms, cent, and fft arrays
fun void getRmsCentroidAndFFT() {
50 => int maxDataPoints;
128::samp => now;
rms.upchuck();
for (0 => int i; i < maxDataPoints; 1 +=> i) {
rms.fval(0) => rmsArr[i];
centroid.upchuck();
centroid.fval(0) => cent[i];
0 => float highestBinMagnitude;
0 => int highestBin;
f.upchuck();
//find highest bin
for (1 => int j; j < FFT_SIZE; j++) {
if (f.fval(j) > highestBinMagnitude) {
f.fval(j) => highestBinMagnitude;
j => highestBin;
}
}
highestBin => highestFFTBin[i];
highestBinMagnitude => highestFFTVal[i];
}
}
//Analyze data using the specified number of data points, return the float array corresponding to
//the osc message format described at top
//requires that the arrays have been populated
fun float[] analyzeNumDataPoints(int numDataPoints) {
float centroidTotal;
float centroidMin;
float centroidMax;
float centroidStdDev;
float rmsTotal;
float rmsMin;
float rmsMax;
float rmsStdDev;
float highestBin;
float highestBinMagnitude;
new float[numDataPoints] @=> float centroidData[];
new float[numDataPoints] @=> float rmsData[];
//get 1st centroid and rms
rmsArr[0] +=> rmsTotal;
rmsArr[0] => rmsMin => rmsMax => rmsData[0];
cent[0] +=> centroidTotal;
cent[0] => centroidMin => centroidMax => centroidData[0];
//do rest
for (1 => int i; i < numDataPoints; i++) {
rmsArr[i] +=> rmsTotal;
Math.max( rmsArr[i], rmsMax ) => rmsMax;
Math.min( rmsArr[i], rmsMin ) => rmsMin;
rmsArr[i] => rmsData[i];
cent[i] +=> centroidTotal;
Math.max( cent[i], centroidMax ) => centroidMax;
Math.min( cent[i], centroidMin ) => centroidMin;
cent[i] => centroidData[i];
//compute current highestBin
if (highestFFTVal[i] > highestBinMagnitude) {
highestFFTVal[i] => highestBinMagnitude;
highestFFTBin[i] => highestBin;
}
}
//calculate
rmsTotal / numDataPoints => float rmsAvg;
centroidTotal / numDataPoints => float centroidAvg;
//get std dev
float rmsDevSquaredTotal;
float centroidDevSquaredTotal;
for (0 => int i; i < numDataPoints; i++) {
(rmsData[i] - rmsAvg)*(rmsData[i] - rmsAvg) +=> rmsDevSquaredTotal;
(centroidData[i] - centroidAvg)*(centroidData[i] - centroidAvg) +=> centroidDevSquaredTotal;
}
Math.sqrt(rmsDevSquaredTotal / numDataPoints) => rmsStdDev;
Math.sqrt(centroidDevSquaredTotal / numDataPoints) => centroidStdDev;
float result[9];
centroidAvg => result[0];
centroidStdDev => result[1];
centroidMin => result[2];
centroidMax => result[3];
rmsAvg * rmsMultiplier => result[4];
rmsStdDev * rmsMultiplier => result[5];
rmsMin * rmsMultiplier => result[6];
rmsMax * rmsMultiplier => result[7];
highestBin => result[8];
return result;
}
//Pretty inefficient but does the trick
//Looks for a peak in the last peakWindow
fun void peakDetector() {
while (true){
if (adc.last() > threshold || adc.last() < -1 * threshold) {
1 => peakDetected; // analyze
now => lastPeakTime;
} else if (now > lastPeakTime + peakWindow) {
0 => peakDetected;
}
peakPollRate => now; //This is silly; would be better do do a low-pass filter envelope and look for peaks there. But don't worry about it right now.
}
}
//run in parallel to send osc feature names
fun void sendOscFeatureNames() {
while (true) {
xmit2.startMsg( "/oscCustomFeaturesNames sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss");
for (0 => int i; i < 11; i++) {
numDataPoints[i] => int n;
xmit2.addString(n + "centroidAvg");
xmit2.addString(n + "centroidStdDev");
xmit2.addString(n + "centroidMin");
xmit2.addString(n + "centroidMax");
xmit2.addString(n + "rmsAvg");
xmit2.addString(n + "rmsStdDev");
xmit2.addString(n + "rmsMin");
xmit2.addString(n + "rmsMax");
xmit2.addString(n + "highestFFT");
}
.1::second => now;
}
}