如何使用 NAudio 实时计算 FFT(ASIO 输出)
Posted
技术标签:
【中文标题】如何使用 NAudio 实时计算 FFT(ASIO 输出)【英文标题】:How to calculate FFT using NAudio in realtime (ASIO out) 【发布时间】:2014-06-03 13:29:04 【问题描述】:我正在编程克隆吉他(小提琴)英雄作为本学年的期末项目。
这个想法是从我的电子小提琴中获取输入,通过 FFT 对其进行分析,进行一些逻辑和绘图,然后通过扬声器输出。可能是并行线程中的一些步骤。
我已经实现了 Asio 低延迟输入输出,但我在实现实时 FFT 时遇到了很大的问题。
这是一个设置 asioOut 和 sampleAggregator 的代码。样本聚合器应存储每次调用 AudioAvailable() 时添加的样本,并在样本数超过 fftLength 时触发 FFT 计算。
private static int fftLength = 8192;
private SampleAggregator sampleAggregator = new SampleAggregator(fftLength);
void asiostartPlaying(object sender, EventArgs e)
sampleAggregator.PerformFFT = true;
sampleAggregator.FftCalculated += new EventHandler<FftEventArgs>(FftCalculated);
var asioOut = new AsioOut();
BufferedWaveProvider wavprov = new BufferedWaveProvider(new WaveFormat(48000, 1));
asioOut.AudioAvailable += new EventHandler<AsioAudioAvailableEventArgs> (asio_DataAvailable);
asioOut.InitRecordAndPlayback(wavprov, 1, 25);
asioOut.Play();
void asio_DataAvailable(object sender, AsioAudioAvailableEventArgs e)
byte[] buf = new byte[e.SamplesPerBuffer*4];
for (int i = 0; i < e.InputBuffers.Length; i++)
Marshal.Copy(e.InputBuffers[i], buf, 0, e.SamplesPerBuffer*4);
Marshal.Copy(buf, 0, e.OutputBuffers[i], e.SamplesPerBuffer*4);
for (int i = 0; i < buf.Length; i=i+4)
float sample32 = BitConverter.ToSingle(buf, i);
sampleAggregator.Add(sample32);
e.WrittenToOutputBuffers = true;
SampleAggregator 是取自 NAudio fft result gives intensity on all frequencies C# 的类。
Asio 以 Int32LSB 样本类型输出数据。在 buf 中有从 0 到 255 的值。
这是在计算 fft 时应该调用的函数(从 SampleAggregator 类触发)。
void FftCalculated(object sender, FftEventArgs e)
for (var i = 0; i < e.Result.Length; i++)
Debug.WriteLine("FFT output.");
Debug.WriteLine(e.Result[i].X);
Debug.WriteLine(e.Result[i].Y);
但 FFT 总是输出 NaN 作为结果。
我觉得转换成float有问题。
有人能指出我正确的方向吗?
EDIT_1:我将 DataAvailable() 中的循环更改为
for (int i = 0; i < e.SamplesPerBuffer * 4; i++)
float sample32 = Convert.ToSingle(buf[i]);
sampleAggregator.Add(sample32);
FFT 现在输出数据。但我认为它们是不正确的。 错误一定是在 asio 样本和浮点值之间的转换。但是我对字节操作不太满意。
e.GetAsInterleavedSamples 有什么帮助吗?
来自 FFT 的原始数据示例: X:-5,304741 Y:-0,7160959 X: 6,270798 Y:-0,4169312 X:-8,851931 Y:-0,4485725
我注意到,FFT 原始数据中的前几个和最后几个值比其他数据大。计算量级很棘手。
【问题讨论】:
我不知道 NAudio,但是你为什么将sampleAggregator.PerformFFT
设置为 false
?我猜想计算每个窗口的 FFT 一定是真的。
这是一个很好的观点。我正在试验它,忘记在***的例子中设置它。谢谢。
考虑使用 Rx 以获得更好的程序流程。
您确定您的 ASIO 驱动程序提供浮点样本吗?获取 16 位或 24 位整数样本更为常见。
我不确定。我从 AsioAudioAvailableEventArgs 发现我的 AsioSampleType 是 Int32LSB。在 buf 中有从 0 到 255(字节?)的值。我刚刚尝试了在 EDIT_1 下进行更多描述的更改。但我认为这不是正确的做法。
【参考方案1】:
问题出在我想将来自 Asio 的样本数据(buf 数组中的一行中的 4 个字节)转换为 fft 时浮动。 BitConvertor 应该可以解决问题,但在我的情况下,它会以某种方式使 fft 输出 NaN。所以我尝试了这种转换。
for (int i = 0; i < e.SamplesPerBuffer * 4; i += 4)
float sample = Convert.ToSingle(buf[i] + buf[i + 1] + buf[i + 2] + buf[i + 3]);
sampleAggregator.Add(sample);
而且效果很好。即使有 192 000 个采样率。
【讨论】:
【参考方案2】:我没有使用过 NAudio,但我们已经用 DirectSound 实现了非常相似的东西。 LightningChart Ultimate SDK 中有用于此目的的工具。 AudioInput 组件从声音设备捕获波形数据,并将数据同时转发到 FFT 计算(SpectrumCalculator 组件)和波形监视器。然后将 FFT 数据可视化为 2D 或 3D 频谱图。 AudioOutput 将数据写入声音设备,以便通过扬声器听到。
总体而言,音频输入/输出、FFT 计算和可视化以非常低的 CPU 负载运行。
我们的库是商业的,但我认为即使您不寻找任何其他组件,看看我们的音频示例也是一个好主意,源代码在演示应用程序 Visual Studio 项目中可见。至少你可能会得到新的想法:-) 我相信你可以为 NAudio 应用一些方法。
从LightningChart web site 下载 LightningChart 演示,运行它无需任何费用。
[我是 LightningChart 组件的 CTO]
【讨论】:
感谢您的链接。你的视觉效果确实不错。但我相信我的问题更具体是 Asio。以上是关于如何使用 NAudio 实时计算 FFT(ASIO 输出)的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 NAudio 和 ASIO 直接读取输入缓冲区和播放?