Maximum amplitude for all FFT bins - possible in Max?

anstahc's icon

Audacity and Adobe Audition (and probably many others) allow you to run FFT analysis on a sound source and export the frequency/amplitude data pairs to a text file. The resulting amplitudes are average peaks for each FFT bin however. I'm looking for a solution that calculates the maximum peak for each bin. In other words, if I run a sound source through, and then run the same source again with 10 extra seconds of silence appended to it, the results produced should be the same. This is not the case with averages because the weight of the extra silence brings the values down.

I've been through Tutorial 25-26 and a bunch of forum posts but I'm still not sure if this is possible, or if it is how to approach it. Any direction would be much appreciated!

Thanks

vichug's icon

you need some kind of sample and hold, which keeps in itself the highest received value - probably a maximum~ with 1 sample feedback would do..; possible in gen~ maybe ? ah wait no, you need to keep one value for each bin. So, probably write it to a buffer, then compare each index with its previous iteration. Make sure that buffer is as large in samples as your fft frequency resolution (which is, er, half the window size ?). It's fun because i am currently going through those tutorials as well :)

vichug's icon

the tricky part is that you would ideally do that with poly~ maybe and it's not possible inside pfft~... not sure if gen~ works inside pfft~ either...

vichug's icon
Max Patch
Copy patch and select New From Clipboard in Max.

so i tried like this :

at each sample, i recall the previous value stored in buffer at current bin index, and compare it with current amplitude of current bin. If it is bigger, it will be written at that index - at a time of next sample, to ensure that the buffer is read *before* it is overwritten. As i'm really noob at fft~, i can't guarantee anything...

anstahc's icon

Thanks for the quick response! Don't worry I'm noob too -- I've never used gen, peek, poke, or buffer so it will take me a little more time to understand what your patch is doing. I'm guessing it works? Not sure how to get the buffer values and load them into a coll or save to .txt or something.

Max Patch
Copy patch and select New From Clipboard in Max.

I just finished a patch that gets current amplitude and max amplitude for specific bins. There are quite a few threads looking for this but I couldn't find a solution in any of them. Either got the typical "check the manual/tutorials" response or got what they needed and never posted the complete solution so I thought I'd post it.

Not sure if it's the cleanest or most efficient solution but I confirmed with Audacity's output that it works. I'd love to combine with yours as soon as I understand what it's doing, maybe build out an FFT super patch. Seems to be a confusing topic for many and hasn't really changed over the course of several years. Ultimately I'd like to basically re-build Voxengo SPAN with access to the freq/amp data. The more experienced Max users could probably put this together in a day or two but by myself who knows how long. I do know that it would be really useful for a lot of people.

anstahc's icon

That was a lot easier to understand than I thought. Thanks for adding the comments in the gen~ object. I added the piece that stores the buffer into a coll~ object. Not sure about efficiency -- when clicking the store button there's maybe a 1 second delay for FFT-size 65536. FFT-size 131,072 damn near crashes my computer for about 15 seconds but still worked.

EDIT: I changed the coll~ object to text~. No freezing up with larger FFT-sizes.

Max Patch
Copy patch and select New From Clipboard in Max.

1 question: Do you set the buffer size from the 2nd outlet of fftinfo~ (spectral frame size) instead of the 1st oulet (FFT frame size) to ignore everything past the nyquist? Or am I really confusing things now?

vichug's icon

you assumed right about that question, although i really wonder if it's a good idea :) Also, what i thought after is, are there negative amplitudes ? if so then a [abs~] is needed at some point, probably inside gen~ subpatch. But maybe there is not, because there are negative values for the real part of real/imaginary coordinates, and i'm not sure there is ever a negative value for the amplitude of polar coordinates.

I did not test that thing, so i can't tell if it's working...

Max Patch
Copy patch and select New From Clipboard in Max.

Lastly : to write those values into a textfile easily, you could use [capture~] though you have to tell him the signal vector size as an argument during creation, can't change that later - it would be possible to, like, create that with scripting and [thispatcher] but doesn't seem cool.

Also, now i am thinking that maybe we should rather use [fft~] rather than a subpatcher with [pfft~], though i'm not sure of that, because [pfft~] uses windowing and multiple instances of the same patcher, which is cool when you want to resynthesize, but i'm not sure it's appropriate here... anyway the writing-vlaues-from-buffer part should be placed outside of the [pfft~] world maybe ?

anstahc's icon

I don't think there are any negative amplitude values. atodb will return NaN. It's possible that the maximum positive amps are overwriting any NaNs but I ran tests with and without abs~ and there was no difference.

fft~ has a max fft-size of either 4096 or 8192. pfft~ can go up to 131,072 so I'm going to stick with that.

You're right about moving the print to text piece out of pfft~. I didn't realize the buffer could be accessed outside of it. I'm so used to running into scope barriers with other programming languages that I sometimes forget how global Max is.

vichug's icon

hah, Max is not always that global, but some things sure are convenients... anyway, could i ask you if you would consider sharing that working patch of yours ? :)

anstahc's icon

I'm working in Max For Live so the main patch might be formatted a little weird (and you'll probably have to change plugin~/plugout~). You have to save "fftmax_bin" and "fftmax_all" first, save a new blank patch in the same directory, close it, re-open it, then paste the main patch code in. If you don't the ~pfft inlets/outlets won't show and there will be broken connections.

Max Patch
Copy patch and select New From Clipboard in Max.

Main patch

Max Patch
Copy patch and select New From Clipboard in Max.

fftmax_bin.maxpat

Max Patch
Copy patch and select New From Clipboard in Max.

fftmax_all.maxpat

vichug's icon

Thanks a lot !