天天看点

C#使用UDP+NAudio封装的语音对讲与同步录音功能类库

采用UDP网络通信和NAudio类库实现了语音对讲与录音的功能类库,步骤如下:

1、下载NAudio源码,地址https://github.com/naudio/NAudio

2、新建工程:类库(.NET Framework)

3、将NAudio源码中的INetworkChatCodec类和UncompressedPcmChatCodec类拷贝至工程

4、添加对log4net.dll和NAudio.dll的引用

5、新建NAudioRecorder.cs类,代码如下:

using NAudio.Wave;
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using log4net;

namespace NAudioClass
{
    public class NAudioRecorder
    {
        private WaveFileWriter waveFile = null;
        private WaveIn waveIn;
        private UdpClient udpSender;
        private UdpClient udpListener;
        private IWavePlayer waveOut;
        private BufferedWaveProvider waveProvider;
        private INetworkChatCodec selectedCodec;
        private IPEndPoint endPoint;
        private volatile bool connected = false;
        private string fileName = string.Empty;
        private int inputDeviceNumber = 0;
        private int port = 46900;
        private string addressReturn = "192.168.1.100";

        private readonly static ILog LogHelper = LogManager.GetLogger("NAudioClass");

        /// <summary>
        /// 录音结束后保存的文件路径
        /// </summary>
        public void SetFileName(string name)
        {
            fileName = name;
        }

        /// <summary>
        /// 设置连接地址,端口,设备号
        /// </summary>
        public void SetEndPoint(string address, int p, int deviceNumber = 0)
        {
            addressReturn = address;
            port = p;
            inputDeviceNumber = deviceNumber;
        }

        /// <summary>
        /// 开始对话并录音
        /// </summary>
        public void StartSpeechRec()
        {
            selectedCodec = new UncompressedPcmChatCodec();
            //制定服务端的IP和端口
            endPoint = new IPEndPoint(IPAddress.Parse(addressReturn), port);
            //发送消息
            Connect(endPoint, inputDeviceNumber, selectedCodec);
        }

        /// <summary>
        /// 停止对话和录音
        /// </summary>
        public void StopSpeechRec()
        {
            connected = false;
            fileName = string.Empty;

            waveIn.DataAvailable -= waveIn_DataAvailable;
            waveIn.StopRecording();
            waveOut.Stop();

            udpSender.Close();
            udpListener.Close();

            if (waveIn != null)
            {
                waveIn.Dispose();
                waveIn = null;
            }
            if (waveOut != null)
            {
                waveOut.Dispose();
                waveOut = null;
            }
            if (selectedCodec != null)
            {
                selectedCodec.Dispose();
                selectedCodec = null;
            }
            if (waveFile != null)
            {
                waveFile.Dispose();
                waveFile = null;
            }
        }

        private void Connect(IPEndPoint endPoint, int inputDeviceNumber, INetworkChatCodec codec)
        {
            try
            {
                waveIn = new WaveIn();
                waveIn.DeviceNumber = inputDeviceNumber;
                waveIn.WaveFormat = codec.RecordFormat;
                waveIn.DataAvailable += waveIn_DataAvailable;
                if(!string.IsNullOrEmpty(fileName))
                {
                    waveFile = new WaveFileWriter(fileName, waveIn.WaveFormat);
                }
                waveIn.StartRecording();
                LogHelper.Debug("NAudio StartRecording");

                udpSender = new UdpClient();
                udpListener = new UdpClient(endPoint.Port);
                udpListener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                udpSender.Connect(endPoint);

                waveOut = new WaveOut();
                waveProvider = new BufferedWaveProvider(codec.RecordFormat);
                waveOut.Init(waveProvider);
                waveOut.Play();

                connected = true;
                var state = new ListenerThreadState { Codec = codec, EndPoint = endPoint };
                ThreadPool.QueueUserWorkItem(ListenerThread, state);
            }
            catch(Exception ex)
            {
                LogHelper.ErrorFormat("NAudio 设备连接失败,error:", ex);
                throw new Exception("设备连接失败。");
            }
        }
        /// <summary>
        /// 打开音频开始录音回调函数
        /// </summary>
        private void waveIn_DataAvailable(object sender, WaveInEventArgs e)
        {
            try
            {
                //发送音频
                byte[] encoded = selectedCodec.Encode(e.Buffer, 0, e.BytesRecorded);
                int num = udpSender.Send(encoded, encoded.Length);
                //写入文件
                if (waveFile != null)
                {
                    waveFile.Write(e.Buffer, 0, e.BytesRecorded);
                    waveFile.Flush();
                }
            }
            catch (Exception ex)
            {
                LogHelper.ErrorFormat("NAudio 音频发送写入失败,error:", ex);
                throw new Exception("音频发送写入失败。");
            }

        }
        private void ListenerThread(object state)
        {
            var listenerThreadState = (ListenerThreadState)state;
            var endPoint = listenerThreadState.EndPoint;
            try
            {
                while (connected)
                {
                    try
                    {
                        byte[] b = udpListener.Receive(ref endPoint);
                        byte[] decoded = listenerThreadState.Codec.Decode(b, 0, b.Length);
                        waveProvider.AddSamples(decoded, 0, decoded.Length);
                    }
                    catch
                    {
                        
                    }
                }
            }
            catch (SocketException ex)
            {
                LogHelper.ErrorFormat("NAudio Socket连接异常,error:", ex);
                throw new Exception("Socket连接异常。");
            }
        }

        private class ListenerThreadState
        {
            public IPEndPoint EndPoint { get; set; }
            public INetworkChatCodec Codec { get; set; }
        }
    }
}
           

6、生成dll,调用的时候添加该dll,新建NAudioRecorder对象后直接引用方法。