网站建设 安庆,网站上的幻灯片如何做,网页制作工具中可进行网页内容定位,设计类比赛网站4DirectSound开发高级技巧 4.1Dsound驱动模型#xff08;DirectSound Driver Models#xff09;
在VXD驱动模型下#xff0c;所有的DirectSound的混音工作都是由Dsound.vxd来完成的#xff0c;一个虚拟的设备驱动程序。Dsound.vxd也提供操作声卡从Cpu接收数据的缓冲区的…4DirectSound开发高级技巧 4.1Dsound驱动模型DirectSound Driver Models
在VXD驱动模型下所有的DirectSound的混音工作都是由Dsound.vxd来完成的一个虚拟的设备驱动程序。Dsound.vxd也提供操作声卡从Cpu接收数据的缓冲区的方法这其实和DirectSound的主缓冲区是类似的。DirectSound应用程序可以给主缓冲区设置特定的属性例如采样频率或者采样精度也就改变了硬件设备。
在WDM驱动模式下DirectSound并不直接操作硬件当然操作硬件加速缓冲区除外。相应的DirectSound将数据送往内核混音器内核混音器的工作就是将多个格式的音频流调整为一个统一的格式将它们进行混音然后将结果送到硬件上进行播放。其实它的工作和Dsound.vxd的工作类似。一个重要的区别在于Dsound.vxd仅仅对DirectSound的内存缓冲区进行混音内核混音器会对所有的windows音频数据进行混音包括那些使用waveOut win32函数输出数据的应用DirectSound和Wave格式的音频输出设备不能同时打开在WDM驱动模式下是不成立的。 最重要的是内核混音器和音频硬件的关系内核混音器就是一个系统中的软件可以用来指定硬件的DMA缓冲区。它根据它要进行混音的音频数据格式来选择硬件的格式它会将它混音的音频数据以一种高质量的形式进行输出或者根据硬件的情况选择近似的音频质量。
这里有一个很重要的暗示就是DirectSound不能设置硬件的DMA缓冲区格式。对于你的应用程序而言硬件格式其实就是根据你实际播放的音频的格式来定的。如果你play的是44kHZ内核混音器就会将所有的数据都混音成44kHZ同时也保证硬件以44Khz进行输出。
作为应用程序的开发者你没法来选择系统驱动模式因为驱动模式的选择有声卡类型windows版本以及安装的驱动程序来控制。由于这个原因你在测试你的程序时要注意所有的情况DirectSound或许用的是Dsound.vxd或者用的是内核混音器你要确保你的应用程序对两种方式都支持。 4.2设置硬件的扩展属性System Property Sets 4.3Property Sets for DirectSound Buffers DirectSound设备的属性可以通过下面的类对象来获取该类的ID为CLSID_DirectSoundPrivate (11AB3EC0-25EC-11d1-A4D8 -00C04FC28ACA). 该类支持IKsPropertySet接口CLSID和属性的定义在Dsconf.h文件中可以找到。
1要想创建该类的对象首先要通过LoadLibrary加载Dsound.dll
2调用GetProcAddress函数来获取DllGetClassObject函数接口。
3通过DllGetClassObject函数来获取IClassFactory接口CLSID_DirectSoudnPrivate类厂对象接口。
4调用IClassFactory::CreateInstance来创建CLSID_DirectSoundPrivate对象并获取IKsPropertySet接口IID_IKsPropertySet。
CLSID_DirectSoundPrivate对象只支持一个属性设置接口DSPROPSETID_DirectSoundDevice( 84624f82-25ec-11d1-a4d8 -00c04fc28aca)这个属性设置接口暴露了下面三个只读的属性
DSPROPERTY_DIRECTSOUNDDEVICE_WAVEDEVICEMAPPING
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION
DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE
DSPROPERTY_DIRECTSOUNDDEVICE_WAVEDEVICEMAPPING这个属性根据指定的设备的名称来获取该设备的GUID。 获取的属性数据包含在 DSPROPERTY_DIRECTSOUNDDEVICE_WAVEDEVICEMAPPING_DATA结构里这个结构包含下面的成员 Member Type Description DeviceName String [in] Name of device DataFlow DIRECTSOUNDDEVICE_DATAFLOW [in] Direction of data flow, either DIRECTSOUNDDEVICE_DATAFLOW_RENDER or DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE DeviceID GUID [out] DirectSound device ID DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION属性 该属性可以返回指定GUID设备的全部的属性描述可以通过下面的结构返回数据 DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA这个结构的成员如下 Member Type Description Type DIRECTSOUNDDEVICE_TYPE [out] Device type: DIRECTSOUNDDEVICE_TYPE_EMULATED, DIRECTSOUNDDEVICE_TYPE_VXD or DIRECTSOUNDDEVICE_TYPE_WDM DataFlow DIRECTSOUNDDEVICE_DATAFLOW [in, out] Direction of data flow, either DIRECTSOUNDDEVICE_DATAFLOW_RENDER or DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE DeviceID GUID [in] DirectSound device GUID, or NULL for the default device of the type specified by DataFlow Description String [out] Description of DirectSound device Module String [out] Module name of the DirectSound driver Interface String [out] PnP device interface name WaveDeviceID ULONG [out] Identifier of the corresponding Windows Multimedia device DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE属性 这个属性用来枚举所有的DirectSound的播放或者录音设备。可以通过 DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_DATA结构将属性值返回这个结构的成员如下 Member Type Description Callback LPFNDIRECTSOUNDDEVICEENUMERATECALLBACK [in] Application-defined callback function. When IKsPropertySet::Get is called, this function is called once for each device enumerated. It takes as parameters a DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA structure describing the enumerated device, and the value in Context. Context LPVOID [in] User-defined value to be passed to the callback function for each device enumerated. 4.4如何优化DirectsoundOptimizing DirectSound Performance
主要讲三个方面的内容如何用硬件进行混音动态声音管理有效地使用缓冲区 许多声卡都拥有自己的辅助缓冲区可以处理3-D特技和混音特技。这些缓冲区就像它们的名字一样其实是存在于系统的内存中而不是在声卡上但是这些缓冲区是由声卡来直接操作的所以比需要处理器来控制软件缓冲区速度要快许多。所以在硬件条件允许的条件下要多申请硬件的缓冲区特别是3-D缓冲区。 缺省的情况下DirectSound会优先申请硬件缓冲区但是能够申请多少硬件的缓冲区是由硬件设备的性能决定的。硬件在同一时刻能播放的声音数量越多可申请的硬件缓冲区的数量就越多当创建一个缓冲区时DirectSound就分配一个hardware voice缓冲区销毁时就释放hardware voice。如果一个应用程序创建了很多的缓冲区但是很多缓冲区是由软件来销毁的也就是说这些缓冲区是由CPU而不是声卡来控制和混音的。 注意DirectSound 声音管理器分配的是硬件混音资源并不是缓冲区在一个PCI板卡上缓冲区在分配前后都占用相同大小的内存不管是将该缓冲区分配给硬件还是软件混音器。 动态的声音管理通过在缓冲区播放才进行voice allocation获取提前结束那么权限比较低的音频数据播放释放他们的资源从而可以减轻硬件设备的压力。 当缓冲区正在play的时候为了延迟硬件资源用来分配给混音3-D特技可以在创建缓冲区对象时将DSBUFFERDESC结构的dwFlages设置为DSBCAPS_LOCDEFER标志传递给IDirectSound8::CreateSoundBuffer.函数当你对这样的缓冲区进行IDirectSoundBuffer8::Play or IDirectSoundBuffer8::AcquireResources操作时DrectSound会尽可能的在硬件上play。 当调用Play的时候你可以试图通过传递下面表格中的参数将其他正在使用的硬件 voice资源释放掉。从而供你的buffer使用 DSBPLAY_TERMINATEBY_TIME Select the buffer that has been playing longer than any other candidate buffers. DSBPLAY_TERMINATEBY_DISTANCE Select the 3-D candidate buffer farthest from the listener. DSBPLAY_TERMINATEBY_PRIORITY Select the buffer that has the lowest priority of candidate buffers, as set in the call to Play. If this is combined with one of the other two flags, the other flag is used only to resolve ties.
DirectSound会根据你设置的标志对所有正在play的buffer进行搜索如果找到符合条件的缓冲区DirectSound就会停掉该资源然后将该资源分配给你的心的缓冲区使用。如果没有找到符合条件的缓冲区那么你的新创建的缓冲区只好在软件缓冲区播放了当然如果你设置了DSBPLAY_LOCHARDWARE标志此时play调用就会失败。 下面我们看看如何更有效地使用缓冲区。 当使用流缓冲区的时候要限制通知的次数和数据读写的次数。不要创建有很多通知positions的缓冲区或者太小的缓冲区。流缓冲区在小于3个通知位置时工作的效率最高。当你改变一个辅助缓冲区的控制项时此时效率就会受影响所以要尽可能少的调用IDirectSoundBuffer8::SetVolume, IDirectSoundBuffer8::SetFrequency.
IDirectSoundBuffer8::SetPan, 函数例如你有个习惯总是喜欢在控制面板上来回的拖动左右声道的位置。 一定要记住3D缓冲区是需要占用的更多的CPU的。所以要注意下面的事情 1将经常播放的sounds放到硬件缓冲区中 2 Dont create 3-D buffers for sounds that wont benefit from the effect. 3通过IDirectSound3DBuffer8::SetMode函数设置DS3DMODE_DISABLE标志来停止对3d缓冲区的3d处理 4最好批量的参数进行调整。 4.5向主缓冲区写数据Writing to the Primary Buffer 当应用程序需要一些特殊的混音或者特技而辅助缓冲区不支持这些功能那么DirectSOund允许直接曹操主缓冲区 当你获得操作主缓冲区的权限时其他的DirectSound特性就变得不可用了辅助缓冲区没法混音硬件加速混音器也无法工作。 大多数的应用程序应该使用辅助缓冲区避免直接操作主缓冲区因为可以申请大块的辅助缓冲区可以提高足够长的写入时间从而避免了音频数据产生缝隙的危险。 只有当主缓冲区硬件时你才能操作它所以你可以通过IDirectSoundBuffer8::GetCaps函数来查询该函数的参数结构dwFlages成员设置为DSBCAPS_LOCHARDWARE如果你想锁定一个正在被软件枚举的主缓冲区会失败的。 你通过IDirectSound8::CreateSoundBuffer函数来创建主缓冲区只要设置DSBCAPS_PRIMARYBUFFER标志即可。同时要保证你的协作度为DSSCL_WRITEPRIMARY。
下面的代码演示了如何获取向主缓冲区写数据的权限
BOOL AppCreateWritePrimaryBuffer( LPDIRECTSOUND8 lpDirectSound, LPDIRECTSOUNDBUFFER *lplpDsb, LPDWORD lpdwBufferSize, HWND hwnd) { DSBUFFERDESC dsbdesc; DSBCAPS dsbcaps; HRESULT hr; WAVEFORMATEX wf; // Set up wave format structure. memset(wf, 0, sizeof(WAVEFORMATEX)); wf.wFormatTag WAVE_FORMAT_PCM; wf.nChannels 2; wf.nSamplesPerSec 22050; wf.nBlockAlign 4; wf.nAvgBytesPerSec wf.nSamplesPerSec * wf.nBlockAlign; wf.wBitsPerSample 16; // Set up DSBUFFERDESC structure. memset(dsbdesc, 0, sizeof(DSBUFFERDESC)); dsbdesc.dwSize sizeof(DSBUFFERDESC); dsbdesc.dwFlags DSBCAPS_PRIMARYBUFFER; // Buffer size is determined by sound hardware. dsbdesc.dwBufferBytes 0; dsbdesc.lpwfxFormat NULL; // Must be NULL for primary buffers. // Obtain write-primary cooperative level. hr lpDirectSound-SetCooperativeLevel(hwnd, DSSCL_WRITEPRIMARY); if SUCCEEDED(hr) { // Try to create buffer. hr lpDirectSound-CreateSoundBuffer(dsbdesc, lplpDsb, NULL); if SUCCEEDED(hr) { // Set primary buffer to desired format. hr (*lplpDsb)-SetFormat(wf); if SUCCEEDED(hr) { // If you want to know the buffer size, call GetCaps. dsbcaps.dwSize sizeof(DSBCAPS); (*lplpDsb)-GetCaps(dsbcaps); *lpdwBufferSize dsbcaps.dwBufferBytes; return TRUE; } } } // Failure. *lplpDsb NULL; *lpdwBufferSize 0; return FALSE; } 下面的代码演示了应用程序如何混音的其中CustomMixer函数是用来将几个音频流混音的函数 。AppMixIntoPrimaryBuffer 应该定时的被调用。 BOOL AppMixIntoPrimaryBuffer( APPSTREAMINFO* lpAppStreamInfo, LPDIRECTSOUNDBUFFER lpDsbPrimary, DWORD dwDataBytes, DWORD dwOldPos, LPDWORD lpdwNewPos) { LPVOID lpvPtr1; DWORD dwBytes1; LPVOID lpvPtr2; DWORD dwBytes2; HRESULT hr; // Obtain write pointer. hr lpDsbPrimary-Lock(dwOldPos, dwDataBytes, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2, 0); // If DSERR_BUFFERLOST is returned, restore and retry lock. if (DSERR_BUFFERLOST hr) { lpDsbPrimary-Restore(); hr lpDsbPrimary-Lock(dwOldPos, dwDataBytes, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2, 0); } if SUCCEEDED(hr) { // Mix data into the returned pointers. CustomMixer(lpAppStreamInfo, lpvPtr1, dwBytes1); *lpdwNewPos dwOldPos dwBytes1; if (NULL ! lpvPtr2) { CustomMixer(lpAppStreamInfo, lpvPtr2, dwBytes2); *lpdwNewPos dwBytes2; // Because it wrapped around. } // Release the data back to DirectSound. hr lpDsbPrimary-Unlock(lpvPtr1, dwBytes1, lpvPtr2, dwBytes2); if SUCCEEDED(hr) { return TRUE; } } // Lock or Unlock failed. return FALSE; }