# Adequate representation of frequency domain amplitude/magnitude of FFT of a signal

I'm quite new to the subject and am having fun playing around with the FFT. What I am currently doing is trying to sample an audio signal and display its frequency spectrum at the same time. This works alright with what I am doing so far, but there are still a few missing puzzle pieces.

This is what I have implemented so far:

• I get the raw audio data (i.e. my signal) and sample it with a sampling frequency of 44.1 kHz.
• For each time instance of the audio file where I update the spectrum (e.g. every 1/24 th second), I grab a certain amount of samples from my sampled signal around the time instance, apply a window function to it (Hann window) and then I transform it using the fftw library.
• Then I look at what I get. Since all my input data is real, I get n/2 + 1 complex values, am I correct in that assumption?
• Now, I'd like to display this spectrum. I calculate the magnitude of each of the n/2 + 1 vectors using the l2-norm and try to display that.

The last part is where I'm note sure of how to proceed. First, is this magnitude I calculate called the amplitude in frequency domain or am I mixing stuff up? As far as I know, the amplitude of the original signal in the window I transformed is somehow spread across my spectrum (i.e. my frequency bins). What I am looking for is a nice mapping in order to display these frequency bins nicely (in fact...I always group a few together to get larger frequency bands). Here is what I am trying to do: For my visualization, I have 12 bands and each has 15 discrete levels. So effectively what I am doing so far is trying to map the maximum of all bins in a frequency band into {0,1,...15}.

I have just been playing around so far, trying things like logarithms and linear mappings, but none seem be giving me the results I am expecting. For example, the lower frequencies might have a very high amplitude where higher ones are comparatively low, even though the corresponding audio signal would have me expecting higher magnitudes for the higher frequency bands.

So my main question, could this be mainly due to the fact that I am always picking the maximum? Would a mean value over all bins in a band be better?

My gut tells me thats just the tip of the iceberg. A linear transformation is, for example, difficult to adjust since I don't know what the maximum frequency magnitude is when I am calculating my spectrum on the fly.

I would appreciate it if some of you Gurus could help me learn what I am missing and, perhaps, tell me if there is anything horribly wrong with what I am doing up to the point where I want to visualize the calculated spectrum.

Cheers! Brick

-

I did a fair amount of frequency-content visualization in the past, although I don't have the code in front of me... Sorry I don't have more time but I think this might be of help, so here's what I recall:

1. First off, I'm not sure if this is part of what is giving you headaches, but since a real signal run through an FFT doesn't have a complex (phase) signal component, your spectrum is going to be reflected about 0, and you really only want the values >= 0; so, generally that means you will just drop half of your FFTed signal. (I am not 100% sure, but I believe that it will depend on the FFT you use where these are located, IIRC I generally took the values from 0-n/2...) Not only can the reflection end up throwing you off, but taking the wrong half (or both together) could be causing what you are seeing with the low frequencies seemingly being high and vice versa.

2. A super-simple power spectrum is easy: you can just take the square of the magnitude of each bin. For a complex number $x + iy$ the magnitude is given by $\sqrt{x^2 + y^2}$, thus the power just becomes $x^2+y^2$. So, for a complex point in code, this amounts to adding the squares of point.real and point.imaginary.

3. Although I think some of the links below will help you if you want absolute power bins, I was used to using relative dB power spectrums; IIRC we called our computation a dBm or SNR (signal-to-noise ratio) estimate, although I think that was fudging a bit... To do this, find the maximum power value in your FFT bins and scale from there, with 0 dB at the top. So, again IIRC, find $p_{max}$ and then each point is given by $10\cdot log_{10}(\frac{p}{p_{max}})$. This gives you a plot with 0 dBm at the top, and the lowest value can change from run to run. So you actually end up with all 0 or negative values, since what you're computing is approximate "dBm down" from the highest power bin rather than an absolute power in this case; this was how we measured our signal-to-noise ratio. (For reference, every 3 dBm increase represents a doubling of power; so 3 dBm down represents a drop by approximately 50%.)

4. We definitely always used power-of-two FFTs, they are generally much faster; if this isn't enough data to do much with, just average your FFTs together; I think probably you'll have fairly good luck with raw averages, but the links below might help you be a little more rigorous if you want or need some rigor. :)

