2

I'm trying to get frequency from iPhone / iPod music library for a spectrum app on iPod library, helping myself with reading-audio-samples-via-avassetreader to get audio samples and then with using-the-apple-fft-and-accelerate-framework and Apple vDSP Samples, but somehow I'm wrong somewhere and unable to calculate the frequency.

So step by step:

  • read audio sample
  • Hanning window
  • calculate fft

Is this the correct way to get frequencies from an iPod mp3 library?

Here is my code:

static COMPLEX_SPLIT    A;  
static FFTSetup         setupReal;  
static uint32_t         log2n, n, nOver2;  
static int32_t          stride;  
static float            *obtainedReal;  
static float            scale;  

+ (void)initialize  
{  
    log2n = 10;  
   n = 1 << log2n;  

    stride = 1;  
    nOver2 = n / 2;  
    A.realp = (float *) malloc(nOver2 * sizeof(float));  
    A.imagp = (float *) malloc(nOver2 * sizeof(float));  

    obtainedReal = (float *) malloc(n * sizeof(float));  
    setupReal = vDSP_create_fftsetup(log2n, FFT_RADIX2);  
}  


- (float) performAcceleratedFastFourierTransForAudioBuffer:(AudioBufferList)ioData   
{     
    NSUInteger * sampleIn = (NSUInteger *)ioData.mBuffers[0].mData;
    for (int i = 0; i < nOver2; i++) {
    double multiplier = 0.5 * (1 - cos(2*M_PI*i/nOver2-1));
        A.realp[i] = multiplier * sampleIn[i];
        A.imagp[i] = 0;
    }

    memset(ioData.mBuffers[0].mData, 0, ioData.mBuffers[0].mDataByteSize);  
    vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);  

    vDSP_zvmags(&A, 1, A.realp, 1, nOver2);           

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

    int peakIndex = 0;  
    for (size_t i=1; i < nOver2-1; ++i) {  
        if ((obtainedReal[i] > obtainedReal[i-1]) && (obtainedReal[i] > obtainedReal[i+1]))         
        {  
            peakIndex = i;  
            break;  
        }  
    }  

    //here I don't know how to calculate frequency with my data   
    float frequency = obtainedReal[peakIndex-1] / 44100 / n;

    vDSP_destroy_fftsetup(setupReal);  
    free(obtainedReal);  
    free(A.realp);  
    free(A.imagp);  

    return frequency;  
}  

I got 1.485757 and 1.332233 as my first frequencies

TylerH
  • 20,799
  • 66
  • 75
  • 101
Fabrice W
  • 51
  • 1
  • 7
  • You're going to need a suitable window function prior to the FFT, e.g. Hanning. – Paul R May 02 '11 at 16:58
  • Thank you, i'm gonna take a look to hanning window ! – Fabrice W May 02 '11 at 19:44
  • @Paul R, i just added hanning window, but always got 32 or 0 as result, it's much better than before but the result is still wrong i guess, since i think a frequency of 32 in the beginning must be wrong, thanks. – Fabrice W May 02 '11 at 22:14

1 Answers1

3

It looks to me like there is a problem in the conversion to complex input for the FFT. vDSP_ctoz() splits a buffer where real and imaginary components are interleaved into two buffers, one real and one imaginary. Your input to that function appears to be just real data that has been casted to COMPLEX. This means that your input buffer to vDSP_ctoz() is only half as long as it needs to be and some garbage data beyond the buffer size is getting converted.

You either need to create sampleOut to be 2*n in length and set every other value (the real parts) or better yet, you can bypass the vDSP_ctoz() and directly copy your input data into A.realp and set A.imagp to zeros. vDSP_ctoz() should only be needed when interfacing to a source that produces interleaved complex data.

Edit

Ok, I think I was wrong on my first suggestion since the vDSP documentation says that the real input of the real-to-complex in-place fft should be formatted into the split complex format such that imagp contains even samples and realp contains the odd samples. I have not actually used the vDSP library, but I am familiar with a lot of other FFT libraries and I missed that detail.

You should be able to find the peaks using A.realp after the call to vDSP_zvmags(&A, 1, A.realp, 1, nOver2); At that point, A.realp should contain the magnitude squared of the FFT output, which is scalar. If you are going to do the scaling, it should be done before the mag2 operation, but it may not be needed if you are just looking for the peaks.

To get the real frequencies represented by the FFT output, use this formula:

F = (i * Fs) / N,   i=0,1,...,N/2

where

i is the index of the FFT output buffer Fs is the audio sampling rate N is the FFT length

so your calculation might look like this:

float frequency = (peakIndex * 44100) / n;

Keep in mind that vDSP only returns the first half of the input spectrum for real input since the second half is redundant. So the FFT output represents frequencies from 0 to Fs/2.

One other note is that I don't know if your peak finding algorithm will work very well since FFT output will not be smooth and there will often be a lot of oscillation. You are simply taking the first sample where the two adjacent samples are lower. If you just want to find a single peak, it would be better just to find the max magnitude across the entire output. If you want to find multiple peaks, you will have to do something more sophisticated.

Jason B
  • 12,835
  • 2
  • 41
  • 43
  • @Jason thanks i'll look for that ! What about the calcul for the frequency ? i believe that i'm doing wrong with this one too. – Fabrice W May 03 '11 at 14:44
  • @Jason I just edited my answer with your help, results are much better now like 1.58, 1.49 etc etc but still missing something i guess. – Fabrice W May 03 '11 at 15:13
  • You know, I think I may have told you wrong. After reading the documentation, it does say that the real data should be split such that the even samples are in the real buffer and the odd samples are in the imaginary buffer. I think you may have been right initially. – Jason B May 03 '11 at 16:01
  • @Jason thank you ! i'm gonna look for something more sophisticated for to get the peak, can you please give me guidance to do the thing in the right way ? thanks – Fabrice W May 03 '11 at 21:47
  • Do you want to find multiple peaks, or just a single peak? What are you trying to do? – Jason B May 04 '11 at 13:55
  • @Jason Don't know which is better to make a spectrum to represent the ipod library mp3, so i think i'll try both, what do you think about this ? any advices ? – Fabrice W May 04 '11 at 18:06
  • Are you wanting to display the spectrum, or are you wanting to in some other way characterize the mp3 file? What exactly are you trying to represent? – Jason B May 04 '11 at 18:24
  • @Jason, i'm trying to display a spectrum for now, maybe something more complicated but this'll be later. – Fabrice W May 04 '11 at 21:20
  • If you just want to display the spectrum, there is no need to find the peaks. Just plot the magnitude squared of the FFT output. – Jason B May 05 '11 at 12:55
  • @Jason thank you for sharing some time with me ! that help a lot ! – Fabrice W May 05 '11 at 14:33