7

我有一个 NAudio 的 wasapi 环回记录和数据的 FFT 的工作实现。我得到的大部分数据都是应该的,但每隔一段时间(10 秒到几分钟的间隔)它会在几乎所有频率上显示幅度。

错误图像

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

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

好吧,我可能是错的,问题可能出在我说不是的地方,但它确实似乎源于 FFT 或缓冲区数据(数据本身或样本的聚合)。不知何故,我怀疑缓冲区本身是否像这样损坏。

如果有人知道可能导致这种情况的原因,我将不胜感激!

更新

所以我决定画出整个 FFT 结果范围而不是其中的一半。它显示了一些奇怪的东西。我不确定 FFT,但我认为傅立叶变换应该给出一个反映在中间的结果。这当然不是这里的情况。

图片是线性比例的,所以图片的正中间是 FFT 结果的中点。底部是第一个,顶部是最后一个。

整体

我正在播放一个 10kHz 的正弦波,它在那里给出了两条水平线,但顶部超出了我的范围。线条似乎也反映在图片的底部四分之一周围,所以这对我来说也很奇怪。

更新 2

所以我将 FFT 大小从 4096 增加到 8192 并再次尝试。这是我弄乱正弦频率的输出。

图3

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

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

更新 3

我已经对很多东西进行了一些测试。我添加的最新内容是样本重叠,以便在下一个 FFT 开始时重用样本数组的后半部分。在 Hamming 和 Hann windows 上,它给了我巨大的强度(很像我发布的第二张图片),但不是 BlackmannHarris。禁用重叠可以消除每个窗口函数上的最大错误。即使使用 BH 窗口,上图中的较小误差仍然存在。我仍然不知道为什么会出现这些线条。

我当前的表单允许控制使用哪个窗口功能(前面提到的三个)、重叠(开/关)和多个不同的绘图选项。这使我可以比较所有受影响的各方在更改时的效果。

我会进一步调查(我很确定我在某些时候犯了错误),但是非常欢迎好的建议!

4

1 回答 1

13

问题在于我处理数据数组的方式。现在工作就像一个魅力。

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

// 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 类:

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; }
}

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

于 2013-12-06T01:33:00.747 回答