近日正在学习DirectX,主要用于视频监控和流媒体方面应用,学习《Visual C++音视频处理技术和工程实践》已经有大半,一直想写些感受,兴趣所致,今天重新学习了下DirectSound 3D语音特效,并编写一些代码,在此分享出来,希望对大家能有所帮助。
大家都有这种感觉,当我们离发声源越来越远的时候,声音越来越小。DirectSound就是模仿这些现象,从数学理论角度加以描述。当然,影响音效的因素不光只有这些,还有如Doppler效应等,但是DirectSound就是这么神奇,它能模仿大多数的声音特效,这就是神奇的3D语音特效,在游戏等方面有着广泛的应用。
对于DirectSound中的3D声效的播放,在操作之前需要将创建的主缓冲区设置为DSBCAPS_CTRL3D标志,并由主缓冲区创建一个IDirect3DListener8听者对象,然后调用IDirect3DListener8的接口函数设置听着的位置和方向,从而达到3D语音的效果,接下来介绍下整个特效的处理过程。
创建DSound对象,并初始化主缓冲区
在DirectSound中对音频的操作之前都需要创建一个Dsound对象,该对象贯穿这个程序始终,一般创建之后立马创建音频主缓冲区,该对象的创建函数是DirectSoundCreate8,返回一个LPDIRECTSOUND8对象。这部分代码具体如下;
//创建Dsound对象
HRESULT hr;
if(FAILED(hr = DirectSoundCreate8(NULL,&g_pDsd,NULL)))
return FALSE;
if(FAILED(hr = g_pDsd->SetCooperativeLevel(m_hWnd,DSSCL_PRIORITY)))
return FALSE;
//初始化Directsound 的主缓冲区,并设置格式
LPDIRECTSOUNDBUFFER pDSBPrimary = NULL;
DSBUFFERDESC dsbdesc ;
ZeroMemory(&dsbdesc,sizeof(DSBUFFERDESC));
dsbdesc.dwSize = sizeof(DSBUFFERDESC);
dsbdesc.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER;
if(FAILED(hr = g_pDsd->CreateSoundBuffer(&dsbdesc,&pDSBPrimary ,NULL)))
return FALSE;
获取3D缓冲区对象
IDirectSound3DBuffer8接口通常从DirectSound的次缓冲区获取,次缓冲区获取通过DSBUFFERDESC结构的dwFlags成员设置DSBCAPS_CTRL3D标志创建,具体代码如下;
DSBUFFERDESC dsbd;
ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
dsbd.dwSize= sizeof(DSBUFFERDESC);
// 设置DSBCAPS_CTRL3D标志
dsbd.dwFlags= DSBCAPS_CTRL3D| DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY |DSBCAPS_GETCURRENTPOSITION2;
//dsbd.dwBufferBytes =MAX_AUDIO_BUF * BUFFERNOTIFYSIZE ;//如果采用流buffer,请将此句放开
dsbd.dwBufferBytes =g_pWaveFile->GetSize(); // 如果采用流buffer,请屏蔽掉此句
dsbd.guid3DAlgorithm = guid3DAlgorithm;
dsbd.lpwfxFormat = g_pWaveFile->m_pwfx;
// 创建次缓冲区
if(FAILED(hr = g_pDsd->CreateSoundBuffer(&dsbd,&g_pDSBuffer,NULL)))
return ;
for(int i =0; i< MAX_AUDIO_BUF;i++)
{
g_aPosNotify[i].dwOffset = i* BUFFERNOTIFYSIZE ;
g_aPosNotify[i].hEventNotify = g_event[i];
}
//获取IDirectSound3DBuffer接口
if(FAILED(hr = g_pDSBuffer->QueryInterface(IID_IDirectSound3DBuffer, (VOID**)&g_pDS3DBuffer )))
........
// 设置3D缓冲区参数
g_dsBufferParams.dwMode = DS3DMODE_HEADRELATIVE;
g_pDS3DBuffer->SetAllParameters( &g_dsBufferParams, DS3D_IMMEDIATE );
return ;
获取听者对象(IDirectSound3DListener8)
获取IDirectSound3DListener8对象其实很简单,首先声明一个LPDIRECTSOUND3DLISTENER对象,然后调用QueryInterface即可。获取的IDirectSound3DListener8后,需要对特效参数进行设置,例如通过SetPosition设置听者的位置,具体的API参照MSDN即可,这部分代码如下;
LPDIRECTSOUND3DLISTENER g_pDSListener = NULL; // 3D 听者之中呢
//获取听者对象
if( FAILED( hr = pDSBPrimary->QueryInterface( IID_IDirectSound3DListener, (VOID**)&g_pDSListener ) ) )
return FALSE;
......
if(FAILED(hr = g_pDSBuffer->QueryInterface(IID_IDirectSoundNotify,(LPVOID *) &g_pDSNotify )))
return ;
g_pDSNotify->SetNotificationPositions(MAX_AUDIO_BUF,g_aPosNotify);
g_pDSNotify->Release();
FLOAT fDopplerFactor;
FLOAT fRolloffFactor;
FLOAT fMinDistance;
FLOAT fMaxDistance;
......
g_dsListenerParams.flDopplerFactor = fDopplerFactor;
g_dsListenerParams.flRolloffFactor = fRolloffFactor;
if( g_pDSListener )
{
// 设置全特性
g_pDSListener->SetAllParameters( &g_dsListenerParams, DS3D_DEFERRED );
// 提交延时
g_pDSListener->CommitDeferredSettings();
}
读取音频数据,播放
读取WAV音频数据,我们可以直接采用Cwavefile类实现,该类是由微软封装的,专门用于操作wav文件。音频数据的播放直接调用Dsound中的Play函数即可,
//打开wav文件
g_pWaveFile = new CWaveFile;
g_pWaveFile->Open(strFileName,NULL,WAVEFILE_READ);
WAVEFORMATEX* pwfx = g_pWaveFile->GetFormat();
....
DWORD res;
LPVOID lplockbuf;
DWORD len;
DWORD dwWrite;
// 锁定内存
g_pDSBuffer->Lock(0,0,&lplockbuf,&len,NULL,NULL,DSBLOCK_ENTIREBUFFER);
// 读取数据
g_pWaveFile->Read((BYTE*)lplockbuf,len,&dwWrite);
// 释放到主缓冲区
g_pDSBuffer->Unlock(lplockbuf,len,NULL,0);
// 设置播放位置
g_pDSBuffer->SetCurrentPosition(0);
//播放
g_pDSBuffer->Play(0,0,DSBPLAY_LOOPING);
Close
if(g_pDSListener)
g_pDSListener->Release();
if(g_pDsd)
g_pDsd->Release();
通过上面的几步3D特效处理也并不是很难,进一步学习可以参考MSDN相关文档,www.codeproject.com相关内容也不错。