Cocos2d-x 3.0心得(05)-音效管理

来源:互联网 发布:java类声明 编辑:程序博客网 时间:2024/06/15 10:18

相对于bgm,想自由的控制音效要麻烦不少,而且这种枯燥的东西恐怕也没几个人能耐下性子慢慢研究,算是作个记录吧。

跟bgm一样,下面的东西只针对iOS有效,移植其他平台,还是老老实实用SimpleAudioEngine更轻松。


cocos里面,音效的播放原理大致有两步,让我们从外层的开始说起。

首先,要播放一个音效文件,我们得把它加载进内存,这通过[CDSoundEngine loadBuffer]方法来处理,加载得到的东西,我们称它做buffer。这里需要我们提供一个id,用来识别不同的音效文件对应的buffer。。。是的,这个id是我们自定义的,所以我们需要自己想办法来管理这些id。CDSoundEngine内部用数组来缓存buffer,id则是作为数组下标,所以id必须是非负整数,而且不宜过大,通常是以递增并且可回收的形式生成这些id。在cocos里,这些id被称为“soundId”。留意这些叫法,因为后面还有别的id。

加载得到的buffer,必须被附加到一个音效播放的槽位上,才能播放。这就好像是OpenGL里图片跟纹理的关系一样。这些槽位被称为source。和纹理一样,source也有自己的id,cocos里称为“sourceId”。sourceId是由OpenAL生成,当我们创建了一个source,并为它附加了一个buffer之后,就可以播放了,顺便还可以控制一下音调、音量之类的属性。


现在我们已经可以控制音效了,看起来不怎么复杂。下面来看看cocos怎么管理这些音效。

CDSoundEngine的_buffers成员维护了所有被加载的音效数据。_buffers是一个动态数组,初始化大小为64,每当调用loadBuffer时,通过我们传递的soundId作为下标,将加载的音效数据保存在对应的位置下。如果给出的soundId超过数组范围,则_buffers会自动扩容;如果给定的soundId下已经存在音效数据,则会先释放掉,然后加载新的并保存。要主动释放音效数据,则通过[CDSoundEngine unloadBuffer]方法,传递soundId作为参数。

CDSoundEngine的_sources成员保存音效播放槽位的状态,这也是一个数组。CDSoundEngine里允许同时播放最多32个音效,因此_sources数组的空间也就是32。每一个_sources元素记录了一个sourceId(对应一个OpenAL Source对象,用来控制音效属性、播放状态)、被附加的buffer(音效数据,必须被附加到source上才能播放)。

CDSoundEngine还有一个音效分组的概念。_sourceGroups成员是一个数组,它分段保存了_sources的下标,以及这些下标对应的source的其他状态。默认情况下,分组只有1个。那这是用来干嘛的?嗯,比方说,你想让玩家单独控制界面音效、战斗音效、人物语音的开关,就可以用到分组了。不过对于一个手机游戏来说这貌似太奢侈了。


抛开那些总音量、总开关之类的东西,上面3个成员算是最主要的部分了,来看看它们是怎么工作的。

首先初始化,_buffers数组预设了64个空间,每个空间都还没有数据,但是会保留一个OpenAL Buffer对象,并记录bufferId。注意,这个bufferId是由OpenAL生成,在加载音效时,需要这个id选择一个Buffer对象来保存数据;在播放音效时,同样用这个id来指定将要播放的音效。_buffers数组自己的下标则被称为soundId。没错,就是我们一开始提到的那个玩意。整个CDSoundEngine中各类id多如牛毛,不小心弄混了会让人头大。

接着是_sources数组的初始化,设置32个空间,每个空间保留一个OpenAL Source对象,并记录sourceId。sourceId也是OpenAL生成,用来控制一个音效的播放状态、音量、音调等等。现在source上还没有附加buffer,因此播放不了任何东西。

然后是_sourceGroups数组,默认情况下这个数组只有1个空间,记录所有source的下标和使用情况。source的下标(sourceIndex)是用来在_sources数组中作索引,跟sourceId是不同的。_sourceGroups数组自己的下标则被称为groupId。


要播放音效,首先我们要加载音效;要加载音效,则要确定放到_buffers的哪一个下标上。这就是[CDSoundEngine loadBuffer]方法中,soundId这个参数的由来。一般来说,我们都是从0开始,依次递增;另外,如果我们主动释放掉了某些音效数据,最好把对应的id回收以便重用。

loadBuffer成功之后,可以直接调用[CDSoundEngine playSound]来播放。参数soundId指定需要播放的音效,sourceGroupId通常都是0,pitch控制音调,1为原音调,大于1音调变高,小于1音调变低沉;pan控制声道,-1为完全左声道,1为完全右声道,0则平衡;gain控制音量,1为原大小;loop则表示是否循环。

playSound方法首先通过循环检查来找到一个没有被使用的source,然后将soundId对应的buffer附加到source上,再进行播放。“没有被使用的source”包含两种概念,一个是指source当前没有播放任何音效(附加了buffer,但是播放已经停止,也包含在内);另一个是指source当前没有被锁定。锁定的概念我们下面会说到。任何一个不满足的话,都会略过这个source继续查找下一个,直到找到一个可用的source,或者所有source都不可用,这时候就放不了任何音效了。

虽然说是循环检查,但_sourceGroups还是会保存最后一次查找到的sourceIndex,下一次查找会从这里开始,以减少循环次数。另外,如果希望允许在source不够用的情况下,新的音效顶掉旧的正在播放的音效,可以设置_sourceGroup的nonInterruptible为false,而这其实也是默认的设置。

playSound成功的话, 会返回sourceId,可以用来停止音效或作其他控制。


相对于playSound,CDSoundEngine还有一个soundSourceForSound方法,可以更自如的控制音效。soundSourceForSound同样会先查找一个可用的source,并且将指定的buffer附加到source上,然后,创建一个CDSoundSource对象,返回给调用者。CDSoundSource是对OpenAL source的一个简单封装,可以设置source的pitch、pan、gain、loop,控制播放、暂停等。

soundSourceForSound跟playSound最大的不同在于,返回CDSoundSource之前,会先锁定对应的source。我们上面提过,在循环检查可用的source时,被锁定的source是会被略过的。playSound返回的sourceId,通常只在短时间内控制有效,如果经过的时间长了,可能sourceId已经被别的音效使用,这时候再用sourceId,会控制到预想之外的音效。而soundSourceForSound返回的CDSoundSource,只要不release,就会永久锁定对应的source。这同时也表示,使用CDSoundSource需要格外小心,否则的话,就会发现一段时间之后游戏里所有的音效都放不出来了。

0 0
原创粉丝点击