2016-05-02 13 views
0

Ich versuche, ein Programm zu schreiben, das die Note auf einem Klavier identifiziert. Ich habe herausgefunden, dass der Goertzel-Filter ein einfach zu implementierender Algorithmus ist, aber ich weiß nicht, wie ich ihn benutze .Erkennung von Klaviernoten mit C# mit NAudio

Hier ist der 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; 
     } 
    } 
} 

Der Code nicht korrekt funktioniert, aber wie soll ich tun in der Konsole schreiben: jedes Mal, „Sie haben C4 gespielt“ Ich habe die Note C4 auf das Mikrofon spielen?

+1

Hallo und willkommen! Ich denke deine Frage ist ein wenig unklar in was du eigentlich Hilfe brauchst. Dein Code sagt _ "Du hast C4 gespielt" _, also brauchst du Hilfe bei diesen Methoden, weil etwas mit ihnen nicht stimmt, oder brauchst du Hilfe beim Aufruf dieser Methoden? – Binke

+0

Hallo und danke! Ich brauchte Hilfe mit den Methoden, weil es irgendwo einen Fehler gab und ich nicht herausfinden konnte, wo. Ich habe bereits die Antwort auf diese Frage, aber ich werde versuchen, in Zukunft klarer zu sein. –

Antwort

1

Es sieht so aus, als ob Sie annehmen, dass der Mikrofoneingang 16-Bit-PCM-Samples bei 44100 Hz ist. Das ist nicht unbedingt der Fall. Sie können das ‚default‘ Mikrofon-Format überprüfen, sowie zwingen sie zu dem, was Sie erwarten, wie folgt:

private void InitializeSound() 
{ 
    waveIn = new WaveIn(); 

    // Add this here to see what the waveIn default format is. 
    // Step through this line in the debugger. If this isn't 
    // 44100Hz sampling rate, 16-bit PCM, 1-channel, then that's 
    // probably what's going wrong. 
    WaveFormat checkformat = waveIn.WaveFormat; 

    // Note that these are the default values if we used the 
    // parameterless WaveFormat constructor, but just expanding 
    // here to show that we're forcing the input to what you're 
    // expecting: 
    WaveFormat myformat = new WaveFormat(44100, 16, 2); 
    waveIn.WaveFormat = myformat; 
    SampleRate = myformat.SampleRate; 

    waveIn.DataAvailable += WaveInDataAvailable; 

    waveOut = new WaveOut(); 
    buffer = new BufferedWaveProvider(waveIn.WaveFormat); 
    waveOut.Init(buffer); 
} 

ich auf dem endianness bin nicht sicher, wenn Sie die short zu float konvertieren in Ihr Event-Handler (Ich bin alt, und ich kann mich nicht mehr erinnern, welches Ende das ist :)), also könnte das auch ein Problem sein. Sie würden wahrscheinlich besser dran BitConverter.ToInt16 mit zu tun, anstatt der Verschiebung/hinzufügen, dass Sie jetzt tun:

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 = BitConvert.ToInt16(e.Buffer, index); 
     float sample32 = (float)sample; 
     sample32 /= (float)Int16.MaxValue; 
     floatBuffer.Add(sample32); 
    } 

    if (NotePlayed(floatBuffer.ToArray(), e.BytesRecorded)) 
    { 
     Console.WriteLine("You have played C4"); 
    } 
} 

Es sieht auch wie die end Parameter NotePlayed ungenutzt sind, was wirklich gut ist! Wenn man die Bedeutung eines end Parameters in Betracht zieht, wäre e.BytesRecorded sowieso nicht der richtige Wert, da dies nicht die Anzahl der Abtastungen ist.

+0

Vielen Dank für Ihre Hilfe! Ich tat, was Sie sagten, und jetzt funktioniert es, das Problem war, dass die Standard-Abtastrate 8000 Hz und nicht 44100 Hz war. Jetzt jedes Mal, wenn ich eine C4 Note spiele, bekomme ich mehrere "Sie haben C4" Linien in der Konsole geschrieben gespielt, aber ich denke, das ist normal, da die Note, die ich spiele, für eine Weile in Resonanz ist. Nochmals vielen Dank für Ihre schnelle und vollständige Antwort. –

+0

Ich bin froh, dass ich helfen konnte :) Ja, Sie werden die Zeichenfolge mehrmals sehen, ich vermute einmal für jedes 'DataAvailable' Ereignis, das generiert wird, bevor die Notiz verschwindet. –