c#语音聊天

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

c#语音聊天.txt举得起放得下叫举重,举得起放不下叫负重。头要有勇气,抬头要有底气。学习要加,骄傲要减,机会要乘,懒惰要除。人生三难题:思,相思,单相思。实现方法概要要想实现语音聊天,有几个步骤是必须的(就是我不说,相信你应该也能想得到一些):a语音采集:采集的作用就是从你的麦克风中获取数据,我采用DirectSound类来实现这个技术。参考:C#中利用DirectSound录音(b语音编码:利用语音编码算法对采集到的话音进行压缩编码,进行编码的目的是为了减少网络带宽的压力。)c语音传输:将采集到的声音传输到网络上的其它主机,我采用SocketUDP方式来实现。参考:C#Socket编程笔记(d语音解码:如果所传输的语音进行过压缩编码,则必须对语音进行解码,否则无法得到原始语音数据。)e语音播放:当对方通过网络传输到本机时(,如果需要解码则先执行d),进行实时播放。上面红色标记的步骤,可以省略。在本软件中,我并未采用这两个步骤,因为当我采用了这两个步骤后,发现语音时延异常的严重。我采用的编解码算法是G.729,利用的是g729.dll库文件,压缩效果不错,但是时延比较严重,可能是自己哪里没有设置好。如果有朋友使用过该算法,且时延低的,希望不吝赐教。接下来,重点介绍语音采集、语音传输、语音播放的实现。语音采集由于所实现的方法与录音方法一致,因此不会着墨过多,如果你不能很好的理解,请先参考:C#中利用DirectSound录音与录音不同的是,录音我们需要建立一个WAVE文件来存储这些采集到的数据,而在语音聊天中,则不需要存储,当采集到一些数据后,就立刻发送出去,因此也不需要开辟很大的空间来存放PCM数据。我们先来回顾下采集的基本步骤:1.设置PCM格式,设置相关的参数,如:采样频率、量化位数等。2.建立采集用的设备对象,建立采集用的缓冲区对象。3.设置缓冲区通知,设置通知被触发后的事件。通知是用于当缓冲区的读指针达到某预设位置时触发通知事件,提醒我们可以对某部分的数据进行传送了。4.开始采集声音。5.当通知被触发后,建立一个新的线程来处理数据传送的事件。(建立一个新的线程,就是为了防止采集过程被中断)。主要代码///summary///设置音频格式,如采样率等////summary///returns设置完成后的格式/returnsprivateWaveFormatSetWaveFormat(){WaveFormatformat=newWaveFormat();format.FormatTag=WaveFormatTag.Pcm;//设置音频类型format.SamplesPerSecond=11025;//采样率(单位:赫兹)典型值:11025、22050、44100Hzformat.BitsPerSample=16;//采样位数format.Channels=1;//声道format.BlockAlign=(short)(format.Channels*(format.BitsPerSample/8));//单位采样点的字节数format.AverageBytesPerSecond=format.BlockAlign*format.SamplesPerSecond;returnformat;//按照以上采样规格,可知采样1秒钟的字节数为22050*2=44100B约为43K}///summary///创建捕捉设备对象////summary///returns如果创建成功返回true/returnsprivateboolCreateCaputerDevice(){//首先要玫举可用的捕捉设备CaptureDevicesCollectioncapturedev=newCaptureDevicesCollection();Guiddevguid;if(capturedev.Count0){devguid=capturedev[0].DriverGuid;}else{System.Windows.Forms.MessageBox.Show(当前没有可用于音频捕捉的设备,系统提示);returnfalse;}//利用设备GUID来建立一个捕捉设备对象capture=newCapture(devguid);returntrue;}///summary///创建捕捉缓冲区对象////summaryprivatevoidCreateCaptureBuffer(){//想要创建一个捕捉缓冲区必须要两个参数:缓冲区信息(描述这个缓冲区中的格式等),缓冲设备。WaveFormatmWavFormat=SetWaveFormat();CaptureBufferDescriptionbufferdescription=newCaptureBufferDescription();bufferdescription.Format=mWavFormat;//设置缓冲区要捕捉的数据格式iNotifySize=mWavFormat.AverageBytesPerSecond/iNotifyNum;//1秒的数据量/设置的通知数得到的每个通知大小小于0.2s的数据量,话音延迟小于200ms为优质话音iBufferSize=iNotifyNum*iNotifySize;bufferdescription.BufferBytes=iBufferSize;bufferdescription.ControlEffects=true;bufferdescription.WaveMapped=true;capturebuffer=newCaptureBuffer(bufferdescription,capture);//建立设备缓冲区对象}//设置通知privatevoidCreateNotification(){BufferPositionNotify[]bpn=newBufferPositionNotify[iNotifyNum];//设置缓冲区通知个数//设置通知事件notifyEvent=newAutoResetEvent(false);notifyThread=newThread(RecoData);//通知触发事件notifyThread.IsBackground=true;notifyThread.Start();for(inti=0;iiNotifyNum;i++){bpn[i].Offset=iNotifySize+i*iNotifySize-1;//设置具体每个的位置bpn[i].EventNotifyHandle=notifyEvent.Handle;}myNotify=newNotify(capturebuffer);myNotify.SetNotificationPositions(bpn);}//线程中的事件privatevoidRecoData(){while(true){//等待缓冲区的通知消息notifyEvent.WaitOne(Timeout.Infinite,true);//录制数据RecordCapturedData(Client,epServer);}}//真正转移数据的事件,其实就是把数据传送到网络上去。privatevoidRecordCapturedData(SocketClient,EndPointepServer){byte[]capturedata=null;intreadpos=0,capturepos=0,locksize=0;capturebuffer.GetCurrentPosition(outcapturepos,outreadpos);locksize=readpos-iBufferOffset;//这个大小就是我们可以安全读取的大小if(locksize==0){return;}if(locksize0){//因为我们是循环的使用缓冲区,所以有一种情况下为负:当文以载读指针回到第一个通知点,而Ibuffeoffset还在最后一个通知处locksize+=iBufferSize;}capturedata=(byte[])capturebuffer.Read(iBufferOffset,typeof(byte),LockFlag.FromWriteCursor,locksize);//capturedata=g729.Encode(capturedata);//语音编码try{Client.SendTo(capturedata,epServer);//传送语音}catch{thrownewException();}iBufferOffset+=capturedata.Length;iBufferOffset%=iBufferSize;//取模是因为缓冲区是循环的。}上述代码可以很好的采集到声音数据,几乎与原始声音一致。如果你已经可以实现录音,那么以上对你来说应该并不陌生。语音传输这部分并不是很难,如果你熟悉socket编程,那么就可以PASS这一部分了,与以往传输不同的只是现在传输的是语音而已。如果你没接触过socket,那可以瞧瞧C#Socket编程笔记。感觉这部分叫“语音传输”并不是很恰当,因为其实真正用于传输的语句只有一句。除了语音传输之外,我们还需要对网络进行监听,从而能捕获对方发送给自己的语音信息。但是,也不知道叫什么好,就估且这么叫着吧。在这一部分,我主要讲下大致流程。1.建立socket对象,在实例化这个对象的时候有一个参数是设置使用的协议,在本软件中,我采用的是UDP。为什么要采用UDP?建立TCP能不能传送语音,答案肯定是能的。在本软件中,我考虑的主要是语音延时问题,采用TCP在建立连接和维护连接中对时间和系统资源的开销较大,因此会有明显的时延发生,严重影响了实时性。另外,因为UDP是无连接的,这使得采用UDP可以支持日后功能上的扩展(如:组播)。2.绑定本机的IP和端口,因为一个主机可能会有不止一个IP地址,如回发地址:127.0.0.1和局域网地址:192.168.#.#。为了增加可用性,我这里选择绑定到任何本机可用的IP地址(IPAddress.Any),而端口我们约定默认为8000。3.启动监听线程,来监听网络。我采用异步的方式,以便获得更好的系统响应度。privateThreadListenThread;privatebyte[]bytData;///summary///监听方法,用于监听远程发送到本机的信息////summarypublicvoidListen(){ListenThread=newThread(newThreadStart(DoListen));ListenThread.IsBackground=true;//设置为后台线程,这样当主线程结束后,该线程自动结束ListenThread.Start();}privateEndPointepRemote;///summary///监听线程////summaryprivatevoidDoListen(){bytData=newbyte[intMaxDataSize];epRemote=(EndPoint)(newIPEndPoint(IPAddress.Any,0));while(true){if(LocalSocket.Poll(5000,SelectMode.SelectRead)){//每5ms查询一下网络,如果有可读数据就接收LocalSocket.BeginReceiveFrom(bytData,0,bytData.Length,SocketFlags.None,refepRemote,newAsyncCallback(ReceiveData),null);}}}///summary///接收数据////summary///paramname=iar/paramprivatevoidReceiveData(IAsyn

1 / 9
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功