新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     >>计算机科学论坛<<     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → 利用Directsound 3D实现游戏中3D音效 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 9755 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 利用Directsound 3D实现游戏中3D音效 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 利用Directsound 3D实现游戏中3D音效

    在开始本文前,我要说一句,Directsound 3D可以说是Directsound的精华所在,我个人的感觉,在3D游戏的开发中,3D音效一般都是采用Directsound 3D来完成的。

      DirectSound 3D是微软公司所推出的,它利用声音大小的比例调整与都卜勒效应,来达到以软件来模拟3D音效的效果,创立了在三维空间定位音效文件的标准方式。任何应用程序透过它和支持DirectSound 3D的[URL=http://www.yesky.com/key/460/460.html]声卡[/URL],便可以获得所需的效果。由于这是许多声卡厂商与微软共同制定的,现在大部分的声卡都支持这项技术。 本文就是将Directsound 3D技术介绍给大家,你们可以在你们的程序中使用它,相信会给你的程序增色不少。

      下面我们就开始吧,在开始前,我还要先介绍一下Directsound 3D相关的几个基本概念。然后通过一个例子来演示如何使用Directsound 3D,[URL=http://www.yesky.com/key/747/45747.html]源码[/URL]附在后面,可以下载。

      3D空间 声源 听者

      Directsound 3D是通过软件模拟来实现3D音效的,所以要先讲一下Dsound 的3D模拟空间。这个空间类似现实空间,可以用笛卡儿[URL=http://www.yesky.com/key/1381/41381.html]坐标系[/URL]来描述Dsound 的3D空间,有x,y,z三个坐标轴坐标轴。

      在这个模拟空间中Dsound提供了模拟的声源对象和倾听者对象(listener),声源和听者的关系可以通过三个变量来描述:在三维空间的位置,以及运动的速度,以及运动方向。

      位置即声源和听者在三维空间的所在位置,随着两者的相对位置不同,则听者便会听到不同的声音效果。

      速度为声源和听者在三维空间中的移动速度,此项特性同样会改变两者在空间的坐标,以产生不同的声音效果。

      声源和听者相对运动的方向也会影响听者听到的声音效果,因为声音是具有方向性的。这个下面会谈到。

      知道了3D声源以及3D环境中的听者,那么怎么产生3D音效呢?一般来说,在产生3D音效的时候,主要有下面的几种情况,1 是声源不动,而听者在模拟的3D空间进行运动,2 是听者不动,让声源在模拟的3D空间进行运动,3 听者和声音同时在运动。如下图:

    按此在新窗口浏览图片
    图1 声源不动,听者移动产生3D音效

    按此在新窗口浏览图片
    图2 听者不动,声源移动产生3D音效

      Directsound给我们提供了听者和声源对象的接口,我们可以通过上面提到的三种方式设置改变声源或者听者的位置,运动速度和方向就可以形成3D音效了,

      在3D环境中,我们通过IDirectSound3DBuffer8接口来表述声源,这个接口只有创建时设置DSBCAPS_CTRL3D标志的Directsound buffer才支持这个接口,这个接口提供的一些函数用来设置和获取声源的一些属性。在一个虚拟的3D环境中,我们可以通过主缓冲区来获取IDirectSound3DListener8接口,通过这个接口我们可以控制着声学环境中的多数[URL=http://www.yesky.com/key/2011/32011.html]参数[/URL],比如多普勒变换的数量,音量衰减的比率。

      接口很简单,但是大量的计算工作Directsound都在内部帮助我们完成了。


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2006/9/7 16:01:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客2
    发贴心情 
    最大最小距离

      当听者越接近声源,那么听到的声音就越大,距离减少一半,音量会增加一倍。但是,当你继续接近到声源,当距离缩短到一定距离后,音量就不会持续增加。这就是声源的最小距离。

      声源的最小距离,就是声音的音量开始随着距离大幅度衰减的起始点。例如,对于飞机,这个最小距离也许是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。

    按此在新窗口浏览图片
    图3 最大最小距离

      处理模式

      Sound buffers 有三种处理模式,normal, head-relative, and disabled。

      在正常模式下,声源的位置和方向是真实世界中的绝对值,这种模式适合声源相对于听者不动的情形。

      在head reative 模式下,声源的所有3D特性都跟听者的当前的位置,速度,以及方向有关,当听者移动,或者转动方向,3D buffer 就会自动的重新调整 world space 。这种模式可以应用实现一种在听者头部不停的嗡嗡叫的声音,那些一直跟随着听者的声音,根本没有必要用3D声音来实现。

      在 disable 模式下,3 D 声音失效,所有的声音听起来好像发自听者的头部。

      声音的锥效应

      没有方向的声音在各个方向上的振幅都相同,有方向的声音在该方向上的振幅最大,声音的锥效应分为内部的锥效应,和外部的锥效应,锥的外部的角度应该大于等于锥的内部角度。

      在锥的内部,在考虑到buffer中的基本的音量,以及距离听者的距离远近,何听者的方向,声音的音量就跟没有锥效应一样。
    在锥的外部,正常的音量被削弱了,从0到负的百分之几分贝。

      在锥体的内部和外部之间,是一个过渡带,从内部的volume 到外部的volume,这个音量的逐渐的降低。下面的图表显示了声音的锥效应。

    按此在新窗口浏览图片
    图4 声音锥

      每一个的3D buffer都有一个声音锥,但是缺省的情况下,每一个3D声音 buffer都像一个没有衰减的 声源,因为锥体内外都没有声音的音量的降低,锥的角度内外的角度是360度,除非我们的应用程序改变这个设置,否则的话,3D声音没有方向感。

      利用3D声音,可以给你的程序添加比较奇异的特技,例如,你可以将声音源放到房间的中间,将声源的方向朝向门的方向,然后将声音的锥体角度包含门的宽度,将锥体的外部设置的更宽一些,然后将锥体的外部音量设置为0。这样,当听者只有在门的附近才能听到声音,在正对门的位置,声音才更响。

      关于Directsound 3D的一些相关的概念先介绍到这里,下面我们就可以开始我们的3D音效之旅了。我提供了一个demo,下面我们分析如何编程来实现3D音效。Demo的界面如下:

    按此在新窗口浏览图片
    图5 demo界面

      这里要说明一下,这个demo实现3D音效的方法是采用的第二种方法,即听者不动,使声源运动的方法来产生3D音效。这里设置声源在一个平面上来运动,设置y轴为零。

      象声源一样,3D世界中的听者也有位置,速度和方向。 如下图

    按此在新窗口浏览图片
    图6 听者的坐标系

      向上的向量指向头的顶部,前向的向量指向听者的面部正前方,缺省时,front向量是(0.0, 0.0, 1.0),,顶部的向量是(0.0, 1.0, 0.0).,如果需要的话,Directsound可以调整front 向量,因为它在top向量的右向。所以,我们这个demo设置的buffer运动轨迹在一个平面内,如上图,我们选择的平面是y=0,所以,我们在这个demo听起来,就好像声音在绕着我们的头部不停的在转动。

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2006/9/7 16:02:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客3
    发贴心情 
    下面一步一步跟我来做吧。

      第1步 声明一些我们要用到的变量。

    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; //是否正在播放

      第2步初始化Directsound,创建主缓冲区,设置主缓冲区的音频格式,并通过主缓冲区获取听者(listener)接口,这些工作可以在对话框的初始化中来做。

    //初始化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;  

      第3步,创建辅助缓冲区,通过辅助缓冲区获取3dbuffer指针

    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 );

      第4步 设置多普勒因子以及最小最大距离。

    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 );

      第5步 然后就是通过调用辅助缓冲区的play方法,来进行播放音频文件了。

    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);

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2006/9/7 16:03:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客4
    发贴心情 
    第6步最后一个很重要的地方,就是我们在程序中设置了一个时钟,这个时钟事件是用来触发Directsound声源的运动如下:

      声源的运动轨迹根据我们设置的fxscale 和fyscale 的大小,就是我们拖动竖直和水平的两个slider,是一个椭圆形状,当然,如果fxscale = fyscale时,就是个圆形轨迹了。我们可以调整,然后通过ontimer事件来不断调整buffer的运动轨迹。

    按此在新窗口浏览图片
    图7声源运动的轨迹

    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的参数。
    }

      好了,介绍到这里,基本上都介绍清楚了,如果你还有什么疑惑的话,请参看demo的代码,对于程序员来说,看代码,比看文章能理解的更清楚些。

      结束语

      本文通过一个实例来演示了如何利用Directsound 实现游戏开发中的3D立体声音效,随文提供了一个demo源码,你可以下载,本文的demo在windowx xp,2000 + VC.7.0下调试。关于更详细的资料,请参考msdn上directx部分。

      最后提醒两个问题:

      1 在我的demo程序中,关于播放音频文件,我提供了两种方法,一种是比较简单的静态buffer,适合比较小的wave文件,另一种方法是采用Streaming buffer,开一个比较小的缓存,然后一边播放,一边将向缓冲中填充数据,这个适合比较大的wave文件。当然后一种方法比较麻烦,需要单独起一个线程。我在程序中都作了详尽的注释,如果你足够仔细,你肯定会发现。

      2 一定要记住,3D音效只支持单声道的wave文件,不要试图对一个多声道的wave文件实现三维立体声效果,不会成功的。

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2006/9/7 16:05:00
     
     longshentailang 帅哥哟,离线,有人找我吗?
      
      
      威望:1
      等级:计算机学士学位
      文章:325
      积分:2990
      门派:XML.ORG.CN
      注册:2006/6/20

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给longshentailang发送一个短消息 把longshentailang加入好友 查看longshentailang的个人资料 搜索longshentailang在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看longshentailang的博客5
    发贴心情 
    好东西!
    支持版主!
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2006/9/10 17:31:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/4/28 13:22:08

    本主题贴数5,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    113.281ms