网页功能: 加入收藏 设为首页 网站搜索  
DirectSound 无题
发表日期:2006-08-23作者:[转贴] 出处:  

目录:
  音效的表现
  Play Sound using PlaySound()
  使用DirectSound
  相关事宜
  末语

文档内容:

□ 音效的表现

  WINDOW下音效档的使用,大抵上就是使用WAV档,通常音效只是一小段,不会超过数秒的声音档,当然你可以疯狂一点,将整个WAV档当成音乐来播放,如同前面提到的,与其要这样做,倒不如将WAV档弄成CD音轨,在WINDOW下播放一片CD片,也是如同想像中的简单,这边我们不谈这部份,专心探讨WAV档的使用时机与程式作法的差异。我会讨论不同的作法,以及解释为什麽有些人宁愿舍简就繁,抱头猛钻DirectSound(当然这不一定是正确的)。

□ Play Sound using PlaySound()

  是的,在WINDOW API的支援下,播放小段WAV档的最佳方案就是使用PlaySound()函示,他简单到什麽地步呢?比方我要播放sound.wav档,我只要这麽做就好:

PlaySound("sound.wav",NULL,SND_FILENAME|SND_ASYNC);

最後的叁数SND_ASYNC 代表音效开始拨放以後,不等待,直接从函示返回,当然其他的叁数诸如SND_LOOP可以让音效重复播放,直到下次呼叫PlaySound()为止。

这边我们必须探讨PlaySound()的使用时机,以及其限制,否则我们很难想像,为什麽还需要DirectSound的存在。PlaySound()的第一个限制就是WAV档案的大小,事实上是有限制的,当WAV档案太大的话,PlaySound()无法处理。第二个限制就是混音的功能,PlaySound()没办法帮上任何一点忙,比方说,在一个场景里面,同时有一只小鸡,一只鸭,他们同时在叫,这时候我需要同时表现这两种声音,我这麽做:

PlaySound("chicken.wav",NULL,SND_FILENAME|SND_ASYNC);

PlaySound("duck.wav",NULL,SND_FILENAME|SND_ASYNC);

不行,在第一个PlaySound()呼叫的时候,他确实正要播放鸡叫的声音,但是接着马上又呼叫第二个函示,结果鸡叫的声音停止了,转而播放鸭子叫的声音,所以你永远只会听到鸭子的叫声。你会说,如果我不使用非同步作法呢?你会得到的结果是这样的:你先听完鸡叫的声音,然後鸡叫完以後,才听见鸭叫的声音,总之事情就是这个样子,PlaySound()好用,但是他的功能在这边完全派不上用场了。

在这边我们归纳PlaySound()使用的时机:凡是不使用到混音的功能,仅单纯播放片段WAV档者,使用之。换句话说,如果你的游戏同时间只会存在一种音效,PlaySound()将会是最好的选择,不必使用到DirectSound的功能。事实上我们看到国内的游戏大致上都可以用PlaySound()搞定,开门的声音,开宝箱的声音,战斗砍人的声音,实际上同时间只有一种音效播放中,用PlaySound()才是明智的选择。但是我真的见过某些程式,明明只播放单音的WAV档,却还煞有其事地使用复杂的DirectSound,我宁可相信他是为了练习,而不是因为不知道有PlaySound()可用而舍简就繁。

另外在VC的整合环境下,有些人使用PlaySound()遇到的问题都是Link Error,找不到PlaySound()的连结点,事实上这个函示包含在Winmm.lib里面,整合环境的专案内定值不包含这个函示库,要自行加入。

□ 使用DirectSound

  在程式需要混音的时候,DirectSound就显得相当有价值了,因为他会替你处理掉许多内部的动作,你所需要做的,就是用,用就对了,也不必管他内部在搞什麽鬼。关於DirectSound的内容,说真的也相当繁杂,我仅就应用的层面,提醒一下哪些时候该使用什麽方式,并举一些例子供叁考。

  在了解DirectSound的时候,最重要的就是区分音效Buffer的不同,其中分成两种 primary buffer (主要音效缓冲区)与 secondary buffer(次要音效缓冲区)。Primary buffer通常我们不会去管他,直接交给
