7

我有一个使用 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;
        }
    }

以上正在做一些事情,但我认为前两个乐队的内容太多了,我正在演奏没有那么多低音的夏奇拉。

谢谢!

4

1 回答 1

10

您可能想在这里解决两个不同的问题:

(1) 窗口函数

您需要在 FFT 之前对数据应用窗函数,否则会出现频谱泄漏,从而导致频谱非常模糊。频谱泄漏的一个令人不快的副作用是,如果您有任何类型的显着 DC (0 Hz) 分量,那么这将导致您在条形图上看到的那种 1/f 形状。

(2) 对数幅度/频率轴

人类的听力在强度和频率轴上基本上都是对数的。不仅如此,语音和音乐在频谱的低频部分往往具有更多的能量。为了获得更令人愉悦和有意义的强度与频率显示,我们通常将幅度和频率轴都设为对数。在幅度轴的情况下,这通常通过绘制 dB re full scale 来处理,即

magnitude_dB = 10 * log10(magnitude);

在频率轴的情况下,您可能希望将您的 bin 分组为频带,每个频带可能是一个八度音程(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 的音频输入硬件上限)。

Note that while having a magnitude (dB) intensity scale is pretty much mandatory for this kind of application, the log frequency axis is less critical, so you could try with your existing linear binning for now, and just see what effect you get from applying a window function in the time domain (assuming you don't already have one) and converting the magnitude scale to dB.

于 2011-10-12T06:45:32.057 回答