how to interpret the magnitude of FFT

In one of my project, I record an audio using a mic connected to a PC, and calculate the FFT using Python. I used PyAudio for the recording. Upon calculating the magnitude, I noticed that its range can vary depending on the format (16 bit vs 32 bit) of the recording. I don't know if I did something wrong or is there an explanation for this. So how do you magnitude of, say, 150 at 2000Hz or magnitude of 1200 at 4000Hz? Are there any physical meanings to the numbers or are they meaningful only in a relative sense?

Furthermore, I want to take the audio data and convert it to a A-weighted decibel reading much like those given in handheld decibel meters. Is this something I can do from the FFT? A simple example would be nice.

• Hello. I suggest that you ask this question on dsp.stackexchange.com that might be more fitted. Commented Jun 20, 2019 at 18:41

There's lots here. Firstly, just saying "16-bit" or "32-bit" isn't enough. You'll note that "16-bit" is probably an integer of sorts - either signed or unsigned. 32-bit will most likely be a floating point representation, with the maximum value being -1.0 or 1.0 - so the comparative values between the two will be very different.

Weighting curves for SPL can be found here:

http://www.sengpielaudio.com/BerechnungDerBewertungsfilter.pdf

You would have to implement this based on the FFT output and then reference a known calibration source in order to achieve an accurate SPL measurement.

As @audionuma mentioned, it might be a good idea to try your luck on the DSP stack as this question has relevance there as well.

Don't worry about the magnitudes of the sample values. As @Mark said, these may be respresented differently depending on the system.

To get an A-weighted SPL reading from an FFT:

• First you'll need to calibrate your microphone. Ideally, you should use a measurement microphone with a flat response up to 20kHz. Set up a monitor speaker. Set up your microphone ~1m away. Play some white noise. Using an SPL meter, adjust the levels until the reading at the plane of your microphone is 94dB(A). Record for ~10s with a sample rate of 44.1 or 48kHz.

• Open the file and FFT the portion that contains the white noise. Convert the magnitudes to dB (relative to a full scale sine). Delete the bins above 20kHz. Discard the phase information.

• Apply A-weighting to every bin in the FFT. The equation can be found in the link that @Mark posted. This will window the amplitude data, keeping mid-frequencies like 1kHz intact and attenuating the frequency extremes.

• Now sum all the bins: sum=10*log10(10(bin1/10) + 10(bin2/10) + 10(bin3/10)...). This is your reference value that corresponds to 94dB(A). You can use it to create an adjustment constant, in dB, for subsequent measurements. For example, if the sum is -20dB, your adjustment factor would be 114.

• Now you can measure SPL with your mic. Python can handle the FFT, A-weighting, bin addition, and adjustment factor. Test it out with a few steady noise waveforms and tones to make sure it agrees with the handheld meter.

• Most SPL meters don't respond much over 8kHz, but your mic will. So if there's a discrepancy in results when measuring high frequency content, try deleting bins over 8kHz instead of 20kHz.

• When I sum all the bins, why did you use 10*log10(10^(bin1/10) + 10^(bin2/10) + 10^(bin3/10)...) and not 20*log10(10^(bin1/20) + 10^(bin2/20) + 10^(bin3/20)...), or does it matter? Commented Jul 15, 2019 at 16:29
• I used 10*log in this case since it's just an addition. Each 10^(bin/10) converts a dB value back to the linear scale (amplitude in this case, relative to a full scale sine). Then 10*log(sum) converts it back into dB.
– 77RD
Commented Jul 16, 2019 at 5:43
• thanks. I think we are on the same page now. It's just most literature uses dB= 20 * log (amplitude), so if that's the formula I used to get dB, then I should use 20*log10(10^(bin1/20) + ...) for summing the bins, correct? Commented Jul 16, 2019 at 13:19
• To convert your FFT bins (amplitude) into dB, you'd use 20*log. It can be thought of as a voltage. If it isn't already, normalize the FFT output so full scale is amplitude 1: 20*log(amplitude/reflevel) = bin value. so a full scale sine would be 20*log(1/1) = 0dB, and a sine wave with amplitude 0.5 would be 20*log(0.5/1) = -6dB. however when adding bins together, you'd use 10*log. 20*log would only be used if the signals were perfectly coherent (same frequency and phase). Since the bin values are all different frequencies, they are incoherent.
– 77RD
Commented Jul 16, 2019 at 14:02