Compute the spectral centroid in maxmsp
Hello,
I'm trying to compute the spectral centroid in max.
The spectral centroid can be see as the barycentre of the magnitudes of the spectrum of frequencies..
I know there are some externals which does that, but I'd like to implement it myself to understand how msp works.
This is what I did. This patch is inspired by the MSP tutorial 26. I use the edge~ object on the third outlet of the fft, to compute the centroid frame by frame.
How you can see, it seems to work if there in only one frequency in the fft (message box 1). In the case of a "real" signal (message box 2, cycle~440). It's not stable at all.
Why ? Any idea ?
Thanks a lot for your help.
I think you're better off using pfft~ do to a windowed transform. There is a lot of noise at the edges of an unwindowed fft~.
Here's a main patch:
And here's what goes in the pfft~:
It's a little rough, but it's heading in the right direction.
You might also want to have a look to gen~.pfft_centroid.maxpat in the gen example folder. It shows how to calculate the centroid using gen~, the codebox version demonstrates also some of the new gen~ features of Max 6.0.7.
Thanks a lot for your answers.
I just have max 5, so the gen~ object will be for another time.
To capture the fft frame by frame, the poke~object seems to be very useful.
I'm not sure to understand if the edge~ object if fast enough to make all the frame to be compute, but..well it seems to work!
Thanks again for your help
edge~ is not fast enough for my method to be 100% accurate. If the vector size is low, and the scheduler is in audio interrupt, then the result should be reasonably accurate. The gen~ method is better.
Emmanuel - just toying around with the gen~.pfft_centroid.maxpat in my Max 7 folder and realised the value it outputs is half of what it should be.
I fixed the output by changing the code a little so that the sumEnergyIndex value uses the frequency instead of the index when it's multiplied by the energy. Then stripped the *= SAMPLERATE / FFTSIZE and *= FFTHOP / VECTORSIZE later on in the code. Now when I feed a sine wave into it, the centroid correctly reflects the input frequency.
History previousCentroid(0.);
History sumEnergy(0.);
History sumEnergyIndex(0.);
real = in1;
imag = in2;
index = in3;
centroid = previousCentroid;
if (index < (VECTORSIZE-1))
{
// accumulate energy values
energy = real * real + imag * imag;
sumEnergy += energy;
sumEnergyIndex += ( index * SAMPLERATE / FFTSIZE ) * energy;
}
else
{
// calculate centroid value
centroid = sumEnergyIndex / sumEnergy;
// reset energy sums
sumEnergy = 0.;
sumEnergyIndex = 0.;
previousCentroid = centroid;
}
out1 = centroid;
**edit - I can't ever get these forum codes working properly.. sigh