我正在使用a nice FFT library I found online来查看我是否可以编写音高检测程序。到目前为止,我已经能够成功地让磁带库对包含少量正弦波的测试音频信号进行FFT计算,包括440Hz的一个正弦波(我使用16384个样本作为大小,采样率为44100Hz)。
FFT输出如下:
433.356Hz - Real: 590.644 - Imag: -27.9856 - MAG: 16529.5
436.047Hz - Real: 683.921 - Imag: 51.2798 - MAG: 35071.4
438.739Hz - Real: 4615.24 - Imag: 1170.8 - MAG: 5.40352e+006
441.431Hz - Real: -3861.97 - Imag: 2111.13 - MAG: 8.15315e+006
444.122Hz - Real: -653.75 - Imag: 341.107 - MAG: 222999
446.814Hz - Real: -564.629 - Imag: 186.592 - MAG: 105355
正如您所看到的,441.431Hz和438.739Hz频段都显示出相同的高幅度输出(“MAG:”后面的最右侧数字),因此很明显目标频率440Hz介于两者之间。提高分辨率可能是一种关闭方式,但这会增加计算时间。
如何计算两个频率仓之间的确切频率?
更新
我尝试了在DSPGuru网站上讨论的Barry Quinn's "Second Estimator",并取得了优异的成绩。以下显示440Hz方波的结果 - 现在我只有0.003Hz!
这是the code I used。我只是改编了this example我找到了,这是为了Swift。感谢大家的宝贵意见,这是一次很棒的学习之旅:)。
答案 0 :(得分:1)
Sinc插值可用于精确地内插(或重建)FFT结果区间之间的频谱。零填充FFT将产生类似的内插频谱。您可以使用具有逐次逼近的高质量插值器(例如窗口Sinc内核)来估计实际光谱峰值,以达到S / N允许的任何分辨率。除非在插值内核中包含光谱共轭图像的影响,否则此重建可能无法在DC或Fs / 2 FFT结果区附近工作。
有关时域重建的详细信息,请参阅:https://ccrma.stanford.edu/~jos/Interpolation/Ideal_Bandlimited_Sinc_Interpolation.html和https://en.wikipedia.org/wiki/Whittaker%E2%80%93Shannon_interpolation_formula,但相同的插值方法分别适用于带限或时间限制信号的域,频率或时间。
如果您需要精度较低的估算器,而计算开销要小得多,则抛物线插值(以及其他类似的曲线拟合估算器)可能会起作用。有关抛物线的详细信息,请参阅:https://www.dsprelated.com/freebooks/sasp/Quadratic_Interpolation_Spectral_Peaks.html和https://mgasior.web.cern.ch/mgasior/pap/FFT_resol_note.pdf,其他曲线拟合峰值估算值请参见http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.555.2873&rep=rep1&type=pdf。
答案 1 :(得分:1)
计算" true"频率,一旦我使用 parabola fit algorithm 。它对我的用例非常有用。
这是我为了找到基本频率而遵循的方式:
例如,HPS表示基本(最强)音高集中在DFT的x
个音箱中;如果bin x
属于峰值y
,则抛物线拟合频率取自峰值y
,即您正在寻找的音高。
如果你不是在任何 bin中寻找基本音高,但确切的频率,只需对该音箱应用抛物线。
一些代码可以帮助您入门:
struct Peak
{
float freq ; // Peak frequency calculated by parabola fit algorithm.
float amplitude; // True amplitude.
float strength ; // Peak strength when compared to neighbouring bins.
uint16_t startPos ; // Peak starting position (DFT bin).
uint16_t maxPos ; // Peak location (DFT bin).
uint16_t stopPos ; // Peak stop position (DFT bin).
};
void calculateTrueFrequency( Peak & peak, float const bins, uint32_t const fs, DFT_Magnitudes mags )
{
// Parabola fit:
float a = mags[ peak.maxPos - 1 ];
float b = mags[ peak.maxPos ];
float c = mags[ peak.maxPos + 1 ];
float p = 0.5f * ( a - c ) / ( a - 2.0f * b + c );
float bin = convert<float>( peak.maxPos ) + p;
peak.freq = convert<float>( fs ) * bin / bins / 2;
peak.amplitude = b - 0.25f + ( a - c ) * p;
}