|
Wwise SDK 2023.1.17
|
在 Wwise 声音引擎中,使用 Event(事件)和 Game Syncs(游戏同步器)控制播放。这些元素引用 SoundBank 中存储的内部声音结构,并最终引用松散的媒体文件。所有这些内容在使用之前必须由声音引擎完成加载。
![]() | 备注: PrepareEvent 和 LoadBank 不可共用,因为两者都会将数据加载到内存中。 |
您可以显式或隐式加载 SoundBank 。这两种方法(将在下文中详细介绍)提供的选择都暗含优缺点,需要根据特定场景对这些优缺点做出评估。
您可以使用声音引擎 API 的 AK::SoundEngine::LoadBank() 方法之一显式加载 SoundBank 。在加载 SoundBank 后,它里面的所有对象就准备就绪了。
在下例中,将显式加载 SoundBank BANK_01。此 SoundBank 通过 ID 进行标识,ID 在 Wwise_IDs.h 头文件中定义。有关使用字符串(Unicode 或 ANSI)或 ID 标识 SoundBank 的讨论,请参阅 标识 SoundBank 。其中包含事件 PLAY_SOUND_01 及其相关的声音结构和媒体(请参阅 Wwise 帮助,了解有关使用 Wwise 生成 SoundBank 的详情)。加载后,事件会发送给声音引擎。
PrepareEvent/PrepareGameSync API 可以隐式加载 SoundBank 中未包含的媒体。为此,您必须首先通过 LoadBank() 显式加载包含 Event 或 Game Sync 定义的 SoundBank 。这些 SoundBank 通常只含 Event 和结构,不含媒体,因此很轻量。请参阅 Wwise 帮助,了解如何定义没有媒体数据的 SoundBank 。当 SoundBank 中不包含被引用的媒体时,文件系统中必须以松散媒体文件的形式提供该媒体。在加载 Event 和 Game Sync 的定义后,必须对它们做“Prepare”操作以便使用(AK::SoundEngine::PrepareEvent() 和 AK::SoundEngine::PrepareGameSyncs())。在对事件或游戏同步器做 Prepare 操作时,声音引擎将从文件系统中隐式提取被引用的媒体文件。可把声音结构包含在不包含事件定义的 SoundBank 中。然后 Event 所在的 SoundBank 中将包含对含有结构数据的 SoundBank 的引用,在 对事件做 Prepare 操作时,被引用 SoundBank 中的结构将与媒体一起隐式加载。包含的所有结构将从链接的 SoundBank 中加载,而非仅从 Event 所引用的 SoundBank 中加载,为此,把 Event 和结构绑定在同一个 SoundBank 中通常更为实用。
在下例中,SoundBank BANK_02 显式加载,它所引用的媒体隐式加载。它包含事件 PLAY_SOUND_02 的定义和相关声音结构。由于它不包含事件相关媒体,因此若不提前 对事件做 Prepare 操作,则该事件的发送将会失败。因此,在成功加载 SoundBank 后,先 对事件做 Prepare 操作,然后再发送。在 对事件做 Prepare 操作时,声音引擎将自动加载成功发送事件所需要的所有媒体。
获得有关如何使用 AK::SoundEngine::PrepareEvent 和 AK::SoundEngine::PrepareGameSyncs 的完整示例,请参阅以下章节:
SoundBank 加载是在单独的声音引擎线程中执行的。主要 API 的所有 LoadBank()、PrepareEvent() 和 PrepareGameSyncs() 功能均可通过同步和异步加载方案获取。
同步 AK::SoundEngine::LoadBank() 功能是阻塞功能。这些 API 函数将在加载 SoundBank 完成或发生错误时将返回。
异步 AK::SoundEngine::LoadBank() 功能立即返回,并在完成请求的操作后,以 cookie 作为参数来调用回调函数。异步调用时,必须在回调函数中执行错误处理。
cookie 参数是可选参数,为方便操作而提供。如果您不使用它,只需赋予 NULL 值。声音引擎将不使用此指针;它仅通过回调函数返回。
搭配 LoadBank()、UnloadBank()、PrepareEvent() 和 PrepareGameSyncs() 的异步版本使用的回调函数必须遵循此原型:
您负责实现回调函数,因此您必须保证它的有效性。
参数 in_bankID 在针对 PrepareEvent 和 PrepareGameSync 接收回调时用不到,请直接忽略。
请参阅 AkBankCallbackFunc 了解传递给回调函数的参数的定义。
如前面 标识 SoundBank 中述,您可以自己从文件中加载 SoundBank ,然后为相应的 LoadBank() 重载提供指针和大小,也可以指定 SoundBank 标识符(ID 或字符串,见上文讨论),让声音引擎通过 Stream Manager 加载 SoundBank 文件。
使用以下某个 LoadBankMemoryView() 原型来加载位于内存中最终位置的 SoundBank:
声音引擎内不复制内存,因此必须确保在卸载 SoundBank 之前,内存一直保持有效。根据具体平台要求,SoundBank 加载涉及的内存指针上可能要用到一些对齐限制。在所有平台上,内存必须与 AK_BANK_PLATFORM_DATA_ALIGNMENT 字节对齐。某些平台可能有不同的要求,因此您应该检查对应平台的 SDK 文档。
或者,使用以下某个 LoadBankMemoryCopy() 原型来加载位于内存中临时位置的 SoundBank:
SoundBank 将被复制到声音引擎内新分配的位置,完成加载操作后便可立即释放传递的指针。
LoadBank() 解析随附指针前几个字节中存储的 SoundBank ID,并返回此 ID。保存好 SoundBank ID,以备稍后卸载 SoundBank 。
![]() | 备注: 如果您选择自己执行文件 I/O,并向声音引擎馈送指针,则必须使用显式 SoundBank 加载。隐式 SoundBank 加载可能无法预测。按照 PrepareXXXX 命令,您无法确定要加载的 SoundBank 数量以及这些 SoundBank 的内存需求。这就是没有 PrepareEvent 和 PrepareGameSyncs 内存版的原因。 |
每当需要读取文件时,声音引擎总是使用 Stream Manager。请参阅 标识 SoundBank 了解有关 SoundBank 标识与 I/O 的讨论。
您可以使用 AK::SoundEngine::SetBankLoadIOSettings() 功能,在 Stream Manager 方面微调声音引擎 SoundBank 加载程序的行为。有关 I/O 的详情,请参阅 流播放/流管理器 一节。
声音引擎解析 SoundBank 的元数据,并在声音引擎的默认池中创建它的对象。
在涉及 I/O 的显式 LoadBank() 函数中,会从磁盘读取媒体并将其复制到内存中。在 I/O 期间,将按照声音引擎初始化设置中 AkInitSettings::uBankReadBufferSize 指定的大小分块读取 SoundBank。该数值越大,读取次数就越少,但会增加 SoundBank I/O 期间的内存用量。
有许多 UnloadBank() 重载可用于显式卸载 SoundBank :
同样,搭配 Preparation_Unload 标志使用 PrepareEvent() 函数来减少与这些事件相关的结构和媒体的引用计数。当隐式加载的 SoundBank 中所有对象的引用计数减少到 0 时,它会自动卸载。
另外,要搭配 Preparation_Unload 标志使用 PrepareGameSyncs() 函数来卸载采用了 Prepare 操作的事件所加载的媒体。只有当选择了指定游戏同步器时,这些事件才会播放。请注意,游戏同步器没有引用计数,使用 Preparation_Unload 标志调用它一次将立即卸载未被引用的媒体。
![]() | 备注: 如果在卸载 SoundBank 时其所引用的声音正在播放,并且 SoundBank 中包含声音的声音结构,则声音将停止播放。如果 SoundBank 中只包含媒体,但其它已加载的 SoundBank 中有媒体和声音结构,则声音有可能会停止。这取决于媒体是使用该 SoundBank 中的数据播放还是使用其它 SoundBank 中的数据播放。请参阅 复制库内容 。 |
![]() | 备注: 如果有事件更改过此声音的参数(例如 SetVolume 事件),则此更改信息将被移除。如果参数已被 RTPC、State 或 Swtich 更改过,则参数将保留在内存中,并将在重新加载 SoundBank 时自动应用。 |
以下代码使用 Unicode 字符串标识符和默认 SoundBank 内存分配方法同步加载和卸载 SoundBank 。它通过 PrepareEvent() 隐式加载和卸载 SoundBank 。
除 LoadBank() 和 PrepareEvent() 函数外,还可对 SoundBank 做 Prepare 操作。您可以使用以下任一方法对 SoundBank 做 Prepare:
这两种方法使用的是相同的 PrepareBank 机制,只是在实现上有少许不同,但最常用的是 AkBankContent_All。.
使用 AkBankContent_All 来对 SoundBank 做 Prepare 可以克服 LoadBank() 机制的一些弱点,同时还可以发挥 PrepareEvent() 机制的优势。在使用此方法时, SoundBank 中仍可包含所有内容类型(事件、结构数据和媒体文件),但是此方法不直接加载媒体文件,而是运用类似于 PrepareEvent 的机制把所有媒体加载到内存中。在使用 PrepareBank() 加载媒体时,Wwise 首先查看媒体文件在内存中是否已经存在,然后再加载。这可以避免内存中出现媒体文件重复,从而把内存占用保持在最低水平。
AkBankContent_All 是 PrepareBank() 的默认加载机制,它会检查 SoundBank 中需要载入内存池的媒体项。如果特定事件的媒体在 SoundBank 中不存在,则稍后可调用 PrepareEvent() 从松散文件中加载它。
在搭配 AkBankContent_StructureOnly 使用 PrepareBank() 时,会从 SoundBank 中加载事件和结构元数据,但会忽略 SoundBank 中包含的媒体。由于 PrepareEvent() 必须把媒体当作磁盘中的松散文件进行访问,并且无法读取包含在 SoundBank 中的文件,因此只有当您打算稍后使用其它加载机制加载 SoundBank 时,AkBankContent_StructureOnly 才有用。在使用 PrepareEvent() 分别加载媒体的大多数场景中,媒体不应包含在 SoundBank 中,AkBankContent_StructureOnly 标志将产生与 AkBankContent_All 相同的结果。
AkBankContent_StructureOnly 标志可能有用的一个场合是实现多个加载的配置。游戏可有使用 PrepareEvent() 按需加载松散文件的“工具模式”,以及使用 LoadBank() 整体加载同一 SoundBank 的“游戏模式”。
如果需要,可通过 API 同步或异步调用 PrepareBank()。然而,建议不要对同一 SoundBank 同时使用 AkBankContent_All 和AkBankContent_StructureOnly ,因为媒体一旦使用 AkBankContent_All 进行加载,卸载时将释放所有内容,包括事件、结构和媒体。
当您想要重置声音引擎的内容时,调用 AK::SoundEngine::ClearBanks() 函数非常有用。请注意,在调用 SoundEngine::Term() 之前,您不必调用 ClearBanks()。 ClearBanks 在内部调用AK::SoundEngine::ClearPreparedEvents。
在调用此函数后:
无论某个事件已经做了多少次 Prepare,调用 AK::SoundEngine::ClearPreparedEvents() 函数将取消到目前为止已做了 Prepare 操作的所有事件。在调用 AK::SoundEngine::ClearBanks() 时,内部将调用 AK::SoundEngine::ClearPreparedEvents() 。
各个 SoundBank 只可加载一次。如果您试图第二次显式加载某个库,则将导致 SoundBank 加载错误。
The Wwise sound engine allows you to have the same Events, sound structures, or media in two or more banks, and have them all loaded at the same time.
![]() | 备注: 对于不支持媒体重定位的 XMA 和 OpusNX,若某一声音同时用于多个正在播放的 SoundBank,而该 SoundBank 已被卸载,则将停止播放该声音。 播放不会过渡到其它有这份声音并且已加载的 SoundBank 中。 |
在使用 PrepareEvent 时,如果多个不同事件需要加载同一媒体内容,则内存中不会出现重复加载该媒体复制品的情况。 多个事件可引用同一媒体对象,只有当引用同一媒体对象的所有事件都被取消了 Prepare 操作时才会卸载此媒体对象。
Using LoadBank along with PrepareEvent to load the same media content may cause media duplication because banks are loaded as an entity when using LoadBank.