Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert audio stream to frequency

Tags:

c#

audio

naudio

I've managed to successfully get a stream of audio data going to an output device (speaker) using NAudio:

private void OnDataAvailable(object sender, WaveInEventArgs e)
        {
            var buffer = e.Buffer;
            var bytesRecorded = e.BytesRecorded;
            Debug.WriteLine($"Bytes {bytesRecorded}");

And the sample output:

Bytes 19200
Bytes 19200
Bytes 19200
Bytes 19200
Bytes 19200
Bytes 19200
Bytes 19200
Bytes 19200
Bytes 19200
Bytes 19200
Bytes 19200
Bytes 23040
Bytes 19200
Bytes 19200
Bytes 19200
Bytes 19200
Bytes 19200

I then transform (FFT) this to x and y values using https://stackoverflow.com/a/20414331:

var buffer = e.Buffer;
            var bytesRecorded = e.BytesRecorded;
            //Debug.WriteLine($"Bytes {bytesRecorded}");
            var bufferIncrement = _waveIn.WaveFormat.BlockAlign;

            for (var index = 0; index < bytesRecorded; index += bufferIncrement)
            {
                var sample32 = BitConverter.ToSingle(buffer, index);
                _sampleAggregator.Add(sample32);
            }

With a sample output of:

x: -9.79634E-05, y: -9.212703E-05
x: 6.897306E-05, y: 2.489315E-05
x: 0.0002080683, y: 0.0004317867
x: -0.0001720883, y: -6.681971E-05
x: -0.0001245111, y: 0.0002880402
x: -0.0005751926, y: -0.0002682915
x: -5.280507E-06, y: 7.297558E-05
x: -0.0001143928, y: -0.0001156801
x: 0.0005231025, y: -0.000153206
x: 0.0001011164, y: 7.681748E-05
x: 0.000330695, y: 0.0002293986

Not sure if this is even possible or if I'm just misunderstanding what the stream is returning, but I'd like to get the frequency of the audio stream in order to do some stuff with Philips Hue. The x, y values above are way to small to use in the CIE colour space. Am I doing something wrong or am I completely misunderstanding what the data is in the buffer in OnDataAvailable?

Thanks!

Edit:

I've modified my OnDataAvailable code based on comments and the tutorial for the Autotune program to be the below:

private void OnDataAvailable(object sender, WaveInEventArgs e)
        {
            var buffer = e.Buffer;
            float sample32 = 0;

            for (var index = buffer.Length > 1024 ? buffer.Length - 1024 : buffer.Length; index < e.BytesRecorded; index += 2)
            {
                var sample = (short) ((buffer[index + 1] << 8) | buffer[index + 0]);
                sample32 = sample / 32768f;
                Debug.WriteLine(sample32);
                LightsController.SetLights(Convert.ToByte(Math.Abs(sample32) * 255));
                _sampleAggregator.Add(sample32);
            }
            var floats = BytesToFloats(buffer);

            if (sample32 != 0.0f)
            {
                var pitchDetect = new FftPitchDetector(sample32);
                var pitch = pitchDetect.DetectPitch(floats, floats.Length);
                Debug.WriteLine($"Pitch {pitch}");
            }
        }

The hope is that I only use the last set of elements from the buffer as it doesn't seem to clear itself and I'm only interested in the latest set of data available in order to get the frequency of the current audio. However, i still get an index exception ocassionally when the DetectPitch method is called. Where am I going wrong? I was hoping to use the frequency to change the colour and brightness of hue bulbs.

like image 295
Adam Short Avatar asked Feb 15 '17 18:02

Adam Short


1 Answers

Use

fPeak = SamplingRate * BinNumberOfPeak / FFTLength ;

like image 141
SACn Avatar answered Oct 18 '22 07:10

SACn