DirectSound操作即可,只有在你需要完全控制混音的情况下,你才会需要动到primary buffer的内容,根据我的经验,这部份是不需要操心的。需要我们操心的部份是在 secondary buffer,这种buffer理论上可以产生不限制的个数,而primary buffer仅有一个,当然实际能产生多少 secondary buffer要看记忆体的多寡来决定。当我们同时播放secondary buffer的时候,其资料会流向 primary buffer,并且DirectSound会自动完成许多内部的动作,比方音效格式的转换,混音的支援等等。

  播放WAV的方式,也分成两种,第一种是小片段的音效,使用 static buffer,播放资料量极大的WAV档案,你需要用到的是 streaming buffer,这两种的差别有必要搞清楚,所谓static buffer就是静态,小量的音效,通常我们会让他一直常驻在记忆体里面,而不会感到心虚,毕竟所占的记忆体极小。Streaming buffer则是播放大的WAV档,采用分批播放的一种方式,所以程式中必须不停地写入各段的资料。他们使用的方式大概是这样的,如同前面所提到的阿鸡阿鸭WAV音效,档案小,又必须经常使用者,我们使用static buffer,而准备将WAV当音乐播放者(很疯狂),因为一首三分钟的WAV音乐,通常占用数十MB的空间,所以没有别的选择,必须使用streaming buffer。

  好了,疯狂的事情留给别人去做,我这边示范一下混音的方式,就如同星海争霸那种整个场景都是动物惨叫声的情况,我们也模拟一下,同时播放五种动物的叫声,当然他们是很和平相处的。在这边的程式范例,有些撷取自DirectX内附的SAMPLE,比方处理WAV格式的部份,我们可以在 SAMPLE里边找到WAVE.C这个档案直接套用。
许多函示呼叫,旗标用法,因为太庞杂了,所以请各位叁考DirectSound的文件,我不拟赘述,介绍三个函示如下:

BOOL InitDSound( HWND hwnd)
{
    HRESULT hr;

    // Create DirectSound.
    if ( FAILED( hr = DirectSoundCreate(NULL, &lpds, NULL ) ) )
        return FALSE;

    // Set cooperative level.
    if ( FAILED( hr = lpds->SetCooperativeLevel( hwnd, DSSCL_PRIORITY ) ) )
        return FALSE;

    return TRUE;
} // 初始化DirectSound

BOOL InitDSound( HWND hwnd) 仅简单地初始化DirectSound,事实上跟DirectDraw的初始化部份相当类似,所以感觉上都是基本的调调,没什麽好特别讨论的。接着介绍第二个函示:

LPDIRECTSOUNDBUFFER LoadStatic (LPDIRECTSOUND lpds,LPSTR lpzFileName)
{
    WAVEFORMATEX *pwfx;
    HMMIO hmmio;
    MMCKINFO mmckinfo;
    MMCKINFO mmckinfoParent;
    LPDIRECTSOUNDBUFFER lpdsbStatic=NULL;
    LPVOID lpvAudio1;
    DWORD dwBytes1;
    DSBUFFERDESC dsbdesc;
    UINT cbBytesRead;

    if ( WaveOpenFile( lpzFileName, &hmmio, &pwfx, &mmckinfoParent ) != 0 )
        return FALSE;

    if ( WaveStartDataRead( &hmmio, &mmckinfo, &mmckinfoParent ) != 0 )
        return FALSE;

    if ( lpdsbStatic == NULL )
    {
        memset( &dsbdesc, 0, sizeof( DSBUFFERDESC ) );
        dsbdesc.dwSize = sizeof( DSBUFFERDESC );
        dsbdesc.dwFlags = DSBCAPS_STATIC;
        dsbdesc.dwBufferBytes = mmckinfo.cksize;
        dsbdesc.lpwfxFormat = pwfx;
        if ( FAILED( lpds->CreateSoundBuffer(&dsbdesc, &lpdsbStatic, NULL ) ) )
        {
            WaveCloseReadFile( &hmmio, &pwfx );
            return FALSE;
        }
    }

    lpdsbStatic->Lock( 0, 0, &lpvAudio1, &dwBytes1, NULL, NULL, DSBLOCK_ENTIREBUFFER );
    WaveReadFile( hmmio, dwBytes1, ( BYTE * )lpvAudio1,&mmckinfo, &cbBytesRead );
    lpdsbStatic->Unlock( lpvAudio1, dwBytes1, NULL, 0 );
    WaveCloseReadFile( &hmmio, &pwfx );

    return lpdsbStatic;
}

LoadStatic() 主要功能载入WAV档,在这个函示里面,我们也使用到WAVE.C里面的函示,专门处理WAV档。
当我们传入WAV的档名以後,呼叫成功以後,会传回一个static secondary buffer的指标。这代表我们可以开始对音效做处理了,不用说,当然就是播放罗。底下的函示便是:

void PlayStatic(LPDIRECTSOUNDBUFFER lpdsbStatic)
{
    if ( lpdsbStatic == NULL ) return;

    lpdsbStatic->SetCurrentPosition(0);

    lpdsbStatic->Play(0,0,0);
}

