我试图弄清楚如何创建一个良好的信号频谱包络。
基本上,我目前正在对音频的窗口部分应用 FFT 并生成频谱幅度的条形图样式表示。
然而,除此之外,我真的很想在顶部覆盖一个好的光谱包络。不幸的是,我似乎无法想出一个好的解决方案。
我尝试了一个简单的峰值选择算法,在该算法中我找到了频谱中的每个峰值(我实际上只是寻找当前值大于任一侧存储桶中的值的点)。这会返回一组峰值,然后我使用 catmull-rom 插值在峰值上绘制线。
正如您在以下屏幕截图中看到的那样,这合理地遵循了频谱:

然而它并不理想。例如,在该图像中,您可以看到它如何不遵循左侧的光谱。
缩小时:

您会看到信封非常参差不齐。它并不可怕,但这真的是构建光谱包络的最佳方式吗?
有没有更好的方法来挑选理想的峰,使其真正包含光谱?如果是这样,任何人都可以指出我的算法吗?
catmull-rom 插值也是最好的方法吗?谁能建议一种更好的插值方法并告诉我如何实现它?
问题与语言无关,但我用 C++ 编写。
编辑:好的,我找到了自动回归建模的实现。
然后我实现了如下方法:
std::vector< float > coefficients( 3 );
AutoRegression( &mSpecBuffer.front(), kFFTSizeDiv2, 3, &coefficients.front(), MAXENTROPY );
mPeakBuffer.clear();
mPeakBuffer.push_back( Peak( 0, mSpecBuffer[0] ) );
mPeakBuffer.push_back( Peak( 1, mSpecBuffer[1] ) );
mPeakBuffer.push_back( Peak( 2, mSpecBuffer[2] ) );
int x = 3;
int xMax = kFFTSizeDiv2;
while( x < xMax )
{
const float k3 = (mPeakBuffer.end() - 3)->peakHeight;
const float k2 = (mPeakBuffer.end() - 2)->peakHeight;
const float k1 = (mPeakBuffer.end() - 1)->peakHeight;
const float k = (k1 * coefficients[0]) + (k2 * coefficients[1]) + (k3 * coefficients[2]);
mPeakBuffer.push_back( Peak( x, k ) );
x++;
}
这给了我以下结果:

在我看来,这看起来好多了。
缩小后它看起来仍然非常好:

所以我只想检查一下,这是否正确?如果是这样,我将沿着这条道路进一步工作:)