1、DirectSound是如何播放一段PCM音频的
这里只是简单的介绍一下播放声音的步骤。
第一步,创建一个设备对象。
在你的代码中你可以通过调用DirectSoundCreat8函数来创建一个支持IDirectSound8接口的对象,这个对象通常代表缺省的播放设备。当然你可以枚举可用的设备,然后将设备的GUID传递给DirectSoundCreat8函数。
注意,Directsound虽然基于COM,但是你并不需要初始化com库,这些Directsound都帮你做好了,当然,如果你使用DMOs特技,你就要自己初始化com库了,切记。
第二步,创建一个辅助Buffer,也叫后备缓冲区
你可以通过IDirectSound8::CreateSoundBuffer来创建buffer对象,这个对象主要用来获取处理数据,这种buffer称作辅助缓冲区,以和主缓冲区区别开来,Direcsound通过把几个后备缓冲区的声音混合到主缓冲区中,然后输出到声音输出设备上,达到混音的效果。
第三步,获取PCM类型的数据
将WAV文件或者其他资源的数据读取到缓冲区中。
第四步,将数据读取到缓冲区
你可以通过 IDirectSoundBuffer8::Lock.方法来准备一个辅助缓冲区来进行写操作,通常这个方法返回一个内存地址,见数据从你的私人buffer中复制到这个地址中,然后调用IDirectSoundBuffer8::Unlock.
第五步,播放缓冲区中的数据
你可以通过IDirectSoundBuffer8::Play方法来播放缓冲区中的音频数据,你可以通过IDirectSoundBuffer8::Stop来暂停播放数据,你可以反复的莱停止,播放,音频数据,如果你同时创建了几个buffer,那么你就可以同时来播放这些数据,这些声音会自动进行混音的。
看到了吧,Directsound混音很简单,我们只要在一个设备上创建几个辅助的缓冲区,然后将数据读取到缓冲区中,同时的播放,Directsound就会自动在主缓冲区自动混音的。至于同时可以播放几个辅助缓冲区则有硬件设备的性能决定。
2、DirectSound录音用到的三个非常重要的对象:
1)·IDirectSoundCapture8 ,设备对象,根据你录音的设备创建的设备对象,利用该对象可以获取设备的属性。
2)·IDirectSoundCaptureBuffer8,缓冲区对象,该对象由设备对象创建,主要用来操作音频数据
3)·IDirectSoundNotify8 ,事件通知对象,该对象用来通知应用程序从缓冲区中将数据取走,写入文件保存起来。
利用DirectSound录音的主要思路:
就是先根据选择的录音设备创建设备对象,然后通过设备对象创建辅助缓冲区对象,开始录音的时候,设备将数据写入缓冲区,应用程序主动的从缓冲区将数据读出来写文件即可,就实现了录音功能。
这里简单介绍一下dsound的通知功能,应用程序会创建一个通知对象,然后将通知对象邦定,然后设定通知位置(position),什么是通知位置呢,比如缓冲区的大小为4000字节,如果你想当数据达到缓冲区一半的时候能得到通知开始copy数据,那么此时你就可以将通知位置设定为2000,通知位置可以任意的设定,当缓冲区的数据达到你设定的位置时,就会通知应用程序将缓冲区的数据copy到文件中,缓冲区是循环利用的,当缓冲区填充满了以后,就会从头开始充填数据,所以,缓冲区就是一边读,一边写的过程。
211 语音信号捕获的实现
语音信号的捕获功能是通过调用DirectSound中的应用编程接口(AP I: App lication Programming In2terface) 函数实现的[ 4 ] 。其中DirectSoundCap ture函数提供了从输入设备捕获数字声音信号的接口。使用该接口之前先要建立IDirectSoundCap ture接口的一个实例, 然后用该接口方法创建一个捕获缓冲区,具体的声音捕获工作可由捕获缓冲区对象的方法完成。该系统采用DirectSound进行语音信号捕获的实现步骤如下[ 5, 6 ] 。
1) 枚举声音捕获设备。软件实现时采用DirectSound提供的AP I接口DirectSoundCap tureEnumerate选取音频捕获设备, 在查找硬件设备时, 要用到一个回调函数DSCEnumProc, 该函数要从DSEnumCall2back的形式声明, 在每次DirectSound找到一个设备时被调用。如果找到了所需要的设备, 回调函数就会返回False, 否则必须返回True, 继续进行查找。
2) 创建DirectSoundCap ture对象。调用DirectSoundCap tureCreate函数可以创建DirectSoundCap ture对象, 该函数返回IDirectSoundCap ture接口的一个指针。
3) 创建DirectSound捕获缓冲区。调用IDirectSoundCap ture∷CreateSoundBuffer方法可以创建一个捕获缓冲区。本系统创建的捕获缓冲区为流式缓冲区, 同时将捕获缓冲区的大小设置为013 s。缓冲区太小会出现数据溢出的情况; 太大会增加系统处理时延。CreateSoundBuffer方法中有个参数是Dscbuffer2desc结构, 该结构描述了要创建的声音缓冲区的特征。Dscbufferdesc结构的最后1个参数指向Wavefor2matex结构的指针, 指定了捕获缓冲区中波形声音的格式。Waveformatex结构的参数取值如下:
wfx1wFormatTag = Wave- Format- PCM;
wfx1nChannels = 1;
wfx1nSamp lesPerSec = 8000;
wfx1wB itsPerSamp le = 16;
wfx1nBlockAlign = wfx1wBitsPerSamp le /83 wfx1nChannels;
wfx1nAvgBytesPerSec = wfx1wB itsPerSamp le3 wfx1nBlockAlign;
wfx1cbSize = 0。
4) 获得捕获缓冲区的信息。通过调用IDirectSoundCap tureBuffer∷GetStatus方法得到当前捕获缓冲区的工作状态。该方法采用一些标志的组合, 对一个Dword类型的变量m- sgmbol赋值, 指出缓冲区是否正在捕获声音数据。最后调用IDirectSoundCap tureBuffer∷GetCurrentPosition方法返回当前缓冲区中读取和捕获指针所处的位置。
5) 捕获缓冲区通知。首先使用QueryInterface方法获得一个IdirectSoundNotify接口指针, 然后调用Win32的GreatEvent函数创建一个事件对象, 将该事件的句柄传送给Dsbpositionnotify结构的hEventNoti2fy∷SetNotificationPosition方法。最后用Dsbpositionnotify结构的数组存放多个通知点的信息。
6) 使用捕获缓冲区进行声音捕获。
①系统采用了环状的回绕缓冲区, 使声音捕获工作能持续地进行, 在调用IDirectSoundCap ture∷Start方法时, 将Dword类型的参数dwFlags设置为Dscbstart- Loop ing, 使输入指针到达缓冲区末尾时,能自动地回到缓冲区的开头重新开始捕获声音数据。
②调用On- Waiting ( ) 函数, 处理等待捕获到的声音数据量达到设定的通知事件的要求。
③当捕获到足够多的数据后, 调用IDirectSoundCap tureBuffer∷GetCurrentPosition方法获得当前的读取位置指针, 然后把将要读取的内存块大小的偏移量作为参数传给Lock方法。
④使用Lock方法得到内存块的大小和地址后, 可从捕获缓冲区中取出捕获到的声音数据, 将其存放到编码缓冲区中。
⑤使用IDirectSoundCap tureBuffer∷Unlock方法对前面锁定的缓冲区解锁。
⑥重复②~⑤步骤, 直到所有的数据都已经捕获完毕并且输入到编码缓冲区中, 然后调用IDirect2SoundCap tureBuffer∷Stop方法, 停止声音数据捕获, 捕获指针将自动地回到缓冲区的开头处[ 3, 7 ] 。
212 语音信号回放的实现
在对解码后的语音数据进行回放时, 同样利用了DirectSound AP I接口函数, 实现步骤如下[ 8 ] 。
1) 枚举可用的声音设备。同211中1) 。
2) 创建DirectSound对象。使用DirectSoundCreate函数创建DirectSound对象。
3) 设置DirectSound的协作优先级别。用IDirectSound∷SetCooperativeLevel方法, 将协作等级设置为一般协作等级(Dsscl- Normal) , 使应用程序之间可以轮流切换使用声音设备
4) 创建DirectSound回放缓冲区。首先要对一个Dsbufferdesc结构赋值, 然后调用IDirectSound∷CreateSoundBuffer方法。
5) 播放DirectSound缓冲区。用IdirectSoundBuffer∷Lock方法锁定次缓冲区的一个位置。
①将声音数据从解码后的缓冲区中写到次缓冲区中。
②使用IdirectSoundBuffer∷Unlock方法对缓冲区解锁。
③将声音数据送到主缓冲区中, 再从主缓冲区送到输出设备, 使用IdirectSoundBuffer∷Flag方法进
行播放。
6) 释放DirectSound。当所有的声音数据回放完毕后, 调用Release释放所有已获得的DirectSound
DirectSound对象的建立
(1)建立DirectSound对象
(2)设定共享层级
(3)设定主缓冲区的格式
首先要建立一个代表声卡的DirectSound对象,我们先定义LPDIRECTSOUND pDS,然后用DirectSoundCreate(NULL, &pDS, NULL);方法来建立它,如果你想察看这个对象是否成功的建立,可以定义一个HRESULT result变量,由它接受DirectSoundCreate方法的返回值得并判断是否等于DS_OK,如果不等于它,则可以用MessageBox()方式弹出对话框来告诉程序员建立失败。注意DirectSoundCreate()中的第一个参数,是NULL,表示使用目前预设的声卡。也可以调用DirectSoundEnumerate取得可用的声卡。
然后要设定程序协调层级,使用pDS调用SetCooperativeLevel方法来实现,注意这个方法有两个参数,第一个参数代表应用程序的主窗口,而第二个参数则设定使用资源的优先权。
最后要看看缓冲区的概念,主缓冲区可以看作一个DirectSound是用来播放声音,产生混音效果的区域,它可以自动生成,也可以自己建立,但如果自己建立并设定其播放模式,在设置协调层级时,标志位必须设定为DSSCL_PRIORITY,次缓冲区则存储播放声音的文件。在加载声音文件后,只要调用Play()方法,声音就会自动的送入主缓冲区中并进行播放。
在初始化过程中,应重点注意DSBUFFERDESC结构,它担负着区分主次缓冲区以及缓冲区明细初始化的重任,在使用它时,首先要清空,可以使用memset()方法来将其所有内存中的位设为0,同时要设置结构的大小,并确定它的标志位,以及设置缓冲区大小与格式,其具体的初始化过程可以看文章结尾的例子。
在完成了初始化工作后,应该先把需要播放的声音文件载入到已经完成初始化的次缓冲区中。这里重点讲下如何读入一个声音文件以及取得其中的信息与播放的资料。