我有一个使用NAudio的音频播放器,我想为每个频段显示实时强度。
我为1024个样本的每个块触发了一个事件:
public void Update(Complex[] fftResults)
{
// ??
}
我想要的是一组数字,表示每个频段的强度。让我们说我想将窗口划分为16个波段。
例如,当有更多低音频率时,它可能如下所示:
░░░░░░░░░░░░░░░░
▓▓▓░░░░░░░░░░░░░
▓▓▓░░░░░░░░░░░░░
▓▓▓▓░░░░░░░░░░░░
▓▓▓▓▓░░░░░░░░░░░
▓▓▓▓▓▓▓▓░░░▓░░▓░
如果可以使用该数据,我应该在该事件处理程序中添加什么内容?
数据来了(Complex [])已经用FFT进行了转换。 这是一个立体声流。
首先尝试:
double[] bandIntensity = new double[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
public void Update(Complex[] fftResults)
{
// using half fftResults because the others are just mirrored
int band = 0;
for (int n = 0; n < fftResults.Length/2; n++)
{
band = (int)((double)n / (fftResults.Length / 2) * bandIntensity.Length);
bandIntensity[band] += Math.Sqrt(fftResults[n].X * fftResults[n].X + fftResults[n].Y * fftResults[n].Y);
bandIntensity[band] /= 2;
}
}
以上是做了一些事情,但我认为前两个乐队太多了,而且我正在演奏没有那么多低音的夏奇拉。
谢谢!
答案 0 :(得分:8)
您可能希望在此处解决两个不同的问题:
(1)窗口函数
您需要在FFT之前对数据应用window function,否则您将获得spectral leakage,这将导致非常模糊的频谱。光谱泄漏的一个令人不快的副作用是,如果你有任何类型的重要DC(0 Hz)分量,那么这将导致你在条形图上看到的那种1 / f形状。
(2)记录幅度/频率轴
人类听力在强度和频率轴上基本上是对数的。不仅如此,语音和音乐往往在频谱的低频部分拥有更多的能量。为了获得更加令人愉悦和有意义的强度与频率的显示,我们通常使幅度和频率轴都成对数。在幅度轴的情况下,通常通过绘制dB re full scale,即
来处理magnitude_dB = 10 * log10(magnitude);
在频率轴的情况下,您可能希望将频段分组为频段,每个频段可能是一个八度音阶(2:1频率范围),或者更常见的是更高分辨率,第三个八度音阶。因此,如果您只想要10个“条形”,那么您可以使用以下八度音阶:
25 - 50 Hz
50 - 100 Hz
100 - 200 Hz
200 - 400 Hz
400 - 800 Hz
800 - 1600 Hz
1600 - 3200 Hz
3200 - 6400 Hz
6400 - 12800 Hz
12800 - 20000 Hz
(假设您的采样率为44.1 kHz,音频输入硬件的上限为20 kHz)。
请注意,虽然这种应用具有幅度(dB)强度标度几乎是强制性的,但对数频率轴的关键性较低,因此您现在可以尝试使用现有的线性分级,并看看效果如何通过在时域中应用窗口函数(假设您还没有)并将幅度比例转换为dB。