2

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

goocreations
  • 2,938
  • 8
  • 37
  • 59

4 Answers4

2

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

chwi
  • 2,752
  • 2
  • 41
  • 66
  • I'm writing my own C++ program. Can you maybe explain the example above. What does 0: and 1: meen and what is fs? – goocreations May 19 '12 at 10:08
  • fs = sample frequency, 0:x = all steps from zero to x, same for 1:N/2. abs = absolute value. The end is plot(x,y), so fbins is x and the rest is y – chwi May 19 '12 at 13:46
  • Drop a link to pastebin with your C-code next time, I'm a bit curious on how you will solve this :) – chwi May 21 '12 at 11:41
  • I'm still looking, so I can't give a code yet. So any ideas on this are more than welcome! – goocreations May 21 '12 at 17:31
  • I'm more in to embedded C, not C++, so I'm just curious. let's hope some others can provide more. maybe you could help by describing what platform/operating system you are working on – chwi May 21 '12 at 21:44
  • The source code from the link only uses one calculation of 500 samples, hence he doesn't do the splitting and combining again. – goocreations May 22 '12 at 05:27
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 =)

tthegarde
  • 21
  • 2
  • I'm trying to implement this for Java. What I don't understand is why you only iterate over N/2 and not N? – Stefan Mar 30 '13 at 11:33
2

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!

Goz
  • 61,365
  • 24
  • 124
  • 204
1

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).

hotpaw2
  • 70,107
  • 14
  • 90
  • 153
  • Thanks, but I don't just want a spectrum for a second or so, but for the entire song. – goocreations May 20 '12 at 06:07
  • If length of FFT is N, why do you plot only N/2? –  Dec 29 '14 at 05:22
  • For strictly real data input, the 2nd half of an FFT result is just the conjugate mirror of the first half (e.g. redundant). The half above N/2 is only useful (different) if the time domain data is complex (both real and imaginary components non-zero). – hotpaw2 Dec 29 '14 at 08:01