如何使用kissFFT计算峰值?

How can i calculate the peak using kissFFT?

本文关键字:计算 何使用 kissFFT      更新时间:2023-10-16

我想在真实音频设备上应用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;
    }
}

这里我总是得到peak=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

我怀疑您是(a)未能在FFT之前应用合适的窗口函数和/或(b)您有一个大的DC组件。这将导致一个大的,可能是模糊的(由于频谱泄漏)峰值在0赫兹。你可以通过打印/绘制你的频谱阵列来验证这一点。

修改:

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

  • 改变你的峰值查找循环,使它开始于bin 0以上的某个地方,例如

    const int PEAK_MIN = BUFFER_SIZE / 1024;

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

有一种相当简单但计算量很大的方法可以使用自相关方法对声音进行音高检测。我不明白为什么它不能适用于吉他!然而,当存在不止一个基频时,它就会陷入困境。然而,我不知道有什么算法可以处理这个问题。

你需要收集足够的样本来覆盖至少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分辨率的严重限制。

希望有帮助!