带有 NAudio 和 WPFSoundVisualizationLib 的频谱分析仪
Posted
技术标签:
【中文标题】带有 NAudio 和 WPFSoundVisualizationLib 的频谱分析仪【英文标题】:Spectrum Analyzer with NAudio & WPFSoundVisualizationLib 【发布时间】:2012-11-15 13:47:27 【问题描述】:我正在开发 C#4.0/WPF 实时频谱分析仪(作为另一个项目的基础)。我使用 NAudio 最新版本在声卡上获取实时音频输出,并使用 WPFSoundVisualizationLib (http://wpfsvl.codeplex.com/) 获取 Spectrum Analyzer WPF Control。有了这个神奇的工具,工作几乎完成了,但它不能正常工作:-(
我有一个功能性的 Spectrum,但信息不是权利,我不明白问题出在哪里......(我已经将我的 Spectrum 与 Equalify 进行了比较,这是 Spotify 的 Spectrum/均衡器,但我没有具有相同的行为)
这是我的主要课程:
using System;
using System.Windows;
using WPFSoundVisualizationLib;
namespace MySpectrumAnalyser
public partial class MainWindow : Window
private RealTimePlayback _playback;
private bool _record;
public MainWindow()
InitializeComponent();
this.Topmost = true;
this.Closing += MainWindow_Closing;
this.spectrum.FFTComplexity = FFTDataSize.FFT2048;
this.spectrum.RefreshInterval = 60;
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
if (this._record)
this._playback.Stop();
private void Button_Click_1(object sender, RoutedEventArgs e)
if (this._playback == null)
this._playback = new RealTimePlayback();
this.spectrum.RegisterSoundPlayer(this._playback);
if (!this._record)
this._playback.Start();
this.Dispatcher.Invoke(new Action(delegate
this.btnRecord.Content = "Stop";
));
else
this._playback.Stop();
this.Dispatcher.Invoke(new Action(delegate
this.btnRecord.Content = "Start";
));
this._record = !this._record;
还有我的环回分析器(它实现了 ISpectrumPlayer 以便与 WPFSoundVisualizationLib Spectrum 控件一起使用)。
LoopbackCapture 继承 NAudio.CoreAudioApi.WasapiCapture。
从 Wasapi 接收的数据是一个字节数组(32 位 PCM,44.1kHz,2 通道,每个样本 32 位)
using NAudio.Dsp;
using NAudio.Wave;
using System;
using WPFSoundVisualizationLib;
namespace MySpectrumAnalyser
public class RealTimePlayback : ISpectrumPlayer
private LoopbackCapture _capture;
private object _lock;
private int _fftPos;
private int _fftLength;
private Complex[] _fftBuffer;
private float[] _lastFftBuffer;
private bool _fftBufferAvailable;
private int _m;
public RealTimePlayback()
this._lock = new object();
this._capture = new LoopbackCapture();
this._capture.DataAvailable += this.DataAvailable;
this._m = (int)Math.Log(this._fftLength, 2.0);
this._fftLength = 2048; // 44.1kHz.
this._fftBuffer = new Complex[this._fftLength];
this._lastFftBuffer = new float[this._fftLength];
public WaveFormat Format
get
return this._capture.WaveFormat;
private float[] ConvertByteToFloat(byte[] array, int length)
int samplesNeeded = length / 4;
float[] floatArr = new float[samplesNeeded];
for (int i = 0; i < samplesNeeded; i++)
floatArr[i] = BitConverter.ToSingle(array, i * 4);
return floatArr;
private void DataAvailable(object sender, WaveInEventArgs e)
// Convert byte[] to float[].
float[] data = ConvertByteToFloat(e.Buffer, e.BytesRecorded);
// For all data. Skip right channel on stereo (i += this.Format.Channels).
for (int i = 0; i < data.Length; i += this.Format.Channels)
this._fftBuffer[_fftPos].X = (float)(data[i] * FastFourierTransform.HannWindow(_fftPos, _fftLength));
this._fftBuffer[_fftPos].Y = 0;
this._fftPos++;
if (this._fftPos >= this._fftLength)
this._fftPos = 0;
// NAudio FFT implementation.
FastFourierTransform.FFT(true, this._m, this._fftBuffer);
// Copy to buffer.
lock (this._lock)
for (int c = 0; c < this._fftLength; c++)
this._lastFftBuffer[c] = this._fftBuffer[c].X;
this._fftBufferAvailable = true;
public void Start()
this._capture.StartRecording();
public void Stop()
this._capture.StopRecording();
public bool GetFFTData(float[] fftDataBuffer)
lock (this._lock)
// Use last available buffer.
if (this._fftBufferAvailable)
this._lastFftBuffer.CopyTo(fftDataBuffer, 0);
this._fftBufferAvailable = false;
return true;
else
return false;
public int GetFFTFrequencyIndex(int frequency)
int index = (int)(frequency / (this.Format.SampleRate / this._fftLength / this.Format.Channels));
return index;
public bool IsPlaying
get return true;
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
GetFFTData 由 WPF 控件每 60 毫秒调用一次以更新 Spectrum。
【问题讨论】:
什么不起作用?有任何错误信息吗?如果是这样,它们发生在哪个位置和位置?您需要更详细地描述错误。 你真的需要说出你期望得到的和你实际得到的,否则没人能帮忙。 在转换成float
之前你知道数据是什么类型吗?如果不是float
,不知道你的转换功能能不能用。
我没有任何错误,正如我所说,“我有一个功能频谱,但信息不是权利”。我将我的 Spectrum 与 Equalify(Spotify 的 Spectrum/均衡器)进行比较,但我没有相同的行为。
Oups,Enter 立即发送评论...我在 DataAvailable 方法中收到一个字节数组,并使用 ConvertByteToFloat 方法将其转换为 float[]。字节数组表示在 Wasapi 中读取的最后一个缓冲区(我认为是 PCM 16bits / 44.1KHhz)
【参考方案1】:
我回答这个问题可能有点晚了,但我们到了。
你快到了。您只需要提供 FFT 返回的复数的幅度,而不是 X 值。
所以在 for 循环中,而不是这个:
this._lastFftBuffer[c] = this._fftBuffer[c].X;
这样做:
float amplitude = (float)Math.Sqrt(this._fftBuffer[c].X * this._fftBuffer[c].X + this._fftBuffer[c].Y * this._fftBuffer[c].Y);
this._lastFftBuffer[c] = amplitude;
干杯!
【讨论】:
【参考方案2】:我有一个使用another question 代码的有效频谱分析仪。这是一个非常粗糙的版本,但您只需稍加修改即可使用它。
我不知道您的代码实际上有什么问题,但至少提供的代码对我有用。如果您在使用时仍然有不正确的光谱,则问题出在其他地方。
【讨论】:
【参考方案3】:你的输入波格式肯定是 IEEE 浮点数吗?如果是 32 位 int 呢?
我猜这是 IEEE 浮点数...MSDN Here 中没有解释
我尝试将我的字节数组转换为 Int32 数组(以浮点数形式),但结果最差:
private float[] ConvertByteToFloat(byte[] array, int length)
int samplesNeeded = length / 4;
float[] floatArr = new float[samplesNeeded];
for (int i = 0; i < samplesNeeded; i++)
floatArr[i] = (float)BitConverter.ToInt32(array, i * 4);
return floatArr;
【讨论】:
以上是关于带有 NAudio 和 WPFSoundVisualizationLib 的频谱分析仪的主要内容,如果未能解决你的问题,请参考以下文章
带有效果的 Windows 8.1 应用音频(NAudio 或 SharpDX)