分享
 
 
 

Directsound开发指南(4)

王朝other·作者佚名  2006-02-01
窄屏简体版  字體: |||超大  

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如何优化Directsound(Optimizing 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 Don't create 3-D buffers for sounds that won't 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;

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有