1

我想在真实的音频设备上应用 FFT 并从中计算峰值

这是我的代码..

    N=8192
    kiss_fft_cpx out[N/2 +1];

    int len = fft->N / 2 + 1;

    kiss_fft_scalar* samples = &samples2[0]; //inputs from the mic

    kiss_fftr(fft->config, samples, out);

    for (int i = 0; i < len; i++) {

        float re = scale(out[i].r) * N;
        float im = scale(out[i].i) * N;

        if (i > 0)
            spectrum[i] = sqrtf(re * re + im * im) / (N / 2);
        else
            spectrum[i] = sqrtf(re * re + im * im) / N;

    }

现在我使用代码计算选择。但每次它返回 0

float peak = 0;
float maxEnergy = 0;
for (int i = 0; i < BUFFER_SIZE / 2 + 1; i++) {

    float binEnergy = spectrum.at(i);

    if (binEnergy > maxEnergy) {

        maxEnergy = binEnergy;
        peak = i;

    }

}

在这里我总是得到峰值= 0。请帮忙

这是前 25 个 FFT 样本的频谱输出:

 06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[0]: 0.036530
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[1]: 0.011568
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[2]: 0.012086
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[3]: 0.011568
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[4]: 0.040397
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[5]: 0.011568
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[6]: 0.012086
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[7]: 0.011568
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[8]: 0.044121
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[9]: 0.011568
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[10]: 0.012086
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[11]: 0.011568
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[12]: 0.040396
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[13]: 0.011568
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[14]: 0.012086
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[15]: 0.011568
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[16]: 0.116464
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[17]: 0.011568
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[18]: 0.012086
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[19]: 0.011568
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[20]: 0.040397
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[21]: 0.011568
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[22]: 0.012086
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[23]: 0.011568
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(257) > specturm[24]: 0.044121
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(267) > peak   2223.000000
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(270) > FREQUENCY   4342.773438
06-05 17:28:34.512 : INFO / HelloWorld ( 3351 : 3351 ) : void GuitarTunerMainForm::calculateFrequency(Tizen::Base::ByteBuffer *)(281) > octave 8
4

2 回答 2

2

有一种相当简单但计算量大的方法可以使用自相关方法对语音进行音高检测。我不明白为什么它也不适用于吉他!然而,当有多个基频时,它会遇到困难。但是,我不知道处理该问题的算法。

您需要采集足够的样本以覆盖至少 3 个音高周期。然后,您对信号进行自相关(可以使用 FFT 有效地执行自相关)。

一个你有你的自相关信号,你会发现滞后 0 处的最大峰值。第二高的峰值应该是你的音高。

通过在自相关之前使用汉明窗之类的方法对输入信号进行加窗,您可以获得更好的结果。

此外, Praat名声的 Paul Boersma 还提出了一种更准确的音高检测方法

基本上使用他的方案。您获取窗口函数的自相关,然后存储以备后用。接下来,您将输入信号窗口化。自相关该信号。现在除以窗函数的自相关。最后,选择最高峰,滞后 0 的偏移量是您的音高检测的样本数。

值得注意的是,您确实需要对自相关峰值进行插值以获得最佳结果。我个人使用了抛物线插值,我获得的精度提高是巨大的。抛物线插值非常简单:

void ParabolicInterpolation( const float kA, const float kB, const float kC, float& p, float& m )
{
    p   = 0.5f * ((kA - kC) / (kA - (2.0f * kB) + kC));
    m   = (0.25f * (kA - kC) * p);
}

其中 kB 是您确定的自相关峰值,kA 是它之前的自相关样本,kC 是之后的样本。

编辑:如果您不需要上述方法提供的那种精度,还有另一种非常简单的计算基频的方法,称为谐波产品频谱(查看本演示文稿的开头)。基本上你从你的 FFT 开始。您将其转换为幅度谱。最后,您将其下采样 2x、3x 和 4x。然后将样本相乘。最大的峰值将取决于您的基频。但是,这受到您的 FFT 分辨率的严重限制。

希望有帮助!

于 2013-06-06T10:12:36.660 回答
2

我怀疑您 (a) 未能在 FFT 之前应用合适的窗口函数和/或 (b) 您的直流分量很大。这将导致在 0 Hz 处出现一个大的、可能模糊的(由于频谱泄漏)峰值。您可以通过打印/绘制光谱阵列来验证这一点。

为了解决这个问题:

  • 在 FFT 之前应用合适的窗口函数(例如Hann )

  • 更改您的寻峰循环,使其从 bin 0 上方的某个位置开始,例如

    const int PEAK_MIN = BUFFER_SIZE / 1024;

    for (int i = PEAK_MIN; i < BUFFER_SIZE / 2 + 1; i++) { ...

于 2013-06-05T07:39:01.837 回答