6

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.

Community
  • 1
  • 1
Adam Short
  • 498
  • 7
  • 28
  • 1
    Have you seen this post: http://stackoverflow.com/questions/15009084/implementing-fftpitchdetector-in-c-sharp ? – David Tansey Feb 15 '17 at 18:27
  • @DavidTansey I have not. Will look into it and report back. – Adam Short Feb 15 '17 at 18:29
  • @DavidTansey I modified my OnDataAvailable code to match and pass in "floats" and "floats.Length" to pitchDetect.DetectPitch but I get an index out of range exception at `SmbPitchShift.smbFft(fftBuffer, frames, -1);` and if commented out, at `float real = fftBuffer[bin * 2];`. Any ideas? – Adam Short Feb 15 '17 at 19:01
  • Modifed to match what -- do you mean the code shown by the OP in that post or the code shown (via link to an article) in Mark Heath's answer there? Also if you look at the comments beneath Mark's answer you will see that someone had an `index out of range` problem and Mark suggested how to fix it in an additional comment. – David Tansey Feb 15 '17 at 19:23
  • @DavidTansey both, they're the same code. From Mark's answer, isn't the point of the SampleAggregator to make things a power of two, so I shouldn't have to change what I pass through to pitch detector? – Adam Short Feb 15 '17 at 19:25

1 Answers1

1

Use

fPeak = SamplingRate * BinNumberOfPeak / FFTLength ;

SACn
  • 1,862
  • 1
  • 14
  • 29
  • What's "BinNumberOfPeak"? And where's "FFTLength"? You're answer doesn't provide much detail. I'd like to understand what the data NAudio is returning represents as well as how to get the frequency(ies) from the data? – Adam Short Feb 26 '17 at 20:26
  • FFT Length = 4096 (= N number of computation points) and Bin number is index in FFT array 0, 1, 2 .. Frequency peak computation is tricky based on one-sided or two-sided spectra. You must follow [point 4 here](http://www.gaussianwaves.com/2014/07/how-to-plot-fft-using-matlab-fft-of-basic-signals-sine-and-cosine-waves/) for understanding authentic calculations. – SACn Feb 27 '17 at 14:31