tags:

views:

404

answers:

3

I'm looking to perform an FFT on a linear PCM audio file (with potentially more than one audio channel) on OS X. What is the best way to go about this?

Several sources have indicated that Apple's Accelerate Framework is what I need. If so, how should I extract and properly prepare the floating point data for use in those FFT functions?

A: 

It seems to me that you should be looking into Core Audio... I'm not very familiar with it, but it looks like it should get the channel deinterleaving for you already, and it works directly with PCM data. Because of my low familiarity, take this with a grain of salt, but I'd try putting the FFT in an Audio Unit, taking a single channel of PCM data as input, storing the results of the FFT somewhere accessible, and passing the input through to the output.

As far as actually performing the FFT, the main challenge seems, to me, to be in coercing the PCM input to the double * vector that the FFT routine wants to operate against. Looking through vDSP.h (part of the accelerate framework), I see functions like vDSP_vflt16D (to convert a vector of 16-bit integers to a vector of double-precision real numbers), which looks like it'd solve your problem.

Core Audio Introduction

Aidan Cully
vDSP_vflt16 does do the work of converting those 16-bit integers into 32-bit floats but I'm afraid that's not enough for this. I need to convert these values into 32-bit floating point complex numbers.
Anonymous
+2  A: 

When doing an FFT on audio data, the samples should go into the real portion and the imaginary portion should be zero.

Most FFT libraries, including Apple's vDSP, include a method called a "real FFT", where the input is real (no imaginary component) and the output is complex.

dmazzoni
A: 

Here's roughly what you want to do. Fill in your own input and output functions.

    // Stick new data into inData, a (float*) array
    fetchFreshData(inData); 

    // (You might want to window the signal here... )
    doSomeWindowing(inData);

    // Convert the data into a DSPSplitComplex 
    // Pardon the C++ here. Also, you should pre-allocate this, and NOT
    // make a fresh one each time you do an FFT. 
    mComplexData = new DSPSplitComplex;
    float *realpart = (float *)calloc(mNumFrequencies, sizeof(float));
    float *imagpart = (float *)calloc(mNumFrequencies, sizeof(float));
    mComplexData->realp = realpart;
    mComplexData->imagp = imagpart;

    vDSP_ctoz((DSPComplex *)inData, 2, mComplexData, 1, mNumFrequencies);

    // Calculate the FFT
    // ( I'm assuming here you've already called vDSP_create_fftsetup() )
    vDSP_fft_zrip(mFFTSetup, mComplexData, 1, log2f(mNumFrequencies), FFT_FORWARD);

    // Don't need that frequency
    mComplexData->imagp[0] = 0.0;

    // Scale the data
    float scale = (float) 1.0 / (2 * (float)mSignalLength);
    vDSP_vsmul(mComplexData->realp, 1, &scale, mComplexData->realp, 1, mNumFrequencies);
    vDSP_vsmul(mComplexData->imagp, 1, &scale, mComplexData->imagp, 1, mNumFrequencies);

    // Convert the complex data into something usable
    // spectrumData is also a (float*) of size mNumFrequencies
    vDSP_zvabs(mComplexData, 1, spectrumData, 1, mNumFrequencies);

    // All done!
    doSomethingWithYourSpectrumData(spectrumData);

Hope that helps.

alexbw