2011-01-27 86 views
4

我设法使用发现的代码here发送来自麦克风的音频。我不能使用NAudio来做这个。使用NAudio发送播放音频

从CodeProject代码具有明确的代码进行编码和解码,如:

G711.Encode_aLaw 
G711.Decode_uLaw 

翻译并返回字节在网络上发送。

上面的CodeProject应用程序有可能为NAudio获得一些示例代码吗?

回答

0

这是一个快速的C#控制台应用程序,我使用NAudio,麦克风输入,扬声器输出,使用u-Law或A-Law编码编写。 NAudio.Codecs命名空间包含A-Law和u-Law编码器和解码器。

这个程序不是不是通过网络发送数据(这不是很难做,我只是不想这样做)。我会把它留给你。相反,它包含一个“发件人”线程和一个“接收器”线程。

麦克风DataAvailable事件处理程序只是将字节缓冲区放入队列中(它使得缓冲区副本 - 您不想保留事件中的实际缓冲区)。 “发送者”线程抓取排队的缓冲区,将PCM数据转换为g.711并将其放入第二个队列中。这个“进入第二个队列”的部分是你要发送到特定应用的远程UDP目的地的地方。

“接收器”线程从第二个队列中读取数据,将其转换回PCM,并将其提供给WaveOut(扬声器)设备正在使用的BufferedWaveProvider。您可以将此输入替换为您的联网应用程序的UDP套接字接收。

请注意,该程序保证PCM输入和输出(麦克风和扬声器)使用相同的WaveFormat。对于网络端点,您也必须这样做。

无论如何,它的工作原理。所以这是代码。我不会详细说明。有很多意见可以帮助你理解发生了什么:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using NAudio.Wave; 
using NAudio.Codecs; 

namespace G711MicStream 
{ 
    class Program 
    { 
     delegate byte EncoderMethod(short _raw); 
     delegate short DecoderMethod(byte _encoded); 

     // Change these to their ALaw equivalent if you want. 
     static EncoderMethod Encoder = MuLawEncoder.LinearToMuLawSample; 
     static DecoderMethod Decoder = MuLawDecoder.MuLawToLinearSample; 



     static void Main(string[] args) 
     { 
      // Fire off our Sender thread. 
      Thread sender = new Thread(new ThreadStart(Sender)); 
      sender.Start(); 

      // And receiver... 
      Thread receiver = new Thread(new ThreadStart(Receiver)); 
      receiver.Start(); 

      // We're going to try for 16-bit PCM, 8KHz sampling, 1 channel. 
      // This should align nicely with u-law 
      CommonFormat = new WaveFormat(16000, 16, 1); 

      // Prep the input. 
      IWaveIn wavein = new WaveInEvent(); 
      wavein.WaveFormat = CommonFormat; 
      wavein.DataAvailable += new EventHandler<WaveInEventArgs>(wavein_DataAvailable); 
      wavein.StartRecording(); 

      // Prep the output. The Provider gets the same formatting. 
      WaveOut waveout = new WaveOut(); 
      OutProvider = new BufferedWaveProvider(CommonFormat); 
      waveout.Init(OutProvider); 
      waveout.Play(); 


      // Now we can just run until the user hits the <X> button. 
      Console.WriteLine("Running g.711 audio test. Hit <X> to quit."); 
      for(; ;) 
      { 
       Thread.Sleep(100); 
       if(!Console.KeyAvailable) continue; 
       ConsoleKeyInfo info = Console.ReadKey(false); 
       if((info.Modifiers & ConsoleModifiers.Alt) != 0) continue; 
       if((info.Modifiers & ConsoleModifiers.Control) != 0) continue; 

       // Quit looping on non-Alt, non-Ctrl X 
       if(info.Key == ConsoleKey.X) break;     
      } 

      Console.WriteLine("Stopping..."); 

      // Shut down the mic and kick the thread semaphore (without putting 
      // anything in the queue). This will (eventually) stop the thread 
      // (which also signals the receiver thread to stop). 
      wavein.StopRecording(); 
      try{ wavein.Dispose(); } catch(Exception){} 
      SenderKick.Release(); 

      // Wait for both threads to exit. 
      sender.Join(); 
      receiver.Join(); 

      // And close down the output. 
      waveout.Stop(); 
      try{ waveout.Dispose(); } catch(Exception) {} 

      // Sleep a little. This seems to be accepted practice when shutting 
      // down these audio components. 
      Thread.Sleep(500); 
     } 


