利用Directsound编程实现实时混音_第1页
利用Directsound编程实现实时混音_第2页
利用Directsound编程实现实时混音_第3页
利用Directsound编程实现实时混音_第4页
利用Directsound编程实现实时混音_第5页
全文预览已结束

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、利用Directsound编程实现实时混音开发者在线B更新时间:2007-10-作者:李强来源:天极开发本文关键词:DirectSound编程实时混音摘要:将多个音频文件或多路音频数据同时输出到音频输出设备上,就可同时听到多个不同的声音,这就 是混音。在游戏开发,网络视频会议开发中都会用到混音技术,本文详细介绍如何利用Directsound实现 几路不同的音频进行实时的混音。关键词:Directsound混音在游戏开发中比较常用的音效素材都是比较短的,所以一般常用的API是playsound()函数,比如 我们要在游戏背景中播放一个test.wav音效素材,只要简单的调用下面的函数即可Play

2、Sound(test.wav”,NULL,SND_FILENAME|SND_ASYNC);如此简单,事实上我们看到,国内的游戏大致上都可以用PlaySound()搞定。但是既然是简单,从 功能上就要受限了,如果遇到复杂的场景就没法用playsound实现了,比如在场景中既有开门的声音,又 有砍人的声音,你如果用playsound就没法同时听到两种声音,只能是一个声音完了再听到另外一个声音, 这时,就需要混音了。在网络视频会议开发中,如果不同的客户端同时发言,如何将多个不同端点的话音数据经网络传输到 达某一个端点,经该端点的Wave设备输出,能同时听到多个人的话音,从而实现局域网络中多方的话音

3、交谈,这也需要用到混音技术。在网络上实现话音交谈,特别强调实时性,要尽量保证话音的平滑、连续,因此为了保证话音数据连 续,减少话音数据存储带来的延时,在具体实现中,话音的录制和播放都不采用文件的形式,录制和播放 的话音数据都存在缓冲区中。在Windows系统中,一般情况下,高层Wave接口函数无法直接播放缓冲 区中的话音数据,而必须用底层函数来实现,常用的是Windows API中的Wave函数。将Wave数据在 Wave设备上输出使用的是WaveOutWrite函数,但是该函数不支持多路Wave数据的同时播放,为了 能达到多路Wave数据同时播放的效果,对缓冲区中多路Wave数据进行必要的预

4、处理后,再提交给Wave 输出设备播放,实现原理如图1所示。图1多路Wave混音的实现原理这种混音的方式效果跟你采用的算法有很大关系,但是如果我们采用Directsound进行混音,就简单 多了,我们只需要将我们要混音的内容传给它,Directsound会在内部自动进行混音的。下面我们就进入 Directsound混音编程。在了解Directsound如何混音前我们先来看看DirectSound是如何播放一段wave音频的。这里只是简单的介绍一下播放声音的步骤。第一步,创建一个设备对象。在你的代码中你可以通过调用DirectSoundCreat8函数来创建一个支持IDirectSound8接口

5、的对 象,这个对象通常代表缺省的播放设备。当然你可以枚举可用的设备,然后将设备的GUID传递给 DirectSoundCreat8 函数。注意,Directsound虽然基于COM,但是你并不需要初始化com库,这些Directsound都帮你做 好了,当然,如果你使用DMOs特技,你就要自己初始化com库了,切记。第二步,创建一个辅助Buffer,也叫后备缓冲区你可以通过IDirectSound8:CreateSoundBuffer来创建buffer对象,这个对象主要用来获取处理 数据,这种buffer称作辅助缓冲区,以和主缓冲区区别开来,Direcsound通过把几个后备缓冲区的声音 混合

6、到主缓冲区中,然后输出到声音输出设备上,达到混音的效果。第三步,获取PCM类型的数据将WAV文件或者其他资源的数据读取到缓冲区中。第四步,将数据读取到缓冲区你可以通过IDirectSoundBuffer8:Lock.方法来准备一个辅助缓冲区来进行写操作,通常这个方法 返回一个内存地址,见数据从你的私人buffer中复制到这个地址中,然后调用 IDirectSoundBuffer8:Unlock.第五步,播放缓冲区中的数据你可以通过IDirectSoundBuffer8:Play方法来播放缓冲区中的音频数据,你可以通过 IDirectSoundBuffer8:Stop来暂停播放数据,你可以反复的

7、莱停止,播放,音频数据,如果你同时创 建了几个buffer,那么你就可以同时来播放这些数据,这些声音会自动进行混音的。看到了吧,Directsound混音很简单,我们只要在一个设备上创建几个辅助的缓冲区,然后将数据读取到 缓冲区中,同时的播放,Directsound就会自动在主缓冲区自动混音的。至于同时可以播放几个辅助缓冲 区则有硬件设备的性能决定。在WDM驱动模式下,混音的工作由核心混音器来完成,不同的辅助缓冲区可能具有不同的WAV格 式(例如,不同的采样频率),在必要的时候,辅助缓冲区的格式要转换成主缓冲区,或者核心混音器的 格式。在VXD驱动模式下,如果你的辅助缓冲区都采用相同的音频格式

8、,并且硬件的音频格式也和你的音频 格式匹配,此时,混音器不用作任何的转换。你的应用程序可以创建一个主缓冲区,然后通过 IDirectSoundBuffer8:SetFormat来设置硬件的输出格式。要注意,只有你的协作度一定要是Priority Cooperative Level.,并且,一定要创建辅助缓冲区前设置主缓冲区,DirectSound会将你的设置保存 下来。在WDM模式下,对主缓冲区的的设置没有作用,因为主缓冲区的格式是由内核混音器来决定的。 下面开始吧,让我们看看如何进行混音吧,假设我们的背景需要混音的素材是下面的三个wave文件, test1 .wav test2.wav te

