Wwise 内存管理
SoundBank
初始化Bank
我们现在打包出来的路径下能看到一个叫Init.bnk这个Bank,它包含了工程所有通用信息。是在生成SoundBank时自动生成的,每个工程只有一个。在启动游戏的时候加载的第一个Bank必须得是它。如果不是它会影响其他的Bank无法加载。
正常SoundBank
包含Event和播放他们所需要的对象和音频数据。如果当前的event所播放的音频数据是流播放数据,那它会记录到相应的.wem文件。
在Unity中使用SoundBank
使用标识类型
-
字符串
- 提高代码的可读性
- 把字符串转换成声音引擎使用的ID需要少量的CPU。
- 声音引擎中不存储字符串。
-
ID
- 代码可读性不好
- 声音引擎在内部使用ID,因此无需占用任何额外的CPU
- 无需占用额为内存
LoadBank
我们使用AkSoundEngine.LoadBank来使用字符串加载SoundBank的时候,它会返回给我们一个SoundBank ID这个ID是存在SoundBank里的,所以这个ID和我们在wwise内看到的一样。
UnloadBank
使用UnloadBank来卸载SoundBank的时候,我们需要传入的是它的ID,根据上面的加载,我们需要好好保管这个ID,以便我们能正常的卸载ID。
通过内存加载
LoadBankMemoryView
这个方案其实就是通过使用Unity自带的本地资源二进制加载,然后通过AkSoundEngine.LoadBankMemoryView把这部分的数据序列化成声音引擎能使用的数据。它加载的资源是位于内存中的实际位置,不会拷贝资源,所以在使用的时候必须保证内存一直有效,直到卸载掉SoundBank。
LoadBankMemoryCopy()
也是通过序列化数据加载soundbank,这个和上面那个方法的不同之处,是会把数据拷贝到声音引擎的分配内存中,所以当数据被加载完成之后,就可以从unity中卸载掉。
PrepareBank
AkBankContent_All
这个是一个PrepareBank的一个枚举选项,使用这个选项的时候,在加载SoundBank的时候是把它所有的内容都加载进去,在加载媒体文件的时候,运用于类似于PrepareEvent的机制,先查询媒体文件在不在内存中,如果不在就加载媒体文件,如果存在了就不在加载。提高内存的利用率。
AkBankContent_StructureOnly
使用这个选项的加载方式,只会加载时间和结构元数据,会忽略SoundBank中的媒体文件。当SoundBank中不包含媒体文件的时候,它和上面的那种模式可以产生相同的结果。
清空SoundBank
调用ClearBanks()函数可以重置声音引擎中的内容。调用这个函数之后:
- 所有声音停止播放。
- 所有SoundBank均会被卸载,包括Init.Bank
- 所有的State管理均会清空。
注意点
- 每个SoundBank只可加载一次。如果试图显式加载一个SoundBank两次的时候,它在第一次加载之后的返回值将会是AK_BankAlreadyLoaded,告诉你已经被加载过了。当你把媒体存在两个不同的SoundBank里,你就可以显示加载它两次,内存中就会出现两份资源,这个时候你就可以使用PrepareEvent的特性,让同一份媒体资源只在内存中存在一份。
- 在使用SoundBankID加载的时候,需要在设置中取消勾选“Use SoundBank Names”这个选项,然后生成出来的SoundBank就是ID.bnk格式的。如果使用Wwise自带的Unity插件,就需要注意,它里面初始化的Init.bnk。在使用ID格式是找不到的,所以需要把Init.bnk改成初始化bank的ID名字。
- 当把音效资源当成stream加载的时候,会在SoundBank中记录下来音效资源的流信息,在使用event的时候就会去使用Stream Manager来加载流音效资源播放。
流播放
有限的带宽往往会使得存储设备的访问速度成为游戏的瓶颈。因此,为了保持良好的数据传输请求顺序,游戏通常会集中访问 I/O。排序的依据是相对优先级、相对于传输大小的开销要求、吞吐量和延迟。音频一般需要大量的 I/O 资源:音乐和较长的声音在播放时通常从磁盘传输。这块后面单独写一个文章用来讲解,展示先放下不讲了。
使用wwise的event
AKGameObj
在播放wwise的时候,我们首先需要确定游戏对象,因为不管是听者还是发声器,我们都需要知道他们在世界坐标中的位置,通过调用AkSoundEngine.SetObjectPosition我们可以设置场景中物体的位置和朝向。这个物体记录到声音引擎中,当我们需要给一个听者或者一个发声器设置场景坐标的时候,就可以使用这个记录的信息。通过AkSoundEngine.RegisterGameObj这个方法,我们可以把场景中的物体在声音引擎中记录一个id,这样的话,我们就可以通过id找到这个物体。AkSoundEngine.UnregisterGameObj通过这个方法,我们就把这个物体从声音引擎里移除掉了。在wwise里去记录gameobject对应id是通过AkSoundEngine.GetAkGameObjectID这个方法来计算的。这样我们把unity场景中的一个物体的位置和旋转信息就在声音引擎通过id记录下来的。
AkAudioListener
对于声音我们需要一个收听音乐的节点,这个脚本就是为了给我们定义一个听者的信息。通过AkSoundEngine.AddDefaultListener来设置一个默认的听者,它将指派到所有的发声器。AkSoundEngine.AddListener可以给发声器设置一个听者,用来覆盖掉默认听者。RemoveListener和RemoveDefaultListener是用来取消听者设置。
AkEvent
播放wwise的事件调用的接口是AkSoundEngine.PostEvent它会返回一个id,这个id可以通过调用stopPlayingID来停止event的播放,但是在看unity内的代码的时候发现只在Windows做了拓展。还有一个停止Event的方法调用AkSoundEngine.ExecuteActionOnEvent传入枚举为stop
AkAmbient
当我们为了节省内存的时候,可以把使用相同Event实例只生成一个声音实例,以便节省内存,调用AkSoundEngine.SetMultiplePositions相当只用了一个声音实例却可以设置不同的位置。
AkEnvironment
当我们想给声音加一个Auxiliary Bus来达到一个混响的效果,我们可以调用AkSoundEngine.SetGameObjectAuxSendValues来添加一个混响的效果。
AkSwitch
当我们使用wwise内的音乐切换功能,相当于在同一组声音内根据某一种规则进行播放的切换。可以使用AkSoundEngine.SetSwitch传入组id和你要切换的id加上播放声音的对象。
Spatial Audio
这个是Wwise一个提供和空间音频相关联的服务的一个模块。普通的wwise提供了一些正常的根据位置,方向,角度的声音渐变效果,和一些声音左右声道的切换的效果,但是我们如果需要对声音进行一些反射,衍射和透射的效果的话,还是需要用到Spatial Audio这个模块。这些的处理很依赖游戏引擎,并且很消耗性能。
Room和Portal
通过Room和Portal进行一次简单的几何抽象,对发声体的声音传播进行准确的建模。对于房间驱动的声音传播,其主要特性为衍射,耦合以及混响的空间化。Room没有尺寸大小,他们通过Portal相互连通,并形成由房间和开口组成的网络。其他房间发出的声音可以通过开口传播至听者所在房间。
衍射
对于相邻Room内的每个发声体,Spatial Audio都会计算相连Portal最近边缘与Shadow Boundary的衍射角。通过衍射角映射衍射系数,通过获取这个系数,我们可以修改发声体的值,obstruction值或者Diffraction值。
还需要考虑到Room的漫反射能量。这部分的衍射也会同时计算出来,在Spatial Audio假定漫反射能量沿着Portal垂直方向从Room向外渗透。因此计算相对于Portal法向量的衍射角。
透射
当听者和发生源不在同一Room且两者无法直接通过Portal看到彼此时,Spatial Audio会向游戏对象应用透射损失系数,并据此对声音穿透墙壁的情形进行建模。通过设置Occlusion曲线或Transmission Loss内置参数。
Room之间的透射损失依据Room设置参数进行计算,并取听者所在Room和发声体所在Room当中的最大值。
若发声体被一个或多个几何表面阻挡,则应用几何构造的透射损失系数。若发声体和听者之间存在不止一个表面,则采用最大透射损失系数。
若声音即被几何构造所阻挡,又跟听者不在同一个room,则应用所有透射损失中的最大值。
在unity中使用
AkRoom
调用AkSoundEngine.SetRoom来设置room(gameobject的id),然后通过音频引擎获取到这个id的gameobject对象,从而得到坐标,设置room的朝向和辅助音频总线等信息,还有就是几何信息(比如collider这样的信息)。值得注意的一点,因为wwise的游戏对象和room的id都是通过AkSoundEngine.GetAkGameObjectID计算出来的,所以当这个对象被设置成游戏对象的时候,不要重复用在roomid。
AkRoomPortal
调用AkSoundEngine.SetRoomPortal来设置Portal的信息
- protal的id,通过这个id找到gameobject在wwise内注册的信息。
- 设置protal的collider的位置信息,中心点,正前方
- 设置protal的大小信息
- 设置protal是否激活状态
- 设置protal连接的两个room的id
AkRoomAwareObject
当wwise调用了SpatialAudio的时候,它会针对每个发声体和听者调用AkSoundEngine.SetGameObjectInRoom把他们设置到相应的room内。
使用AkEvent
我们可以将RoomID当成发声体的ID来设置PostEvent的gameobject对象。
Geometry
通过Geometry可以把三角形网格发送给Spatial Audio实现reflect模拟早期反射,和模拟声音从发声体到听者位置中产生的边缘衍射。
终结
上面我就讲解了下wwise的音频资源在unity内api的使用方案,其实很多声音的效果我们都是可以在wwise内做好的,比如声音的衰减方式,比如弹壳掉落地上的声音随机在左右声道播放,等等效果。它把很多需要我们在unity中自己实现的一些声音表现,使用wwise让音频制作人员给做好了,我们在游戏内就只需要播放一下声音的事件,和声音资源的管理方案。