geir58
geir58

Reputation: 21

Passing a realtime FFT setup to execution function

I'm trying to do a realtime FFT on microphone data in iOS using Novacaine and I cant seem to pass the FFT setup and ring buffer to my FFT function.

For testing, my plan was to do the setup in the viewWillAppear method, then execute my FFT function when a button is pressed. I'll then deallocate the memory for my buffers and destroy the FFTsetup when the window closes.

Here's what I have so far. I've tried several variations of passing arguments to viewWillAppear but nothing seems to be working. Any suggestions are appreceated.

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    ringBuffer = new RingBuffer(8192, 2);
    audioManager = [Novocaine audioManager];

[audioManager setInputBlock:^(float *data, UInt32 numFrames, UInt32 numChannels) {


// Setup FFT here
int numSamples = 8192;


// Setup the length
vDSP_Length log2n = log2f(numSamples);

// Calculate the weights array. This is a one-off operation.
FFTSetup fftSetup = vDSP_create_fftsetup(log2n, FFT_RADIX2);

// For an FFT, numSamples must be a power of 2, i.e. is always even
int nOver2 = numSamples/2;

// Populate *window with the values for a hamming window function
float *window = (float *)malloc(sizeof(float) * numSamples);
vDSP_hamm_window(window, numSamples, 0);
// Window the samples
vDSP_vmul(data, 1, window, 1, data, 1, numSamples);

// Define complex buffer
COMPLEX_SPLIT A;
A.realp = (float *) malloc(nOver2*sizeof(float));
A.imagp = (float *) malloc(nOver2*sizeof(float));

}];
}

- (IBAction)buttonPressed:(id)sender {

    // do myFFT here

    // Pack samples:
    // C(re) -> A[n], C(im) -> A[n+1]
    vDSP_ctoz((COMPLEX*)data, 2, &A, 1, numSamples/2);

    //Perform a forward 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 magnitudes
    float *amp = (float *)malloc(sizeof(float) * numSamples);
    amp[0] = A.realp[0]/(numSamples*2);
    float max = 0;
    int indexOfMax = -1;
    for(int i=1; i<numSamples; i++) {
        amp[i]=A.realp[i]*A.realp[i]+A.imagp[i]*A.imagp[i];
        //printf("i[%ld]: %.1f %ldHz \n", (long)i, amp[i], (long)22000 * i/numSamples);

        if(amp[i] > max) {
            max = amp[i];
            indexOfMax = i;
        }
    }

    long fmax = ((long)indexOfMax - numSamples/2)*44100/4096;
    printf("max frequency is %ld\n", fmax);
    free(amp);

} 

Upvotes: 2

Views: 108

Answers (1)

hotpaw2
hotpaw2

Reputation: 70703

The real-time audio input block isn't for doing any heavy computation, such as an FFT. Instead the input block should just (quickly!) copy the data into a large enough ring buffer.

Later, during a displayLink timer or when the button is pressed, you can check the ring buffer to see if it has enough new data, and then do the FFT.

Your code also seems to confuse the FFT size numSamples with the (usually much smaller) actual amount of samples received by the callback, which is numFrames. You can't do the FFT until enough callbacks have been called for numFrames to sum up to be equal or greater than the FFT size.

Upvotes: 2

Related Questions