9、st3.wav。首先定义一下我们需要的几个变量:LPDIRECTSOUND8 g_pDS = NULL;LPDIRECTSOUNDBUFFER g_pDsbuffer3 = NULL;CWaveFile* g_pWaveFile;/WAVEFORMATEX g_wfxInput; 输入的音频格式这里简单介绍一下CWaveFile类,Directsound里封装了一个CWaveFile类用来操作wav文件,可以通过open来写入文件的头信息,write来写入文件的数据,Getsize函数获取文件的长度,close关 闭文件。你可以在DirectSound的路径下找到这个类的定义(SDKroot)

10、samplesC+CommonSrcDsutil.cpp。首先初始化DirectsoundBOOL InitDirectSound()if ( FAILED( hr = DirectSoundCreate(NULL, & g_pDS, NULL ) return FALSE;/ Set cooperative level.if ( FAILED( hr = g_pDS -SetCooperativeLevel( hwnd, DSSCL_PRIORITY ) return FALSE;return TRUE;在初始化Directsound的时候,创建设备对象最简单的方法就是通过DirectSou

11、ndCreate8函数, 这个函数的第一个参数指定了和这个对象邦定的设备的GUID,你可以通过枚举设备来获取这个设备的 GUID,如果这个参数也可以为NULL,缺省的系统的声音输出设备就是DSDEVID_DefaultPlayback。 当你创建完设备对象后,一定要调用IDirectSound8:SetCooperativeLevel来设置协作度,否则,你 不会听到声音的。DirectSound 定义了三种水平,DSSCL_NORMAL, DSSCL_PRIORITY, and DSSCL_WRITEPRIMARY在Priority层次的协作度下,应用程序可以有优先权使用硬件资源,比如使用硬件

12、进行混音,当然也 可以设置主缓冲区的媒体格式,游戏程序应该采用这个层次的协作度,这个层次的协作度在允许应用程序 控制采用频率和位深度的同时,也给应用程序很大的权力,这个层次的协作度允许其他应用程序的声音和 游戏的音频同时被听到,不影响。下面的函数加载wave文件,然后将音频数据读取到缓冲区中,然后通过Directsound创建了的静态 辅助缓冲区,将音频数据copy到Directsound的静态辅助缓冲区,然后就可以play 了。LPDIRECTSOUNDBUFFER LoadWaveFile(LPSTR IpzFileName)DSBUFFERDESC dsbdesc;HRESULT hr;

13、BYTE *pBuffer;DWORD dwSizeRead;LPDIRECTSOUNDBUFFER lpdsbStatic=NULL;if( FAILED( hr = g_pWaveFile-Open( lpzFileName, &g_wfxInput,WAVEFILE_WRITE )return NULL;DWORD dwSize = g_pWaveFile-GetSize();pBuffer = new BYTEdwSize;g_pWaveFile-Read(pBuffer,dwSize,&dwSizeRead);if(dwSizeRead 0)memset(dsbdesc,0,size

14、of(DSBUFFERDESC);dsbdesc.dwSize = sizeof(DSBUFFERDESC);dsbdesc.dwFlags =DSBCAPS_STATIC;dsbdesc.dwBufferBytes =dwSizeRead;dsbdesc.lpwfxFormat = g_wfxInput;if ( FAILED( g_pDS-CreateSoundBuffer(&dsbdesc, & lpdsbStatic, NULL )g_pWaveFile-Close();delete pBuffer;return NULLLPVOID lpvWrite;DWORD dwLength;i

15、f (DS_OK = lpdsbStatic -Lock(0, / Offset at which to start lock.0, / Size of lock; ignored because of flag.&lpvWrite, / Gets address of first part of lock.&dwLength, / Gets size of first part of lock. NULL, / Address of wraparound not needed. NULL, / Size of wraparound not needed. DSBLOCK_ENTIREBUFF

16、ER) / Flag.memcpy(lpvWrite, pBuffer, dwLength);lpdsbStatic -Unlock(lpvWrite, / Address of lock start.dwLength, / Size of lock.NULL, / No wraparound portion.0); / No wraparound size.delete pBuffer;return lpdsbStatic;这里我想简单的讲一下Directsound的辅助缓冲区,在Directsound中,辅助缓冲区分两类,一种 是Static Buffer,这种buffer主要用于播放那些

17、比较短的音频,可以将文件中的音频数据全部copy到 Static buffer中,如果音频文件比较大,未了限制内存的开销,就要用到Streaming buffer,一般来说, Streaming buffer只能包含几秒钟的数据量,然后在播放的过程中不断的更新streaming buffer中的 数据。静态缓冲区的创建和管理和流缓冲区很相似,唯一的区别就是它们使用的方式不一样,静态缓冲区 只填充一次数据,然后就可以play,然而,流缓冲区是一边play,一边填充数据。上面创建的就是得静态的buffer,如果你要播放比较长的音频文件,你就要使用streaming buffer 了。流缓冲区用来播放那些比较长的声音,因为数据比较长,没法一次填充到缓冲区中,一边播放,一边 将新的数据填充到buffer中。可以通过IDirectSoundBuffer8:Play函授来播放缓冲区中的内容,注意在该函数的参数中一定要 设置 DSBPLAY_LOOPING 标志。通过IDirectSoundBuffer8:Stop方法中断播放,该方法会立即停止缓冲区播放,因此你要确保所 有的数据都被播放,你可以通过拖动播放位置或者设置通

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论