iosfftaudio-processingcaf

How to play and read .caf PCM audio file


I have an app that selects a song from the iPod Library then copies that song into the app's directory as a '.caf' file. I now need to play and at the same time read that file into Apples FFT from the Accelerate framework so I can visualize the data like a spectrogram. Here is the code for the FFT:

void FFTAccelerate::doFFTReal(float samples[], float amp[], int numSamples)
{
int i;
vDSP_Length log2n = log2f(numSamples);

//Convert float array of reals samples to COMPLEX_SPLIT array A
vDSP_ctoz((COMPLEX*)samples,2,&A,1,numSamples/2);

//Perform FFT using fftSetup and A
//Results are returned in A
vDSP_fft_zrip(fftSetup, &A, 1, log2n, FFT_FORWARD);

//Convert COMPLEX_SPLIT A result to float array to be returned
amp[0] = A.realp[0]/(numSamples*2);
for(i=1;i<numSamples;i++)
    amp[i]=sqrt(A.realp[i]*A.realp[i]+A.imagp[i]*A.imagp[i])/numSamples;
}

//Constructor
FFTAccelerate::FFTAccelerate (int numSamples)
{
vDSP_Length log2n = log2f(numSamples);
fftSetup = vDSP_create_fftsetup(log2n, FFT_RADIX2);
int nOver2 = numSamples/2;
A.realp = (float *) malloc(nOver2*sizeof(float));
A.imagp = (float *) malloc(nOver2*sizeof(float));
}

My question is how to I loop through the '.caf' audio file to feed the FFT while at the same time playing the song? I only need one channel. Im guessing I need to get 1024 samples of the song, process that in the FTT and then move further down the file and grab another 1024 samples. But I dont understand how to read an audio file to do this. The file has a sample rate of 44100.0 hz, is in linear PCM format, 16 Bit and I believe is also interleaved if that helps...


Solution

  • Try the ExtendedAudioFile API (requires AudioToolbox.framework).

    #include <AudioToolbox/ExtendedAudioFile.h>
    
    NSURL   *urlToCAF = ...;
    
    ExtAudioFileRef caf;
    OSStatus    status;
    
    status = ExtAudioFileOpenURL((__bridge CFURLRef)urlToCAF, &caf);
    if(noErr == status) {
        // request float format
        const UInt32 NumFrames = 1024;
        const int ChannelsPerFrame = 1;  // Mono, 2 for Stereo
    
        // request float format
        AudioStreamBasicDescription clientFormat;
        clientFormat.mChannelsPerFrame = ChannelsPerFrame;
        clientFormat.mSampleRate = 44100;
    
        clientFormat.mFormatID = kAudioFormatLinearPCM;
        clientFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved; // float
        int cmpSize = sizeof(float);
        int frameSize = cmpSize*ChannelsPerFrame;
        clientFormat.mBitsPerChannel = cmpSize*8;
        clientFormat.mBytesPerPacket = frameSize;
        clientFormat.mFramesPerPacket = 1;
        clientFormat.mBytesPerFrame = frameSize;
    
        status = ExtAudioFileSetProperty(caf, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);
         if(noErr != status) { /* handle it */ }
    
        while(1) {
            float   buf[ChannelsPerFrame*NumFrames];
            AudioBuffer ab = { ChannelsPerFrame, sizeof(buf), buf };
            AudioBufferList abl;
            abl.mNumberBuffers = 1;
            abl.mBuffers[0] = ab;
    
            UInt32  ioNumFrames = NumFrames;
            status = ExtAudioFileRead(caf, &ioNumFrames, &abl);
    
            if(noErr == status) {
                // process ioNumFrames here in buf
                if(0 == ioNumFrames) {
                    // EOF!
                    break;
                } else if(ioNumFrames < NumFrames) {
                    // TODO: pad buf with zeroes out to NumFrames 
                } else {
                     float amp[NumFrames]; // scratch space
                     doFFTReal(buf, amp, NumFrames);
                }
            }
        }
    
        // later
        status = ExtAudioFileDispose(caf);
        if(noErr != status) { /* hmm */ }
    }