我正在研究C#4.0 / WPF实时频谱分析仪(作为另一个项目的基础)。我使用NAudio last版本在声卡上获得实时音频输出,使用WPFSoundVisualizationLib(http://wpfsvl.codeplex.com/)进行频谱分析仪WPF控制。有了这个神奇的工具,工作几乎完成了,但它不能正常工作: - (
我有一个功能谱,但信息不是权利,我不明白问题的来源......(我将我的Spectrum与Equalify,Spotify的频谱/均衡器进行比较,我不知道有相同的行为)
这是我的主要课程:
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;
}
}
WPF控件每隔60ms调用一次GetFFTData来更新Spectrum。
答案 0 :(得分:2)
我可能有点迟到回答这个问题,但我们来了。
你快到了。您只需要提供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;
干杯!
答案 1 :(得分:0)
您的传入波形格式肯定是IEEE浮点数吗?如果它是32位int怎么办?
我猜这是一个IEEE浮动......这在MSDN Here
中没有解释我试图在Int32数组中转换我的字节数组(在float中转换)但结果最差:
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;
}
答案 2 :(得分:0)
我有一个使用another question代码的工作频谱分析仪。这是一个非常粗糙的版本,但您可以稍作修改使用它。
我不知道您的代码究竟出了什么问题,但至少提供的代码对我有用。如果您在使用时仍有不正确的频谱,则问题出在其他地方。