NAudio fft结果给出了所有频率C#的强度

NAudio fft result gives intensity on all frequencies C#

我有NAudio的wasapi回送记录和数据FFT的有效实现。
我得到的大多数数据都是应有的值,但是每隔一段时间(间隔10秒到几分钟)就会显示几乎所有频率上的振幅。

The error image

基本上,图片从右到左滚动,时间和频率从底部的最低频率以对数标度递增。线是错误。据我所知,那些不应该在那里。

我得到音频缓冲区,并将样本发送到实现NAudio FFT的聚合器(应用Hamming窗口)。在以任何方式修改数据(FFT结果)之前,我已经检查了数据(图片不是来自原始FFT输出,而是按Desibel缩放比例),以确认FFT结果正在显示这些行。我还可以指出图片是使用LockBits修改的,所以我认为那里的逻辑有问题,但这就是为什么我检查了显示相同问题的FFT输出数据的原因。

好吧,我可能错了,问题可能出在我未曾说过的地方,但实际上它似乎起源于FFT或缓冲区数据(数据本身或样本的聚合)。我以某种方式怀疑缓冲区本身是否已损坏。

如果有人有什么想法会导致这种情况,我将不胜感激!

更新

因此,我决定绘制整个FFT结果范围,而不是一半。它显示出一些奇怪的东西。我不确定FFT是否有效,但我认为傅里叶变换应将结果反映在中间。这里肯定不是这种情况。

图片是线性比例的,因此图片的确切中间是FFT结果的中间点。底部是第一个,顶部是最后一个。

wholefft

我正在播放10kHz正弦波,在那给出了两条水平线,但顶部不在我的范围内。似乎线条也被镜像在图片的底部四分之一周围,所以对我来说也很奇怪。

更新2

所以我将FFT大小从4096增加到8192,然后重试。这是我的输出与正弦频率混淆。

picture3

结果似乎被镜像了两次。一次在中间,然后再在上半部分和下半部分。巨大的线条现在消失了。.看起来这些线条现在仅出现在下半部分。

在使用不同的FFT长度进行了进一步测试之后,该行似乎完全是随机的。

更新3

我已经做了很多测试。我添加的最新内容是样本重叠,以便在下一个FFT的开头重用样本数组的后半部分。在汉明和汉恩的窗户上,它给了我很大的强度(非常像我发布的第二张照片),但是没有布莱克曼·哈里斯。禁用重叠功能可消除每个窗口功能上的最大错误。即使在BH窗口中,如顶部图片中的较小错误仍然保留。我仍然不知道为什么出现这些行。

我当前的形式允许控制使用哪个窗口功能(前面提到的三个),重叠(打开/关闭)以及多个不同的绘图选项。这使我可以比较更改时所有受影响方的影响。

我将作进一步调查(我很确定自己在某些时候犯了一个错误),但是很好的建议绝对值得欢迎!


问题出在我处理数据数组的方式上。 现在像魅力一样工作。

代码(删除了多余的代码,并可能添加了错误):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Other inputs are also usable. Just look through the NAudio library.
private IWaveIn waveIn;
private static int fftLength = 8192; // NAudio fft wants powers of two!

// There might be a sample aggregator in NAudio somewhere but I made a variation for my needs
private SampleAggregator sampleAggregator = new SampleAggregator(fftLength);

public Main()
{
    sampleAggregator.FftCalculated += new EventHandler<FftEventArgs>(FftCalculated);
    sampleAggregator.PerformFFT = true;

    // Here you decide what you want to use as the waveIn.
    // There are many options in NAudio and you can use other streams/files.
    // Note that the code varies for each different source.
    waveIn = new WasapiLoopbackCapture();

    waveIn.DataAvailable += OnDataAvailable;

    waveIn.StartRecording();
}

void OnDataAvailable(object sender, WaveInEventArgs e)
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(new EventHandler<WaveInEventArgs>(OnDataAvailable), sender, e);
    }
    else
    {
        byte[] buffer = e.Buffer;
        int bytesRecorded = e.BytesRecorded;
        int bufferIncrement = waveIn.WaveFormat.BlockAlign;

        for (int index = 0; index < bytesRecorded; index += bufferIncrement)
        {
            float sample32 = BitConverter.ToSingle(buffer, index);
            sampleAggregator.Add(sample32);
        }
    }
}

void FftCalculated(object sender, FftEventArgs e)
{
    // Do something with e.result!
}

和Sample Aggregator类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
using NAudio.Dsp; // The Complex and FFT are here!

class SampleAggregator
{
    // FFT
    public event EventHandler<FftEventArgs> FftCalculated;
    public bool PerformFFT { get; set; }

    // This Complex is NAudio's own!
    private Complex[] fftBuffer;
    private FftEventArgs fftArgs;
    private int fftPos;
    private int fftLength;
    private int m;

    public SampleAggregator(int fftLength)
    {
        if (!IsPowerOfTwo(fftLength))
        {
            throw new ArgumentException("FFT Length must be a power of two");
        }
        this.m = (int)Math.Log(fftLength, 2.0);
        this.fftLength = fftLength;
        this.fftBuffer = new Complex[fftLength];
        this.fftArgs = new FftEventArgs(fftBuffer);
    }

    bool IsPowerOfTwo(int x)
    {
        return (x & (x - 1)) == 0;
    }

    public void Add(float value)
    {
        if (PerformFFT && FftCalculated != null)
        {
            // Remember the window function! There are many others as well.
            fftBuffer[fftPos].X = (float)(value * FastFourierTransform.HammingWindow(fftPos, fftLength));
            fftBuffer[fftPos].Y = 0; // This is always zero with audio.
            fftPos++;
            if (fftPos >= fftLength)
            {
                fftPos = 0;
                FastFourierTransform.FFT(true, m, fftBuffer);
                FftCalculated(this, fftArgs);
            }
        }
    }
}

public class FftEventArgs : EventArgs
{
    [DebuggerStepThrough]
    public FftEventArgs(Complex[] result)
    {
        this.Result = result;
    }
    public Complex[] Result { get; private set; }
}

我就是这样。 我可能已经错过了一些东西。
希望这可以帮助!