Directsound给我们提供了听者和声源对象的接口,我们可以通过上面提到的三种方式设置改变声源或者听者的位置,运动速度和方向就可以形成3D音效了,
在3D环境中,我们通过IDirectSound3DBuffer8接口来表述声源,这个接口只有创建时设置DSBCAPS_CTRL3D标志的Directsound buffer才支持这个接口,这个接口提供的一些函数用来设置和获取声源的一些属性。在一个虚拟的3D环境中,我们可以通过主缓冲区来获取IDirectSound3DListener8接口,通过这个接口我们可以控制着声学环境中的多数参数,比如多普勒变换的数量,音量衰减的比率。
接口很简单,但是大量的计算工作Directsound都在内部帮助我们完成了。
最大最小距离
当听者越接近声源,那么听到的声音就越大,距离减少一半,音量会增加一倍。但是,当你继续接近到声源,当距离缩短到一定距离后,音量就不会持续增加。这就是声源的最小距离。
声源的最小距离,就是声音的音量开始随着距离大幅度衰减的起始点。例如,对于飞机,这个最小距离也许是100m,但是对于蜜蜂,这个最小距离是2 cm,根据这个最小距离,当听者距离飞机400m时,声音的音量就要衰减一半,对于蜜蜂来说,当超过4cm的时候,音量衰减一半。下面这个图表现了,最小和最大距离对飞机和蜜蜂音量的影响。
Directsound的缺省的最小距离DS3D_DEFAULTMINDISTANCE 定义为1个单位,或者是1米。我们规定,声音在1米处的音量是full volume,在2米处衰减一半,4米处衰减为1/4,一次类推。对于大多数声音来说,我们要设置一个比较大的最小距离,这样,当声音运动的时候,不至于衰减的这么快。
最大距离,就是就是声源的音量不再衰减的距离,我们称为声源的最大距离。对于Directsound 3D buffer缺省的最大的距离DS3D_DEFAULTMAXDISTANCE 是1 billion。 也就是说,当声音超出我们的听觉范围以外的时候,衰减还是在继续。在VXD驱动下,为了避免不必要的计算处理,我们在创建buffer的时候就要设置一个合理的最大距离。
最大距离同时也用来避免某种声音听不到。例如,如果你将某种声音的最小距离设置为100m,那么声音可能在1000m的处衰减的可能就听不到了,你可以将最大的距离设置为800m,这样你就可以保证声音在无论多远处都为原音量的1/10。
记得,缺省的单位是m。
LPDIRECTSOUND8 g_pDsd = NULL; //Directsound 对象指针 LPDIRECTSOUNDBUFFER g_pDSBuffer = NULL; //辅助缓冲区指针 LPDIRECTSOUND3DBUFFER g_pDS3DBuffer = NULL; // 3D 声源对象指针 LPDIRECTSOUND3DLISTENER g_pDSListener = NULL; // 3D 听者对象指针 DS3DBUFFER g_dsBufferParams; // 3D buffer properties DS3DLISTENER g_dsListenerParams; // Listener properties CWaveFile *g_pWaveFile= NULL; //读写wave文件用到的一个类对象 BOOL g_bPlaying = FALSE; //是否正在播放 |
//初始化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;//一定不要忘记创建主缓冲区的时候要设置DSBCAPS_CTRL3D标志。只有指定了这个标志,后面你才能从这个辅助缓冲区中请求到3D的buffer指针。 if(FAILED(hr = g_pDsd->CreateSoundBuffer(&dsbdesc,&pDSBPrimary ,NULL))) return FALSE; //获取3D 模拟世界中的听者指针。 if( FAILED( hr = pDSBPrimary->QueryInterface( IID_IDirectSound3DListener, (VOID**)&g_pDSListener ) ) ) return FALSE; WAVEFORMATEX wfx; ZeroMemory( &wfx, sizeof(WAVEFORMATEX) ); wfx.wFormatTag = (WORD) WAVE_FORMAT_PCM; wfx.nChannels = WAVECHANNEL ; wfx.nSamplesPerSec = WAVESAMPLEPERSEC ; wfx.wBitsPerSample = WAVEBITSPERSAMPLE ; wfx.nBlockAlign = (WORD) (wfx.wBitsPerSample / 8 * wfx.nChannels); wfx.nAvgBytesPerSec = (DWORD) (wfx.nSamplesPerSec * wfx.nBlockAlign); if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) ) //设置缓冲区的音频格式。 return FALSE; |
DSBUFFERDESC dsbd; ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) ); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_CTRL3D| DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY |DSBCAPS_GETCURRENTPOSITION2; //看看创建辅助缓冲区的buffer时设置的标志,3D的标志自然是需要的,还有一个标志需要注意,DSBCAPS_CTRLPOSITIONNOTIFY,如果你采用的流(stream)buffer的话,就需要边播放,边向buffer中填充数据,就需要设置这个标志,这样,在directsound播放到指定位置时,就会触发事件。 //dsbd.dwBufferBytes =MAX_AUDIO_BUF * BUFFERNOTIFYSIZE ;//如果采用流buffer,可以设置适当的buffer大小。 dsbd.dwBufferBytes =g_pWaveFile->GetSize(); // 如果采用静态buffer,那么buffer的大小就是文件的大小了。 dsbd.guid3DAlgorithm = guid3DAlgorithm; dsbd.lpwfxFormat = g_pWaveFile->m_pwfx; if(FAILED(hr = g_pDsd->CreateSoundBuffer(&dsbd,&g_pDSBuffer,NULL))) return ; //通过辅助缓冲区,来获取3D buffer的指针。 if(FAILED(hr = g_pDSBuffer->QueryInterface(IID_IDirectSound3DBuffer, (VOID**)&g_pDS3DBuffer ))) return ; g_dsBufferParams.dwSize = sizeof(DS3DBUFFER); g_pDS3DBuffer->GetAllParameters( &g_dsBufferParams ); //设置3Dbuffer的属性。 g_dsBufferParams.dwMode = DS3DMODE_HEADRELATIVE; g_pDS3DBuffer->SetAllParameters( &g_dsBufferParams, DS3D_IMMEDIATE ); |
FLOAT fDopplerFactor; FLOAT fRolloffFactor; FLOAT fMinDistance; FLOAT fMaxDistance; CSliderCtrl *pDopplerSlider = (CSliderCtrl*)GetDlgItem(IDC_DOPPLER_SLIDER); fDopplerFactor = pDopplerSlider->GetPos(); CSliderCtrl *pRolloffSlider= (CSliderCtrl*)GetDlgItem(IDC_ROLLOFF_SLIDER); fRolloffFactor = pRolloffSlider->GetPos(); CSliderCtrl *pMinDistSlider = (CSliderCtrl*)GetDlgItem(IDC_MINDISTANCE_SLIDER ); fMinDistance = pMinDistSlider->GetPos(); CSliderCtrl *pMaxDistSlider = (CSliderCtrl*)GetDlgItem(IDC_MAXDISTANCE_SLIDER ); fMaxDistance = pMaxDistSlider->GetPos(); g_dsListenerParams.flDopplerFactor = fDopplerFactor; g_dsListenerParams.flRolloffFactor = fRolloffFactor; if( g_pDSListener ) { g_pDSListener->SetAllParameters( &g_dsListenerParams, DS3D_DEFERRED ); g_pDSListener->CommitDeferredSettings(); } g_dsBufferParams.flMinDistance = fMinDistance; g_dsBufferParams.flMaxDistance = fMaxDistance; if( g_pDS3DBuffer ) g_pDS3DBuffer->SetAllParameters( &g_dsBufferParams, DS3D_DEFERRED ); |
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); |
void CDsound3DPlayDemoDlg::OnTimer(UINT nIDEvent ) { FLOAT fXScale; FLOAT fYScale; fXScale = (((CSliderCtrl*)GetDlgItem(IDC_HORIZONTAL_SLIDER ))->GetPos())/100.0f; fYScale = (((CSliderCtrl*)GetDlgItem(IDC_VERTICAL_SLIDER ))->GetPos())/100.0f; FLOAT t = timeGetTime()/1000.0f; // Move the sound object around the listener. The maximum radius of the // orbit is 27.5 units. D3DVECTOR vPosition; vPosition.x = ORBIT_MAX_RADIUS * fXScale * (FLOAT)sin(t); vPosition.y = 0.0f; vPosition.z = ORBIT_MAX_RADIUS * fYScale * (FLOAT)cos(t); D3DVECTOR vVelocity; vVelocity.x = ORBIT_MAX_RADIUS * fXScale * (FLOAT)sin(t+0.05f); vVelocity.y = 0.0f; vVelocity.z = ORBIT_MAX_RADIUS * fYScale * (FLOAT)cos(t+0.05f); memcpy( &g_dsBufferParams.vPosition, &vPosition, sizeof(D3DVECTOR) ); memcpy( &g_dsBufferParams.vVelocity, &vVelocity, sizeof(D3DVECTOR) ); UpdateGrid( vPosition.x, vPosition.z ); if( g_pDS3DBuffer ) g_pDS3DBuffer->SetAllParameters( &g_dsBufferParams, DS3D_IMMEDIATE ); //随着模拟buffer的运动,我们要不断地改变3Dbuffer的参数。 } |