PlayStatic()仅简单地使用Play(),就完成播放的动作了,其他的事情不用我们操心,在这边比较复杂的倒是搞定WAVE的内容与格式,还好有WAVE.C的帮助,我们没有遭遇太大的难关。至於这三个函示怎麽使用呢?
底下做个简单的示范:

首先定义一个全域的变数lpds,供InitDSound()使用,这样的指标整个程式只有一个,所以定义成全域变数是很合理的作法。紧接着数数看你需要几个static buffer,以我们的例子来说,我需要五个,所以我定义的变数如下:

LPDIRECTSOUNDBUFFER lpdsb[5]; //配置五个音效指标

要替这五个指标做初始化的的工作,只要笨笨地呼叫LoadStatic()即可,作法大致相同:

// 替这五个指标作初始化的工作

lpdsb[0]=LoadStatic(lpds,"chicken.wav");

lpdsb[1]=LoadStatic(lpds,"duck.wav");

lpdsb[2]=LoadStatic(lpds,"horse.wav");

lpdsb[3]=LoadStatic(lpds,"pig.wav");

lpdsb[4]=LoadStatic(lpds,"dragon.wav");

到了这里,已经完成所有的任务了,这时候我们可以随时使用PlayStatic()来播放音效,因为我们要测试一下混音的效果,所以我故意连续Play五次:

PlayStatic(lpdsb[0]);

PlayStatic(lpdsb[1]);

PlayStatic(lpdsb[2]);

PlayStatic(lpdsb[3]);

PlayStatic(lpdsb[4]);

这下子没问题了,你要怎麽混音都随便你,执行完毕会有五种动物的声音同时呈现,真是壮观的「音面」呀,虽然「场面」上倒是不知道这些动物躲在哪里啦。

□ 相关事宜

  关於3D音效的作法,要配合3D的游戏比较具有真正的意义,不过我们也可以使用简单的方式让音效更具真实感,在上面说到的 static buffer,每一个都可以当成独立的物件看待,其中包含控制音效的音量,喇叭的左声道右声道,更重要的是,你设定其中一个音效的音量,不会影响到另外一个音效的音量,换句话说,他们独立作业就是了。一个简单的应用方式,如果你判断某物体距离你越来越远,则可以让那个物体的音量渐渐减低,制造远近距离的感觉。又有如战斗方式的程式,砍杀敌人的声音可以分成左右声道来表现,比方从左边杀过来,则让右边的音效消音,只让左边的喇叭发声,都是可行的办法。而程式要控制这些部份只要增加一个旗标即可,在dsbdesc.dwFlags 加上DSBCAPS_CTRLDEFAULT即可。如果忘了加入这个旗标,则所有设定音量,左右声道,音频的呼叫都会失败。

□ 末语

  看完这篇文章,音乐音效的处理大致上已经没有问题了,有问题的倒是要如何做出动人的音乐,这部份当然不是我们程式者的任务。你可以到我的网站取得此次介绍的类别与函示库,直接套用即可,另外我会写一个混音的小范例,让你实际听一下效果如何。

  呃...事实上呢,有些东西是需要自己写的,有些公式化的部份则有现成的东西套用是最好不过。什麽东西你不需要自己写呢?凡是那些属於固定格式,不具创意性的东西,别人写好,拿来用就是了。比方说,最近自己常常幻想如果有一整组的现成函示库支援图形格式的转换该有多好。当然这东西自己写不是问题,只是将时间用在这种没什麽技巧可言的地方,会感觉有点可惜就是了。每个人处事的原则不一样,看待事物的想法也不一样,我没说我是对的,这只是我个人的原则罢了。大家应该有自己的主见。

我来说两句】 【加入收藏】 【返加顶部】 【打印本页】 【关闭窗口
中搜索 DirectSound 无题
本类热点文章
  DirectSound
  DirectSound 无题
  WAVE文件格式剖析
  游戏背景音乐
  DirectX Audio的强大功能
  使用DIRECTX 优化声音特性
  利用MCI播放MIDI、WAVE
  标准MIDI文件格式
  游戏音乐与音效的播放
  SEAL声音函数手册
  VC++5.0下MIDI、WAV及CD的播放
最新分类信息我要发布 
最新招聘信息

关于我们 / 合作推广 / 给我留言 / 版权举报 / 意见建议 / 广告投放  
Copyright ©2003-2024 Lihuasoft.net webmaster(at)lihuasoft.net
网站编程QQ群   京ICP备05001064号 页面生成时间:0.00535