Spectrum Analyzer with NAudio and WPFSoundVisualizationLib

I am working on a C # 4.0 / WPF real-time spectrum analyzer (as the basis of another project). I use the latest version of NAudio to receive real-time audio data on a sound card and WPFSoundVisualizationLib (http://wpfsvl.codeplex.com/) for the WPF spectrum analyzer. With these wonderful tools, the work is almost complete, but it does not work correctly: - (

I have a functional Spectrum, but the information is not right, and I don’t understand where this problem comes from ... (I compared my Spectrum with Equalify, Spectrum / Equalizer for Spotify, and I do not have the same behavior)

This is my main class:

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

And my loopback analyzer (which implements ISpectrumPlayer for use with the WPFSoundVisualizationLib Spectrum control).

LoopbackCapture inherits NAudio.CoreAudioApi.WasapiCapture.

The data received from Wasapi is a byte array (32 bits PCM, 44.1 kHz, 2 channels, 32 bits per sample)

 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 is called by the WPF control every 60 ms to update Spectrum.

+4
source share
3 answers

I may be a little late to answer this question, but we are here.

You are almost there. You just need to provide the amplitude of the complex numbers returned by the FFT, instead of the value of X.

So in a for loop instead:

 this._lastFftBuffer[c] = this._fftBuffer[c].X; 

do the following:

 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; 

Hooray!

+2
source

Is your incoming wave format definitely an IEEE float? what if it's a 32 bit int?

I assume this is an IEEE float ... this is not explained on MSDN here

I tried converting an array of bytes into an Int32 array (cast in float), but the result is worse:

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

I have a working spectrum analyzer, using code is another question . This is a very crude version, but you can use it with a few changes.

I don’t know what is really wrong with your code, but at least the provided code works for me. The problem is elsewhere if you still have the wrong spectrum when using.

0
source

Source: https://habr.com/ru/post/1446242/


All Articles