【问题标题】:Why are my frequency values for iPhone FFT incorrect?为什么我的 iPhone FFT 频率值不正确?
【发布时间】:2014-05-08 04:38:23
【问题描述】:

我一直在尝试使用 Apple Accelerate 框架中的 FFT 获得准确的频率,但我无法弄清楚为什么我的值偏离了真实频率。

我一直使用这篇文章 http://www.dspdimension.com/admin/pitch-shifting-using-the-ft/ 作为我实施的基础,在努力达到现在的点之后,我完全被难住了。

到目前为止,我已经在 -> Hanning 窗口 -> FFT -> 相位计算 -> 奇怪的最终输出中获得了音频。我认为我的数学在某个地方会有问题,但我现在真的没有想法。

输出比应有的低很多,例如,我输入 440Hz 并打印出 190Hz,或者我输入 880Hz 并打印出 400Hz。在大多数情况下,这些结果是一致的,但并非总是如此,而且任何事物之间似乎也没有任何共同因素......

这是我的代码:

enum
{
    sampleRate  = 44100,
    osamp       = 4,
    samples     = 4096,
    range       = samples * 7 / 16,
    step        = samples / osamp
};

NSMutableArray *fftResults;

static COMPLEX_SPLIT    A;
static FFTSetup         setupReal;
static uint32_t         log2n, n, nOver2;
static int32_t          stride;

static float            expct = 2*M_PI*((double)step/(double)samples);
static float            phase1[range];
static float            phase2[range];
static float            dPhase[range];

- (void)fftSetup
{
    // Declaring integers
    log2n = 12;
    n = 1 << log2n;
    stride = 1;
    nOver2 = n / 2;

    // Allocating memory for complex vectors
    A.realp = (float *) malloc(nOver2 * sizeof(float));
    A.imagp = (float *) malloc(nOver2 * sizeof(float));

    // Allocating memory for FFT
    setupReal = vDSP_create_fftsetup(log2n, FFT_RADIX2);

    // Setting phase
    memset(phase2, 0, range * sizeof(float));

}
        // For each sample in buffer...
        for (int bufferCount = 0; bufferCount < audioBufferList.mNumberBuffers; bufferCount++)
        {
            // Declaring samples from audio buffer list
            SInt16 *samples = (SInt16*)audioBufferList.mBuffers[bufferCount].mData;


            // Creating Hann window function
            for (int i = 0; i < nOver2; i++)
            {
                double hannMultiplier = 0.5 * (1 - cos((2 * M_PI * i) / (nOver2 -  1)));

                // Applying window to each sample
                A.realp[i] = hannMultiplier * samples[i];
                A.imagp[i] = 0;
            }

            // Applying FFT
            vDSP_fft_zrip(setupReal, &A, stride, log2n, FFT_FORWARD);

            // Detecting phase
            vDSP_zvphas(&A, stride, phase1, stride, range);

            // Calculating phase difference
            vDSP_vsub(phase2, stride, phase1, stride, dPhase, stride, range);

            // Saving phase
            memcpy(phase2, phase1, range * sizeof(float));

            // Extracting DSP outputs
            for (size_t j = 0; j < nOver2; j++)
            {
                NSNumber *realNumbers = [NSNumber numberWithFloat:A.realp[j]];
                NSNumber *imagNumbers = [NSNumber numberWithFloat:A.imagp[j]];

                [real addObject:realNumbers];
                [imag addObject:imagNumbers];

            }

            // Combining real and imaginary parts
            [resultsCombined addObject:real];
            [resultsCombined addObject:imag];

            // Filling FFT output array
            [fftResults addObject:resultsCombined];
        }

    }


    int fftCount = [fftResults count];
    NSLog(@"FFT Count: %d",fftCount);

    // For each FFT...
    for (int i = 0; i < fftCount; i++)
    {
        // Declaring integers for peak detection
        float peak = 0;
        float binNumber = 0;

        // Declaring integers for phase detection
        float deltaPhase;

        static float trueFrequency[range];


        for (size_t j = 1; j < range; j++)
        {
            // Calculating bin magnitiude
            float realVal   = [[[[fftResults objectAtIndex:i] objectAtIndex:0] objectAtIndex:j] floatValue];
            float imagVal   = [[[[fftResults objectAtIndex:i] objectAtIndex:1] objectAtIndex:j] floatValue];
            float magnitude = sqrtf(realVal*realVal + imagVal*imagVal);

            // Peak detection
            if (magnitude > peak)
            {
                peak = magnitude;
                binNumber = (float)j;
            }

            // Getting phase difference
            deltaPhase = dPhase[j];

            // Subtract expected difference
            deltaPhase -= (float)j*expct;

            // Map phase difference into +/- pi interval
            int qpd = deltaPhase / M_PI;

            if (qpd >= 0)
                qpd += qpd&1;
            else
                qpd -= qpd&1;

            deltaPhase -= M_PI * (float)qpd;

            // Getting bin deviation from +/i interval
            float deltaFrequency = osamp * deltaPhase / (2 * M_PI);

            // Calculating true frequency at the j-th partial
            trueFrequency[j] = (j * (sampleRate/samples)) + (deltaFrequency * (sampleRate/samples));

        }

        UInt32 mag;
        mag = binNumber;

        // Extracting frequency at bin peak
        float f = trueFrequency[mag];

        NSLog(@"True frequency = %fHz", f);

        float b = roundf(binNumber*(sampleRate/nOver2));

        NSLog(@" Bin frequency = %fHz", b);

    }

【问题讨论】:

  • 看看这里,我不认为您实际上是在将 FFT 输出索引转换为实际频率!!! stackoverflow.com/questions/4364823/…
  • 我已经看过了,我已经得到了那里正在讨论的 bin 输出(最后 2 行代码),你指的是什么?我追求确切的频率,我认为我的问题在于我的相位计算,该页面似乎没有涉及到那个......除非我误解了你在说什么!
  • 你能解释为什么相位会影响精确的频率吗?不应该只影响实力吗?
  • 我的理解是,如果频率等于 bin 频率,那么它将没有相位差,但是如果频率与 bin 频率不同。那么它的相位可以用来计算它离 bin 频率有多远:即精确频率 = delta 频率 + bin 频率,其中 delta 频率是通过这个相位计算的。好吧,我认为至少是这样,因为我的程序不起作用!
  • fft 只有有限的频率分辨率等于 bin 宽度。该阶段没有按照您的预期进行。如果您点击 trumpetlicks 发布的链接,您应该没问题。

标签: ios objective-c signal-processing fft accelerate-framework


【解决方案1】:

请注意,预期相位差(即使对于以 bin 为中心的频率)取决于 FFT 对的窗口偏移或重叠,以及 FFT 结果的 bin 数量或频率。例如如果您将窗口偏移很小(1 个样本),则 2 个 FFT 之间的展开相位差将小于偏移较大的情况。在相同的偏移下,如果频率更高,则两个 FFT 的相同 bin 之间的预期相位差会更大(或者它会缠绕更多)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-02
    • 1970-01-01
    • 2015-04-28
    • 1970-01-01
    • 2018-02-14
    • 2023-04-07
    • 2017-02-07
    • 1970-01-01
    相关资源
    最近更新 更多