Windows下的声卡编程技术.pdf
第35卷第3期2003年9月西安建筑科技大学学报(自然科学版)J1Xian U niv.of A rch.&Tech.(N atural Science Edition)Vol.35No.3Sep.2003W indow s下的声卡编程技术朱晓娟,杨放(西安建筑科技大学信控学院,陕西 西安710055)摘要:首先分析了声卡用作A?D卡的优点和缺点;接着利用具体实例,着重介绍了作为A?D卡的声卡编程技术和技巧;最后结合作者的实践,指出声卡用作为A?D卡时需要注意的地方.文章对利用声卡进行多媒体编程及数字语音处理也很有帮助.关键词:A?D转换;声卡;W indow s编程中图分类号:TP313文献标识码:A文章编号:100627930(2003)0320281204Programm ing of sound card in W indowsZH U X iao2juan,YA N G Fang(School of Information and Control Eng.,Xian U niv.of A rch.&Tech.,Xian 710055,China)Abstract:First,this article analyses the strong point as well as the shortcom ing of a sound card used as an A?D card.Then,it introduces some technique and artifice on programm ing a sound card as an A?D card,and qives some examplesduring the introduction.A t last,according to the authors practice,some attentive points are given.To program asound card as a multi media device or digital voice processing,this article is also helpful.Key words:A?D conversion;S ound card;W indow s p rog ramm ing1声卡的基本特点声卡作为语音信号与计算机的接口卡件,其最基本的一项功能就是A?D转换.实际上,除了语音外,很多信号的频率都落在音频范围内(比如机械量信号,过程量信号等),当我们需要对这些信号进行采集时,使用声卡作为采集卡是一种相当令人满意的解决方案,其理由是:(1)价格便宜.一般声卡的价格,比起自己从头到尾开发一块采集卡的成本低得多.比起目前市场上的采集卡的价格,更是不可同日而语.相应地,产品成本也会降低;(2)即买即用.完全省略了A?D卡的硬件开发过程,很大程度上缩短了产品的开发周期;(3)灵活性好.量化位数可编程(8位或16位);采样频率可编程(一般声卡的最高采样频率可达200kHz,并且连续可调);采样通道可编程(1通道或2通道);由于可以使用在W indow s操作系统下,因此可用通用的软件开发工具对其进行开发(如Delphi,VB,VC等).当然也有其局限性,那就是声卡一般只能作为PC插卡用在PC机上,很难用微控制器对其进行控制(因为要用到中断和DMA技术),因而很难用在小型的仪器仪表里.再者由于声卡是专门针对音频信号而设计的,所以它的采样速率不可能很高.收稿日期:2003203219作者简介:朱晓娟(19702),女,陕西人,西安建筑科技大学讲师,主要从事控制理论与控制工程方面的教学与研究.2编程技术既然声卡本身就是一块很好的A?D卡,硬件部分已经不需操心了,那么最终的问题就是怎样对其编程才能够取得A?D的数据.事实上,声卡是PC的一种多媒体设备,所以可以用W indow s的M C I(M edia Control Interface)命令来控制声卡.M C I它提供了一组与设备无关的控制命令,是一种访问多媒体设备的高层次方法.也正因为它属于一种高层次方法,所以它提供给程序员的灵活性有限,利用M C I命令来控制声卡录音时,程序员不能在录音的过程中访问内存中的采样数据,只有在录音完成后通过访问3.WAV文件才可以得到采样数据,尽管最终还是得到了采样数据,但是这样做一方面嫌其麻烦,更重要的是存取文件需要耗费时间,声卡在采样的过程中有可能会停止下来等待文件操作,造成了采样的断续.在一些实时性要求比较高的场合(比如波形分析,实时控制等),断续的采样明显是不行的.W indow s的低级波形音频函数提供了对声卡的最大灵活性的操作,它允许在采样过程中随机地访问内存中的每个采样数据,完全可以克服使用M C I命令所遇到的实时性问题.W indow s以动态连接库Mm system.dll的形式提供低级波形音频函数,在Mm system.dll中总共包括了以下几个有关波形录入的函数:waveInA ddBuffer:向声音输入设备发送缓冲区;waveInClose:关闭声音输入设备waveInGetDevCaps:获取声音输入设备性能;waveInGetErrorText:获取声音出错信息文本waveInGetI D:获取声音输入设备I D;waveInGetN umDevs:返回声音输入设备数量waveInGetPosition:获取声音输入设备位置;waveInM essage:向声音输入设备发送信息waveInOpen:打开声音输入设备;waveInPrepareHeader:预备声音输入缓冲区waveInReset:停止声音输入设备工作;waveInStart:开启声音输入设备工作waveInStop:停止声音输入;waveInU nprepareHeader:清除预备的声音文件头需要说明的是:不同的编程工具多会含有对这些低级波形音频函数进行说明的头文件(比如在Delphi4.0中,对Mm system.dll说明的文件是Mm system.pas),所以在不同的编程工具中调用这些函数时有可能会使用不同的名称.与使用其他设备一样,要想用波形音频函数来控制声卡,必须要经过以下的步骤:(1)打开波形输入设备.函数waveInOpen用于打开波形输入设备,其原型如下:WORD waveInOpen(lphW aveIn,wDeviceI D,lpFormat,dwCallback,dwCallback Instance,dwFlags)L PHW aveIn:lphW aveIn该变量用来接收波形输入设备的句柄,该句柄应当保存下来,因为其他的波形输入函数还会用到它.WORD wDeviceI D该变量用来指明波形输入设备的标记号.当PC中有多个声卡(准确地说是波形输入设备)时,操作系统会为每一块声卡分配一个标记号.可以用waveInGetN umDevs函数来得到能够作为波形输入设备的数目N,则wDeviceI D的取值范围为0N-1.如果想得到每个标记号所对应的录音性能,可以使用函数waveInGetDevCaps.若把wDeviceI D设为WAV EMA PPER(即-1),则系统会自动选择一个符合要求的设备(根据lpFormat的要求).lpFormat是一个指向PCMWAV EFORMA T数据结构的指针,应当在这个数据结构中指明所期望的采样模式,这个数据结构的定义是这样的:Typedef structure pcmwaveformattagWAV EFORMA T wf;?有关PCM格式设置的另外一种数据结构WORD wBitsPerSample;?量化位数PCMWAV EFORMA T;Typedef structure waveformattag282西安建筑科技大学学报(自然科学版)第35卷WORD wFormatTag;?采样数据格式,目前只能用PCM格式WORDnChannels;?通道数目(1或2)DWORD nSamplesPerSec;?采样速率DWORD nA vgBytesPerSec;?每秒采样得到的数据WORD nBlockA lign;?记录区块对齐的单位.此值为nChannels3wBitsPerSample?8WAV EFORMA T;DWORD dwCallback.定义回调函数的地址或回调窗口的句柄.回调函数的地址或回调窗口用来处理波形输入设备产生的消息.DWORD dwCallback Instance.这是一个用户自定义的数据,该数据会一并传给回调函数(或窗口).DWORD dwFlags.定义打开波形输入设备的标记.CALLBACKW I NDOW定义dwCallback为窗口句柄.CALLBACKFUNCT I ON定义dwCallback为函数地址.另外还可以在此指定:WAV EFORMA TQU ERY只查询波形输入设备是否支持给定格式而不真的打开波形输入设备.WAV EALLOW SYNC同步方式开启波形输入设备,录音工作在后台进行.(2)为采样数据分配缓冲空间在W indow s环境下,可以用GlobalA llocPtr来获取一段内存空间,但是由于W indow s操作系统采用了虚拟存储管理机制,这块内存空间随时有可能会被置换到硬盘上,读写硬盘所耗费的时间会造成采样的不连续.因此,在将缓冲区送往波形输入设备之前,必须调用W aveInPrepareHeader函数以保证缓冲区不会被置换到硬盘上.当然在用GlobalFreePtr来释放缓冲区之前,必须先要用W aveInU nprepareHeader函数来解除这种保护.下面几行Delphi语句说明了使用录音缓冲区的过程.Recorder.W aveHdr1:=GlobalA llocPtr(GHND or GM EMSHARE,Sizeof(WAV EHDR);Recorder.W aveBuffer1:=GlobalA llocPtr(GHND or GM EMSHARE,1024);Recorder.W aveHdr1.lpData:=Recorder.W aveBuffer1;Recorder.W aveHdr1.dwBufferL ength:=1024;W aveInPrepareHeader(Recorder.W aveHandle,Recorder.W aveHdr1,sizeof(WAV EHDR);W aveInA ddBuffer(Recorder.W aveHandle,Recorder.W aveHdr1,sizeof(WAV EHDR);W aveInU nprepareHeader(Recorder.W aveHandle,Recorder.W aveHdr1,sizeof(TWAV EHDR);GlobalFreePtr(Recorder.W aveBuffer1);但是,如果只为波形输入设备开辟一个缓冲区,则当该缓冲区被采样数据填满后,波形输入设备就无缓冲区可用,不得不停止采样,从而造成了采样的断续.所以在实际应用中,至少要为波形输入设备准备两个缓冲区,用上述方法同时送给波形输入设备.(3)启动波形输入设备当上述一切都准备好后,用W aveInStart启动波形输入设备,即可开始进行数据采集,在采集的过程中,一旦有缓冲区被采样数据填满,系统就回调W aveInOpen中指定的dwCallback函数(或向指定的窗口发送消息).在Delphi4.0中,回调函数的格式是这样的:382第3期朱晓娟等:W indow s下的声卡编程技术procedure CallBack(uM sg,dw Instance,dwParam 1,dwParam 2:DWORD);stdcall;其中uM sg是W indow s的消息标记号,有三种情况:MMW I MOPEN表示波形输入设备开启成功MMW I MDA TA表示一个缓冲区已满.此时dwParam 1中携带有数据缓冲区头结的指针.正是通过这个指针,才可以随机地访问缓冲区中的每一个采样数据.MMW I MCLOSE表示波形输入设备关闭成功.当波形输入设备关闭后,别忘了用W aveIn2PrepareHeader和GlobalFreePtr来释放缓冲区内存.(4)关闭语音输入设备waveInStop(hW aveIn)停止语音输入waveInReset(hW aveIn)重置语音输入设备waveInClose(hW aveIn)关闭语音输入设备.其中hW aveIn是W aveInOpen得到的设备句柄.在关闭语音输入设备前,必须重置语音输入设备,否则系统会出现这样的错误提示:“MM SYS2TEM 033媒体数据仍在播放中,请重置设备或等到数据播放完毕”.但是只有当一个缓冲区填满数据后,才能重置语音输入设备.以上波形输入函数,若调用成功则返回0;否则返回非0,此时可以用waveInGetErrorText函数来得到出错信息,这样做的目的是方便调试.3经验与结论以上阐述了作为A?D卡的声卡编程技术,根据自己实际经验,还必须注意以下几点:(1)声卡的采样频率并不只限于11025Hz,22050Hz,44100 Hz三种,大多数声卡的采样频率是连续可调的(当然会存在一定的偏差).(2)有的声卡的最高采样频率可达200K Hz(有可能随不同品牌而异).(3)缓冲区不能设得太小,否则也会造成采样的不连续.在作者的声卡上,若采用16为量化,22050Hz的采样速率,缓冲区设为1K字节,理论上每秒钟可以得到2205032个字节的数据,实际上每秒钟只能得到大约1600032个字节的数据.若缓冲区设为2K字节,则与理论值一致.(4)A?D转化后的数据格式是PCM格式,即:若是8位量化,对应着8位无符号数据,0对应着负满幅值,128对应着零电平,255对应着正满幅值;若是16位量化,对应着16位有符号数据,-32768对应着负满幅值,0对应着零电平,32767对应着正满幅值.编程过程中应注意所声明的数据类型是否与之相符合,比如在Delphi4.0中,8位无符号数据对应着Byte型数据,16位有符号数据对应着SmallInt型数据.(5)由于声卡的输入端往往带有隔直电容,所以不能用声卡直接对直流量进行采集.解决的办法就是将这个隔直电容短接.参考文献:1黄伟伦.M s2W indow s多媒体程序设计实务与范例M.武汉:华中理工大学出版社,1996.2Paul Perry.多媒体开发指南M.北京:清华大学出版社,1995.482西安建筑科技大学学报(自然科学版)第35卷