如何获得 iOS Accelerate FFT 结果的频率幅度?

Posted

技术标签:

【中文标题】如何获得 iOS Accelerate FFT 结果的频率幅度?【英文标题】:How to get iOS Accelerate FFT results in frequency magnitudes? 【发布时间】:2014-09-22 21:32:51 【问题描述】:

我使用此代码(基于 Apple 的 audioRouch 示例):

void FFTHelper::ComputeFFT(Float32* inAudioData, Float32* outFFTData)

    if (inAudioData == NULL || outFFTData == NULL) return;

    // Generate a split complex vector from the real data
    vDSP_ctoz((COMPLEX *)inAudioData, 2, &mDspSplitComplex, 1, mFFTLength);

    // Take the fft and scale appropriately
    vDSP_fft_zrip(mSpectrumAnalysis, &mDspSplitComplex, 1, mLog2N, kFFTDirection_Forward);
    vDSP_vsmul(mDspSplitComplex.realp, 1, &mFFTNormFactor, mDspSplitComplex.realp, 1, mFFTLength);
    vDSP_vsmul(mDspSplitComplex.imagp, 1, &mFFTNormFactor, mDspSplitComplex.imagp, 1, mFFTLength);

    // Zero out the nyquist value
    mDspSplitComplex.imagp[0] = 0.0;

    // Complex vector magnitudes squared; single precision.
    // Calculates the squared magnitudes of complex vector A.
    vDSP_zvmags(&mDspSplitComplex, 1, outFFTData, 1, mFFTLength);


在最简单的可能 - 1Hz 正弦波上计算 FFT(上移 1 个单位):

    Float32 waveFreq            = 1.0;
    int     samplesCount        = 1024;
    Float32 samplesPerSecond    = 1000;        //sample rate
    Float32 dt = 1 / samplesPerSecond;
    Float32 sd = M_PI * 2.0 * waveFreq;

    FFTHelper *mFFTHelper = new FFTHelper(samplesCount);

    Float32 NyquistMaxFreq  = samplesPerSecond/2.0;
    Float32 fftDataSize     = samplesCount/2.0;

    Float32 *sinusoidOriginal = (Float32 *)malloc(sizeof(Float32) * samplesCount);
    Float32 *outFFTData = (Float32 *)malloc(sizeof(Float32) * fftDataSize);

    // 2. Generate sin samples:
    for (int i = 0; i < samplesCount; i++) 

        Float32 x = dt * i;
        sinusoidOriginal[i] = sin(sd * x) + 1;
        [originalPlot addVector2D:GLVector2DMake(x, sinusoidOriginal[i])];
    

    mFFTHelper->ComputeFFT(sinusoidOriginal, outFFTData);

    for (int i = 0; i < fftDataSize; i++) 

            Float32 hz = ((Float32)i / (Float32)fftDataSize) * NyquistMaxFreq;
            GLfloat mag = outFFTData[i];
            [fftPlot addVector2D:GLVector2DMake(hz, 0)];
            [fftPlot addVector2D:GLVector2DMake(hz, mag)];

    

我得到的结果是:

黑线是来自 FTT 的绘图仪结果,在其频率处水平定位。 DC 值(左起第 1 条黑线)看起来没问题,正确表示 y = sin(x) + 1 垂直偏移量。

但是为什么代表正弦方程中唯一频率的第二条黑线没有幅度 = 1 并且不完全保持在 1Hz?

谁能指点我使用 vDSP 函数将 FFT 结果从输入信号转换为幅度单位?

【问题讨论】:

【参考方案1】:

简答:

您似乎正在使用我在这个地方的回答中的代码:https://***.com/a/19966776/468812 这很棒,但是:

您无法避免信号中出现这些额外频率。

您生成的信号(正弦波)是无限信号。 如果你“裁剪它”并且“只使用一个信号”,它会在两端引入“削波噪声”。

但是您可以通过在 FFT 之前获取更大的输入块和 using windowing 来最小化噪声。 Accelerate Framework 提供了一些良好且简单的窗口函数。 (例如Hann Function、vDSP_hann_window) 另外 - 使用更大的输入块。输入越大,频率检测越精确。

参见this article,google:Spectral Leakage,窗函数。

【讨论】:

以上是关于如何获得 iOS Accelerate FFT 结果的频率幅度?的主要内容,如果未能解决你的问题,请参考以下文章

iOS Accelerate低通FFT滤波器镜像结果

Apple 的 Accelerate vDSP:如何从 FFT 中获取复数向量的参数

如何在 iOS 中使用 vDSP 将声音文件转换为 FFT

使用 Apple Accelerate 框架选择实数和复数 2D FFT

iPhone Accelerate Framework FFT 转换二维数组

Apple Accelerate vDSP fft vs DFT 和比例因子