     /// <summary> 
     /// Grabs the mic data and just queues it up for the Sender. 
     /// </summary> 
     /// <param name="sender"></param> 
     /// <param name="e"></param> 
     static void wavein_DataAvailable(object sender, WaveInEventArgs e) 
     { 
      // Create a local copy buffer. 
      byte [] buffer = new byte [e.BytesRecorded]; 
      System.Buffer.BlockCopy(e.Buffer, 0, buffer, 0, e.BytesRecorded); 

      // Drop it into the queue. We'll need to lock for this. 
      Lock.WaitOne(); 
      SenderQueue.AddLast(buffer); 
      Lock.ReleaseMutex(); 

      // and kick the thread. 
      SenderKick.Release(); 
     } 


     static 
     void 
     Sender() 
     { 
      // Holds the data from the DataAvailable event. 
      byte [] qbuffer = null; 

      for(; ;) 
      { 
       // Wait for a 'kick'... 
       SenderKick.WaitOne(); 

       // Lock... 
       Lock.WaitOne(); 
       bool dataavailable = (SenderQueue.Count != 0); 
       if(dataavailable) 
       { 
        qbuffer = SenderQueue.First.Value; 
        SenderQueue.RemoveFirst(); 
       } 
       Lock.ReleaseMutex(); 

       // If the queue was empty on a kick, then that's our signal to 
       // exit. 
       if(!dataavailable) break; 

       // Convert each 16-bit PCM sample to its 1-byte u-law equivalent. 
       int numsamples = qbuffer.Length/sizeof(short); 
       byte [] g711buff = new byte [numsamples]; 

       // I like unsafe for this kind of stuff! 
       unsafe 
       { 
        fixed(byte * inbytes = &qbuffer[0]) 
        fixed(byte * outbytes = &g711buff[0]) 
        { 
         // Recast input buffer to short[] 
         short * buff = (short *)inbytes; 

         // And loop over the samples. Since both input and 
         // output are 16-bit, we can use the same index. 
         for(int index = 0; index < numsamples; ++index) 
         { 
          outbytes[index] = Encoder(buff[index]); 
         } 
        } 
       } 

       // This gets passed off to the reciver. We'll queue it for now. 
       Lock.WaitOne(); 
       ReceiverQueue.AddLast(g711buff); 
       Lock.ReleaseMutex(); 
       ReceiverKick.Release(); 
      } 

      // Log it. We'll also kick the receiver (with no queue addition) 
      // to force it to exit. 
      Console.WriteLine("Sender: Exiting."); 
      ReceiverKick.Release(); 
     } 

     static 
     void 
     Receiver() 
     { 
      byte [] qbuffer = null; 
      for(; ;) 
      { 
       // Wait for a 'kick'... 
       ReceiverKick.WaitOne(); 

       // Lock... 
       Lock.WaitOne(); 
       bool dataavailable = (ReceiverQueue.Count != 0); 
       if(dataavailable) 
       { 
        qbuffer = ReceiverQueue.First.Value; 
        ReceiverQueue.RemoveFirst(); 
       } 
       Lock.ReleaseMutex(); 

       // Exit on kick with no data. 
       if(!dataavailable) break; 

       // As above, but we convert in reverse, from 1-byte u-law 
       // samples to 2-byte PCM samples. 
       int numsamples = qbuffer.Length; 
       byte [] outbuff = new byte [qbuffer.Length * 2]; 
       unsafe 
       { 
        fixed(byte * inbytes = &qbuffer[0]) 
        fixed(byte * outbytes = &outbuff[0]) 
        { 
         // Recast the output to short[] 
         short * outpcm = (short *)outbytes; 

         // And loop over the u-las samples. 
         for(int index = 0; index < numsamples; ++index) 
         { 
          outpcm[index] = Decoder(inbytes[index]); 
         } 
        } 
       } 

       // And write the output buffer to the Provider buffer for the 
       // WaveOut devices. 
       OutProvider.AddSamples(outbuff, 0, outbuff.Length); 
      } 

      Console.Write("Receiver: Exiting."); 
     } 


     /// <summary>Lock for the sender queue.</summary> 
     static Mutex Lock = new Mutex(); 

     static WaveFormat CommonFormat; 

     /// <summary>"Kick" semaphore for the sender queue.</summary> 
     static Semaphore SenderKick = new Semaphore(0, int.MaxValue); 
     /// <summary>Queue of byte buffers from the DataAvailable event.</summary> 
     static LinkedList<byte []> SenderQueue = new LinkedList<byte[]>(); 

     static Semaphore ReceiverKick = new Semaphore(0, int.MaxValue); 
     static LinkedList<byte []> ReceiverQueue = new LinkedList<byte[]>(); 

     /// <summary>WaveProvider for the output.</summary> 
     static BufferedWaveProvider OutProvider; 
    } 
}