Анализатор спектра с NAudio и WPFSoundVisualizationLib

Я работаю над анализатором спектра реального времени С# 4.0/WPF (в качестве основы для другого проекта). Я использую последнюю версию NAudio для вывода звука в реальном времени на звуковую карту и WPFSoundVisualizationLib (http://wpfsvl.codeplex.com/) для контроля WPF Spectrum Analyzer. С помощью этих замечательных инструментов работа почти завершена, но она работает неправильно :-(

У меня есть функциональный Спектрум, но информация не является правами, и я не понимаю, откуда взялась проблема... (Я сравниваю свой Спектрум с 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,1 кГц, 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.


person Floyd    schedule 15.11.2012    source источник
comment
Что не работает? Любые сообщения об ошибках? Если да, то какие и где они встречаются? Вам необходимо более подробно описать ошибки.   -  person Olle Sjögren    schedule 15.11.2012
comment
Вам действительно нужно сказать, что вы ожидали получить по сравнению с тем, что вы действительно получили, иначе никто не сможет помочь.   -  person Bjorn Roche    schedule 15.11.2012
comment
Знаете ли вы, какой тип данных, прежде чем преобразовать их в float? Если это уже не float, я не знаю, будет ли работать ваша функция преобразования.   -  person Jason B    schedule 15.11.2012
comment
Ошибок у меня нет, как я уже сказал, Спектрум у меня исправный, но информации нет прав. Я сравниваю свой Spectrum с Equalify (спектр/эквалайзер) для Spotify), и у меня другое поведение.   -  person Floyd    schedule 15.11.2012
comment
Oups, Enter, немедленно отправьте комментарий... Я получаю массив байтов в методе DataAvailable и конвертирую его в float[] с помощью метода ConvertByteToFloat. Массив байтов представляет собой последний буфер, прочитанный в Wasapi (я думаю, PCM 16 бит / 44,1 кГц)   -  person Floyd    schedule 15.11.2012
comment
Исправление: мой волновой формат 32-битный PCM (44,1 кГц, 2 канала, 32 бита на сэмпл)   -  person Floyd    schedule 15.11.2012
comment
Кстати, вы наверняка можете придумать название получше! Сколько есть вопросов, которые соответствуют названию «Анализатор спектра»? Пожалуйста, будьте немного более конкретными.   -  person John Saunders    schedule 15.11.2012
comment
Ваш формат входящей волны определенно IEEE float? что, если это 32-битный int?   -  person Mark Heath    schedule 15.11.2012


Ответы (3)


Возможно, я немного опоздал с ответом, но мы здесь.

Вы почти у цели. Вам просто нужно указать амплитуду комплексных чисел, которые возвращает БПФ, вместо значения 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;

Ваше здоровье!

person max_force    schedule 16.07.2014

У меня есть работающий анализатор спектра, использующий код другой вопрос. Это очень грубая версия, но вы сможете использовать ее с небольшими изменениями.

Я понятия не имею, что на самом деле не так с вашим кодом, но, по крайней мере, предоставленный код работает для меня. Проблема где-то в другом, если у вас все равно получается неправильный спектр при его использовании.

person Typhus    schedule 27.12.2013

Ваш формат входящей волны определенно IEEE float? что, если это 32-битный int?

Я предполагаю, что это поплавок IEEE... это не объясняется в MSDN здесь

Я пытался преобразовать свой массив байтов в массив 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;
}
person Floyd    schedule 15.11.2012