语音通信系统设计毕业论文.doc

语音通信系统设计毕业论文.doc

ID:213320

大小:489.00 KB

页数:79页

时间:2017-07-05

上传者:U-255
语音通信系统设计毕业论文.doc_第1页
语音通信系统设计毕业论文.doc_第2页
语音通信系统设计毕业论文.doc_第3页
语音通信系统设计毕业论文.doc_第4页
语音通信系统设计毕业论文.doc_第5页
资源描述:

《语音通信系统设计毕业论文.doc》由会员上传分享,免费在线阅读,更多相关内容在学术论文-天天文库

盐城工学院本科生毕业设计说明书(2010)语音通信系统设计毕业论文目录1绪论11.1课题背景及意义11.2课题现状11.3开发平台、开发技术12系统需求分析及要求32.1语音通信系统需求分析32.2系统的功能要求32.3系统性能要求33系统整体设计44各模块详细设计74.1音频编/解码74.2音频采集及回放134.3网络的传送和接收224.4界面设计355测试425.1模块测试425.2功能测试42结论47致谢48附录49程序核心代码49 盐城工学院本科生毕业设计说明书(2010)1绪论1.1课题背景及意义即时聊天工具是一种可以让使用者在网络上实时通讯的工具,大部分的即时通讯工具提供了状态信息的特性,如显示联络人名单,联络人是否在线及能否与联络人交谈等。目前在互联网上受欢迎的即时通讯软件包括百度QQ、MSNMessenger、Yahoo!Messenger、AOLInstantMessenger、NETMessengerService、Jabber、ICQ等。在本设计中将讨论一种基于MFCSocket的局域网通信工具的设计与实现方法。基于Socket的局域网通信软件可以为局域网提供一种良好、安全、快速的通信机制,在局域网内部通信、教学、讨论等应用中都具有一定的实用价值,它同时很好地诠释了Socket通信的原理。基于Socket的局域网通信软件应用范围广阔,不但可以处理传统的通信需求,而且也能扩展以适应新型的网络应用,如网络教育,数据影音传输等,拥有广泛的应用前景。1.2课题现状基于局域网的即时通信工具,实际上是互联网即时通信工具的一个小规模版本,广域网上的即时通信工具,如今一般采用UDP或者TCP协议体系来实现,开发技术已经比较成熟,比如较早的ICQ、MSNMessenger、YAHOO通等国外开发的产品,还有国产的有名的QQ、新浪UC、LAVA-LAVA等,这些工具统统都实现了广域网上的即时通信,尽管都是即时通信,实现了即时聊天,以及文件传输的主要功能,但是也各有各的特色,比如ICQ的巨大客户群,MSN的个性化表情,YAHOO通的易操作性等,而QQ也具有一个相当方便的屏幕截图功能,另外就是,所有上述软件都实现了网络即时的视频,语音聊天功能。这些软件,在使用方面各有特色,在实现方面也各有所长,但由于这些产品正在商业运营阶段,其实现方式属于商业机密,具体细节不可能得知,但是它在大的方面无非就是各种利用各种平台上的网络通信接口,建构基于下层TCP/IP,或者UDP/IP协议的软件产品。在局域网内,这些功能的实现跟广域网相比更加简单,因为局域网的网络结构本身比广域网要简单,但是可以借此理解网络协议,以及网络通信工具的实现原理,所以仍然极具研究价值。1.3开发平台、开发技术1.3.1VC++6.0简介我们通常所说的VisualC++实际上是一个完善的、非常强大的C++ 盐城工学院本科生毕业设计说明书(2010)程序开发环境,它的名字是MicrosoftDeveloperStudio。二者之间的细小差别并不重要,通常这两个术语可以互换。但如果你不围绕DeveloperStudio来进行学习,就不能有效地使用VisualC++(DeveloperStudio听起来很像VisualStudio,但它们之间没有关系)。它包含:C++编译器、调试器、应用程序框架生成器、项目管理器、设计和实现菜单等资源的编辑器等等。VisualC++是一个功能强大的可视化应用程序开发工具,用于Windows环境下32位的应用程序的开发,是计算机界公认的最优秀的应用开发工具之一。在提供可视化的编程方式的同时,VisualC++也适用于编写直接对系统底层操作的程序,生成代码的质量也优于其它的开发工具。在VisualC++环境下,利用Microsoft的基本类库MFC(MicrosoftFoundationClassLibrary),可以使用完全的面向对象的方法来进行Windows95/98/NT应用程序的开发,使得Windows程序员从大量的复杂劳动中解救出来,体会到真正的程序语言的强大功能和良好的灵活性。VisualC++编程是一个面向对象的程序设计方法。同传统的结构化程序设计方法相比,它缩短软件的研制时间,提高软件的开发效率,使程序员可以更好地理解和管理庞大而复杂的程序。面向对象的程序设计吸取了结构化程序设计的精华,它利用了人们根据对事物分类和抽象的倾向,引入了类和对象的概念,具有封装性(数据抽象)、继承和多态的特点。与结构化程序设计不同的是,面向对象程序设计是用类抽象代表现实的实体,用类之间的继承关系表示程序设计的抽象过程。函数只是对数据的操作,没有数据的概念,而类是数据和数据操作的集合,由于面向对象的程序设计方法非常近现实,所以越来越流行。VisualC++中集成了大量的最新技术,如ActiveX、COM等技术,程序开发人员可以紧紧地把握住软件开发技术发展的方向,开发出功能强大的应用程序。VisualC++还提供了丰富的技术资源,MSDN(MicrosoftDevelopNetwork)提供了强大的联机帮助支持,同时还可以通过访问Microsoft的网上站点来获得最新的技术文档。1.3.2WindowsSocket网络编程WindowsSockets规范是以U.C.Berkeley大学BSDUNIX中流行的Socket接口为范例定义了一套开放的、支持多种协议的、MicrosoftWindows下的网络编程接口,并不是一种网络协议。它不仅包含了人们所熟悉的BerkeleySocket风格的库函数;也包含了一组针对Windows的扩展库函数,以使程序员能充分地利用Windows消息驱动机制进行编程。WindowsSockets规范本意在于提供给应用程序开发者一套简单的API,使应用程序开发者能够使用,并且网络软件供应商能够实现的一套库函数调用和相关语义。现在的WindowsSockets已经基本上实现了与协议无关,你可以使用 盐城工学院本科生毕业设计说明书(2010)WindowsSockets来调用多种协议的功能,但较常使用的是TCP/IP协议。Socket实际在计算机中提供了一个通信端口,可以通过这个端口与任何一个具有Socket接口的计算机通信。应用程序在网络上传输,接收的信息都通过这个Socket接口来实现。微软为VC定义了WindowsSockets类如CAsyncSocket类和派生于CAsyncSocket的CSocket类,它们简单易用。2系统需求分析及要求2.1语音通信系统需求分析随着现代计算机技术的不断发展,多媒体已经成为现代计算机不可缺少的功能,而计算机的音频,视频功能是其中最为重要的部分。而随着网络的不断发展,网络已经成为人们最重要的交流方式之一。计算机硬件的更新,非凡是海量存储设备和大容量内存在PC机上的实现,对音频媒体进行数字化处理早已经成为可能。现在,企业、机关、学校都建立起了局域网。虽然可以通过文件共享的方式进行通讯,但单使用这种方式,非常不方便。在网上邻居里,只能看到机器名,不清楚对方是谁,也不知道对方机器里有什么资源可以共享,尤其当局域网的机器很多时,这种方式就更加麻烦了。而文字聊天方式比较单一缺乏真实感,所以语音通信就有了必要。但是语音传输需要有很大的网络带宽,对于拨号上网用户,语音传输仍然是奢侈品,不过对于局域网和宽带用户来说,是很不错的选择,本系统就是研究的基于局域网的语音传输,并以语音聊天为例给出了实例。语音聊天方式一般有两种,一种是专门的语音聊天室,采用Web方式,B/S结构,另外一中则是类似于QQ、MSN等聊天工具,采用C/S结构。本设计是采用的后者。TCP/IP网络中两个进程间的相互作用的主机模式是客户机/服务器模式。该模式的建立基于以下两点:1、非对等作用;2、通信完全是异步的。客户机/服务器模式在操作过程中采取的是主动请示方式,首先服务器方要先启动,并根据请示提供相应服务。综上所述,做一个语音通信软件是有必要的,也是能够实现的。2.2系统的功能要求在线朋友指的是在所有使用本系统在局域网上进行信息传递的用户。局域网中在线用户之间的语音聊天就是指在线双方互相传递语音信息的功能。1)在聊天窗口为基础,建立与服务器端的连接。2)与对方连接以后开始语音聊天。3)想结束语音聊天时,按结束键就能与对方断开连接,即结束语音聊天。 盐城工学院本科生毕业设计说明书(2010)2.3系统性能要求1)时间性能要求:在实际应用中由于客户端的大量频繁的访问,服务器响应时间应该尽可能缩短,对于有特殊需求的应用,还要求达到实时响应。2)存储性能要求:根据应用中的实际情况配置适当容量的存储设备,特别是音频存储设备容量方面要适当得配置。3)稳定性安全性要求:要求软件尽可能的稳定,对于一般的应用系统,对安全性要求不高,对于特殊的应用,还需要在安全性方面加以保证,所以要相应提高服务器端的配置。3系统整体设计要实现点对点语音通信,原理非常简单,只要针对一个点实现话音的实时采集、处理、播放,同时能进行可靠的传送和接收,这样两点一连便可通话。对于前者,采用Windows的低层音频服务比较合适,因为低层音频服务中的回调机制为我们提供了很大的方便。当应用程序不断向设备驱动程序提供音频数据时,设备驱动程序控制音频设备在后台完成录音和放音的具体操作,通过回调机制,我们又可以检测到什么时候用完一个数据块,并及时传送下一个数据块,从而保证了声音的连续,有了这种实时采集、回放功能后,接下来的工作就是在网络上传送话音数据。在点对点网络传输方面,选择面向连接的TCP/IP协议,TCP/IP传输协议自动处理分组丢失和交付失序问题,这样我们不用为这些问题操心,只需很好地利用这个连接,在采集话音回放之前一方面将自己的话音传给网络,另一方面接收网络传来的话音,这样便实现了点对点语音通信。其模块框图如图3-1所示,总体结构图为图3-2所示。语音通信系统音频采集音频回放网络传输音频编/解码图3-1模块框图 盐城工学院本科生毕业设计说明书(2010)图3-2总体结构图一个完整的音频通信系统程序要完成以下工作:发送端完成音频采集、压缩编码、码流发送等;接收端则要完成码流接收、解码恢复、音频回放等。其总体程序流程图如图3-3所示。 盐城工学院本科生毕业设计说明书(2010)程序开始启动音频输入/输出呼叫对方连接成功连接失败压缩编码音频输出音频输入Socket发送网络Socket接收程序退出图3-3总体程序流程 盐城工学院本科生毕业设计说明书(2010)4各模块详细设计4.1音频编/解码4.1.1音频编/解码介绍  自然界中的声音非常复杂,波形极其复杂,通常我们采用的是脉冲代码调制编码,即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。  1)采样率和采样大小(位/bit)。  声音其实是一种能量波,因此也有频率和振幅的特征,频率对应于时间轴线,振幅对应于电平轴线。波是无限光滑的,弦线可以看成由无数点组成,由于存储空间是相对有限的,数字编码过程中,必须对弦线的点进行采样。采样的过程就是抽取某点的频率值,很显然,在一秒中内抽取的点越多,获取得频率信息更丰富,为了复原波形,一次振动中,必须有2个点的采样,人耳能够感觉到的最高频率为20kHz,因此要满足人耳的听觉要求,则需要至少每秒进行40k次采样,用40kHz表达,这个40kHz就是采样率。我们常见的CD,采样率为44.1kHz。光有频率信息是不够的,我们还必须获得该频率的能量值并量化,用于表示信号强度。量化电平数为2的整数次幂,我们常见的CD位16bit的采样大小,即2的16次方。采样大小相对采样率更难理解,因为要显得抽象点,举个简单例子:假设对一个波进行8次采样,采样点分别对应的能量值分别为A1-A8,但我们只使用2bit的采样大小,结果我们只能保留A1-A8中4个点的值而舍弃另外4个。如果我们进行3bit的采样大小,则刚好记录下8个点的所有信息。采样率和采样大小的值越大,记录的波形更接近原始信号。  2)有损和无损  根据采样率和采样大小可以得知,相对自然界的信号,音频编码最多只能做到无限接近,至少目前的技术只能这样了,相对自然界的信号,任何数字音频编码方案都是有损的,因为无法完全还原。在计算机应用中,能够达到最高保真水平的就是PCM编码,被广泛用于素材保存及音乐欣赏,CD、DVD以及我们常见的WAV文件中均有应用。因此,PCM约定俗成了无损编码,因为PCM代表了数字音频中最佳的保真水准,并不意味着PCM就能够确保信号绝对保真,PCM也只能做到最大程度的无限接近。我们而习惯性的把MP3列入有损音频编码范畴,是相对PCM编码的。强调编码的相对性的有损和无损,是为了告诉大家,要做到真正的无损是困难的,就像用数字去表达圆周率,不管精度多高,也只是无限接近,而不是真正等于圆周率的值。  3)音频压缩技术  要算一个PCM音频流的码率是一件很轻松的事情,采样率值×采样大小值×声道数 盐城工学院本科生毕业设计说明书(2010)bps。一个采样率为44.1KHz,采样大小为16bit,双声道的PCM编码的WAV文件,它的数据速率则为44.1K×16×2=1411.2Kbps。我们常说128K的MP3,对应的WAV的参数,就是这个1411.2Kbps,这个参数也被称为数据带宽,它和ADSL中的带宽是一个概念。将码率除以8,就可以得到这个WAV的数据速率,即176.4KB/s。这表示存储一秒钟采样率为44.1KHz,采样大小为16bit,双声道的PCM编码的音频信号,需要176.4KB的空间,1分钟则约为10.34M,这对大部分用户是不可接受的,尤其是喜欢在电脑上听音乐的朋友,要降低磁盘占用,只有2种方法,降低采样指标或者压缩。降低指标是不可取的,因此专家们研发了各种压缩方案。4)频率与采样率  采样率表示了每秒对原始信号采样的次数,我们常见到的音频文件采样率多为44.1KHz,这意味着什么呢?假设我们有2段正弦波信号,分别为20Hz和20KHz,长度均为一秒钟,以对应我们能听到的最低频和最高频,分别对这两段信号进行40KHz的采样,我们可以得到一个什么样的结果呢?结果是:20Hz的信号每次振动被采样了40K/20=2000次,而20K的信号每次振动只有2次采样。显然,在相同的采样率下,记录低频的信息远比高频的详细。这也是为什么有些音响发烧友指责CD有数码声不够真实的原因,CD的44.1KHz采样也无法保证高频信号被较好记录。要较好的记录高频信号,看来需要更高的采样率,于是有些朋友在捕捉CD音轨的时候使用48KHz的采样率,这是不可取的!这其实对音质没有任何好处,对抓轨软件来说,保持和CD提供的44.1KHz一样的采样率才是最佳音质的保证之一,而不是去提高它。较高的采样率只有相对模拟信号的时候才有用,如果被采样的信号是数字的,请不要去尝试提高采样率。  5)流特征随着网络的发展,人们对在线收听音乐提出了要求,因此也要求音频文件能够一边读一边播放,而不需要把这个文件全部读出后然后回放,这样就可以做到不用下载就可以实现收听了。也可以做到一边编码一边播放,正是这种特征,可以实现在线的直播,架设自己的数字广播电台成为了现实。4.1.2PCM编码和WAVE音频格式 1)PCM编码  PCM脉冲编码调制是PulseCodeModulation的缩写。前面的文字我们提到了PCM大致的工作流程,我们不需要关心PCM最终编码采用的是什么计算方式,我们只需要知道PCM编码的音频流的优点和缺点就可以了。PCM编码的最大的优点就是音质好,最大的缺点就是体积大。我们常见的AudioCD就采用了PCM编码。  2)WAVE  这是一种经典的音频文件格式,由微软开发。WAV是一种文件格式,符合PIFFResourceInterchangeFile 盐城工学院本科生毕业设计说明书(2010)Format规范。所有的WAV都有一个文件头,这个文件头音频流的编码参数。WAV对音频流的编码没有硬性规定,除了PCM之外,还有几乎所有支持ACM规范的编码都可以为WAV的音频流进行编码。很多朋友没有这个概念,我们拿AVI做个示范,因为AVI和WAV在文件结构上是非常相似的,不过AVI多了一个视频流而已。我们接触到的AVI有很多种,因此我们经常需要安装一些Decode才能观看一些AVI,我们接触到比较多的DivX就是一种视频编码,AVI可以采用DivX编码来压缩视频流,当然也可以使用其他的编码压缩。同样,WAV也可以使用多种音频编码来压缩其音频流,不过我们常见的都是音频流被PCM编码处理的WAV,但这不表示WAV只能使用PCM编码,MP3编码同样也可以运用在WAV中,和AVI一样,只要安装好了相应的Decode,就可以欣赏这些WAV了。在Windows平台下,基于PCM编码的WAV是被支持得最好的音频格式,所有音频软件都能完美支持,由于本身可以达到较高的音质的要求,因此,WAV也是音乐编辑创作的首选格式,适合保存音乐素材。因此,基于PCM编码的WAV被作为了一种中介的格式,常常使用在其他编码的相互转换之中,例如MP3转换成WMA。3)PCM编码的WAV  PCM编码的WAV文件是音质最好的格式,Windows平台下,所有音频软件都能够提供对它的支持。Windows提供的WinAPI中有不少函数可以直接播放wav,因此,在开发多媒体软件时,往往大量采用wav,用作事件声效和背景音乐。PCM编码的wav可以达到相同采样率和采样大小条件下的最好音质,因此,也被大量用于音频编辑、非线性编辑等领域。其特点是音质非常好,被大量软件所支持。适用于多媒体开发、保存音乐和音效素材。所以本设计选用WAV格式的音频进行录制和播放4.1.3G729编/解码本设计使用G729编解码标准对音频进行音频编解码,在VisualC++环境中直接调用G729库来对音频数据进行处理。4.1.3.1G729编/解码标准20世纪80年代末,为满足长途通信的需求,ITU制订8kbps的高质量低延时的语音编码标准。1)技术指标:采样率8kHz、码率8kbps。2)技术特点:采用代数码本,码书简单、无需存储,恢复音质清晰;分析窗采用混合窗,LSP参数采用两级矢量量化;基音分析采用开环基音分析和自适应码本搜索结合,低运算复杂度、高精度;采用10ms分析帧,编码时延小。3)应用状况:被广泛应用于IP电话、视讯电话、视讯会议系统等。4)技术框架:如图4-1所示。 盐城工学院本科生毕业设计说明书(2010)图4-1G729编/解码技术框架4.1.3.2G729编/解码C++实现核心代码//音频解码#pragmacomment(lib,"G729a")//构造函数CAudioCode::CAudioCode(){va_g729a_init_encoder();//初始化编码器va_g729a_init_decoder();//初始化解码器}//析构函数CAudioCode::~CAudioCode(){}//编码音频数据BOOLCAudioCode::EncodeAudioData(char*pin,intlen,char*pout,int*lenr){//编码成功与否标记BOOLbRet=FALSE;//无效输入或输出if(!pin||len!=SIZE_AUDIO_FRAME||!pout)gotoRET;//分块进行编码 盐城工学院本科生毕业设计说明书(2010)va_g729a_encoder((short*)pin,(BYTE*)pout);va_g729a_encoder((short*)(pin+160),(BYTE*)pout+10);va_g729a_encoder((short*)(pin+320),(BYTE*)pout+20);va_g729a_encoder((short*)(pin+480),(BYTE*)pout+30);va_g729a_encoder((short*)(pin+640),(BYTE*)pout+40);va_g729a_encoder((short*)(pin+800),(BYTE*)pout+50);//编码长度if(lenr)*lenr=SIZE_AUDIO_PACKED;//编码成功标记bRet=TRUE;RET://返回returnbRet;}//音频解码BOOLCAudioCode::DecodeAudioData(char*pin,intlen,char*pout,int*lenr){//解码成功与否标记BOOLbRet=FALSE;//无效输入或输出if(!pin||len!=SIZE_AUDIO_PACKED||!pout)gotoRET;//分块解码va_g729a_decoder((BYTE*)pin,(short*)(pout),0);va_g729a_decoder((BYTE*)pin+10,(short*)(pout+160),0);va_g729a_decoder((BYTE*)pin+20,(short*)(pout+320),0);va_g729a_decoder((BYTE*)pin+30,(short*)(pout+480),0);va_g729a_decoder((BYTE*)pin+40,(short*)(pout+640),0);va_g729a_decoder((BYTE*)pin+50,(short*)(pout+800),0);//解码长度if(lenr)*lenr=SIZE_AUDIO_FRAME;//设置解码成功标记bRet=TRUE;RET: 盐城工学院本科生毕业设计说明书(2010)returnbRet;}4.2音频采集及回放本设计采用基于WaveX低级音频API采集音频及实时播放的技术。利用双/多缓冲技术和网络拥塞控制策略可很好的控制音频的实时性和连续性。双/多缓冲技术可以很好的实现声音的快速连续采集和实时顺畅播放。4.2.1方案选择音频的采集WINDOWS下音频的采集,播放有三种模式:1)通过高级音频函数、媒体控制接口MCI[1、2]设备驱动程序;2)低级音频函数MIDIMapper、低级音频设备驱动(WaveXAPI);3)利用DirectX中的DirectSound。  使用MCI的方法极其简便,灵活性较差,因此较难推广;使用低级音频函数的方法相对来说难一点,但是能够对音频数据进行灵活的操控;而采用DirectSound的方法,控制声音数据灵活,效果比前二者都好,但实现起来是三者中最难的。其三者的关系如图4-2所示。图4-2三者关系低层音频服务及重要的数据结构低级音频服务控制着不同的音频设备,这些设备包括WAVE,MIDI和辅助音频设备。 盐城工学院本科生毕业设计说明书(2010)如果想编写一个功能较强大的音频处理程序,那就必须使用低级音频函数和多媒体文件I/O来控制音频设备的输入和输出。因为低级音频函数可直接与音频驱动程序交互,通过窗口消息或回调(CALLBACK)函数来管理音频数据块的记录和播放,控制非常灵活。重要的一点是,低级音频函数为我们提供了一个设备无关的接口。低层音频服务及重要的数据结构低级音频服务控制着不同的音频设备,这些设备包括WAVE,MIDI和辅助音频设备。低级音频服务包括如下内容:1)查询音频设备;  2)打开和关闭设备驱动程序;  3)分配和准备音频数据块;  4)管理音频数据块;  5)应用MMTIME结构;6)处理错误。4.2.2相关数据结构1)WaveX低级音频函数的相关声明和定义在mmsystem.h头文件和Winmm.lib库中。所以如果程序中用到这些函数,必须包含mmsystem.h这个头文件,同时导进Winmm.lib库。如下:#include"mmsystem.h"#pragmacomment(lib,"Winmm.lib")2)声音在采集和播放的时需要有一些统一的格式,包括音频格式类型,声道,采样率等信息。下面的数据结构具体描述了该格式:wFormatTag是音频格式类型,nChannels是声道数,nSamplesPerSec是采样频率,nAvgBytesPerSec是每秒钟的字节数,nBlockAlign是每个样本的字节数,wBitsPerSample是每个样本的量化位数,cbSize是附加信息的字节大小。3)音频数据块有一个头结构,这个结构包含了音频数据缓冲的地址,大小,已录音数据大小等信息和其他各种控制标志。这个结构适用于音频的输入(录音)和输出(播放)缓冲中。详细信息包括:lpData是指定的缓冲块地址,dwBufferLength是指定的缓冲块大小,dwBytesRecorded是已录音数据大小,dwUser是用户数据,dwFlags是控制标志,表明缓冲的使用状态,dwLoops是音频输出时缓冲数据块循环的次数,lpNext和reserved是系统保留数据。在程序实现时,通过设置或修改这个结构的相关参数来实现对音频输入和输出缓冲区的控制。4.2.3音频采集4.2.3.1音频采集原理电脑依靠声卡来处理声音,Windows 盐城工学院本科生毕业设计说明书(2010)把声卡看作是录音和放音设备的组合体,录音时首先要准备一个录音队列,队列中包含录音的基本参数,比如录音数据缓冲区的地址、缓冲区的大小等,为了使录音过程平滑的进行,队列中至少包含两个以上数据缓冲区,当一个缓冲区录完后,Windows系统会给应用程序发送一个录音结束的消息,并自动开始转入下一个缓冲区进行录音,应用程序收到该消息后应该释放含有录音数据的队列,并取出该队列所指向的缓冲区的声音数据,然后将该缓冲区再次加入录音队列,以便继续进行录音。Windows就是这样利用这些数据缓冲区进行周而复始的录入工作。采集声音时,缓冲满了会有一个消息,程序在响应这个消息需要几毫秒~几十毫秒甚至更多的时间,假设为Xms,如果只使用一个缓冲,程序必须在响应完该消息才再次采集声音,那么在这Xms的时间里,没有采集到任何声音;声音的播放也是一样的道理,这样声音就会不连续。因此双缓冲或多缓冲技术是必要的,让输入和输出设备可以循环使用这些缓冲,当程序在响应某块缓冲数据已满或播放完毕消息时,声卡可以继续往下一块缓冲添加数据或播放下一块缓冲的数据,如此循环保障声音的连续性。其基本原理图如图4-3所示。图4-3音频采集原理图4.2.3.2音频采集具体流程在使用音频设备之前,必须打开设备驱动程序。为可靠起见,在播放和记录音频之前,要确定系统音频设备的能力。在用完之后必须关闭音频设备。音频采集流程图如图4-4所示。具体流程如下:1)查询设备数目和能力因为计算机的硬件和性能各不相同,所以在音频采集前,首先要检测有无音频录制设备。通过使用waveOutGetNumDevs和waveOutGetDevCaps来获取波形输出设备的个数和能力。只有在确定设备存在之后,才可以打开设备、使用设备。2)打开波形输出设备在使用录入设备前,必须打开音频录入设备。这里可以调用waveOutOpen为进行重放操作打开特定的波形设备。该函数打开与指定的设备ID相关联的设备,并以给出指定内存句柄的方法返回打开波形设备的句柄。 盐城工学院本科生毕业设计说明书(2010)当有多种波形输出设备时,建议使用WAVE_MAPPER常数作为设备ID,这使waveOutOpen函数会自动挑选最适合播放给定的数据格式的设备。3)准备音频数据块在录制时音频录入会产生大量的数据,所以我们要为这些数据分配缓冲区。在波形重放之前,要准备好音频数据块。将数据块传递给设备驱动程序就实现了重放。使用的函数是waveOutPerpareHeader。4)开始录制经过以上步骤后,一切准备就绪,调用WaveInStart就可以开始录制。当一个缓冲区使用完后音频驱动程序就调用上述在打开录入设备时指定的回调函数。在用完数据块之后,必须用waveOutUnPrepareHeader函数来清除对波形数据块的准备。再将数据读出,然后再把该缓冲区添加给驱动程序。图4-4音频采集流程图4.2.3.3音频采集核心代码//WaveIn.CPPclassCWaveIn{public://线程处理 盐城工学院本科生毕业设计说明书(2010)staticDWORDWINAPIAudioInThreadProc(LPVOIDlpParameter);public://获取数据virtualvoidGetData(char*pBuffer,intiLen);//启动录音BOOLStartRec();//停止录音BOOLStopRec();//获取实例inlineDWORDGetInstance();//获取采样位数inlineWORDGetBit();//获取采样速率inlineDWORDGetSample();//获取频道数inlineWORDGetChannel();//设置采样位数inlinevoidSetBit(WORDwBit);//设置采样速率inlinevoidSetSample(DWORDdwSample);//设置采样速率inlinevoidSetChannel(WORDwChannel);//获取错误信息inlineMMRESULTGetLastMMError();CStringGetLastErrorString();//构造函数CWaveIn();//析构函数virtual~CWaveIn();protected://打开设备BOOLOpenDev();//关闭设备BOOLCloseDev();//停止线程BOOLStopThread();//启动线程 盐城工学院本科生毕业设计说明书(2010)BOOLStartThread();//准备缓存BOOLPerPareBuffer();//释放缓存BOOLFreeBuffer();//开始录音BOOLOpenRecord();//结束录音BOOLCloseRecord();protected://用户实例数据staticDWORDs_dwInstance;protected://频道数WORDm_wChannel;//采样速率DWORDm_dwSample;//采样位数WORDm_wBit;protected://音频输入设备句柄HWAVEINm_hIn;//函数调用返回信息MMRESULTm_mmr;//回调函数指针DWORDm_dwAudioInId;//线程句柄HANDLEm_hAudioIn;//WAVEHDR结构缓存指针WAVEHDR*m_pHdr;//线程启动标志BOOLm_bThreadStart;//设备打开标记BOOLm_bDevOpen;//内存分配标记BOOLm_bAllocBuffer;//录音开始标记 盐城工学院本科生毕业设计说明书(2010)BOOLm_bRecord;};4.2.4音频回放4.2.4.1音频回放原理经过处理过的语音数据通过音频处理传送到这个模块,当接收到语音数据以后它将经过录音的语音数据进行放音,然后把内存释放出来以备保存其他缓冲区里保存的声源信息。由于声音数据具有很强的前后相关性,数据量大、实时性强,又由于声音是连续的,通常把其称之为连续型时基媒体类型,因此需要释放内存。当缓冲区接收到声源以后要是内存不够的话就不能及时把全部语音数据保存并传送,所以经过这个模块以后就得把录音内存释放掉,好让上一个模块及时得到完成。其基本原理图如图4-5所示。图4-5音频回放原理图4.2.4.2音频回放具体流程缓冲区中数据播放完成后系统会向应用程序发送放音完成的消息,应用程序可以重复前面的工作,先释放完成的队列,然后向缓冲区加入新的声音数据,并再次将该缓冲区加入放音队列,让该队列继续工作。音频回放流程图如图4-6所示。具体流程如下:1)管理波形重放在使用低级音频函数播放音频时,应用程序必须不断地向设备驱动程序提供数据块,直到播放结束。WINDOWS提供两种方法管理波形重放:一是使用窗口消息管理,二是使用低级回调函数管理。另外,通过使用waveOutPause、waveOutRestart和waveOutReset来进行暂停、重新启动和停止播放。2)关闭波形设备用完设备之后,必须调用waveOutClose函数关闭波形输出设备,以便其他程序可以使用设备。 盐城工学院本科生毕业设计说明书(2010)图4-6音频回放流程图4.2.4.3音频回放核心代码classCWaveOut{public://线程处理staticDWORDWINAPIAudioOutThreadProc(LPVOIDlpParameter);public://根据文件设置格式BOOLSetFormatByFile(CStringfile);//播音BOOLPlay(char*buf,UINTuSize);//开始播音BOOLStartPlay();//停止播音BOOLStopPlay();//获取缓存数目inlineintGetBufferNum();//减少缓存数目inlinevoidBufferSub();//增加缓存数目inlinevoidBufferAdd(); 盐城工学院本科生毕业设计说明书(2010)//获取实例inlineDWORDGetInstance();//获取采样位数inlineWORDGetBit();//获取采样速率inlineDWORDGetSample();//获取频道数inlineWORDGetChannel();//设置采样位数inlinevoidSetBit(WORDwBit);//设置采样速率inlinevoidSetSample(DWORDdwSample);//设置频道数inlinevoidSetChannel(WORDwChannel);//获取错误信息inlineMMRESULTGetLastMMError();CStringGetLastErrorString();//构造函数CWaveOut();//析构函数virtual~CWaveOut();protected://打开设备BOOLOpenDev();//关闭设备BOOLCloseDev();//停止线程BOOLStopThread();//启动线程BOOLStartThread();protected://用户实例数据staticDWORDs_dwInstance;protected://频道数WORDm_wChannel;//采样速率 盐城工学院本科生毕业设计说明书(2010)DWORDm_dwSample;//采样位数WORDm_wBit;protected://函数调用返回信息MMRESULTm_mmr;//播音设备句柄HWAVEOUTm_hOut;//线程句柄HANDLEm_hAudioOut;//回调函数指针DWORDm_dwAudioOutId;//缓存数目intm_iBufferNum;//重要部分(criticalsection)CCriticalSectionm_csLock;//线程启动标志BOOLm_bThreadStart;//播音设备打开标志BOOLm_bDevOpen;//线程处理friendDWORDWINAPIAudioOutThreadProc(LPVOIDlpParameter);};4.3网络的传送和接收网络的传输和接收部分是本设计最重要的模块,对于主机1传送到主机2的音频数据信息这条数据通路,主机1为客户端,主机2为服务端;而对于主机2传送到主机1的音频数据信息这条数据通路,主机2为客户端,主机1为服务器端。双方通话时,主机1和主机2同时既处于服务器状态又处于客户端状态,即通话状态。主机1呼叫主机2时,主机1进入客户端状态,主机2收到呼叫信息,进入服务器端状态,若主机2同意通话,则两机都进入通话状态,双方可以通过上述两条数据通路进行网络通话,若有一机挂断,两机都返回侦听状态,等待呼叫或被呼叫。4.3.1TCP/IP体系结构与特点4.3.1.1TCP/IP体系结构TCP/IP协议实际上就是在物理网上的一组完整的网络协议。其中TCP是提供传输层服务,而IP则是提供网络层服务。TCP/IP包括以下协议:结构图如图4-7 盐城工学院本科生毕业设计说明书(2010)图4-7TCP/IP结构图IP:网间协议(InternetProtocol)负责主机间数据的路由和网络上数据的存储。同时为ICMP、TCP、UDP提供分组发送服务。用户进程通常不需要涉及这一层。ARP:地址解析协议(AddressResolutionProtocol)此协议将网络地址映射到硬件地址。RARP:反向地址解析协议(ReverseAddressResolutionProtocol)此协议将硬件地址映射到网络地址。ICMP:网间报文控制协议(InternetControlMessageProtocol),此协议处理信关和主机的差错和传送控制。TCP:传送控制协议(TransmissionControlProtocol),这是一种提供给用户进程的可靠的全双工字节流面向连接的协议,它要为用户进程提供虚电路服务,并为数据可靠传输建立检查。(大多数网络用户程序使用TCP)UDP:用户数据报协议(UserDatagramProtocol),这是提供给用户进程的无连接协议,用于传送数据而不执行正确性检查。FTP:文件传输协议(FileTransferProtocol),允许用户以文件操作的方式(文件的增、删、改、查、传送等)与另一主机相互通信。SMTP:简单邮件传送协议(SimpleMailTransferProtocol),SMTP协议为系统之间传送电子邮件。TELNET:终端协议(TelnetTerminalProcotol),允许用户以虚终端方式访问远程主机。HTTP:超文本传输协议(HypertextTransferProcotol),TFTP:简单文件传输协议(TrivialFileTransferProtocol)。4.3.1.2TCP/IP特点 盐城工学院本科生毕业设计说明书(2010)TCP/IP协议的核心部分是传输层协议(TCP、UDP),网络层协议(IP)和物理接口层,这三层通常是在操作系统内核中实现。因此用户一般不涉及。编程时,编程界面有两种形式:一、是由内核心直接提供的系统调用;二、使用以库函数方式提供的各种函数。前者为核内实现,后者为核外实现。用户服务要通过核外的应用程序才能实现,所以要使用套接字(socket)来实现。Socket与TCP/IPc协议的关系如图4-8图4-8Socket与TCP/IPc协议4.3.1.3WindowsSocketVC++对网络编程的支持有socket支持,Winlnet支持,MAPI和ISAPI支持等。其中WindowsSocketsAPI是TCP/IP网络环境里,也是Internet上进行开发最为通用的API。最早美国加州大学Berkeley分校在UNIX下为TCP/IP协议开发了一个API,这个API就是著名的BerkeleySocket接口(套接字)。在桌面操作系统进入Windows时代后,仍然继承了Socket方法。从表4-1中可以看出,主要的WinSockAPI函数。[表4-1]WinSockAPI函数函数功能WSAStartup()连结应用程序与WindowsSocketsDLL的第一个函数WSACleanup()结束WindowsSocketsDLL的使用socket()建立Socketclosesocket()关闭某一Socketbind()将一本地地址与一个SOCKET描述字连接在一起listen()设定Socket为监听状态,准备被连接accept()接受某一Socket的连接要求,以完成面向连接的客户端Socket的连接请求。 盐城工学院本科生毕业设计说明书(2010)connect()要求连接某一Socket到指定的网络上服务端recv()从面向连接的Socket接收信息send()使用面向连接的Socket发送信息WSAAsyncSelect()要求某一Socket有事件(event)发生时通知使用者套接字(Socket)是一种双向的通信接口,可以通过这个端口与任何一个具有Socket端口的计算机通信,套接字是网络通信的基础。Socket在Windows以句柄的形式被创建。使用Socket进行网络通信必须包含下面的几种信息:双方认可的协议,本地主机的IP地址,本地进程的协议端口,对方主机的IP地址,对方进程的协议端口。Socket可分为:1)数据报套接字(DatagramSockets)——对于在TCP/IP上实现的WinSock,数据报套接字使用用户数据报协议(UDP)。数据报套接字提供了一种不可靠的、非连接的数据包通信方式。2)流式套接字(StreamSockets)——流式套接字使用传输控制协议(TCP)。流式套接字可以将数据按顺序无重复地发送到目的地,它提供的是一种可靠的、面向连接的数据传输方式。不管是对单个的数据报,还是对数据包,流式套接字都提供了一种流式数据传输。在TCP/IP网络中两个进程间的相互作用的主机模式是客户机/服务器模式(Client/Servermodel)。该模式的建立基于以下两点:1、非对等作用;2、通信完全是异步的。客户机/服务器模式在操作过程中采取的是主动请示方式:首先服务器方要先启动,并根据请示提供相应服务:(过程如下)1)打开一通信通道并告知本地主机,它愿意在某一个公认地址上接收客户请求。2)等待客户请求到达该端口。3)接收到重复服务请求,处理该请求并发送应答信号。4)返回第二步,等待另一客户请求5)关闭服务器。而客户方过程为:1)打开一通信通道,并连接到服务器所在主机的特定端口。2)向服务器发送服务请求报文,等待并接收应答;继续提出请求……3)请求结束后关闭通信通道并终止。其中基本的套接字有以下几种:1)创建套接字——socket()功能:使用前创建一个新的套接字格式:SOCKETPASCALFARsocket(intaf,inttype,intprocotol);参数:af:通信发生的区域;type:要建立的套接字类型。2)指定本地地址——bind() 盐城工学院本科生毕业设计说明书(2010)功能:将套接字地址与所创建的套接字号联系起来。格式:intPASCALFARbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);参数:s:是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。其它:没有错误,bind()返回0,否则SOCKET_ERROR地址结构说明:structsockaddr_in{shortsin_family;//AF_INETu_shortsin_port;//16位端口号,网络字节顺序structin_addrsin_addr;//32位IP地址,网络字节顺序charsin_zero[8];//保留}3)建立套接字连接——connect()和accept()功能:共同完成连接工作格式:intPASCALFARconnect(SOCKETs,conststructsockaddrFAR*name,intnamelen);SOCKETPASCALFARaccept(SOCKETs,structsockaddrFAR*name,intFAR*addrlen)。4)监听连接——listen()功能:用于面向连接服务器,表明它愿意接收连接。格式:intPASCALFARlisten(SOCKETs,intbacklog)。5)数据传输——send()与recv()功能:数据的发送与接收。格式:intPASCALFARsend(SOCKETs,constcharFAR*buf,intlen,intflags);intPASCALFARrecv(SOCKETs,constcharFAR*buf,intlen,intflags);参数:buf:指向存有传输数据的缓冲区的指针。6)关闭套接字——closesocket()功能:关闭套接字s格式:BOOLPASCALFARclosesocket(SOCKETs)。4.3.1.4MFCCAsyncSocket编程为简化套接字编程,MFC定义了两个套接字类:CAsyncSocket、CSocket。CAsyncSocket类在低层次上对WindowsSocketsAPI进行了封装,其成员函数和WindowsSocketsAPI函数直接相对应。一个CAsyncSocket对象就代表了一个套接字。为了给程序员提供更方便的接口以自动处理网络通信中的一些任务,MFC在CAsycnSocket类的基础上派生了CSocket类,它提供了比CAsyncSocket更高层的WinSock 盐城工学院本科生毕业设计说明书(2010)API接口。CSocket的网络操作是同步方式,即阻塞式的。CSocket建立的Socket不是阻塞的,而是对非阻塞的Socket进行处理后实现的阻塞操作。在阻塞任务执行过程中,Windows线程的消息循环仍然有效,也就是说CSocket类的阻塞操作不影响Windows的消息循环。MFC中Socket相关类的继承关系如图4-9所示。CObjectCAsyncSockCSocket图4-9MFC中Socket相关类的继承关系   建立Socket的WSAStartup过程和bind过程被简化成为Create过程,IP地址类型转换、主机名和IP地址转换的过程中许多复杂的变量类型都被简化成字符串和整数操作,特别是CAsyncSocket类的异步特点,完全可以替代繁琐的线程操作。4.3.1.4.1CAsyncSocketCAsyncSocket类直接派生于CObject类,称为异步套接字对象。由于CAsyncSocket类的构造函数不带参数,需要调用起成员函数Create来创建底层的套接字句柄,决定套接字对象的具体特性,并绑定它的地址。其格式为:BOOLCreate(UINTnSocketPort=0,   IntnSocketType=SOCK_STREAM,   LongIevent=FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE,   LPCTSTRlpszSocketAddress=NULL)CAsyncSocket类的6种网络事件,如表4-2所示。[表4-2]网络事件及重载函数事件涵   义对应处理函数FD_ACCEPT作为服务端等待连接成功OnAccept(intnErrorCode)FD_CONNECT通知请求连接的套接字,连接要求已被处理OnConnect(intnErrorCode)FD_CLOSE通知与其连接的套接字已关闭OnClose(intnErrorCode);FD_READ通知有数据到达OnReceive(intnErrorCode)FD_WRITE通知可以发送数据OnSend(intnErrorCode) 盐城工学院本科生毕业设计说明书(2010)FD_OOB通知将有外带数据到达OnOutOfBandData(intErrorCode);当某个网络事件发生时,MFC框架把消息发送给相应的套接字对象,相当于给了该对象一个通知,告诉它某个重要的事件已经发生,接着自动调用该对象的事件处理函数。  正常情况下,服务器端必须首先创建一个CAsyncSocket套接字对象,并调用它的Create成员函数创建底层套接字句柄。这个套接字对象专门用来侦听来自客户机的连接请求,所以称它为侦听套接字对象。再调用侦听套接字对象的Listen函数,使侦听套接字对象开始侦听来自客户端的连接请求。当Listen函数确认并接纳了一个客户端连接请求后,触发FD_ACCEPT事件,侦听套接字收到通知,MFC框架自动调用侦听套接字的OnAccept事件处理函数。一般需要重载OnAccept函数,再在其中调用侦听套接字对象的Accept函数。创建一个新的空套接字对象,专门用来与客户端连接并进行数据的传输,一般称为连接套接字,并作为参数传递给下一步的Accept成员函数。客户端请求连接到服务器端,在服务器端套接字对象已经进入侦听状态之后,客户应用程序可以调用CAsyncSocket类的Connect成员函数,向服务器发出一个连接请求。调用结束返回时发生FD_CONNECT事件,MFC框架会自动调用客户端套接字的OnConnect事件处理函数。CSocket类是CAsyncSocket的派生类。创建CSocket对象时,首先要调用CSocket类的构造函数创建一个空的CSocket对象,再调用其Create成员函数,创建对象的底层套接字。其格式为:BOOLCreate(     UINTnSocketPort=端口号,     IntnSocketPort=SOCK_STREAM|SOCK_DGRAM,     LPCTSTRlpszSocketAddress=套接字所用的网络地址);4.3.1.4.2CSocketCSocket类使用基类CAsyncSocket的同名成员函数Connect、Listen、Accept来建立服务器和客户机套接字之间的连接,使用方法基本相同。在创建CSocket类对象后,对于流式套接字,首先在服务器和客户机之间建立连接,然后使用Send函数、Receive函数来发送和接收数据。  需要注意的是,CSocket对象从不调用OnConnect和OnSend事件处理函数。CSocket类继承了CAsyncSocket类的许多成员函数,用法基本一致。CSocket类的高级性主要表现在3个方面。1)CSocket结合CArchive类来使用套接字。2)CSocket管理了通信的许多方面,比如字节顺序问题和字符串转换问题。3)CSocket类为Windows消息的后台处理提供了阻塞的工作模式。因此,一般将CSocket与CArchive、CSocketFile 盐城工学院本科生毕业设计说明书(2010)类相结合,来发送和接收数据,这将使编程更为简单。4.3.2网络的传送和接收流程图进行聊天需要建立服务器端Socket和客户端Socket,并且要连接上,这样才能进行信息的传送,通信模块主要定义的就是这部分Socket建立和连接。面向连接的套接字的系统调用时序,如图4-10所示。图4-10套接字的系统调用流程4.3.3网络的传送和接收核心代码//初始化网络BOOLCUdpSocket::Ini(){ 盐城工学院本科生毕业设计说明书(2010)//已经初始化if(m_bIni)//返回returnFALSE;//创建数据报类型的套接字if(!this->Create(NULL,SOCK_DGRAM)){returnFALSE;};//设置初始化完成标记m_bIni=TRUE;returnTRUE;}intCUdpSocket::Send(constvoid*lpBuf,intnBufLen,intnFlags){//音频数据序号m_pFrame->iIndex++;//复制音频格式信息memcpy(m_cBuffer+sizeof(structFrame),lpBuf,nBufLen);//调用基类SendTo()函数发送音频格式信息returnCAsyncSocket::SendTo(m_pFrame,nBufLen+sizeof(structFrame),TALK_REC_PORT,m_ip,nFlags);}//关闭套接字BOOLCUdpSocket::CloseSocket(){//已经关闭if(!m_bIni)returnFALSE;//关闭套接字CAsyncSocket::Close();//设置初始化未完成标记m_bIni=FALSE;//返回returnTRUE;}//设置Ip地址 盐城工学院本科生毕业设计说明书(2010)voidCUdpSocket::SetIp(CStringip){m_ip=ip;}//发送端CSendClientmemberfunctions//重载OnReceive()函数voidCSendClient::OnReceive(intnErrorCode){structTalkFrame*frame;frame=(structTalkFrame*)m_pBuffer;intiLen=sizeof(structTalkFrame);//接收缓存中所有的TalkFrame结构while(iLen>0){//接收数据inti=Receive(m_pBuffer+sizeof(structTalkFrame)-iLen,iLen);//出错if(i==SOCKET_ERROR)//返回return;iLen-=i;}//如果不是正常数据就返回if(strcmp(frame->cFlag,"TalkFrame")!=0){return;}iLen=frame->iLen;//接收缓存中所有的音频数据while(iLen>0){//接收数据inti=Receive(m_pBuffer+sizeof(structTalkFrame)+(frame->iLen-iLen),iLen);//出错if(i==SOCKET_ERROR) 盐城工学院本科生毕业设计说明书(2010)//返回return;iLen-=i;}//对方地址CStringadd;//端口UINTport;switch(frame->iCom){//正常通信帧caseTC_AGREE_TALK://获取连接对方地址和端口GetPeerName(add,port);//提示开始工作m_pInterFace->TalkStart(add);//允许发送数据m_pIn->EnableSend(TRUE);//设置允许工作标志m_pInterFace->m_bWork=TRUE;break;default:break;}//接收端//重载OnAccept()函数voidCListenSocket::OnAccept(intnErrorCode){//客户端地址SOCKADDRadd;//客户端地址长度intiLen;iLen=sizeof(SOCKADDR);//临时socketCSocketsoTemp;//已经连接 盐城工学院本科生毕业设计说明书(2010)if(m_sopClient->m_bConnect){//用临时socket接收连接请求soTemp.Accept(*this);//关闭soTemp.Close();//返回return;}//接受连接请求if(!Accept(*m_sopClient,&add,&iLen)){//返回return;}//设置连接标记m_sopClient->m_bConnect=TRUE;//调用基类OnAccept()函数CSocket::OnAccept(nErrorCode);}//关闭套接字voidCListenSocket::CloseClient(){//关闭套接字m_sopClient->Close();//设置未连接标记m_sopClient->m_bConnect=FALSE;}//CSendClientmemberfunctions//重载OnReceive()函数voidCSendClient::OnReceive(intnErrorCode){structTalkFrame*frame;frame=(structTalkFrame*)m_pBuffer;intiLen=sizeof(structTalkFrame);//接收缓存中所有的TalkFrame结构 盐城工学院本科生毕业设计说明书(2010)while(iLen>0){//接收数据inti=Receive(m_pBuffer+sizeof(structTalkFrame)-iLen,iLen);//出错if(i==SOCKET_ERROR)//返回return;iLen-=i;}//如果不是正常数据就返回if(strcmp(frame->cFlag,"TalkFrame")!=0){return;}iLen=frame->iLen;//接收缓存中所有的音频数据while(iLen>0){//接收数据inti=Receive(m_pBuffer+sizeof(structTalkFrame)+(frame->iLen-iLen),iLen);//出错if(i==SOCKET_ERROR)//返回return;iLen-=i;}//对方地址CStringadd;//端口UINTport;switch(frame->iCom){//正常通信帧caseTC_AGREE_TALK: 盐城工学院本科生毕业设计说明书(2010)//获取连接对方地址和端口GetPeerName(add,port);//提示开始工作m_pInterFace->TalkStart(add);//允许发送数据m_pIn->EnableSend(TRUE);//设置允许工作标志m_pInterFace->m_bWork=TRUE;break;default:break;}4.4界面设计4.4.1MFC及其特点MFC,即微软基础类(MicrosoftFoundationClasses),用于在C++环境下编写应用程序的一个框架和引擎,VC++是WinDOS下开发人员使用的专业C++SDK(SDK,StandardSoftWareDevelopKit,专业软件开发平台),MFC就是挂在它之上的一个辅助软件开发包,MFC作为与VC++血肉相连的部分,同BC++集成的VCL一样是一个非外挂式的软件包,类库,MFC类是微软为VC++专配的。  MFC是WinAPI与C++的结合,API,即微软提供的WinDOS下应用程序的编程语言接口,是一种软件编程的规范,但不是一种程序开发语言本身,可以允许用户使用各种各样的第三方(如我是一方,微软是一方,Borland就是第三方)的编程语言来进行对WinDOS下应用程序的开发,使这些被开发出来的应用程序能在WinDOS下运行,比如VB、VC++、Java、Dehpi等编程语言函数本质上全部源于API。因此用它们开发出来的应用程序都能工作在WinOS的消息机制和绘图里,遵守WinDOS作为一个操作系统的内部实现,这其实也是一种必要,微软如果不提供API,这个世上对Win编程的工作就不会存在,微软的产品就会迅速从时尚变成垃圾,上面说到MFC是微软对API函数的专用C++封装,这种结合一方面让用户使用微软的专业C++SDK来进行Win下应用程序的开发变得容易,因为MFC是对API的封装。微软做了大量的工作,隐藏了好多程序开发人员在Win下用C++&MFC编制软件时的大量内节。进行界面开发,关联一个窗口的动作,但它提供的类中有好多类不与一个窗口关联,即类的作用不是一个界面类,不实现对一个窗口对象的控制(如创建、销毁), 盐城工学院本科生毕业设计说明书(2010)而是一些在WinDOS(用MFC编写的程序绝大部分都在WinDOS中运行)中实现内部处理的类,如数据库的管理类等,学习中最应花费时间的是消息和设备环境,对C++和MFC的学习中最难的部分是指针,C++面向对像程序设计的其它部分,如数据类型,流程控制都不难,建议学习数据结构C++版。  MFC是微软封装了的API。windows作为一个提供功能强大的应用程序接口编程的操作系统,的确方便了许多程序员,传统的win32开发(直接使用windows的接口函数API)对于程序员来说非常的困难,因为,API函数实在太多了,而且名称很乱,从零构架一个窗口动辄就是上百行的代码。MFC是面向对象程序设计与Applicationframework的完美结合,他将传统的API进行了分类封装,并且创建了程序的一般框架。MFC是对WindowsAPI的封装,大大简化了我们的工作;学VC主要就是要学MFC,大约有100多个类,但常用的也就二三十个。面对底层程序,它能很轻松的与WindowsAPI或驱动程序结合,就是在自己的代码中直接使用API函数,而API和驱动程序的资料都是以C语言为基础的,这使得VC程序员能够更轻松的使用WindowsAPI。这样造成了一个很有意思的现象,即入门时VC程序员要付出更多的努力来学习,但是一旦掌握后,开发其他领域的程序或使用第三方软件时,如工业控制类的程序等。4.4.2界面实现过程单击新建,在弹出的对话框中单击工程,点击MFCAppWizard[exe],在右边工程名称[N]:下面的文本框中写入工程名称“test”,单击确定,进入MFC应用程序向导—步骤1,在“您要创建的应用程序类型是:”一栏里选择“基本对话框[D]”,单击下一步,在WindowsSocket[W]选项前打钩,单击下一步,选择默认设置,继续单击下一步,保持默认设置,单击完成。在弹出的新建工程信息对话框中单击确定,在出现的原始test对话框中添加connect和disconnect按钮,添加一个文本框,初始界面如图4-11所示。 盐城工学院本科生毕业设计说明书(2010)图4-11初始界面图4.4.3界面实现代码//testDlg.cpp:implementationfile#include"stdafx.h"#include"test.h"#include"testDlg.h"#ifdef_DEBUG#definenewDEBUG_NEW#undefTHIS_FILEstaticcharTHIS_FILE[]=__FILE__;#endif///////////////////////////////////////////////////////////////////////////////CAboutDlgdialogusedforAppAboutclassCAboutDlg:publicCDialog{public:CAboutDlg();//DialogData//{{AFX_DATA(CAboutDlg)enum{IDD=IDD_ABOUTBOX}; 盐城工学院本科生毕业设计说明书(2010)//}}AFX_DATA//ClassWizardgeneratedvirtualfunctionoverrides//{{AFX_VIRTUAL(CAboutDlg)protected:virtualvoidDoDataExchange(CDataExchange*pDX);//DDX/DDVsupport//}}AFX_VIRTUAL//Implementationprotected://{{AFX_MSG(CAboutDlg)//}}AFX_MSGDECLARE_MESSAGE_MAP()};CAboutDlg::CAboutDlg():CDialog(CAboutDlg::IDD){//{{AFX_DATA_INIT(CAboutDlg)//}}AFX_DATA_INIT}voidCAboutDlg::DoDataExchange(CDataExchange*pDX){CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CAboutDlg)//}}AFX_DATA_MAP}BEGIN_MESSAGE_MAP(CAboutDlg,CDialog)//{{AFX_MSG_MAP(CAboutDlg)//Nomessagehandlers//}}AFX_MSG_MAPEND_MESSAGE_MAP()///////////////////////////////////////////////////////////////////////////////CTestDlgdialogCTestDlg::CTestDlg(CWnd*pParent/*=NULL*/):CDialog(CTestDlg::IDD,pParent){//{{AFX_DATA_INIT(CTestDlg)m_ip=_T(""); 盐城工学院本科生毕业设计说明书(2010)//}}AFX_DATA_INIT//NotethatLoadIcondoesnotrequireasubsequentDestroyIconinWin32m_hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);}voidCTestDlg::DoDataExchange(CDataExchange*pDX){CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CTestDlg)DDX_Text(pDX,IDC_EDIT1,m_ip);//}}AFX_DATA_MAP}BEGIN_MESSAGE_MAP(CTestDlg,CDialog)//{{AFX_MSG_MAP(CTestDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_BUTTON2,OnButton2)ON_BN_CLICKED(IDC_BUTTON3,OnButton3)//}}AFX_MSG_MAPEND_MESSAGE_MAP()///////////////////////////////////////////////////////////////////////////////CTestDlgmessagehandlersBOOLCTestDlg::OnInitDialog(){CDialog::OnInitDialog();//Add"About..."menuitemtosystemmenu.//IDM_ABOUTBOXmustbeinthesystemcommandrange.ASSERT((IDM_ABOUTBOX&0xFFF0)==IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX<0xF000);CMenu*pSysMenu=GetSystemMenu(FALSE);if(pSysMenu!=NULL){CStringstrAboutMenu;strAboutMenu.LoadString(IDS_ABOUTBOX); 盐城工学院本科生毕业设计说明书(2010)if(!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING,IDM_ABOUTBOX,strAboutMenu);}}//Settheiconforthisdialog.Theframeworkdoesthisautomatically//whentheapplication'smainwindowisnotadialogSetIcon(m_hIcon,TRUE);//SetbigiconSetIcon(m_hIcon,FALSE);//Setsmallicon//TODO:Addextrainitializationherem_ip="192.168.10.116";UpdateData(FALSE);m_talk.Ini();returnTRUE;//returnTRUEunlessyousetthefocustoacontrol}voidCTestDlg::OnSysCommand(UINTnID,LPARAMlParam){if((nID&0xFFF0)==IDM_ABOUTBOX){CAboutDlgdlgAbout;dlgAbout.DoModal();}else{CDialog::OnSysCommand(nID,lParam);}}//Ifyouaddaminimizebuttontoyourdialog,youwillneedthecodebelow//todrawtheicon.ForMFCapplicationsusingthedocument/viewmodel,//thisisautomaticallydoneforyoubytheframework.voidCTestDlg::OnPaint() 盐城工学院本科生毕业设计说明书(2010){if(IsIconic()){CPaintDCdc(this);//devicecontextforpaintingSendMessage(WM_ICONERASEBKGND,(WPARAM)dc.GetSafeHdc(),0);//CentericoninclientrectangleintcxIcon=GetSystemMetrics(SM_CXICON);intcyIcon=GetSystemMetrics(SM_CYICON);CRectrect;GetClientRect(&rect);intx=(rect.Width()-cxIcon+1)/2;inty=(rect.Height()-cyIcon+1)/2;//Drawtheicondc.DrawIcon(x,y,m_hIcon);}else{CDialog::OnPaint();}}//Thesystemcallsthistoobtainthecursortodisplaywhiletheuserdrags//theminimizedwindow.HCURSORCTestDlg::OnQueryDragIcon(){return(HCURSOR)m_hIcon;}voidCTestDlg::OnButton2(){//TODO:Addyourcontrolnotificationhandlercodeherethis->UpdateData();m_talk.Start(m_ip);voidCTestDlg::OnButton3(){//TODO:Addyourcontrolnotificationhandlercodeherem_talk.End();} 盐城工学院本科生毕业设计说明书(2010)5测试测试在开发软件过程中是一个不可缺少的部分。因为在开发软件系统的漫长过程中,面对着极其错综复杂的问题,人的主观认识不可能完全符合客观现实,与工程密切相关的各类人员之间的通信和配合也不可能完美无缺,因此,在软件生命周期的各个阶段都不可避免地产生差错。我们力求在每个阶段结束之前通过严格的技术审查,尽可能早地发现并纠正错误;但是,经验表明审查并不能发现所有的差错,此外在编码过程中还不可避免地会引入一些新的错误。如果在软件生产性运行之前,没有发现并纠正软件中的大部分差错,则这些错误迟早在生产过程中暴露出来,那时不仅仅改正这些错误的代价更高,而且往往会造成很恶劣的后果。测试的目的就是在软件投入生产运行之前,尽可能多地发现并纠正软件中的错误。基于上述的原因,我在编码过程中进行了模块测试,编码结束进行了系统测试和验收测试。下面是测试方案,测试过程和测试结果。5.1模块测试1)测试方案采用了白盒测试,即按照程序内部的逻辑结构,检验程序中的每条通路是否都按预定要求正常工作。2)测试过程检验模块之间的接口。A每个被调用模块是否正确接受参数。B每个调用模块是否能调用每个自己想要调用的模块。3)测试结果测试结果表明,每个模块之间的接口都吻合,即被调用模块都能正确接受参数,调用模块能调用自己想要调用的每个模块。5.2功能测试1)测试方法采用了黑盒测试,即检查程序功能是否能要求进行,是否适当地接受数据产生正确的输出信息,并且保持外部信息的完整性。2)测试过程主要检验是否能正确实现每个功能。A每个功能是否按要求正常进行。B用户界面是否友好测试结果表明,每个功能都按要求进行。3)测试结果 盐城工学院本科生毕业设计说明书(2010)结果表明,经过测试每个功能都能按预先要求正常进行且速度达到了预期的标准。用户界面友好。当用户请求语音聊天的时,系统能快速准确地联系到对方并建立语音连接。当然,整个系统在测试过程中仍发现了不少的错误,但在指导老师的指导下改正并完善了所发现的错误。其测试过程的界面分别为:①在要实现通信的两台机器分别将test应用程序打开,初始界面为图5-1所示。图5-1初始状态②将另一方IP地址输入文本框里,点击连接,服务端显示如图5-2所示。 盐城工学院本科生毕业设计说明书(2010)图5-2①点击确定客户端显示如图5-3所示。图5-3②点击是,服务端显示图5-4,客户端显示如图5-5所示。 盐城工学院本科生毕业设计说明书(2010)图5-4图5-5①两端都点击确定,就可以进行语音通信了,若有一方点击断开连接,显示如图5-6所示,点击确定应用程序自动退出。 盐城工学院本科生毕业设计说明书(2010)图5-6 盐城工学院本科生毕业设计说明书(2010)结论为期三个月的毕业设计已经结束了,从中我学到了很多东西,包括治学的也包括做人的。本次毕业论文的研究的课题是语音通信系统,主要实现在局域网里进行语音聊天,来实现彼此沟通、交流信息。本设计主要用了VisualC++编程环境,实现了在LAN中在线用户实现语音交互。在设计里涉及到了网络通信基本原理和Socket编程及语音处理API技术。语音通信系统包括四个模块,分别是通信模块、声源采集模块、音频编解码模块、音频再生模块。回顾起此次毕业设计,至今我仍感慨颇多,从选题到定稿,从理论到实践,可以说是苦多于甜,但是可以学到很多很多的的东西,同时不仅可以巩固了以前所学过的知识,而且学到了很多在书本上所没有学到过的知识。通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,才能真正为社会服务,从而提高自己的实际动手能力和独立思考的能力。在设计的过程中遇到问题,可以说得是困难重重,但是作为毕业设计,难免会遇到过各种各样的问题,同时在设计的过程中发现了自己的不足之处,对以前所学过的知识理解得不够深刻,通过这次课程设使我对c语言有了更深入的认识和了解,还有学会如何查阅文献。很重要的一点是对进行软件设计的整体设计流程及思维方法有了深刻的认识。在期间,发现的许多问题都源于没有认真地按步骤进行设计,不重视需求分析,总体设计部分,对各个方面将会产生的问题考虑不周全。在设计过程中,对于我来说,由于很多都是新知识,这就迫使我到处查阅相关资料,学习新知识,不仅体会到VisualC++编程环境的强大功能,也对新接触到的Socket编程和语言处理API有了大概的了解。由于我本人的技术限制,可能有很多不足之处,望各位老师多加批评和指正,谢谢! 盐城工学院本科生毕业设计说明书(2010)参考文献[1]张海棠.VisualC++6.0编程指南[J].航空工业出版社,1999.[2]张岳新.VisualC++程序设计[M].苏州大学出版社,2005.[3]胡海璐.VisualC++高级编程技术与范例[M].电子工业出版社,2002.[4]易军.VisualC++网络编程[J].中国铁道出版社,2003[5]丁展.VisualC++网络通信实用案例精选[M].邮电出版社,2004.[6]张海棠.VisualC++数据库通用模块开发与系统移植[J].清华大学出版社,2006.[7]赵立新.网络多媒体通信——语音编码技术研究[D].西安电子科技大学,2002.[8]涂友华.基于SIP会议系统的研究[D].北京邮电大学,2004. 盐城工学院本科生毕业设计说明书(2010)致谢首先,也是最主要感谢的是我的指导老师,朱明老师。在整个过程中他给了我很大的帮助,在论文题目制定时,他首先肯定了我的题目大方向,同时又帮我具体分析了具体目标,让我在写作时有了具体方向。在论文提纲制定时,我的思路不是很清晰,经过老师的帮忙,让我具体写作时思路顿时清晰。在完成初稿后,老师认真查看了我的文章,指出了我存在的很多问题。在此十分感谢朱老师的细心指导,才能让我顺利完成毕业论文。同时,对给过我帮助的所有同学和其他指导老师再次表示忠心的感谢!感谢他们这几个月来的悉心教导和帮助。 盐城工学院本科生毕业设计说明书(2010)附录源程序核心代码//音频编/解码#include"stdafx.h"#include"AudioCode.h"extern"C"voidva_g729a_init_encoder();extern"C"voidva_g729a_encoder(short*speech,unsignedchar*bitstream);extern"C"voidva_g729a_init_decoder();extern"C"voidva_g729a_decoder(unsignedchar*bitstream,short*synth_short,intbfi);#defineL_FRAME_COMPRESSED10#defineL_FRAME80//包含动态链接库#pragmacomment(lib,"G729a")//构造函数CAudioCode::CAudioCode(){va_g729a_init_encoder();//初始化编码器va_g729a_init_decoder();//初始化解码器}//析构函数CAudioCode::~CAudioCode(){}//编码音频数据BOOLCAudioCode::EncodeAudioData(char*pin,intlen,char*pout,int*lenr){//编码成功与否标记BOOLbRet=FALSE;//无效输入或输出if(!pin||len!=SIZE_AUDIO_FRAME||!pout)gotoRET;//分块进行编码va_g729a_encoder((short*)pin,(BYTE*)pout);va_g729a_encoder((short*)(pin+160),(BYTE*)pout+10);va_g729a_encoder((short*)(pin+320),(BYTE*)pout+20);va_g729a_encoder((short*)(pin+480),(BYTE*)pout+30);va_g729a_encoder((short*)(pin+640),(BYTE*)pout+40);va_g729a_encoder((short*)(pin+800),(BYTE*)pout+50);//编码长度if(lenr)*lenr=SIZE_AUDIO_PACKED;//编码成功标记bRet=TRUE; 盐城工学院本科生毕业设计说明书(2010)RET://返回returnbRet;}//音频解码BOOLCAudioCode::DecodeAudioData(char*pin,intlen,char*pout,int*lenr){//解码成功与否标记BOOLbRet=FALSE;//无效输入或输出if(!pin||len!=SIZE_AUDIO_PACKED||!pout)gotoRET;//分块解码va_g729a_decoder((BYTE*)pin,(short*)(pout),0);va_g729a_decoder((BYTE*)pin+10,(short*)(pout+160),0);va_g729a_decoder((BYTE*)pin+20,(short*)(pout+320),0);va_g729a_decoder((BYTE*)pin+30,(short*)(pout+480),0);va_g729a_decoder((BYTE*)pin+40,(short*)(pout+640),0);va_g729a_decoder((BYTE*)pin+50,(short*)(pout+800),0);//解码长度if(lenr)*lenr=SIZE_AUDIO_FRAME;//设置解码成功标记bRet=TRUE;RET://返回returnbRet;}//音频录取#include"stdafx.h"#include"WaveIn.h"#include"math.h"#pragmacomment(lib,"Winmm")DWORDCWaveIn::s_dwInstance=0;//线程处理DWORDWINAPICWaveIn::AudioInThreadProc(LPVOIDlpParameter){CWaveIn*pWaveIn;pWaveIn=(CWaveIn*)lpParameter;//缓存charbuffer[1024];//消息处理主循环MSGmsg;while(GetMessage(&msg,0,0,0)){switch(msg.message) 盐城工学院本科生毕业设计说明书(2010){//打开录音设备caseMM_WIM_OPEN:break;//关闭录音设备caseMM_WIM_CLOSE:break;//录音设备返回数据caseMM_WIM_DATA://录音格式WAVEHDR*pWH=(WAVEHDR*)msg.lParam;//释放缓存waveInUnprepareHeader((HWAVEIN)msg.wParam,pWH,sizeof(WAVEHDR));//非法数据if(pWH->dwBytesRecorded!=SIZE_AUDIO_FRAME)break;//复制录音数据memcpy(buffer,pWH->lpData,pWH->dwBytesRecorded);//设置时戳pWaveIn->GetData(buffer,pWH->dwBytesRecorded);//为音频设备增加一个缓存,准备继续录音waveInPrepareHeader((HWAVEIN)msg.wParam,pWH,sizeof(WAVEHDR));waveInAddBuffer((HWAVEIN)msg.wParam,pWH,sizeof(WAVEHDR));break;}}//返回returnmsg.wParam;}//构造函数CWaveIn::CWaveIn():m_wChannel(1),m_dwSample(WISA_POOR),m_wBit(16){m_hIn=0;m_bThreadStart=FALSE;m_bDevOpen=FALSE;m_bAllocBuffer=FALSE;m_bRecord=FALSE;m_pHdr=NULL;m_dwAudioInId=0;s_dwInstance++;}//构析函数CWaveIn::~CWaveIn(){} 盐城工学院本科生毕业设计说明书(2010)//关闭录音设备BOOLCWaveIn::CloseDev(){//设备已经关闭if(!m_bDevOpen){//返回returnFALSE;}//设备句柄无效if(!m_hIn){//返回returnFALSE;}//关闭录音设备m_mmr=waveInClose(m_hIn);//出错if(m_mmr){//设置设备句柄无效m_hIn=0;//设置设备启动标记m_bDevOpen=FALSE;//返回returnFALSE;}//设置设备句柄无效m_hIn=0;//设置设备启动标记m_bDevOpen=FALSE;//返回returnTRUE;}//打开录音设备BOOLCWaveIn::OpenDev(){//已经打开录音设备if(m_bDevOpen){//返回returnFALSE;}//设置录音设备输出格式WAVEFORMATEXwfx;wfx.wFormatTag=WAVE_FORMAT_PCM;wfx.nChannels=m_wChannel;wfx.nSamplesPerSec=m_dwSample; 盐城工学院本科生毕业设计说明书(2010)wfx.nAvgBytesPerSec=m_wChannel*m_dwSample*m_wBit/8;wfx.nBlockAlign=m_wBit*m_wChannel/8;wfx.wBitsPerSample=m_wBit;wfx.cbSize=0;//检查录音设备是否支持设定的输出格式m_mmr=waveInOpen(0,WAVE_MAPPER,&wfx,0,0,WAVE_FORMAT_QUERY);//出错if(m_mmr){//返回returnFALSE;}//打开录音设备m_mmr=waveInOpen(&m_hIn,WAVE_MAPPER,&wfx,m_dwAudioInId,s_dwInstance,CALLBACK_THREAD);//出错if(m_mmr){//返回returnFALSE;}//设置设备打开标记m_bDevOpen=TRUE;//返回returnTRUE;}//启动线程BOOLCWaveIn::StartThread(){//线程已经启动if(m_bThreadStart){//返回returnFALSE;}//创建线程m_hAudioIn=CreateThread(0,0,AudioInThreadProc,this,0,&m_dwAudioInId);//出错if(!m_hAudioIn){//返回returnFALSE;}//设置线程启动标记m_bThreadStart=TRUE;//返回returnTRUE; 盐城工学院本科生毕业设计说明书(2010)}//关闭线程BOOLCWaveIn::StopThread(){//线程已经停止if(!m_bThreadStart){//返回returnFALSE;}//线程句柄有效if(m_hAudioIn){//循环变量intt=50;//线程结束状态信息DWORDExitCode;//线程结束标记BOOLbEnd=FALSE;//发送WM_QUIT消息PostThreadMessage(m_dwAudioInId,WM_QUIT,0,0);//主循环while(t){//获取线程结束状态信息GetExitCodeThread(m_hAudioIn,&ExitCode);//尚未结束if(ExitCode!=STILL_ACTIVE){//设置线程结束bEnd=TRUE;break;}//休眠10毫秒else{Sleep(10);}//修改循环变量t--;}//线程尚未结束if(!bEnd){//终止线程TerminateThread(m_hAudioIn,0);}//录音设备句柄无效 盐城工学院本科生毕业设计说明书(2010)m_hAudioIn=0;}//设置线程结束标记m_bThreadStart=FALSE;//返回returnTRUE;}//启动录音BOOLCWaveIn::StartRec(){//返回值BOOLbRet=FALSE;//启动线程失败if(!StartThread()){gotoExit;};//打开录音设备失败if(!OpenDev()){gotoExit1;};//准备缓存失败if(!PerPareBuffer()){gotoExit2;}//开始录音if(!OpenRecord()){gotoExit3;}//设置返回值bRet=TRUE;gotoExit;Exit3://释放缓存FreeBuffer();Exit2://关闭录音设备CloseDev();Exit1://停止线程StopThread();Exit://返回returnbRet;} 盐城工学院本科生毕业设计说明书(2010)//终止录音BOOLCWaveIn::StopRec(){//停止录音CloseRecord();//休眠1500毫秒Sleep(1500);//释放缓存FreeBuffer();//关闭录音设备if(CloseDev()){//停止线程StopThread();}//返回returnTRUE;}//分配内存BOOLCWaveIn::PerPareBuffer(){//已经分配内存if(m_bAllocBuffer){//返回returnFALSE;}//重置录音设备m_mmr=waveInReset(m_hIn);//出错if(m_mmr){//返回returnFALSE;}//循环变量UINTi;//新建WAVEHDR结构m_pHdr=newWAVEHDR[NUM_BUF];for(i=0;i=1){memcpy(&time,&time1,sizeof(time));TRACE("Len/second%d ",l);l=0;}l=l+iLen;i++;}MMRESULTCWaveIn::GetLastMMError() 盐城工学院本科生毕业设计说明书(2010){returnm_mmr;}CStringCWaveIn::GetLastErrorString(){charbuffer[256];memset(buffer,0,256);waveInGetErrorText(m_mmr,buffer,256);returnbuffer;}voidCWaveIn::SetChannel(WORDwChannel){m_wChannel=(m_wChannel==wChannel)?2:1;}voidCWaveIn::SetSample(DWORDdwSample){m_dwSample=dwSample;}voidCWaveIn::SetBit(WORDwBit){m_wBit=(wBit==8)?8:16;}DWORDCWaveIn::GetInstance(){returns_dwInstance;}WORDCWaveIn::GetBit(){returnm_wBit;}DWORDCWaveIn::GetSample(){returnm_dwSample;}WORDCWaveIn::GetChannel(){returnm_wChannel;}//音频回放#include"stdafx.h"#include"WaveOut.h"#pragmacomment(lib,"Winmm")DWORDCWaveOut::s_dwInstance=0;//线程处理DWORDWINAPICWaveOut::AudioOutThreadProc(LPVOIDlpParameter){CWaveOut*pWaveIn;pWaveIn=(CWaveOut*)lpParameter; 盐城工学院本科生毕业设计说明书(2010)MSGmsg;//消息处理主循环while(GetMessage(&msg,0,0,0)){switch(msg.message){//打开播音设备caseWOM_OPEN:break;//关闭播音设备caseWOM_CLOSE:break;//播音完毕caseWOM_DONE://播音格式WAVEHDR*pwh=(WAVEHDR*)msg.lParam;//释放播音缓存waveOutUnprepareHeader((HWAVEOUT)msg.wParam,pwh,sizeof(WAVEHDR));//减少播音缓存数目pWaveIn->BufferSub();//删除Play调用时分配的内存delete[]pwh->lpData;//删除播音格式deletepwh;break;}}//返回returnmsg.wParam;}//构造函数,用于初始化CWaveOut::CWaveOut():m_wChannel(1),m_dwSample(WOSA_POOR),m_wBit(16){m_hOut=0;m_hAudioOut=0;m_dwAudioOutId=0;m_iBufferNum=0;m_bThreadStart=FALSE;m_bDevOpen=FALSE;s_dwInstance++;}//构析函数CWaveOut::~CWaveOut(){} 盐城工学院本科生毕业设计说明书(2010)//启动线程BOOLCWaveOut::StartThread(){//线程已经启动if(m_bThreadStart){//返回returnFALSE;}//创建线程m_hAudioOut=CreateThread(0,0,AudioOutThreadProc,this,0,&m_dwAudioOutId);//出错if(!m_hAudioOut){//返回returnFALSE;}//设置线程启动标记m_bThreadStart=TRUE;//返回returnTRUE;}//停止线程BOOLCWaveOut::StopThread(){//线程已经停止if(!m_bThreadStart){//返回returnFALSE;}//线程句柄有效if(m_hAudioOut){//循环变量intt=50;//线程结束状态DWORDExitCode;//线程结束标记BOOLbEnd=FALSE;//发送WM_QUIT消息PostThreadMessage(m_dwAudioOutId,WM_QUIT,0,0);while(t){//获取线程结束状态信息GetExitCodeThread(m_hAudioOut,&ExitCode);//线程结束 盐城工学院本科生毕业设计说明书(2010)if(ExitCode!=STILL_ACTIVE){//设置线程尚未结束标记bEnd=TRUE;break;}//休眠10毫秒else{Sleep(10);}//修改循环变量t--;}//线程尚未结束if(!bEnd){//结束线程TerminateThread(m_hAudioOut,0);}//设置音频设备句柄无效m_hAudioOut=0;}//设置线程停止标记m_bThreadStart=FALSE;//返回returnTRUE;}//打开播音设备BOOLCWaveOut::OpenDev(){//设备已经打开if(m_bDevOpen){//返回returnFALSE;}//设置播音输出格式WAVEFORMATEXwfx;wfx.wFormatTag=WAVE_FORMAT_PCM;wfx.nChannels=m_wChannel;wfx.nSamplesPerSec=m_dwSample;wfx.nAvgBytesPerSec=m_wChannel*m_dwSample*m_wBit/8;wfx.nBlockAlign=m_wBit*m_wChannel/8;wfx.wBitsPerSample=m_wBit;wfx.cbSize=0;//检查播音设备是否支持上述格式m_mmr=waveOutOpen(0,WAVE_MAPPER,&wfx,0,0,WAVE_FORMAT_QUERY); 盐城工学院本科生毕业设计说明书(2010)//出错if(m_mmr){//返回returnFALSE;}//打开播音设备m_mmr=waveOutOpen(&m_hOut,WAVE_MAPPER,&wfx,m_dwAudioOutId,s_dwInstance,CALLBACK_THREAD);//出错if(m_mmr){//返回returnFALSE;}//设置设备打开标记m_bDevOpen=TRUE;//初始化播音缓存数目m_iBufferNum=0;//返回returnTRUE;}//关闭播音设备BOOLCWaveOut::CloseDev(){//播音设备已经关闭if(!m_bDevOpen){//返回returnFALSE;}//播音设备句柄无效if(!m_hOut){//返回returnFALSE;}//关闭播音设备m_mmr=waveOutClose(m_hOut);//出错if(m_mmr){//返回returnFALSE;}//设置播音设备句柄无效m_hOut=0;//设置播音设备打开标记 盐城工学院本科生毕业设计说明书(2010)m_bDevOpen=FALSE;//返回returnTRUE;}//开始播音BOOLCWaveOut::StartPlay(){//函数返回标志BOOLbRet=FALSE;//启动线程失败if(!StartThread()){gotoExit;};//打开播音设备失败if(!OpenDev()){gotoExit1;};//设置函数返回标志bRet=TRUE;gotoExit;Exit1://停止线程StopThread();Exit://返回returnbRet;}//停止播音BOOLCWaveOut::StopPlay(){//关闭播音设备CloseDev();//关闭线程StopThread();//返回returnTRUE;}MMRESULTCWaveOut::GetLastMMError(){returnm_mmr;}CStringCWaveOut::GetLastErrorString(){charbuffer[256];memset(buffer,0,256);waveOutGetErrorText(m_mmr,buffer,256); 盐城工学院本科生毕业设计说明书(2010)returnbuffer;}voidCWaveOut::SetChannel(WORDwChannel){m_wChannel=(m_wChannel==wChannel)?2:1;}voidCWaveOut::SetSample(DWORDdwSample){m_dwSample=dwSample;}voidCWaveOut::SetBit(WORDwBit){m_wBit=(wBit==8)?8:16;}DWORDCWaveOut::GetInstance(){returns_dwInstance;}WORDCWaveOut::GetBit(){returnm_wBit;}DWORDCWaveOut::GetSample(){returnm_dwSample;}WORDCWaveOut::GetChannel(){returnm_wChannel;}//播放BOOLCWaveOut::Play(char*buf,UINTuSize){//播音设备未打开if(!m_bDevOpen){//返回returnFALSE;}//延时太大if(GetBufferNum()>PLAY_DELAY){//返回returnTRUE;}//新建WAVEHDR结构,用于接收播音格式LPWAVEHDRpwh=newWAVEHDR;//出错 盐城工学院本科生毕业设计说明书(2010)if(!pwh){//返回returnFALSE;}//新建字符数组,用于接收缓存数据char*p;p=newchar[uSize];//出错if(!p){//返回returnFALSE;}//复制缓存数据CopyMemory(p,buf,uSize);//初始化ZeroMemory(pwh,sizeof(WAVEHDR));//数据长度pwh->dwBufferLength=uSize;//波音数据pwh->lpData=p;//为回放数据作好准备m_mmr=waveOutPrepareHeader(m_hOut,pwh,sizeof(WAVEHDR));//出错if(m_mmr){//返回returnFALSE;}//将数据发往播音设备m_mmr=waveOutWrite(m_hOut,pwh,sizeof(WAVEHDR));//出错if(m_mmr){//返回returnFALSE;}//缓存数目增1m_iBufferNum++;//返回returnTRUE;}//增加缓存数目voidCWaveOut::BufferAdd(){//锁住内存m_csLock.Lock(); 盐城工学院本科生毕业设计说明书(2010)//缓存数目增1m_iBufferNum++;//解除锁定m_csLock.Unlock();}//减少缓存数目voidCWaveOut::BufferSub(){//锁住内存m_csLock.Lock();//缓存数目减1m_iBufferNum--;//解除锁定m_csLock.Unlock();}//获取缓存数目intCWaveOut::GetBufferNum(){//中间变量intiTemp;//锁住内存m_csLock.Lock();//获得缓存数目iTemp=m_iBufferNum;//解除锁定m_csLock.Unlock();//返回returniTemp;}BOOLCWaveOut::SetFormatByFile(CStringfile){#pragmapack(push,1)structFileHeader{charcFlagFiff[4];unsigned__int32iFileLen;charcFlagWave[4];charcFlagFmt[4];charcResove[4];unsigned__int16cFormat;unsigned__int16cChannel;unsigned__int32cSample;unsigned__int32cBytePerSec;unsigned__int16cByteprocess;unsigned__int16cBit;charcFlagDat[4];unsigned__int32iLenData;}; 盐城工学院本科生毕业设计说明书(2010)#pragmapack(pop)CFilefi;if(!fi.Open(file,CFile::modeRead,NULL)){returnFALSE;};structFileHeaderhead;fi.Read((void*)&head,sizeof(head));fi.Close();this->SetChannel(head.cChannel);this->SetSample(head.cSample);this->SetBit(head.cBit);returnTRUE;}//网络传输//初始化#include"stdafx.h"#include"UdpSocket.h"#include"head.h"#ifdef_DEBUG#definenewDEBUG_NEW#undefTHIS_FILEstaticcharTHIS_FILE[]=__FILE__;#endif//CUdpSocket//构造函数CUdpSocket::CUdpSocket(){m_pFrame=(structFrame*)m_cBuffer;m_pFrame->iIndex=0;m_bIni=FALSE;}//析构函数CUdpSocket::~CUdpSocket(){}//Donoteditthefollowinglines,whichareneededbyClassWizard.#if0BEGIN_MESSAGE_MAP(CUdpSocket,CAsyncSocket)//{{AFX_MSG_MAP(CUdpSocket)//}}AFX_MSG_MAPEND_MESSAGE_MAP()#endif////CUdpSocketmemberfunctions//初始化网络BOOLCUdpSocket::Ini(){//已经初始化 盐城工学院本科生毕业设计说明书(2010)if(m_bIni)//返回returnFALSE;//创建数据报类型的套接字if(!this->Create(NULL,SOCK_DGRAM)){//返回returnFALSE;};//设置初始化完成标记m_bIni=TRUE;//返回returnTRUE;}intCUdpSocket::Send(constvoid*lpBuf,intnBufLen,intnFlags){//音频数据序号m_pFrame->iIndex++;//复制音频格式信息memcpy(m_cBuffer+sizeof(structFrame),lpBuf,nBufLen);//调用基类SendTo()函数发送音频格式信息returnCAsyncSocket::SendTo(m_pFrame,nBufLen+sizeof(structFrame),TALK_REC_PORT,m_ip,nFlags);}//关闭套接字BOOLCUdpSocket::CloseSocket(){//已经关闭if(!m_bIni)returnFALSE;//关闭套接字CAsyncSocket::Close();//设置初始化未完成标记m_bIni=FALSE;//返回returnTRUE;}//设置Ip地址voidCUdpSocket::SetIp(CStringip){m_ip=ip;}//客户端//ClientSocket.cpp:implementationfile#include"stdafx.h"#include"ClientSocket.h"#include"head.h"#ifdef_DEBUG 盐城工学院本科生毕业设计说明书(2010)#definenewDEBUG_NEW#undefTHIS_FILEstaticcharTHIS_FILE[]=__FILE__;#endif//CClientSocket//构造函数,初始化CClientSocket::CClientSocket(CInterface*pInterFace,CMyWaveIn*pIn,CUdpSocket*pUdp):m_bConnect(FALSE),m_pInterface(NULL),m_pIn(NULL),m_pUdp(NULL){m_pBuffer=newchar[1024];m_pInterface=pInterFace;m_pIn=pIn;m_pUdp=pUdp;}//析构函数CClientSocket::~CClientSocket(){delete[1024]m_pBuffer;}//Donoteditthefollowinglines,whichareneededbyClassWizard.#if0BEGIN_MESSAGE_MAP(CClientSocket,CSocket)//{{AFX_MSG_MAP(CClientSocket)//}}AFX_MSG_MAPEND_MESSAGE_MAP()#endif//0//CClientSocketmemberfunctionsvoidCClientSocket::OnClose(intnErrorCode){//设置关闭标志m_bConnect=FALSE;//关闭m_pInterface->BeClose();//调用基类OnClose()函数CSocket::OnClose(nErrorCode);}voidCClientSocket::OnReceive(intnErrorCode){structTalkFrame*frame;frame=(structTalkFrame*)m_pBuffer;//接收缓存中的所有TalkFrame结构intiLen=sizeof(structTalkFrame);while(iLen>0){ 盐城工学院本科生毕业设计说明书(2010)//接收inti=Receive(m_pBuffer+sizeof(structTalkFrame)-iLen,iLen);//出错if(i==SOCKET_ERROR)return;iLen-=i;}//如果是非法数据就返回if(strcmp(frame->cFlag,"TalkFrame")!=0){//返回return;}//接收缓存中的所有音频数据iLen=frame->iLen;while(iLen>0){//接收inti=Receive(m_pBuffer+sizeof(structTalkFrame)+(frame->iLen-iLen),iLen);//出错if(i==SOCKET_ERROR)return;//修改循环标志iLen-=i;}//地址CStringadd;//端口UINTport;switch(frame->iCom){//正常通信帧caseTC_NORMAL_TALK://初始化memset(frame,0,sizeof(structTalkFrame));//设置数据帧标志sprintf(frame->cFlag,"TalkFrame");//获得与此套接字相连的地址和端口GetPeerName(add,port);//处于连接状态if(m_pInterface->IsConnect(add)){//设置通信帧标志及其长度frame->iCom=TC_AGREE_TALK;frame->iLen=0;//出错 盐城工学院本科生毕业设计说明书(2010)if(SOCKET_ERROR==Send(m_pBuffer,sizeof(structTalkFrame))){break;};//提示开始通信m_pInterface->TalkStart(add);//设置IP地址m_pUdp->SetIp(add);//允许发送m_pIn->EnableSend(TRUE);//允许工作m_pInterface->m_bWork=TRUE;break;};//设置通信帧标志frame->iCom=TC_DISAGREE_TALK;//设置通信帧长度frame->iLen=0;//发送Send(m_pBuffer,sizeof(structTalkFrame));//关闭Close();break;default:break;//调用基类OnReceive()函数CSocket::OnReceive(nErrorCode);}//侦听//ListenSocket.cpp:implementationfile#include"stdafx.h"#include"ListenSocket.h"#ifdef_DEBUG#definenewDEBUG_NEW#undefTHIS_FILEstaticcharTHIS_FILE[]=__FILE__;#endif//CListenSocket//构造函数,用于初始化CListenSocket::CListenSocket(CInterface*pTemp,CMyWaveIn*pIn,CUdpSocket*pUdp):m_sopClient(NULL),m_pIn(NULL),m_pUdp(NULL){m_sopClient=newCClientSocket(pTemp,pIn,pUdp); 盐城工学院本科生毕业设计说明书(2010)m_pInterface=pTemp;m_pIn=pIn;m_pUdp=pUdp;}//析构函数CListenSocket::~CListenSocket(){deletem_sopClient;}#if0BEGIN_MESSAGE_MAP(CListenSocket,CSocket)//{{AFX_MSG_MAP(CListenSocket)//}}AFX_MSG_MAPEND_MESSAGE_MAP()#endif//0//CListenSocketmemberfunctions//重载OnAccept()函数voidCListenSocket::OnAccept(intnErrorCode){//客户端地址SOCKADDRadd;//客户端地址长度intiLen;iLen=sizeof(SOCKADDR);//临时socketCSocketsoTemp;//已经连接if(m_sopClient->m_bConnect){//用临时socket接收连接请求soTemp.Accept(*this);//关闭soTemp.Close();//返回return;}//接受连接请求if(!Accept(*m_sopClient,&add,&iLen)){//返回return;}//设置连接标记m_sopClient->m_bConnect=TRUE;//调用基类OnAccept()函数CSocket::OnAccept(nErrorCode);}//关闭套接字 盐城工学院本科生毕业设计说明书(2010)voidCListenSocket::CloseClient(){//关闭套接字m_sopClient->Close();//设置未连接标记m_sopClient->m_bConnect=FALSE;}//发送//SendClient.cpp:implementationfile#include"stdafx.h"#include"SendClient.h"#include"head.h"#ifdef_DEBUG#definenewDEBUG_NEW#undefTHIS_FILEstaticcharTHIS_FILE[]=__FILE__;#endifCSendClient::CSendClient(CMyWaveIn*pIn,CInterface*pInterface):m_pIn(NULL),m_pInterFace(NULL){m_pBuffer=newchar[1024];m_pIn=pIn;m_pInterFace=pInterface;}CSendClient::~CSendClient(){delete[1024]m_pBuffer;}BEGIN_MESSAGE_MAP(CSendClient,CAsyncSocket)//{{AFX_MSG_MAP(CSendClient)//}}AFX_MSG_MAPEND_MESSAGE_MAP()#endif//0//CSendClientmemberfunctions//重载OnReceive()函数voidCSendClient::OnReceive(intnErrorCode){structTalkFrame*frame;frame=(structTalkFrame*)m_pBuffer;intiLen=sizeof(structTalkFrame);//接收缓存中所有的TalkFrame结构while(iLen>0){//接收数据inti=Receive(m_pBuffer+sizeof(structTalkFrame)-iLen,iLen); 盐城工学院本科生毕业设计说明书(2010)//出错if(i==SOCKET_ERROR)//返回return;iLen-=i;}//如果不是正常数据就返回if(strcmp(frame->cFlag,"TalkFrame")!=0){return;}iLen=frame->iLen;//接收缓存中所有的音频数据while(iLen>0){//接收数据inti=Receive(m_pBuffer+sizeof(structTalkFrame)+(frame->iLen-iLen),iLen);//出错if(i==SOCKET_ERROR)//返回return;iLen-=i;}//对方地址CStringadd;//端口UINTport;switch(frame->iCom){//正常通信帧caseTC_AGREE_TALK://获取连接对方地址和端口GetPeerName(add,port);//提示开始工作m_pInterFace->TalkStart(add);//允许发送数据m_pIn->EnableSend(TRUE);//设置允许工作标志m_pInterFace->m_bWork=TRUE;break;default:break;}//调用基类OnReceive()函数CAsyncSocket::OnReceive(nErrorCode);}voidCSendClient::OnClose(intnErrorCode) 盐城工学院本科生毕业设计说明书(2010){//设置连接断开标志m_bConnect=FALSE;//关闭m_pInterFace->BeClose();//调用基类OnClose()函数CAsyncSocket::OnClose(nErrorCode);}voidCSendClient::OnConnect(intnErrorCode){//获取连接结果m_pInterFace->ConnectResult(nErrorCode);//调用基类OnConnect()函数CAsyncSocket::OnConnect(nErrorCode);}//接收//RecSocket.cpp:implementationfile#include"stdafx.h"#include"RecSocket.h"#include"head.h"#include"AudioCode.h"#include"WaveOut.h"#ifdef_DEBUG#definenewDEBUG_NEW#undefTHIS_FILEstaticcharTHIS_FILE[]=__FILE__;#endif//CRecSocketexternCAudioCodeg_ACode;externCWaveOut*g_pOut;//构造函数CRecSocket::CRecSocket(){m_bIni=FALSE;m_pFrame=(structFrame*)m_cBuffer;}//析构函数CRecSocket::~CRecSocket(){}#if0BEGIN_MESSAGE_MAP(CRecSocket,CSocket)//{{AFX_MSG_MAP(CRecSocket)//}}AFX_MSG_MAPEND_MESSAGE_MAP()#endif//0//CRecSocketmemberfunctions//初始化 盐城工学院本科生毕业设计说明书(2010)BOOLCRecSocket::Ini(){//已经初始化if(m_bIni)returnFALSE;//创建数据报类型的套接字if(!Create(TALK_REC_PORT,SOCK_DGRAM))returnFALSE;//设置初始化完成标记m_bIni=TRUE;returnTRUE;}voidCRecSocket::OnReceive(intnErrorCode){//接收数据长度m_iLen=sizeof(m_cBuffer);//接收数据m_iLen=this->Receive(m_pFrame,m_iLen);//排序m_sort.ReceiveData((char*)m_pFrame,m_iLen);//静态变量staticinti=0;i++;//调用基类OnReceive()函数CSocket::OnReceive(nErrorCode);}//关闭套接字BOOLCRecSocket::CloseSocket(){//已经关闭if(!m_bIni)returnFALSE;//设置套接字关闭标记m_bIni=FALSE;//调用基类Close()函数CSocket::Close();returnTRUE;}voidCRecSocket::Play(char*pBuffer,intiLen){//解码输出长度intiOut;iOut=sizeof(m_cOut);//解码g_ACode.DecodeAudioData(pBuffer,iLen,m_cOut,&iOut);//播放g_pOut->Play(m_cOut,iOut);}

当前文档最多预览五页,下载文档查看全文

此文档下载收益归作者所有

当前文档最多预览五页,下载文档查看全文
温馨提示:
1. 部分包含数学公式或PPT动画的文件,查看预览时可能会显示错乱或异常,文件下载后无此问题,请放心下载。
2. 本文档由用户上传,版权归属用户,天天文库负责整理代发布。如果您对本文档版权有争议请及时联系客服。
3. 下载前请仔细阅读文档内容,确认文档内容符合您的需求后进行下载,若出现内容与标题不符可向本站投诉处理。
4. 下载文档时可能由于网络波动等原因无法下载或下载错误,付费完成后未能成功下载的用户请联系客服处理。
大家都在看
近期热门
关闭