我有一个工作实现NAudio的wasapi环回录制和数据的FFT。 我获得的大部分数据都是应该的,但每隔一段时间(10秒到几分钟的间隔),它几乎在所有频率上显示振幅。
基本上,图片从右到左滚动,时间和频率从底部的最低频率开始以对数刻度表示。这些线是错误。据我所知,那些人不应该在那里。
我得到音频缓冲区并将样本发送到聚合器(应用汉明窗口),该聚合器实现了NAudio FFT。在我以任何方式修改它之前检查了数据(FFT结果)(图像不是来自原始FFT输出,而是desibel缩放),确认FFT结果给出了那些线。我还可以指出图片是用LockBits修改的,所以我认为那里的逻辑有问题,但这就是我查看显示同样问题的FFT输出数据的原因。
嗯我可能错了,问题可能在某个地方,我说它不是,但它似乎真的来自FFT或缓冲数据(数据本身或样本的聚合)。不知怎的,我怀疑缓冲区本身是否像这样被破坏了。
如果有人知道可能导致这种情况的原因,我将非常感激!
更新
所以我决定绘制整个FFT结果范围而不是它的一半。它表现出一些奇怪的。我不确定FFT,但我认为傅里叶变换应该给出一个反映在中间的结果。这当然不是这种情况。
图片为线性比例,因此图片的中间位置是FFT结果的中间点。底部是第一个,顶部是最后一个。
我正在播放一个10kHz的正弦波,它给出了两条水平线,但顶部超出了我。看起来这些线条反映在图片的底部四分之一处,所以这对我来说也很奇怪。
更新2
所以我将FFT大小从4096增加到8192并再次尝试。这是我输出正弦频率的输出。
看起来结果会被镜像两次。一旦在中间,然后再在上半部和下半部。现在,巨大的线条已经消失了......看起来这些线条现在只出现在下半部分了。
在使用不同的FFT长度进行一些进一步测试之后,该线路中的线条似乎是完全随机的。
更新3
我做了很多测试。我添加的最新内容是样本重叠,以便在下一个FFT开始时重用示例数组的后半部分。在汉明和汉恩的窗户上,它给了我很大的强度(就像我发布的第二张照片一样)但不是布莱克曼哈里斯。禁用重叠会删除每个窗口函数上的最大错误。即使使用BH窗口,顶部图片中的较小错误仍然存在。我仍然不知道为什么会出现这些线条。
我当前的表单允许控制使用哪个窗口功能(前面提到的三个),重叠(开/关)和多个不同的绘图选项。这允许我在更改时比较所有影响方效果。
我会进一步调查(我很确定我在某些方面犯了一个错误)但是很好的建议非常受欢迎!
答案 0 :(得分:10)
问题在于我处理数据数组的方式。现在就像一个魅力。
代码(删除了多余的内容,可能会增加错误):
// 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; }
}
我想是的。我可能错过了一些东西。 希望这有帮助!