Sending audio playback using NAudio

I was able to send audio from the microphone using the code here .

However, I could not do this using NAudio .

Code CodeProject has explicit code for encoding and decoding, for example:

G711.Encode_aLaw G711.Decode_uLaw 

to translate and return bytes for sending over the network.

Can I get sample code for NAudio for the CodeProject application above?

+4
source share
1 answer

Here's a quick C # Console application that I wrote using NAudio, mic input, speaker output, encoded with U-Law or A-Law. The NAudio.Codecs namespace contains A-Law and u-Law encoders and decoders.

This program does not transmit data over the network (this is not difficult to do, I just did not want to do it here). I will leave it to you. Instead, it contains the Sender stream and the Receiver stream.

The DataAvailable microphone event DataAvailable simply drops the byte buffer into the queue (it makes a copy of the buffer β€” you don't want to keep the actual buffer from the event). The Sender stream captures buffer queues, converts PCM data to g.711, and transfers it to the second queue. This part β€œfalls into the second place” is the place where you send the remote UDP destination for your specific application.

The "Receiver" stream reads data from the second queue, converts it back to PCM and transfers it to the BufferedWaveProvider , which is used by the WaveOut device (speaker). You would replace this input with UDP socket reception for your network application.

Please note that the program ensures that the PCM input and output (microphone and speaker) use the same WaveFormat . This is what you will also need to do for network endpoints.

Anyway, it works. So here is the code. I will not go into details. There are many comments to try to understand what is going on:

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

Source: https://habr.com/ru/post/1337399/


All Articles