How to draw a frequency spectrum from a Fourier transform

12,657

Solution 1

What application are you using for this? I assume you are not doing this by hand, so here is a Matlab example:

>> fbins = fs/N * (0:(N/2 - 1)); % Where N is the number of fft samples

now you can perform

>> plot(fbins, abs(fftOfSignal(1:N/2)))

Stolen

edit: check this out http://www.codeproject.com/Articles/9388/How-to-implement-the-FFT-algorithm

Solution 2

I might not be correct on this one, but as far as I'm aware, you have 2 ways to get the spectrum of the whole song.

1) Do a single FFT on the whole song, which will give you an extremely good frequency resolution, but is in practice not efficient, and you don't need this kind of resolution anyway.

2) Divide it into small chunks (like 4096 samples blocks, as you said), get the FFT for each of those and average the spectra. You will compromise on the frequency resolution, but make the calculation more manageable (and also decrease the variance of the spectrum). Wilhelmsen link's describes how to compute an FFT in C++, and I think some library already exists to do that, like FFTW (but I never managed to compile it, to be fair =) ).

To obtain the magnitude spectrum, average the energy (square of the magnitude) accross all you chunks for every single bins. To get the result in dB, just 10 * log10 the results. That is of course assuming that you are not interested in the phase spectrum. I think this is known as the Barlett's method.

I would do something like this:

//  At this point you have the FFT chunks

float sum[N/2+1];

// For each bin
for (int binIndex = 0; binIndex < N/2 + 1; binIndex++)
{
    for (int chunkIndex = 0; chunkIndex < chunkNb; chunkIndex++)
    {
        //  Get the magnitude of the complex number
        float magnitude = FFTChunk[chunkIndex].bins[binIndex].real * FFTChunk[chunkIndex].bins[binIndex].real
            +   FFTChunk[chunkIndex].bins[binIndex].im * FFTChunk[chunkIndex].bins[binIndex].im;

        magnitude = sqrt(magnitude);

        //  Add the energy
        sum[binIndex] += magnitude * magnitude;
    }

    //  Average the energy;
    sum[binIndex] /= chunkNb;
}

//  Then get the values in decibel
for (int binIndex = 0; binIndex < N/2 + 1; binIndex++)
{
    sum[binIndex] = 10 * log10f(sum[binIndex]);
}

Hope this answers your question.

Edit: Goz's post will give you plenty of information on the matter =)

Solution 3

Wow I've written a load about this just recently.

I even turned it into a blog post available here.

My explanation is leaning towards spectrograms but its just as easy to render a chart like you describe!

Solution 4

Commonly, you would take just one of the arrays, corresponding to the point in time of the music in which you are interested. The you would calculate the log of the magnitude of each complex array element. Plot the N/2 results as Y values, and scale the X axis from 0 to Fs/2 (where Fs is the sampling rate).

Share:
12,657
goocreations
Author by

goocreations

Updated on June 13, 2022

Comments

  • goocreations
    goocreations almost 2 years

    I want to plot the frequency spectrum of a music file (like they do for example in Audacity). Hence I want the frequency in Hertz on the x-axis and the amplitude (or desibel) on the y-axis.

    I devide the song (about 20 million samples) into blocks of 4096 samples at a time. These blocks will result in 2049 (N/2 + 1) complex numbers (sine and cosine -> real and imaginary part). So now I have these thousands of individual 2049-arrays, how do I combine them?

    Lets say I do the FFT 5000 times resulting in 5000 2049-arrays of complex numbers. Do I plus all the values of the 5000 arrays and then take the magnitude of the combined 2049-array? Do I then sacle the x-axis with the songs sample rate / 2 (eg: 22050 for a 44100hz file)?

    Any information will be appriciated