iOS FFT Accerelate.framework draw spectrum during playback

benjamin.ludwig picture benjamin.ludwig · May 17, 2012 · Viewed 7.1k times · Source

UPDATE 2016-03-15

Please take a look at this project: https://github.com/ooper-shlab/aurioTouch2.0-Swift. It has been ported to Swift and contains every answer you're looking for, if you cam here.


I did a lot of research and learned a lot about FFT and the Accelerate Framework. But after days of experiments I'm kind of frustrated.

I want to display the frequency spectrum of an audio file during playback in a diagram. For every time interval it should show the magnitude in db on the Y-axis (displayed by a red bar) for every frequency (in my case 512 values) calculated by a FFT on the X-Axis.

The output should look like this: enter image description here

I fill a buffer with 1024 samples extracting only the left channel for the beginning. Then I do all this FFT stuff.

Here is my code so far:

Setting up some variables

- (void)setupVars  
{  
    maxSamples = 1024;

    log2n = log2f(maxSamples);  
    n = 1 << log2n;  

    stride = 1;  
    nOver2 = maxSamples/2;  

    A.realp = (float *) malloc(nOver2 * sizeof(float));  
    A.imagp = (float *) malloc(nOver2 * sizeof(float));  
    memset(A.imagp, 0, nOver2 * sizeof(float));

    obtainedReal = (float *) malloc(n * sizeof(float));  
    originalReal = (float *) malloc(n * sizeof(float));

    setupReal = vDSP_create_fftsetup(log2n, FFT_RADIX2);  
}

Doing the FFT. FrequencyArray is just a data structure that holds 512 float values.

- (FrequencyArry)performFastFourierTransformForSampleData:(SInt16*)sampleData andSampleRate:(UInt16)sampleRate   
{  
    NSLog(@"log2n %i n %i,  nOver2 %i", log2n, n, nOver2);

    // n = 1024
    // log2n 10
    // nOver2 = 512

    for (int i = 0; i < n; i++) {
        originalReal[i] = (float) sampleData[i];
    }

    vDSP_ctoz((COMPLEX *) originalReal, 2, &A, 1, nOver2);

    vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);

    float scale = (float) 1.0 / (2 * n);

    vDSP_vsmul(A.realp, 1, &scale, A.realp, 1, nOver2);
    vDSP_vsmul(A.imagp, 1, &scale, A.imagp, 1, nOver2);

    vDSP_ztoc(&A, 1, (COMPLEX *) obtainedReal, 2, nOver2);

    FrequencyArry frequencyArray;

    for (int i = 0; i < nOver2; i++) {
        frequencyArray.frequency[i] = log10f(obtainedReal[i]); // Magnitude in db???
    }

    return frequencyArray;  
}

The output looks always kind of weird although it some how seems to move according to the music.

I'm happy that I came so far thanks to some very good posts here like this: Using the apple FFT and accelerate Framework

But now I don't know what to do. What am I missing?

Answer

Paul R picture Paul R · May 17, 2012

Firstly you're not applying a window function prior to the FFT - this will result in smearing of the spectrum due to spectral leakage.

Secondly, you're just using the real component of the FFT output bins to calculate dB magnitude - you need to use the complex magnitude:

magnitude_dB = 10 * log10(re * re + im * im);