采用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对象后直接引用方法。