D
D
Denis Nicholas2018-04-03 08:22:09
Audio
Denis Nicholas, 2018-04-03 08:22:09

How to analyze the audio spectrum?

Hello, I ran into the problem of sending data on the spectrum of the audio being played on the computer via wi-fi.
I’ll say right away, so as not to kick too much, I’m doing something in C # for the first time.
I googled the NAudio library, with the help of it I capture the signal that is output from the sound system.
Further, this signal must be decomposed into frequencies using FFT and the maximum values ​​for frequencies should be obtained. I need a division into 3, low, medium and high. How exactly to do this - I did not understand. I found examples, tried to do it according to the examples, but the result turned out to be depressing - it shows plus or minus the same value.
Here is my code:

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

Answer the question

In order to leave comments, you need to log in

1 answer(s)
G
Griboks, 2018-04-03
@dennikolas

If you are already using libraries, then use all options.
https://github.com/andrewkirillov/AForge.NET/blob/...
Dividing by 3? Well, this is already obvious: we take the maximum frequency, divide by 1, 1.5, 3.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question