Using Apple’s vDSP/Accelerate FFT

原文地址:  https://gerrybeauregard.wordpress.com/2013/01/28/using-apples-vdspaccelerate-fft/


If you want to write code for signal processing on the Mac or iOS, you really should take advantage of Apple’s Accelerate framework. It provides an extensive library of highly optimized mathematical functions suitable for a wide range of signal processing applications.

While many of the functions are fairly straightforward to use, and well-documented in thevDSP Programming Guide, the FFT functions are a bit maddening, especially when doing FFTs of real signals, which is pretty much always the case when dealing with audio.

The following code example shows how to do a real-to-complex FFT of a real vector; convert from complex representation to magnitude and phase; convert it back to rectangular/complex representation; and do complex-to-real FFT, with the correct scaling to get back to the original signal.

//  main.cpp
//  AccelerateFFT
//
//  Demo of how to use Apple's blazingly fast but maddeningly confusing
//  real->complex and complex->real FFT functions.
//
//  Created by Gerry Beauregard (g.beauregard [at] ieee.org) on 2013-01-28.
//
//  Use this code however you like. No credit required, but if this code
//  saves you some hair-pulling, a hat-tip and kind comment is always
//  appreciated <span class="wp-smiley wp-emoji wp-emoji-wink" title=";-)">;-)</span>
 
#include <stdio.h>
#include <Accelerate/Accelerate.h>
 
const int LOG_N = 4; // Typically this would be at least 10 (i.e. 1024pt FFTs)
const int N = 1 << LOG_N;
const float PI = 4*atan(1);
 
int main(int argc, const char * argv[])
{
    // Set up a data structure with pre-calculated values for
    // doing a very fast FFT. The structure is opaque, but presumably
    // includes sin/cos twiddle factors, and a lookup table for converting
    // to/from bit-reversed ordering. Normally you'd create this once
    // in your application, then use it for many (hundreds! thousands!) of
    // forward and inverse FFTs.
    FFTSetup fftSetup = vDSP_create_fftsetup(LOG_N, kFFTRadix2);
 
    // -------------------------------
    // Set up a bunch of buffers
 
    // Buffers for real (time-domain) input and output signals.
    float *x = new float[N];
    float *y = new float[N];
 
    // Initialize the input buffer with a sinusoid
    int BIN = 3;
    for (int k = 0; k < N; k++)
        x[k] = cos(2*PI*BIN*k/N);
 
    // We need complex buffers in two different formats!
    DSPComplex *tempComplex = new DSPComplex[N/2];
 
    DSPSplitComplex tempSplitComplex;
    tempSplitComplex.realp = new float[N/2];
    tempSplitComplex.imagp = new float[N/2];
 
    // For polar coordinates
    float *mag = new float[N/2];
    float *phase = new float[N/2];
 
    // ----------------------------------------------------------------
    // Forward FFT
 
    // Scramble-pack the real data into complex buffer in just the way that's
    // required by the real-to-complex FFT function that follows.
    vDSP_ctoz((DSPComplex*)x, 2, &tempSplitComplex, 1, N/2);
 
    // Do real->complex forward FFT
    vDSP_fft_zrip(fftSetup, &tempSplitComplex, 1, LOG_N, kFFTDirection_Forward);
 
    // Print the complex spectrum. Note that since it's the FFT of a real signal,
    // the spectrum is conjugate symmetric, that is the negative frequency components
    // are complex conjugates of the positive frequencies. The real->complex FFT
    // therefore only gives us the positive half of the spectrum from bin 0 ("DC")
    // to bin N/2 (Nyquist frequency, i.e. half the sample rate). Typically with
    // audio code, you don't need to worry much about the DC and Nyquist values, as
    // they'll be very close to zero if you're doing everything else correctly.
    //
    // Bins 0 and N/2 both necessarily have zero phase, so in the packed format
    // only the real values are output, and these are stuffed into the real/imag components
    // of the first complex value (even though they are both in fact real values). Try
    // replacing BIN above with N/2 to see how sinusoid at Nyquist appears in the spectrum.
    printf("\nSpectrum:\n");
    for (int k = 0; k < N/2; k++)
    {
        printf("%3d\t%6.2f\t%6.2f\n", k, tempSplitComplex.realp[k], tempSplitComplex.imagp[k]);
    }
 
    // ----------------------------------------------------------------
    // Convert from complex/rectangular (real, imaginary) coordinates
    // to polar (magnitude and phase) coordinates.
 
    // Compute magnitude and phase. Can also be done using vDSP_polar.
    // Note that when printing out the values below, we ignore bin zero, as the
    // real/complex values for bin zero in tempSplitComplex actually both correspond
    // to real spectrum values for bins 0 (DC) and N/2 (Nyquist) respectively.
    vDSP_zvabs(&tempSplitComplex, 1, mag, 1, N/2);
    vDSP_zvphas(&tempSplitComplex, 1, phase, 1, N/2);
 
    printf("\nMag / Phase:\n");
    for (int k = 1; k < N/2; k++)
    {
        printf("%3d\t%6.2f\t%6.2f\n", k, mag[k], phase[k]);
    }
 
    // ----------------------------------------------------------------
    // Convert from polar coordinates back to rectangular coordinates.
 
    tempSplitComplex.realp = mag;
    tempSplitComplex.imagp = phase;
 
    vDSP_ztoc(&tempSplitComplex, 1, tempComplex, 2, N/2);
    vDSP_rect((float*)tempComplex, 2, (float*)tempComplex, 2, N/2);
    vDSP_ctoz(tempComplex, 2, &tempSplitComplex, 1, N/2);
 
    // ----------------------------------------------------------------
    // Do Inverse FFT
 
    // Do complex->real inverse FFT.
    vDSP_fft_zrip(fftSetup, &tempSplitComplex, 1, LOG_N, kFFTDirection_Inverse);
 
    // This leaves result in packed format. Here we unpack it into a real vector.
    vDSP_ztoc(&tempSplitComplex, 1, (DSPComplex*)y, 2, N/2);
 
    // Neither the forward nor inverse FFT does any scaling. Here we compensate for that.
    float scale = 0.5/N;
    vDSP_vsmul(y, 1, &scale, y, 1, N);
 
    // Assuming it's all correct, the input x and output y vectors will have identical values
    printf("\nInput & output:\n");
    for (int k = 0; k < N; k++)
    {
        printf("%3d\t%6.2f\t%6.2f\n", k, x[k], y[k]);
    }
 
    return 0;
}


Note that in order to do anything really interesting with audio on the Mac or iOS (for example the audio time-stretching in my AudioStretch app), there’s loads of other stuff you need to learn how to do: setting up a real-time audio input and output; how to grab and window (Hanning, Hamming, etc.) frames of audio data; how to manipulate signals in the frequency domain; synthesis using overlap-add methods, etc.  Time-permitting, I might cover some of that in future posts.

你可能感兴趣的:(ios,osx,audio,fft,vDSP)