2010-12-27 20 views
10

Estoy buscando una solución simple que devolverá el valor entero de la entrada de micrófono en C#. Ya estaba comprobando muestras disponibles en la red, pero ninguna de ellas funcionaba en un entorno x64. (VS2008 + W7 x64).Medición de amplitud de micrófono en vivo en C#

¿Hay alguna solución simple que devuelva el valor de la amplitud (o frecuencia) de la entrada del micrófono en C#?

Probé NAudio sin resultados y esto: http://www.codeproject.com/KB/audio-video/cswavrec.aspx?msg=2155497 sin suerte.

+0

¿Has probado DirectX DirectSound? – JYelton

+0

¿Ha intentado configurar su programa desde "Cualquier CPU" a "32 bits solamente"? La mayoría de los programas no se benefician mucho al ejecutarse en modo de 64 bits. – CodesInChaos

+0

Ya lo intenté, pero no tuve suerte hasta ahora. No he encontrado ningún ejemplo simple de directSound también. También probé SlimDX, pero parece que siempre hay problemas con todos esos ejemplos. Además, en mi caso, necesito un valor entero con actualización dinámica (muestreado algunas veces por segundo). Alguien tiene alguna experiencia con eso? Gracias por cualquier ayuda. – Marian

Respuesta

2

Creo que la ruta más fácil es utilizar la antigua API multimedia de Windows porque es muy sencilla.

Aquí está el enlace a MSDN: http://msdn.microsoft.com/en-us/library/dd743586(v=VS.85).aspx

Lo que se hace es que se utiliza la función waveInOpen para obtener un dispositivo de entrada. Para averiguar qué dispositivo usar, no enumera todos los dispositivos pero puede consultar cada uno de ellos. La cantidad de dispositivos instalados se devuelve llamando al waveInGetNumDevs. Luego puede llamar al waveInGetDevCaps para cada dispositivo e inspeccionar esas propiedades.

Cuando tenga el mango de su dispositivo, llame repetidamente al waveInAddBuffer para obtener pequeños trozos de datos. Dependiendo del formato que haya especificado durante waveInOpen, los bytes representan los datos de audio en bruto. Amplitud en muestras de 8 o 16 bits con o sin señal muestreadas con cierta frecuencia.

A continuación, puede aplicar un promedio continuo para suavizar la señal y simplemente imprimir eso.

C# no tiene una API de sonido que yo sepa, por lo que lo que hace es utilizar P/Invoke para obtener las funciones de API de Win32. Esto es bastante directo, solo necesita portar versiones pequeñas de los encabezados de Win32 para poder llamarlos directamente desde C#.

Si eres más duro, podrías escribir una biblioteca contenedora en C++/CLI. No es una mala idea porque permite usar los archivos de encabezado de Windows C/C++ existentes y mezclar C++ y el código administrado de manera interesante. Solo tenga cuidado con los recursos no administrados y tendrá una biblioteca de intropabilidad muy poderosa en muy poco tiempo.

Pero también hay API de audio más avanzadas que comienzan con Windows Vista, los componentes de Windows Core Audio que podrían ser más interesantes más adelante en la línea. Pero para el funcionamiento básico de E/S, las funciones multimedia de Windows lo llevarán más rápido.

He utilizado estas funciones en varias ocasiones al crear sintetizadores de software simples. Lamentablemente, ese código se fue hace mucho tiempo.

1

Recomiendo SlimDX ya que debería funcionar en casi cualquier versión de Windows (x86 o x64) y proporciona la mayoría de las características y flexibilidad. Sin embargo, es difícil ponerse en marcha ya que no hay buenas muestras de código completas. Escribí una clase contenedora para simplificar su uso, aunque por lo que puede ser llamado como esto (he probado este código en Win7 x64):

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... 
    } 

Aquí está la fuente de la clase contenedora SlimDX:

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; 
      } 
     } 
    } 
} 

Totalmente multiproceso para minimizar la latencia. Originalmente lo escribí para una herramienta de análisis de procesamiento de señal en tiempo real y utilicé la salida de flotación en lugar de la corta, pero modifiqué la muestra del código para que coincida con el uso solicitado.Si necesita datos de frecuencia, usaría http://www.mathdotnet.com/Neodym.aspx o http://www.exocortex.org/dsp/ para obtener una buena biblioteca C# FFT.

Cuestiones relacionadas