Getting frequency and amplitude values using FFT~

edbraund's icon

Hello,

Is there away for getting frequency and amplitude values out of fft~?
if so would it be possible for some direction?

mzed's icon

I think you want to spend some time with MSP tutorial #26. Also look at #25.

Quickly, the frequencies of individual fft bins don't change. Each bin represents a bandwidth of (Sample Rate)/(total number of samples). So, a 1024 sample fft has 512 frequency bins of ~43 HZ each -- at 44.1k samples per second. fft~ and pfft~ output a stream of bin numbers.

For amplitude: if you run the real and imaginary outputs of fft~ or pfft~ into cartopol~, the left outlet of cartopol~ is the amplitude for that specific bin.

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

Maybe this is somewhat helpful:

ihave2feet's icon

I'm also having issues with this... mzed, you're example patch didn't seem to work for me!
I'm trying to use the amplitude values that are output to obtain the frequency with the highest amplitude value - to then use generate a saw wave with that frequency... any ideas on how I would go about this?

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

This is rather messy, but seems to work. Using gen~.

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

And without gen~...

One cheap-o pitch tracker!

ihave2feet's icon

Thanks very much!! Now just got to figure out how that works! :)

seabed's icon

Hey all,

I know its been a long time since the last post but I hope I will get an answer. So i am using exactly the same patch that MZED posted (without gen), however I have a signal with sampling rate 256Hz. Do you have any idea what changes should I do so as to work properly? I suppose my question is mostly for MZED but any help is welcomed.

Cheers

mzed's icon

256 Hz is a crazy-low sampling rate. If you're running at a sample rate that Max supports, then my patch should be fine. The sample rate is reported by the dspstate~ object on the right.

seabed's icon

Let me make that more clear. The sampling rate in the audio setup menu is of course set at 44100kHz. However I want to do the same task with your patch but with an EEG data signal which has sampl. rate = 256Hz.

mzed's icon

This particular patch is doing realtime fft~ with Max and only makes sense in that context. I'm not sure how you're getting samples from your device into Max.

In general, the frequency for each bin is approximately: (bin number) * ((sample rate)/(number of bins))

seabed's icon

"My" patch is completely same to yours but instead of having that sound generator I have a .wav sample with s.r. = 256Hz played. The problem is that I get mostly zero-frequency as exit, no matter how much I decrease the bin's range.

seabed's icon

Ηey MZED can you please explain to me how subpatch "find_bin" works? I think that any changes I have to do, should be in there. Thanks

mzed's icon

At the end of each fft (edge~) it uses Uzi to scan the buffered results. For each result, the bin index is stored in the first "i" object. If the amplitude in that bin is the highest so far (tested by peak~), the bin index is banged into the second int object. When Uzi is done, it bangs the final result out of that same int object.

If you post your patch, I could help some more.

seabed's icon

Hey, here is my patch. I load .wav file dok1 (it is attached) to the buffer and then its the same process. The only changes are:

1. Instead of dpstate, I use the fixed number 256 (you should enter it in the number box before the patch starts)
2. I used cycle instead of saw
3. I multiply the outcome with 250 just to have a more audible frequency

The problem is that most of the time the exit is zero frequency. Moreover the frequency resolution should be around 0.063 Hz (sampling rate/fft size) but it doesn't seem like changes of that scale are perceptible.

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

Edit:Actually for a safety reason I cant upload the wav file so here it is a link for it
http://speedy.sh/jMVxd/dok1.rar

seabed's icon

So? Any new thoughts? I am really stuck at this part.

mzed's icon

play~ is going to be playing back samples from the buffer at whatever the sample rate is set to in Max. So, if your samples were recorded at 256k, then pitch is going to be very high. I think you'd want to divide multiply the estimated pitch (right before the saw~) by ((.wav file sample rate)/(current max sample rate)).

I'm still kind of confused by what you're doing though, so I could be giving you the wrong answer.

seabed's icon

Hey, first of all thanks for all this help.

Well, what I am trying to do in a few words is a frequency scaling. I have an EEG data signal which is a .wav file at 256Hz (sampling rate). With your patch I am trying to find the frequency with the highest amplitude value and then scale it to an audible range of 250Hz - 22kHz. Your patch works perfectly for a sample with S.R. = 44100Hz, but not so good for S.R. = 256Hz. What can I do?

seabed's icon

Hi again,

I have also noticed that defining the length of buffer "spectrum" at samples (buffer spectrum @samps 2048) is not compatible with every Max version. Can I do it in another way?

seabed's icon

Hey MZED, I know it's been a long time but I hope I'll get an answer.

I am using your patch that finds the central frequency of a bin with maximum amplitude. I mostly interested in lower frequencies such as 4-30Hz. However is not possible to have a resolution lower than 10.77Hz as long as the range of every bin is 10.77 (samp.rate = 44100/fft_size = 4096). As far as I know Max doesn't support fft size bigger than 4096. So, what can I do to have a better frequency resolution?

seabed's icon

Once again about this lovely patch.

I noticed that it's not stable for frequencies higher than 5kHz. You can try an oscillator at 10kHz, the maximum output frequency is stuck at 5502 Hz. Any idea why is this happening?

EDIT: It seems like the uzi object needs to be same size with the fft size.

