如何使用 KissFFT 计算峰值?

Posted

技术标签:

【中文标题】如何使用 KissFFT 计算峰值?【英文标题】:How can i calculate the peak using kissFFT? 【发布时间】:2013-06-05 07:37:01 【问题描述】:

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

【问题讨论】:

使用 FFT 峰值是做吉他调音器的糟糕方法。改为对音高检测/估计方法进行一些研究。 几个月以来我一直在尝试这个,但我无法让任何音高检测代码工作。 通过将已知频率的正弦波放入输入缓冲区并检查每个步骤来调试代码。 【参考方案1】:

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

解决这个问题:

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

更改您的峰值查找循环,使其从 bin 0 以上的某个位置开始,例如

const int PEAK_MIN = BUFFER_SIZE / 1024;

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

【讨论】:

我试过这个但每次我得到相同的峰值..不是差异值【参考方案2】:

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

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

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

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

还有Praat 名气的 Paul Boersma 想出了一个很远的more accurate method of pitch detection。

基本上使用他的方案。您获取窗口函数的自相关,然后存储以备后用。接下来,您将输入信号窗口化。自相关该信号。现在除以窗函数的自相关。最后,选择最高峰,滞后 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 是之后的样本。

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

希望有帮助!

【讨论】:

以上是关于如何使用 KissFFT 计算峰值?的主要内容,如果未能解决你的问题,请参考以下文章

如何估计密度函数并计算其峰值?

如何计算给定会话日志的峰值并发用户

如何获得python脚本的峰值内存使用量?

如何使用点积获得峰值 CPU 性能?

如何将真实的kiss_fft用于音频分析仪?

如何计算LTE-A终端的峰值速率?