1DirectSound简介(Introduction to DirectSound)
曾经学习过Directshow的开发,对于Dsound一直没有仔细的莱学习,以前只是知道Dsound是做音频开发的,我一直以为它和Dshow的结构体系差不多,经过仔细学习后,发现,其实他们完全
闲话少说,下面我们看看DirectSound到底能帮我们做些什么。
1播放WAVE格式的音频文件或者资源。
2可以同时播放多个音频。
3Assign high-priority sounds to hardware-controlled buffers
4播放3D立体声音
5在声音中添加特技效果,比如回声,动态的改变特技的参数等
6将麦克风或者其他音频输入设备的声音录制成wave格式的文件
呵呵,DirectSound就能做这么多事情,读到这里,我都有点怀疑DirectSound是不是就是封装了mmio系列和wav系列的函数。因为这些底层的API也能够完成这些事情。
2DirectSound初体验(Getting Started with DirectSound)
在开始本节内容前,我会首先提醒一下,如果你想用Directsound开发,那么你首先要包含Dsound.h头文件,其实我可以实话告诉你,你仅仅包含dsound.h你的工程肯定调补通,其实下面的一些头文件也要包含,我第一次就搞了半天才搞好,
#include <windows.h>
#include <mmsystem.h>
#include <mmreg.h>
#include <dsound.h>
如果你还想使用Dsound的API的话,那么你就要在你的vc开发环境中添加Dsound..lib库,
如果你的程序还提示有很多的外部链接找不到,那么我建议你可以将下面的库都添加到你的工程中comctl32.lib dxerr9.lib winmm.lib dsound.lib dxguid.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib,这些是我从Dsound提供的例子中得到的,肯定够你用的,ok,开发环境配置好了。
下面我们简单的来学习一下如果通过Directsound的API播放声音,既然是breif overview,那么详细的内容你可以参考下面的一节内容,这里只是简单的介绍一下播放声音的步骤。
第一步,创建一个设备对象。
在你的代码中你可以通过调用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,那么你就可以同时来播放这些数据,这些声音会自动进行混音的。
呵呵,简单介绍到这里的,如果想深入了解,请继续参考下一部分。
3DirectSound实用开发技巧Using DirectSound
在进行这部分之前,我们首先学习一下Directsound中常用的几个对象,简单学习一下哦
对象 |
数量 |
作用 |
主要接口 |
设备 |
每个应用程序只有一个设备对象 |
用来管理设备,创建辅助缓冲区 |
|
辅助缓冲区 |
每一个声音对应一个辅助缓冲区 |
用来管理一个静态的或者动态的声音流,然后在主缓冲区中混音 |
IDirectSoundBuffer8, IDirectSound3DBuffer8, IDirectSoundNotify8 |
主缓冲区 |
一个应用程序只有一个主缓冲区 |
将辅助缓冲区的数据进行混音,并且控制3D参数. |
IDirectSoundBuffer, IDirectSound3DListener8 |
特技 |
没有,或者 |
来辅助缓冲的声音数据进行处理 |
Interface for the particular effect, such as IDirectSoundFXChorus8 |
首先,要创建一个设备对象,然后通过设备对象创建buffer对象。辅助缓冲区由应用程序创建和管理,DirectSound会自动地创建和管理主缓冲区,一般来说,应用程序即使没有获取这个对象的接口也可以播放音频数据,但是,如果应用程序要想得到IDirectSound3DListener8接口,就必须要自己创建一个主缓冲区。
我们可以将短小的声音文件全部读取到辅助缓冲区中,然后通过一个简单的命令来播放。如果声音文件很长,就必须采用数据流了。
3.1Dsound设备对象(DirectSound Devices)
本节主要讲述下面的几个内容
1如何枚举系统输出声音的设备
2创建设备对象
3设置声音设备的协作度
–
下面首先看看如何枚举系统中的声音输出设备
1如何枚举系统输出声音的设备
如果你的应用程序使用用户首选的输出设备来输出声音,那么你就没有必要来枚举所有的输出设备,如果你通过DirectSoundCreat8函数来创建一个设备对象的同时,就给这个对象指定了一个缺省的设备,当然如果遇到下面的一些情形,你就要来枚举设备对象
例如,你的应用程序并不支持所有的输出设备,或者你的应用程需要两个或者多个设备,或者你希望用户自己来选择输出设备。
枚举设备,你首先要定义一个回调函数,这个回调函数可以被系统中的每个设备来调用,你可以在各函数做任何事情,这个函数的命名也没有任何的限制,但是函数应该以DSEnumCallback为原型,如果枚举没有结束,这个回调函数就返回TRUE,如果枚举结束,例如你找到合适的设备,这个函数就要返回FALSE。
下面是回调函数的一个例子,这个函数将枚举的每一个设备都添加到一个combox中,将设备的GUID保存到一个item 中,这个函数的前三个参数由设备的驱动程序提供,第四个参数有DirectSoundEnumerate函数提供,这个参数可以是任意的32位值,这个例子里是combox的句柄,
BOOL CALLBACK DSEnumProc(LPGUID lpGUID,
LPCTSTR lpszDesc,
LPCTSTR lpszDrvName,
LPVOID lpContext )
{
HWND hCombo = (HWND)lpContext;
LPGUID lpTemp = NULL;
if (lpGUID != NULL) // NULL only for "Primary Sound Driver".
{
if ((lpTemp = (LPGUID)malloc(sizeof(GUID))) == NULL)
{
return(TRUE);
}
memcpy(lpTemp, lpGUID, sizeof(GUID));
}
ComboBox_AddString(hCombo, lpszDesc);
ComboBox_SetItemData(hCombo,
ComboBox_FindString(hCombo, 0, lpszDesc),
lpTemp );
free(lpTemp);
return(TRUE);
}
枚举设备通常都是在对话框初始化的时候才进行的,我们假设hCombo就是combox句柄,hDlg就对话框的句柄,看看我们怎么来枚举设备的吧
if (FAILED(DirectSoundEnumerate((LPDSENUMCALLBACK)DSEnumProc,
(VOID*)&hCombo)))
{
EndDialog(hDlg, TRUE);
return(TRUE);
}
在这个例子中,combox的句柄作为参数传递到DirectSoundEnumerate函数中,然后又被传递到回调函数中,这个参数你可以是你想传递的任意的32位值。
注:第一个被枚举的设备通常称为Primary sound driver,并且回调函数的lpGUID为NULL,这个设备就是用户通过控制面板设置的缺省的输出声音设备,
2创建设备对象
创建设备对象最简单的方法就是通过DirectSoundCreate8函数,这个函数的第一个参数指定了和这个对象邦定的设备的GUID,你可以通过枚举设备来获取这个设备的GUID,你可以传递一个下面的参数来指定一个缺省的设备
DSDEVID_DefaultPlayback 缺省的系统的声音输出设备,这个参数也可以为NULL
SDEVID_DefaultVoicePlayback,缺省的声音输出设备,通常指第二缺省设备,例如USB耳机麦克风
如果没有声音输出设备,这个函数就返回error,或者,在VXD驱动程序下,如果声音输出设备正被某个应用程序通过waveform格式的api函数所控制,该函数也返回error,
下面是创建对象的代码,及其简单
LPDIRECTSOUND8 lpds;
HRESULT hr = DirectSoundCreate8(NULL, &lpds, NULL));
如果你想通过表准的COM调用来创建设备对象,下面我就给出代码,你可以比较一下
HRESULT hr = CoInitializeEx(NULL, 0);
if (FAILED(hr))
{
ErrorHandler(hr); // Add error-handling here.
}
LPDIRECTSOUND8 lpds;
hr = CoCreateInstance(&CLSID_DirectSound8,
NULL,
CLSCTX_INPROC_SERVER,
IID_IDirectSound8,
(LPVOID*) &lpds);
if (FAILED(hr))
{
ErrorHandler(hr); // Add error-handling here.
}
hr = lpds->Initialize(NULL);
if (FAILED(hr))
{
ErrorHandler(hr); // Add error-handling here.
}
CoUninitialize();
3设置声音设备的协作度
因为Windows是一个多任务操作环境,在同一个时刻有可能多个应用程序共用同一个设备,通过协作水平,DirectX就可以保证这些应用程序在访问设备的时候不会冲突,每个Directsound应用程序都有一个协作度,用来确定来接近设备的程度,
当你创建完设备对象后,一定要调用IDirectSound8::SetCooperativeLevel来设置协作度,否则,你不会听到声音的,
HRESULT hr = lpDirectSound->SetCooperativeLevel(hwnd, DSSCL_PRIORITY);
if (FAILED(hr))
{
ErrorHandler(hr); // Add error-handling here.
}
Hwnd参数代表了应用程序的窗口。
DirectSound定义了三种水平,DSSCL_NORMAL, DSSCL_PRIORITY, and DSSCL_WRITEPRIMARY
在NORMAL水平的协作度下,应用程序不能设置主缓冲区的格式,也不能对主缓冲区进行写操作,所有在这个层次的应用程序使用的媒体格式都是22kHZ,立体声,采样精度8位,所以,设备可以在各个应用程序中任意的切换。
在Priority层次的协作度下,应用程序可以有优先权使用硬件资源,比如使用硬件进行混音,当然也可以设置主缓冲区的媒体格式,游戏程序应该采用这个层次的协作度,这个层次的协作度在允许应用程序控制采用频率和位深度的同时,也给应用程序很大的权力,这个层次的协作度允许其他应用程序的声音和游戏的音频同时被听到,不影响。
最高层次的协作度就是Write_primary,当采用这个层次的协作度来使用一个dsound设备时,在非WDM模式驱动下,你的应用程序可以直接操作主缓冲区,在这个模式下,应有程序必须直接的操作主缓冲区。如果你想直接把音频数据直接写入到主缓冲区,你就要将你的协作度模式设置为writeprimary,如果你的应用程序没有设置到这个层次,那么所有的对主缓冲区的IDirectSoundBuffer::Lock都会失败。
当你的应用程序的协作度设置为wirte_primary水平时,并且你的应用程序gains the foreground,那么其他应用程序的辅助缓冲区就会停止,并且标示为lost,当你的应用程序转到background,那么它的主缓冲区就会标示为lost,当它再次回到foreground的时候会恢复到原来的状态,如果你的设备没有出现在你的系统中,那么你就没法设置wirte primary协作度了,所以,在设置协作度之前,可以通过IDirectSound8::GetCaps方法来查询一下设备是否可用。
4设置声音设备(扬声器)
在windowos98或者2000系统中,用户可以通过控制面板来设置扬声器的属性,应用程序可以通过IDirectSound8::GetSpeakerConfig.函数来获取这些属性,我们不建议应用程序通过IDirectSound8::SetSpeakerConfig函授来设置扬声器的属性,因为这些设置的改动会影响到其他用户或者应用程序。
5查询输出设备的性能
你的应用程序可以通过Directsound来检查声音设备的性能,然而许多应用程序并不需要这么做,因为Directsound会自动地利用硬件提供的较好的性能,并不需要你手动地来选择。
但是,However, high-performance applications can use the information to scale their sound requirements to the available hardware. For example, an application might choose to play more sounds if hardware mixing is available than if it is not.
当你调用DirectSoundCreate8创建一个设备对象后,你的应用程序就可以通过IDirectSound8::GetCaps函数来获取设备的性能属性,下面的代码演示了这个过程
DSCAPS dscaps;
dscaps.dwSize = sizeof(DSCAPS);
HRESULT hr = lpDirectSound->GetCaps(&dscaps);
if (FAILED(hr))
{
ErrorHandler(hr); // Add error-handling here.
}
通过 DSCAPS结构,该函数就给我返回硬件设备的一些性能,但是在调用这个函数之前一定要初始化这个结构的dwSize成员。
如果你的应用程序能够操作硬件设备,比如你的协作度是Write_primory级别的,那么你在硬件设备上分配内存的时候,就要调用IDirectSoudn8::GetCaps来查看一下硬件的资源是否满足你的要求。