2010-12-27 5 views
10

Ich bin auf der Suche nach einer einfachen Lösung, die Integer-Wert der Mikrofoneingabe in C# zurückgibt. Ich habe bereits vorhandene Samples im Netz überprüft, aber keine von ihnen funktionierte in einer x64-Umgebung. (VS2008 + W7 × 64).Live-Mikrofon Amplitudenmessung in C#

Gibt es eine einfache Lösung, die den Wert der Amplitude (oder Frequenz) der Mikrofoneingabe in C# zurückgibt?

Ich versuchte NAudio ohne Ergebnisse und dies: http://www.codeproject.com/KB/audio-video/cswavrec.aspx?msg=2155497 ohne Glück.

+0

Haben Sie DirectX DirectSound ausprobiert? – JYelton

+0

Haben Sie versucht, Ihr Programm von "Beliebige CPU" auf "Nur 32 Bit" zu setzen? Die meisten Programme profitieren nicht sehr davon, dass sie im 64-Bit-Modus ausgeführt werden. – CodesInChaos

+0

Ich habe das schon versucht, aber bisher kein Glück. Habe auch kein einfaches directSound-Beispiel gefunden. Ich habe auch SlimDX ausprobiert, aber es sieht so aus, als ob es immer Probleme mit all diesen Beispielen gibt. Außerdem benötige ich integer Wert mit dynamischem Update (einige Male pro Sekunde). Jeder hat etwas Erfahrung damit? Danke für jede Hilfe. – Marian

Antwort

2

Ich denke, der einfachste Weg zu gehen ist, die alte Windows-Multimedia-API zu verwenden, weil es wirklich einfach ist.

Hier ist der Link zu MSDN: http://msdn.microsoft.com/en-us/library/dd743586(v=VS.85).aspx

Was Sie tun, ist, dass Sie die waveInOpen Funktion verwenden, um ein Eingabegerät zu erhalten. Um herauszufinden, welches Gerät Sie verwenden sollen, listen Sie nicht alle Geräte auf, aber Sie können jedes einzelne Gerät abfragen. Die Anzahl der installierten Geräte wird durch den Aufruf waveInGetNumDevs zurückgegeben. Sie können dann für jedes Gerät waveInGetDevCaps aufrufen und diese Eigenschaften überprüfen.

Wenn Sie mit Ihrem Gerät umgehen, rufen Sie wiederholt waveInAddBuffer an, um kleine Datenblöcke zu erhalten. Abhängig vom Format, das Sie während waveInOpen angegeben haben, repräsentieren die Bytes die rohen Audiodaten. Amplitude in 8 oder 16 Bit signiert oder unverstimmt mit einer bestimmten Frequenz abgetastet.

Sie könnten dann einen gleitenden Durchschnitt anwenden, um das Signal zu glätten und nur das zu drucken.

C# verfügt nicht über eine Sound-API, die ich kenne. Sie verwenden also P/Invoke, um an die Win32-API-Funktionen zu gelangen. Dies ist ziemlich einfach, Sie müssen nur kleine Versionen der Win32-Header portieren, um sie direkt aus C# aufrufen zu können.

Wenn Sie mehr Hardcore sind, könnten Sie eine Wrapper-Bibliothek in C++/CLI schreiben. Das ist gar nicht so schlecht für eine Idee, denn Sie können vorhandene Windows C/C++ - Headerdateien verwenden und C++ und verwalteten Code auf interessante Weise mischen. Seien Sie vorsichtig mit den nicht verwalteten Ressourcen und Sie werden in kürzester Zeit eine sehr leistungsfähige Introability-Bibliothek haben.

Aber es gibt auch erweiterte Audio-APIs, die mit Windows Vista beginnen, die Windows Core Audio-Komponenten, die auf der ganzen Linie interessanter sein könnten. Aber für den grundlegenden I/O-Betrieb bringen Sie die Windows-Multimediafunktionen schneller voran.

Ich habe diese Funktionen bei mehreren Gelegenheiten beim Erstellen einfacher Software-Synthesizer verwendet. Leider ist dieser Code längst weg.

1

Ich empfehle SlimDX, da es auf fast jede Version von Windows (x86 oder x64) funktionieren sollte und bietet die meisten Funktionen und Flexibilität. Es ist jedoch mühsam, loszulegen, da es keine guten vollständigen Codebeispiele gibt. Ich schrieb eine Wrapper-Klasse zu deren Nutzung zu vereinfachen, obwohl so kann es wie folgt aufgerufen werden (ich diesen Code auf Win7 x64 getestet):

public void CaptureAudio() 
    { 
     using (var source = new SoundCardSource()) 
     { 
      source.SampleRateKHz = 44.1; 
      source.SampleDataReady += this.OnSampleDataReady; 
      source.Start(); 

      // Capture 5 seconds of audio... 
      Thread.Sleep(5000); 

      source.Stop(); 
     } 
    } 

    private void OnSampleDataReady(object sender, SampleDataEventArgs e) 
    { 
     // Do something with e.Data short array on separate thread... 
    } 

Hier ist die Quelle für die SlimDX Wrapper-Klasse:

using System; 
using System.Collections.Generic; 
using System.Threading; 
using System.Threading.Tasks; 
using SlimDX.DirectSound; 
using SlimDX.Multimedia; 

public class SampleDataEventArgs : EventArgs 
{ 
    public SampleDataEventArgs(short[] data) 
    { 
     this.Data = data; 
    } 

    public short[] Data { get; private set; } 
} 