mzed's icon

Good catch, thanks. Actually, it needs to be the same as the buffer size, which is (fft size / 2). The second half of the fft~ output is a mirror of the first half.

poncie's icon

Hey Seabed, I had a similar problem trying to perform FFT on a low sampling rate signal. I have a guess as to what the problem is. From what I understand, sig~ will turn any number into a signal at the DSP sampling rate (usu. 44100Hz). Whenever a new value comes in, it jumps to the new value (or maybe it's interpolated, but it doesn't matter for this point). So every 1/256 seconds, you're getting a new value, but that's really slow compared to the DSP sampling rate of 44100Hz. The result is that at most FFT window lengths (say, 512), you're only getting 512*(1/44100) = 0.01s of signal for each of your FFT windows, whereas you're only getting a new sample every 1/256 = 0.004s. That is each window only contains roughly two 256Hz-sampled-points (I suppose you could max out the FFT window length to 4096 and get some more points, but it's probably still not going to be good enough). That's my best guess anyway -- let me know if you think it's not right.

Anyway, I was able to get an FFT of arbitrary-sample rate signals using jit.fft. Take a look at my patcher and let me know if you have any ideas or questions.

TLDR, fft~ only operates on DSP sample rate signals, which isn't helpful if you're looking for lower frequencies. Use jit.matrix and jit.fft to do it.

jitter-fft2.maxpat
Max Patch
seabed's icon

Hey Poncie, it's nice that you also working on that stuff. Well I am kind of busy this period but I promise you that next week you will have a more informative reply from me.

So far I "solved" the problem by using pfft~ that allows you to have an FFT bigger than 4096.By this way you can increase the frequency resolution. I will check jit. objects and I will upload my patch as soon as possible. Keep in touch

Cheers

seabed's icon

Hey Poncie, can you explain to me a bit how your patch works?

Augustine Bannatyne - Leudar's icon
Max Patch
Copy patch and select New From Clipboard in Max.

Might be a bit clumsy but I made this frequency splitter for pfft - it outputs 11 different bands (I have another somewhere that does 31 bands) and you could easily work out which had the highest amplitude out of pfft - I guess you could have more frequency bands too :

seabed's icon

Hey Augustine,

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

this is nice if you want to split your signal in bands, but in order to have a better frequency resolution you have to increase the FFT size. Here it is how i did it:

I am still interested in Poncie's patch but I haven't understand exactly how it works. By the way, it might be a good solution to interpolate a signal which is sampled to a lower frequency than 44100Hz. At least this is what I did with the EEG signals.

D Griggs's icon

I'm trying to do this with pfft~ and having trouble. Number~ won't pull signal data from fftin~ bin index output. Its just all 0's. Has anyone stored amplitude data by bin index in an array from pfft~ not just fft~?

mzed's icon

The last example uses pfft~. Just go directly from the fftin~ bin index into poke~, with nothing in between.

Jean-Francois Charles's icon

Yes, you are looking for phase vocoder patches. One example is in the Max Examples -> MSP -> FFT Fun -> phase-vocoder-sampler

Jesse Meijer's icon

Very late (3,5 years) so I hope you guys can still reply to this thread. I'm trying to create a clock sync between multiple workstations using a phasor as the base clock and translating it into a high frequency sine sweep (I was thinking about translating the movement of 0-1 to a sweep of 22050-44100 to get the best resolution). The idea is that this sweep goes into another workstation, these frequencies are analysed and back engineered into a sweep of 0-1 just like the phasor so you have perfect sync for looping audio and other processes. I'm new to FFT so I was hoping you guys could shine some light on this. By the way, I try to work as much as possible in gen~.

Tnx

Roman Thilenius's icon


a sync signal is the last thing i would try to receive using pitch tracking, among other things because it has latency.

what I/O do you have? some will allow you to output the phaser signal just as is, that is still not a very accurate thing, but you could catch the 0 to 1 transition and trigger another phaser at the slave with it.

but now to the question like asked. okay.

but why fft? and stay in the voice range.

[phasor~]
[*~ -1800.]
[+~ 200.]
[cycle~]

Jesse Meijer's icon

Yeah i figured that staying in the voice range is better while messing around last night. I'm using a motu ultralight mk4 as I/O interface. I've tried running a phasor straight through but it's not tight enough. It gets rounded at the edges. Could very well be that my approach is wrong and that I should use clicks rather than sweeps and phasors to sync on audiorate. I know it has latency but so does midi and midi tends to drift. With a frequency sweep, my idea was that the system doesn't only count pulses and when it misses one or two it starts running behind, but that it can always read where it should be in time. That's why I wanted to try syncing this way. Also I can compensate for the latency by delaying everything on the master daw except what it's sending to the slave daw's.

[phasor~] [*~ -1800.] [+~ 200.] [cycle~] you're proposing a sweep 200-2000Hz right?

Getting the cycle to sweep isn't the issue. It's reading where it is in the sweep when you plug it into the slave daw. I thought FFT might be the way. Any idea's or is this just a wrong approach?

Tnx Roman for the swift reply :)

I've added a stripped down and ready to use version of my loop patch. Maybe it's helpful in understanding what I'm trying to achieve.

loop_patch_example.zip
application/x-zip-compressed 16.91 KB