# Rendering a spectrogram of audio data - resulting magnitudes are all too close together

I'm working on creating an application which will render a spectrogram of the audio in an MP3 file. I'm having some trouble, though - after running the audio samples through my program, the resulting magnitudes are all very close together, and don't seem to be very patterned like one would expect. I'm coloring my spectrogram using a pretty typical black/blue/purple/red/yellow/white gradient. The current result I get is this:

The steps I'm performing to get this are:

1. Decode the MP3 file using libmad, getting raw signed 16-bit PCM samples back. The samples are stereo, but at this point (for debugging, mostly) I'm just ignoring the right channel. I've verified that this step works by writing the raw samples back out to a file, importing them into Audacity, and then playing the audio - and it sounds correct.
2. Compute the STFT of these samples (each sample is converted to a complex number of sample + 0i - I realize this isn't the most efficient, but it's very simple and my understanding is that it should produce the same result). I'm not scaling the samples, or converting them from signed to unsigned, or anything else. I'm using a window size of 8192 samples, which is about 185ms of audio data (sampled at 44.1KHz). I'm also applying the Hann function to each of these windows, and overlapping them by about 800 samples. I'm using my own implementation of the FFT, however I've verified it by comparing its outputs to those from Numpy over a very large list of inputs.
3. Plot the result. Each column in the graph is a DFT from list of STFT results (time), and each row is a particular value in the DFT result (a frequency). I'm only plotting the results in DFT bins [1, N/2 - 1], since I'm using real inputs. I'm computing the magnitude as log10(sqrt(r*r + i*i)).

The color range is from [minimum magnitude, maximum magnitude]. I've tried shrinking the range, thinking that outliers may be screwing it up, but even with a range of [5.6, 5.8] the data is still not a real spectrogram. The magnitudes seem to be more or less randomly distributed in that [5.6, 5.8] range.

What issue might be causing this type of output?

I'd love to post code or go into more detail, but as I'm not sure what area the problem might lie in I'm not sure what to expand upon. For what it's worth (although obviously I don't expect anyone to look at all of my code and fix my bug), this project is on GitHub here.

EDIT: I generated a wave file with a single 1000Hz sine wave as suggested, and for the first time ever my application actually produces some (maybe?) useful output. Here's what I get:

Perhaps this provides some more insight as to what's going wrong?

• Given that your algorithm seems roughly correct, suspect a bug in the code, not the algorithm. Debugging from a random mp3 can be difficult. Try debugging using a raw PCM file containing a single pure sinusoid (441.43 Hz or something), and looking at a single column for sane results. Sep 1, 2014 at 20:22
• That was a great suggestion. Doing that has, I think, given me some new clues on where to look. This new spectrogram is weirdly periodic on the frequency axis. Sep 1, 2014 at 20:52
• A periodic output could mean the FFT did nothing (output == input, unchanged), or the input only had a single non-zero data point (input == garbage). Sep 1, 2014 at 21:02
• Spot on. The input is being turned to garbage somewhere between reading it from the disk and passing it to the FFT. Thanks! If you post an answer, I'll give you some rep. Hopefully my mistakes will be useful to anyone else trying to learn / implement this type of thing in the future. Sep 1, 2014 at 23:44

A periodic looking output from an FFT could mean a bug such that the FFT did nothing (output == input, unchanged), or that the input to the FFT only had a single large magnitude or non-zero data point (input == garbage).

The FFT result is so entirely wrong that I suspect you're taking the FFT of the raw MP3 data. You may have decompressed the MP3 data, but did you then pass the buffer holding the uncompressed data to the FFT?

To check if the problem lies in your FFT or in your MP3 code, generate the sine wave numerically instead of reading it from an MP3.

There were some smaller issues in my code, but the main one was that I was interpreting the PCM data with the wrong endianness. @hotpaw2's suggestion was what pushed me in the right direction: try plotting a pure sine wave.

I should've from the beginning been testing with a single sine wave generated in memory, rather than fussing with reading and decompressing files. That would've made it much easier to get the FFT/spectrogram portion working, and then I could've focused on interpreting MP3 data.

I was too focused on finding the bug in the (arguably) more complex portions of my program - the math heavy portions - that I didn't look too hard at the inputs I was getting.

Thanks for the suggestions, all!