public class SoundCardSource : IDisposable 
{ 
    private volatile bool running; 
    private int bufferSize; 
    private CaptureBuffer buffer; 
    private CaptureBufferDescription bufferDescription; 
    private DirectSoundCapture captureDevice; 
    private WaveFormat waveFormat; 
    private Thread captureThread; 
    private List<NotificationPosition> notifications; 
    private int bufferPortionCount; 
    private int bufferPortionSize; 
    private WaitHandle[] waitHandles; 
    private double sampleRate; 

    public SoundCardSource() 
    { 
     this.waveFormat = new WaveFormat(); 
     this.SampleRateKHz = 44.1; 
     this.bufferSize = 2048; 
    } 

    public event EventHandler<SampleDataEventArgs> SampleDataReady = delegate { }; 

    public double SampleRateKHz 
    { 
     get 
     { 
      return this.sampleRate; 
     } 

     set 
     { 
      this.sampleRate = value; 

      if (this.running) 
      { 
       this.Restart(); 
      } 
     } 
    } 

    public void Start() 
    { 
     if (this.running) 
     { 
      throw new InvalidOperationException(); 
     } 

     if (this.captureDevice == null) 
     { 
      this.captureDevice = new DirectSoundCapture(); 
     } 

     this.waveFormat.FormatTag = WaveFormatTag.Pcm; // Change to WaveFormatTag.IeeeFloat for float 
     this.waveFormat.BitsPerSample = 16; // Set this to 32 for float 
     this.waveFormat.BlockAlignment = (short)(waveFormat.BitsPerSample/8); 
     this.waveFormat.Channels = 1; 
     this.waveFormat.SamplesPerSecond = (int)(this.SampleRateKHz * 1000D); 
     this.waveFormat.AverageBytesPerSecond = 
      this.waveFormat.SamplesPerSecond * 
      this.waveFormat.BlockAlignment * 
      this.waveFormat.Channels; 

     this.bufferPortionCount = 2; 

     this.bufferDescription.BufferBytes = this.bufferSize * sizeof(short) * bufferPortionCount; 
     this.bufferDescription.Format = this.waveFormat; 
     this.bufferDescription.WaveMapped = false; 

     this.buffer = new CaptureBuffer(this.captureDevice, this.bufferDescription); 

     this.bufferPortionSize = this.buffer.SizeInBytes/this.bufferPortionCount; 
     this.notifications = new List<NotificationPosition>(); 

     for (int i = 0; i < this.bufferPortionCount; i++) 
     { 
      NotificationPosition notification = new NotificationPosition(); 
      notification.Offset = this.bufferPortionCount - 1 + (bufferPortionSize * i); 
      notification.Event = new AutoResetEvent(false); 
      this.notifications.Add(notification); 
     } 

     this.buffer.SetNotificationPositions(this.notifications.ToArray()); 
     this.waitHandles = new WaitHandle[this.notifications.Count]; 

     for (int i = 0; i < this.notifications.Count; i++) 
     { 
      this.waitHandles[i] = this.notifications[i].Event; 
     } 

     this.captureThread = new Thread(new ThreadStart(this.CaptureThread)); 
     this.captureThread.IsBackground = true; 

     this.running = true; 
     this.captureThread.Start(); 
    } 

    public void Stop() 
    { 
     this.running = false; 

     if (this.captureThread != null) 
     { 
      this.captureThread.Join(); 
      this.captureThread = null; 
     } 

     if (this.buffer != null) 
     { 
      this.buffer.Dispose(); 
      this.buffer = null; 
     } 

     if (this.notifications != null) 
     { 
      for (int i = 0; i < this.notifications.Count; i++) 
      { 
       this.notifications[i].Event.Close(); 
      } 

      this.notifications.Clear(); 
      this.notifications = null; 
     } 
    } 

    public void Restart() 
    { 
     this.Stop(); 
     this.Start(); 
    } 

    private void CaptureThread() 
    { 
     int bufferPortionSamples = this.bufferPortionSize/sizeof(float); 

     // Buffer type must match this.waveFormat.FormatTag and this.waveFormat.BitsPerSample 
     short[] bufferPortion = new short[bufferPortionSamples]; 
     int bufferPortionIndex; 

     this.buffer.Start(true); 

     while (this.running) 
     { 
      bufferPortionIndex = WaitHandle.WaitAny(this.waitHandles); 

      this.buffer.Read(
       bufferPortion, 
       0, 
       bufferPortionSamples, 
       bufferPortionSize * Math.Abs((bufferPortionIndex - 1) % bufferPortionCount)); 

      this.SampleDataReady(this, new SampleDataEventArgs(bufferPortion)); 
     } 

     this.buffer.Stop(); 
    } 

    public void Dispose() 
    { 
     this.Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      this.Stop(); 

      if (this.captureDevice != null) 
      { 
       this.captureDevice.Dispose(); 
       this.captureDevice = null; 
      } 
     } 
    } 
} 

Es ist vollständig multi-threaded, um die Latenz zu minimieren. Ich habe es ursprünglich für ein Signalverarbeitungs-Analysetool in Echtzeit geschrieben und die Fließkommaausgabe statt kurz verwendet, aber ich habe das Codebeispiel so modifiziert, dass es der gewünschten Verwendung entspricht.Wenn Sie Frequenzdaten benötigen, würde ich http://www.mathdotnet.com/Neodym.aspx oder http://www.exocortex.org/dsp/ für eine gute C# FFT-Bibliothek verwenden.