函数DirectSoundCreate8后面的数字其实是该directx版本的最新支持的创建函数(这里我用的是directx 9.0 的SDK),
以后见到的数字基本都是关于版本问题.上面函数的lpcGuidDevice是声卡设备的全局标志,一般为NULL,表示默认声卡,以后见到变量类型为LPUNKNOWN的,都应肯定该参数是没用的,有待以后的开发使用,我们应该把该参数设置为NULL,第二个参数就是directsound的接口对象.
当创建好direcrtsound接口对象时,必须为该对象设置协调级别便于声卡设备与其他WINDOWS程序共享设备.因此,必须调用SetCooperativeLevel(HWND hwnd,DWORD dwLevel); hwnd就是我们当前的应该程序的窗口句柄,参数dwLevel是协调级别,有以下几种取值:1.DSSCL_EXCLUSIVE也就是当前程序独占设备,在directx 8.0以上版本它的作用可以用另外一个取值代替.2.DSSCL_PRIORITY具有优先设置DirectSound设备的权限.在此标志下,directsound必然是独占的.(可调用SetFormat设置主次缓冲区的播放格式).3.DSSCL_NORMAL正常协调级别,其他程序可共享声卡设备进行播放.此标志下,不允许修改主缓冲区的格式,而且会默认在声卡内存中申请到一个8bit和22KHz的单声道主缓冲区.4.DSSCL_WRITEPRIMARY可写主缓冲区,此时主缓冲区不可播放处理,就是说不能把数据放进混声器中,再传到主缓冲区.所有的处理只能在主缓冲区中进行.(可以完全控制声音的播放).
主缓冲区的创建:
这里我们需要用到CreateSoundBuffer(LPCDSBUFFERDESC pcDSBufferDesc,LPDIRECTSOUNDBUFFER *ppDSBuffer,LPUNKNOWN pUnKOuter);同样的道理pUnkOuter为NULL,ppDSBuffer缓冲区指针,pcDSBufferDesc缓冲区描述结构.
DSBUFFERDESC 结构体定义如下:
struct {
DWORD dwSize;//结构体大小
DWORD dwFlags;//缓冲区的用途标志
DWORD dwBufferBytes;//缓冲区大小(如果是设置主缓冲区必须设置为0)
DWORD dwReserved;//保留未用,必须为0
LPWAVEFORMATEX lpwfxFormat;//声音的格式属性
GUID guid3DAlgorithm;//3D声效算法,一般不必理会
};
dwflag 参数的标志说明:
DSBCAPS_CTRL3D //缓冲区具有的3D音效控制能力,不能与DSBCAPS_CTRLPAN一起使用
DSBCAPS_CTRLFREQUENCY //可设置采样频率
DSBCAPS_CTRLFX //缓冲区支持特效处理,但缓冲区必须够大,可容纳更多的数据
DSBCAPS_CTRLPAN //缓冲区可以控制声道
DSBCAPS_CTRLPOSITIONNOTIFY //缓冲区具有播放位置通知能力
DSBCAPS_CTRLVOLUME //缓冲区可设置音量大小
DSBCAPS_GLOBALFOCUS //缓冲是一个全局声音资源,当前程序切换到其他程序依然可以继续播放.
DSBCAPS_LOCDEFER //缓冲区可绑定硬件内存或者软件内存来播放声音
DSBCAPS_LOCHARDWARE //缓冲区必须使用硬件的混声器,如果不支持硬件内存或者混声器,都会导致创建缓冲区失败.
DSBCAPS_LOCSOFTWARE //缓冲区使用软件内存或者使用软件混音
DSBCAPS_MUTE3DATMAXDISTANCE //超过声音可听的最大距离,将停止播放声音
DSBCAPS_PRIMARYBUFFER //说明缓冲区的为主缓冲区(如果没说明,则用作次缓冲区)
DSBCAPS_STATIC //自动使用硬件内存做缓冲区
DSBCAPS_STICKYFOCUS //当程序切换到其他不使用DIRECTSOUND 的程序时,缓冲区继续播放声音,但无法如常进行其他处理
如果要设置声音的格式,就必须先了解WAVEFORMATEX这个结构体
struct WAVEFORMATEX
{
WORD wFormatTag; //指明压缩算法 如.wav文件为WAVE_FORMAT_PCM
WORD nChannels; //声道数量(一般1~2个)
DWORD nSamplesPerSec; //每秒采样的次数
DWORD nAvgBytesPerSec; //每秒采样的总字节数=nSamplesPerSec*nBlockAlign
WORD nBlockAlign; //一次采样的字节数=(nChannels*wBitsPerSamples)/8
WORD wBitsPerSample; //8bit 或16bit,即一个模拟信号的大小所分配的bit位数
WORD cbSize; //附加的数据大小,对于WAVE_FORMAT_PCM类型的数据,设为0
};
在创建主缓冲时,我们在DSBUFFERDESC结构体中的dwFlag标志中只须要说明为DSBCAPS_PRIMARYBUFFER
dwBufferBytes必须为0;lpwfxFormat必须为NULL.
如果要改变主缓冲区的格式,必须在创建默认格式下的主缓冲区后,调用SetFormat,然后传入刚才了解的WAVEFORMATEX的结构体.
SetFormat的原型是:SetFormat(LPCWAVEFORMATEX pcfxFormat);
创建次缓冲区
首先,我们先了解.wav的文件储存格式,它的文件头包括3块:RIFF.fmt.data;RIFF块总共占12字节,4字节为名称"RIFF",4字节为RIFF的数据的大小4,4字节为RIFF的数据"WAVE";fmt块总共占24字节,分别有4字节名称"fmt ",4字节的fmt数据大小为16,fmt的数据又分为2字节的格式标志,2字节的声道数,4字节的采样频率,4字节的每秒采样字节数,2字节的每次采样的字节数,2字节采样的位数;data块名称占4字节,实际声音数据的大小数占4字节,剩下的就是实际的声音数据.
当我们创建了次缓冲区后,我们可以用这样的一个结构体
struct WaveHeader
{
char RiffID[4];
long WaveformChunkSize;
char WaveID[4];
char ForamtID[4];
long FormatChunkSize;
short FormatTag;
short Channels;
long SampleRate;
long BytesPerSec;
short BlockAlign;
short BitsPerSample;
char DataID[4];
long DataSize;
};
利用这样的一个结构体,我们可以从一个音频文件获取音频文件的相关信息,并且可以利用它获取音频文件的实际的声音数据.并将数据读进次缓冲区中.
下面为利用上面的结构体获取文件信息并创建对应的次缓冲区:
FILE *fp
if((fp=fopen("sweet.wav","rb"))==NULL)
return false;
WaveHeader * pWaveHeader=new WaveHeader();
fseek(fp,0,SEEK_SET);
fread(pWaveHeader,1,sizeof(WaveHeader),fp);
if(memcmp(pWaveHeader->RiffID,"RIFF",4)||memcmp(pWaveHeader->WaveID,"WAVE",4)||memcmp(pWaveHeader->FormatID,"fmt ",4)||memcmp(pWaveHeader->DataID,"data",4))
return false;
WAVEFORMATEX pWaveFormat;
ZeroMemory(&pWaveFormat,sizeof(WAVEFORMATEX));
pWaveFormat.wFormatTag=pWaveHeader->FormatTag;
pWaveFormat.nChannels=pWaveHeader->Channels;
pWaveFormat.nSamplePerSec=pWaveHeader->SampleRate;
pWaveFormat.wbitsPerSample=pWaveHeader->BitsPerSample;
pWaveFormat.nBlockAlign=(pWaveFormat.wBitsPerSample/8)*pWaveFormat.nChannels;
pWaveFormat.nAvgBytesPerSec=pWaveFormat.nSamplePerSec*pWaveFormat.nBlockAlign;
DSBUFFERDESC pBufferDesc;
ZeroMemory(&pBufferDesc,sizeof(DSBUFFERDESC));
pBufferDesc.dwsize=sizeof(DSBUFFERDESC);
pBufferDesc.dwFlags=DSBCAPS_CTRLVOLUME|DSBCAPS_GLOBALFOCUS|DSBCAPS_STATIC;
pBufferDesc.dwBufferBytes=pWaveHeader->DataSize;
pBufferDesc.lpwfxFormat=&pWaveFormat;
//m_pDirectSound为已经创建的directsound的接口对象
//pDirectSoundBuffer为次缓冲区的接口指针
//pDirectSoundBuf8为最新版本次缓冲区的接口指针(可以认为原接口指针需要更新为最新版本才能使用)
if(FAILED(m_pDirectSound->CreateSoundBuffer(&pBufferDesc,&pDirectSoundBuffer,NULL)))
return E_FAIL;
if(FAILED(pDirectSoundBuffer->QueryInterface(IID_IDirectSoundBuffer8,(void **)&pDirectSoundBuf8)))
{
pDirectSoundBuffer->Release();
return E_FAIL;
}
接下来就是将声音数据装入次缓冲区:
我们要将数据传入次缓冲区,那么必须用到两个函数LOCK()与UNLOCK();
首先看看LOCK函数的原型:
HRESULT LOCK(DWORD dwoffset, //锁定缓冲区的起此位置
DWORD dwBytes, //锁定的字节数
LPVOID * ppvAudioPtr1,//第一位置指针
LPDWORD pdwAudioBytes1,//第一缓冲区的大小
LPVOID * ppvAudioPtr2,//第二位置指针(本人认为这样应该是提高了数据存取的速度)
LPDWORD pdwAudioBytes2,//第二缓冲区的大小
DWORD dwFlags //锁定标志,默认为DSBLOCK_FROMWRITECURSOR=0
);
Unlock(LPVOID pvAudioPtr1,DWORD dwAudioBytes1,LPVOID pvAudioPtr2,DWORD dwAudioBytes2);
接着,把刚才得到的sweet.wav的数据放入次缓冲中.
BYTE * pSoundPtr1=NULL;
BYTE * pSoundPtr2=NULL;
DWORD dwSoundSize1,dwSoundSize2;
if(FAILED(pDirectSoundBuf8->Lock(0,pWaveHeader->DataSize,(void **)&pSoundPtr1,&dwSoundSize1,(void **)&pSoundPtr2,&dwSoundSize2,0)))
return E_FAIL;
fread(pSoundPtr1,1,dwSoundSize1,fp);
if(pSoundPtr2 !=NULL)
fread(pSoundPtr2,1,dwSoundSize2,fp);
//写入完成后解锁
pDirectSoundBuf8->Unlock(pSoundPtr1,dwSoundSize1,pSoundPtr2,dwSoundSize2);
最后就是声音的播放和控制
声音的播放和控制其实很简单,播放只需要调用idrectsoundbuffer8的Play函数,其实这个函数是将次缓冲区的声音数据送到混声器中,与其他声音进行混合,最后输到主缓冲区自动播放.
Play函数原型:Play(DWORD dwReserved1,//保留
DWORD dwPriority,//优先权, 未使用DSBCAPS_LOCDEFER 必须设置为0
DWORD dwFlags//播放方式 可以使用true or false 来控制是否使用循环播放还是播放一次 也可以使用 DSBPLAY_LOOPING,DSBPLAY_LOCHARDWARE(只能通过硬件中的缓冲区播放),DSBPLAY_LOCSOFTWARE(只能通过软件中的缓冲区进行播放).
)
如果要控制播放位置,可以调用directsoundbuffer8的SetCurrentPosition函数.
SetCurrentPosition函数原型:SetCurrentPosition(DWORD dwNewPosition //便宜位置);
如果要停止只需要调用directsoundbuffer8的stop函数,该函数没参数.
如果要设置声音音量,可以调用directsoundbuffer8的SetVolume函数.
函数原型:SetVolume(LONG lVolume//音量值);最大值DSBVOLUME_MAX=0;DSBVOLUME_MIN=-10000;
欢迎交流心得~~