dennikolas
@dennikolas
PHP макака

Как анализировать аудиоспектр?

Привет, столкнулся с задачей, слать по wi-fi данные о спектре воспроизводимого аудио на компе.
Сразу скажу, чтоб сильно не пинали, на C# первый раз что-то делаю.
Нагуглил библиотеку NAudio, при помощи неё захватываю сигнал, который выводится со звуковухи.
Дальше этот сигнал нужно разложить по частотам, при помощи FFT и получить максимальные значения для частот. Мне нужно разделение на 3, низкие, средние и высокие. Как именно это сделать - так и не понял. Нашёл примеры, попытался сделать по примерам, но результат получился удручающим - показывает плюс-минус одну и ту же величину.
Вот мой код:
namespace AudioSpectr
{
    public partial class Form1 : Form
    {
        private Socket client;
        private IPEndPoint remote_point;

        private WasapiLoopbackCapture _capture;
        private object _lock;
        private int _fftPos;
        private int _fftLength;
        private Complex[] _fftBuffer;
        private float[] _lastFftBuffer;
        private bool _fftBufferAvailable;
        private int _m;

        private float[] colorMusic;

        private Dictionary<int, MMDevice> devices;
        private Button button1;
        private Button button2;
        private Label label1;
        private Label label2;
        private Label label3;
        private ComboBox comboBox1;
        private TextBox textBox1;
        private Label label4;
        private TextBox textBox2;
        private BackgroundWorker backgroundWorker1;
        private Button button3;
        private Dictionary<int, string> comboBoxDev;
        public Form1()
        {
            InitializeComponent();

            button2.Enabled = false;
            label3.Visible = false;

            devices = new Dictionary<int, MMDevice>();
            comboBoxDev = new Dictionary<int, string>();
            int count = 0;
            foreach (var wasapi in new MMDeviceEnumerator().EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active))
            {
                comboBoxDev.Add(count, wasapi.FriendlyName);
                devices.Add(count, wasapi);
                count += 1;
            }

            comboBox1.DataSource = new BindingSource(comboBoxDev, null);
            comboBox1.DisplayMember = "Value";
            comboBox1.ValueMember = "Key";

            textBox1.Text = Properties.Settings.Default.ip;
            textBox2.Text = Properties.Settings.Default.token;

            this._lock = new object();
            this._fftLength = 9728;
            this._m = (int)Math.Log(this._fftLength, 2.0);
            this._fftBuffer = new Complex[this._fftLength];
            this._lastFftBuffer = new float[this._fftLength];

            this.colorMusic = new float[3] { 0, 0, 0};
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {            
            ComboBox cmb = (ComboBox)sender;
            int selectedIndex = cmb.SelectedIndex;

            _capture = new WasapiLoopbackCapture(devices[selectedIndex]);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Start();
            button1.Enabled = false;
            button2.Enabled = true;
            button3.Enabled = false;
            label3.Visible = true;
            textBox1.ReadOnly = true;
            textBox2.ReadOnly = true;
            comboBox1.Enabled = false;
            try
            {
                this.client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                this.remote_point = new IPEndPoint(IPAddress.Parse(textBox1.Text), 5545);
                this._capture.DataAvailable += this.DataAvailable;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                this.button2_Click(sender, e);
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            Stop();
            button1.Enabled = true;
            button2.Enabled = false;
            button3.Enabled = true;
            label3.Visible = false;
            textBox1.ReadOnly = false;
            textBox2.ReadOnly = false;
            comboBox1.Enabled = true;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Properties.Settings.Default.ip = textBox1.Text;
            Properties.Settings.Default.token = textBox2.Text;
            Properties.Settings.Default.Save();
        }

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

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {

        }

        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++)
                        {
                            float amplitude = this.GetPower(this._fftBuffer[c]);
                            this._lastFftBuffer[c] = amplitude;
                        }
                        
                        for (byte n = 2; n < 6; n++)
                        {
                            if (_lastFftBuffer[n] > this.colorMusic[0]) this.colorMusic[0] = _lastFftBuffer[n];
                        }
                        for (byte n = 6; n < 11; n++)
                        {
                            if (_lastFftBuffer[n] > this.colorMusic[1]) this.colorMusic[1] = _lastFftBuffer[n];
                        }
                        for (byte n = 11; n < 32; n++)
                        {
                            if (_lastFftBuffer[n] > this.colorMusic[2]) this.colorMusic[2] = _lastFftBuffer[n];
                        }

                        string json = JsonConvert.SerializeObject(new Packet(textBox2.Text, this.colorMusic));
                        Console.WriteLine(json);
                        byte[] send_buffer = Encoding.ASCII.GetBytes(json);
                        this.client.SendTo(send_buffer, remote_point);

                        this._fftBufferAvailable = true;
                    }
                }
            }
        }

        private float GetPower(Complex c)
        {
            // not entirely sure whether the multiplier should be 10 or 20 in this case.
            // going with 10 from here http://stackoverflow.com/a/10636698/7532
            float intensityDB = (float)(10 * Math.Log10(Math.Sqrt(c.X * c.X + c.Y * c.Y)));
            return intensityDB;
        }

        public WaveFormat Format
        {
            get
            {
                return this._capture.WaveFormat;
            }
        }

        public void Start()
        {
            this._capture.StartRecording();
        }

        public void Stop()
        {
            this._capture.StopRecording();
        }

        public bool GetFFTData(float[] fftDataBuffer)
        {
            lock (this._lock)
            {
                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 class Packet
    {
        public string token { get; set; }
        public float[] colorMusic { get; set; }

        public Packet(string token, float[] colorMusic)
        {
            this.token = token;
            this.colorMusic = colorMusic;
        }
    }
}
  • Вопрос задан
  • 382 просмотра
Решения вопроса 1
Griboks
@Griboks Куратор тега C#
Если уж используете библиотеки, то используйте все варианты.
https://github.com/andrewkirillov/AForge.NET/blob/...

Разделение на 3? Ну это уже очевидно: берём максимальную частоту, делим на 1, 1.5, 3.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы