I am trying to write a program that identifies the note I play on a piano,I found out that the Goertzel filter is a easy to implement algorithm but I don't know how to use it.
Here is the code:
using NAudio.Wave;
using System.Windows;
using System;
using System.Collections.Generic;
namespace WpfTest {
public partial class MainWindow : Window {
private BufferedWaveProvider buffer;
private WaveIn waveIn;
private WaveOut waveOut;
private const double TargetFreaquency = 261.626;//C4 note
private const int SampleRate = 44100;
public MainWindow() {
InitializeComponent();
InitializeSound();
waveIn.StartRecording();
waveOut.Play();
}
private void InitializeSound() {
waveIn = new WaveIn();
waveOut = new WaveOut();
buffer = new BufferedWaveProvider(waveIn.WaveFormat);
waveIn.DataAvailable += WaveInDataAvailable;
waveOut.Init(buffer);
}
private void WaveInDataAvailable(object sender, WaveInEventArgs e) {
buffer.AddSamples(e.Buffer, 0, e.BytesRecorded);
var floatBuffer = new List<float>();
for (int index = 0; index < e.BytesRecorded; index += 2) {
short sample = (short)((e.Buffer[index + 1] << 8) |
e.Buffer[index + 0]);
float sample32 = sample / 32768f;
floatBuffer.Add(sample32);
}
if (NotePlayed(floatBuffer.ToArray(), e.BytesRecorded)) {
Console.WriteLine("You have played C4");
}
}
private bool NotePlayed(float[] buffer, int end) {
double power = GoertzelFilter(buffer, TargetFreaquency, buffer.Length);
if (power > 500) return true;
return false;
}
private double GoertzelFilter(float[] samples, double targetFreaquency, int end) {
double sPrev = 0.0;
double sPrev2 = 0.0;
int i;
double normalizedfreq = targetFreaquency / SampleRate;
double coeff = 2 * Math.Cos(2 * Math.PI * normalizedfreq);
for (i = 0; i < end; i++) {
double s = samples[i] + coeff * sPrev - sPrev2;
sPrev2 = sPrev;
sPrev = s;
}
double power = sPrev2 * sPrev2 + sPrev * sPrev - coeff * sPrev * sPrev2;
return power;
}
}
}
The code is not working correctly but how should I do to write in the console:"You have played C4" every time I play the C4 note to the microphone?
It looks like you're assuming that the microphone input will be 16-bit PCM samples at 44100Hz. That's not necessarily the case. You can check the 'default' microphone format, as well as force it to what you're expecting, as follows:
I'm not sure on the endianness when you're converting the
short
tofloat
in your event handler (I'm old, and I can't remember which end is which anymore :)), so that might also be an issue. You would probably be better off usingBitConverter.ToInt16
to do that, rather than the shift/add that you're doing now:It also looks like the
end
parameter toNotePlayed
is unused, which is actually good! Inferring the meaning of anend
parameter,e.BytesRecorded
wouldn't be the correct value anyway since that's not the sample count.