5. Don't forget that your 0th bin is the DC component of the signal, in other words, the power in your signal that wasn't related to frequency; you can either just mask it out after taking the FFT (much less computationally intensive, but it may (or may not) have some ill effects depending on your application from bypassing or ignoring any rigorous DSP techniques [e.g. using a windowing function]... (although TBH I did some pretty intensive DSP that I'm sure was theoretically much worse for not having it, but that worked about 5x as fast and gave us results that were just as good, so, as always, YMMV.)

6. And don't forget also (like I did :D) to place a lower bound on your data; just go through all your data points and choose a number (perhaps 60 or 90 db down e.g. -60 or -90) and anything lower than that, set to that.

Hope that gets you started a bit, I know I got a lot of help when I was first doing this kind of thing, and playing around with averages, FFT sizes, etc. and just looking at the results should help a good bit; sounds like you're on the right track.

There's good information at [The Mathworks] on this kind of plotting... (http://www.mathworks.com/help/signal/ug/psd-estimate-using-fft.html) Also there seems to be a very good summary of what you'll need to know regarding window functions and more detail on the power spectrum processing relating to this at WaveMetrics.

A full example of a spectrum analyzer starts on page 4-34 of this paper (big thanks to Dr. Steven Tretter, Professor Emeritus, Dept. of Electrical and Computer Engineering at the University of Maryland for having a good resources that's got more than just theory in it up and available for anyone to learn from, BTW!) Unfortunately webcitation is not working for me at the moment so hopefully someone can grab a webcite at some point in case it moves.

caveat emptor: it's been a while since I was working much with frequency domain analysis, and it was with ultrasonic and satellite signals, not audio; however, the principles should be the same here...

[edit log: 2013-03-05: 1. was able to grab webcitations for the paper from Dr. Tretter (http://www.ece.umd.edu/faculty/tretter, I'll try to pull a webcite for that later as well) and for the Power Spectra page from WaveMetrics before the site stopped accepting requests again, so, yay! 2. expanded on a comment I made answering a question about my answer from Brick (OP) relating to the 0th bin (DC component) of the FFT signal, for the benefit of any others who might come across this in the future; and 3. a quick mention of some real-world pragmatism vs. theory during my (5 year) stint working with DSP and Satellite signals. 2013-03-06: Added info about capping values RE: OP questions below.]

-
Hi, thank you for the info and the links. With the help of this, I've got it working quite nicely. Just to make sure, when I transform my window, I don't use the first value of the fft result as I noticed it doesn't seem to represent a frequency bin. What exactly is it? – YeahShibby Mar 5 '14 at 15:31
awesome, glad to hear that my memory was good enough to get you some use out of it! (that's more than most of my engineering experience has done for me, career-wise, the past few years :D) as for your question, yes, I wish I had thought of mentioning that: that's the 'DC' component you are seeing... I'm sure there are more complex (and/or correct :D) answers, but basically it's how much the average of all the samples in the period of the FFT differed from zero. one way of looking at it: that's the power in the signal during a period that is NOT (as you deduced) related to frequency. :) – shelleybutterfly Mar 6 '14 at 17:42
Right now I'm still trying my best to calibrate my analyzer. Are there any defacto-standard proceedings for doing this? I calculate the db power as you mentioned. this will give me values somewhere between -150db (or possibly lower) and 0db. If band 3, for example, typically has db values between -100 and -30 for those that show up in a song, I map the db value into [-100,-30] interval, where everything smaller than -100 is set to -100 and everything larger than -30 is set to -30. Then I map this value linearly into {0,...,15} as I only display 16 discrete levels. – YeahShibby Mar 7 '14 at 20:22
ahh yes, capping was another thing that's apparently slipped from my mind over time, apparently :) I am not sure about a standard way, but as I recall we just capped the lower bound at something reasonable; definitely 150dBm down is a bit much; I think we just used something like 90... @ 50% power loss per 3dB down, even that is barely more than noise at that point. probably 90 is overkill for audio but I would just play around and pick something that looks decent; I wouldn't be surprised if it was more like 60 or so. – shelleybutterfly Mar 7 '14 at 23:31
Also, I wouldn't cap the top value if you're using my suggestions: you are choosing the p$_{max}$ to use in your scaling function based on the largest value in your data, so there will always be a 0db point in your scaled data, and it's the 0dB point that everything else is referenced to, so 30 dB down is only $(0.5^{10}\times100)\%$ the power in the 0dB bin, or $~0.01\%$; in other words, you're making values that are that very small fraction of your 0dB bin look the same strength. I would just cap the lower bound at whatever you decide and then quantize that to your 16 values linearly. – shelleybutterfly Mar 7 '14 at 23:45

The correct way to group multiple bins together is to multiply each complex fft bin output by its complex conjugate (which gives the bin power) then add all the bin powers together and divide by the number of bins in the group. If you want to display in db (which is the conventional approach) then take 10*log10() of the result. Negative db values are normal and expected.

Note that if your audio signal is music, it is quite normal that the spectrum falls off with frequency.

Also from your description it sounds like you are not using a 2 ^n number of points. It's best to stick with 2^n data buffer sizes to maximize the efficiency of the fft computation.

Bob

-
Hi Bob, thanks for your input! Multiplying each bin output by its complex conjugate would, if I'm not mistaken, simply give me the l2-norm of the bin value, squared (i.e. real^2 + imag^2). This is what I am already doing, apart from the fact that I am then taking the square root. Add all bins in a band and divide by the number of bins. So I'm effectively calculating the mean power value of each band, got it. What do you mean that the spectrum "falls off with frequency"? Do you mean the power for higher frequency bands/bins is typically lower as opposed lower frequency bands/bins? – YeahShibby Mar 3 '14 at 8:41

Many audio FFT visualizations use log(magnitude) instead of linear, as that makes it easier to find a scale that makes data visible a graph.

The energy contained in band that contains multiple FFT result bins would be the sum, not the max.

If the "certain amount" of samples you grab for each FFT is a small fraction of those per 1/24 second, then you may want to average the magnitude of several FFT windows that fit within that display frame time.

-
Thanks for the info on the energy for contained in on band. To the log-magnitude: Wouldn't that also lead to the problem that values smaller than 1 are mapped to negative values? This, again, makes it hard for me to map the log into {0,1,...,15}. I'm sure there is a trick here. As for the "certain amount", I'll be a bit more clear on that: Since my discrete time steps are 1/24 (for example), I take 1/24 * sampleFrequenfy = 1838 (give or take) samples from k*1/24, i.e. half before and half after. – YeahShibby Mar 3 '14 at 1:41
Any negative log() values can be made visible by a suitable offset that fits with your audio sample input range. – hotpaw2 Mar 3 '14 at 2:33