Wwise 2023.1 包含自 2017 年引入 API 以来对 Wwise Authoring API (WAAPI) 最大幅度的更新。如果还没看过 Wwise 2023.1 新增功能,建议先了解一下。里面列出了大量与 WAAPI 相关的改进。在本文中,我们来详细说说其中的一些改进。
何为 WAAPI?
WAAPI 用于通过外部应用程序(如 Python 脚本或 C++ 程序)与 Wwise 设计工具进行通信。有关详细信息,请参阅以下页面:https://www.audiokinetic.com/zh/library/edge/?source=sdk&id=waapi.html。
借助 WAAPI,可执行以下常见任务:
- 导入音频文件
- 创建对象结构
- 生成 SoundBank
- 查询工程(通过 WAQL)
- 等等
事实上,各位可能在不知不觉中已经用到了 WAAPI。比方说,以下场合就有使用:
- 将文件从 REAPER 传到 Wwise:
- Audiokinetic ReaWwise (https://github.com/audiokinetic/ReaWwise)
- LKC GRIM SYNC (https://www.lkctools.com/grimsync)
- 游戏引擎集成:
- Wwise Unreal Integration
- Wwise Unity Integration
- 音频文件管理:
- Basehead - Wwise Integration (https://baseheadinc.com/)
- Soundly - Wwise Integration (https://getsoundly.com/)
- Soundminer - Wwise Integration (https://store.soundminer.com/)
除此之外,将 WAAPI 集成到游戏开发管线当中还方便自动完成某些任务,进而提高日常工作流程的数据质量。
这个版本的目标
在每一 Wwise 版本中,我们都会在兼顾自身开发目标的同时设法处理客户反馈。对于这个版本,客户最为关注的一点是确保能够在版本控制环境下正常使用 WAAPI。还有就是通过重构其中一些数据模型(包括 Interactive Music 对象)来扩大 WAAPI 的覆盖范围。在本文稍后部分,我们会详细介绍音乐对象。下面,我们先来说说版本控制。
对版本控制的支持
在 2017 年最初开发 WAAPI 的时候,为了交付最简可行产品 (MVP),我们不得不减掉一些元素。其中一项就是对版本控制的支持。现在,WAAPI 已经集成到众多用户的管线中,所以我们必须弥补这一不足。
下面列出了我们要解决的问题:
- 在导入音频文件时阻止弹窗。比如,在使用 ReaWwise 时,每次从 REAPER 渲染 Wwise 中都会弹出版本控制窗口。这样会对 REAPER 用户界面造成阻碍,进而给用户带来糟糕的使用体验。
- 创建、删除、移动和重命名 Work Unit。所有涉及文件系统的操作都要在版本控制系统中注册。并且,很多 WAAPI 函数都会创建 Work Unit。
- 在调用命令行时使用版本控制。在使用 WwiseConsole.exe 时,整个版本控制系统都不可用。
- 管理音频文件。之前无法自动重新组织原始音频文件。只有通过 File Manager 移动或重命名音频文件才不会破坏工程。但是对于较大的工程,自动组织素材非常重要。
为了实现这些目标,我们必须重新审视版本控制插件的工作方式。对此的主要目标是让所有 API 静默运行并与版本控制系统完全兼容。
测试策略
在开始任何工作之前,都必须考虑测试策略。对版本控制系统的测试无疑是项繁琐的任务,这一点不能大意。改进的幅度很大,以至于很容易在不知不觉中破坏到某些东西。好在借助新增的功能可以自动实施测试,这在以前是做不到的。
因此,对我们来说,在自动测试框架下支持 Perforce 是自然的。鉴于测试框架已经涵盖所有的 WAAPI 功能,我们可以采用同样的方法来使用新的版本控制功能,因而对改进的效果也会更有信心。
用户界面
首先,必须控制由版本控制插件创建的用户界面。这些是阻碍自动化流程的主要因素。总的来说,弹窗有两种:
- 自定义弹窗,如“签出”对话框(见下图)
- Source control Log
每次移除对话框都要在 WAAPI 函数中提供相应选项,以便对文件自动执行 Check Out 或 Mark for Add。最终,现在共有 10 个不同的 WAAPI 函数支持自动版本控制操作:
- ak.wwise.console.project.open
- ak.wwise.ui.project.open
- ak.wwise.core.audio.import
- ak.wwise.core.audio.importTabDelimited
- ak.wwise.core.object.copy
- ak.wwise.core.object.create
- ak.wwise.core.object.delete
- ak.wwise.core.object.move
- ak.wwise.core.object.set
- ak.wwise.core.project.save
首先,向所有这些 API 添加版本控制支持并非易事。在 Wwise 设计工具的核心,有太多代码路径需要修改。现在已经有了很宽的测试自动化覆盖范围,但在操作 Work Unit 时可将之进一步扩大。
其次,必须找东西来替代日志对话框。为此,我们将所有版本控制消息转到 Logs 视图并为其创建了新的选项卡:
好消息是 WAAPI 中已经暴露统合日志。只需在 API 中添加一个新的通道即可。
进一步优化自动化流程
至此,我们已经涵盖了大部分的间接版本控制操作。比如,在通过 ak.wwise.core.object.set 创建 Work Unit 对象时触发关联 WWU 文件的 Add 操作。但是,对 WAV 文件的直接版本控制操作(如重命名或移动 WAV 文件)怎么办呢?我们没有办法直接测试版本控制的核心,当前只能通过非版本控制操作进行测试。
最终,我们决定在 WAAPI 中添加整套版本控制 API。为此,添加了以下直接映射到版本控制功能的函数:
- ak.wwise.core.sourceControl.add
- ak.wwise.core.sourceControl.checkOut
- ak.wwise.core.sourceControl.commit
- ak.wwise.core.sourceControl.delete
- ak.wwise.core.sourceControl.getSourceFiles
- ak.wwise.core.sourceControl.getStatus
- ak.wwise.core.sourceControl.move
- ak.wwise.core.sourceControl.revert
- ak.wwise.core.sourceControl.setProvider
为了更进一步,在使用 waapi-server、generate-soundbank 和 tab-delimited-import 时,我们直接在 WwiseConsole 中添加了对版本控制的支持。有了这些,便可进一步拓展自动化流程,从而实现真正的功能区隔。
扩大 WAAPI 覆盖范围
下面,我们来详细说说如何扩大 WAAPI 覆盖范围。在过去几年中,我们对 Wwise 中的诸多数据模型进行了重构,以使其具有通用性并可通过现有 WAAPI 函数访问。在这个版本中,我们将重点放在了 Interactive Music Hierarchy 上。比方说,之前无法在 WAAPI 中配置某些音乐对象。对此,我们在 2023.1 中暴露了与音乐对象相关的内容:
- Music Switch Container 中的关联和参数
- Music Sequence Container 中的 Playlist 根对象
- Music Stinger 中的 Trigger 和 Segment 引用
- 音乐对象中的 Transition 根对象
除了以上所列音乐元素,我们还暴露了以下元素:
- Dialogue Event 中的条目
- Audio File Source 中的标记点
- Random/Sequence Container 中的 Playlist
Interactive Music 示例
现在,我们来仔细看看其中的一些代码。在此,我们会使用 Music Switch Container、Music Playlist Container、Music Segment、Music Track、Music Cue 和 Music Transition 创建一个可播放的 Interactive Music 结构。如果各位想依照代码操作,可从 GitHub 下载整个示例:
https://gist.github.com/ak-brodrigue/b98d12e67167eb0e00291cd9c2c02164
该示例使用 ak.wwise.core.object.set 在单个 WAAPI 调用中创建了整个音乐结构。注意,在创建结构时还会导入音频文件。这在早期 Wwise 版本中是无法做到的。
我们先来说说对象定义这个概念。在调用 ak.wwise.core.object.set 时,会将对象定义传给函数。对象定义至少包含以下字段:
- type:对象的类型(详见 https://www.audiokinetic.com/zh/library/edge/?source=SDK&id=wobjects_index.html)。
- name:对象的名称(对于有些对象类型,该字段可能为空)。
此外,对象定义还可选择包含以下字段:
- children:定义一组其他对象定义。
- import:定义要将哪些音频文件导入到对象中(2023.1 新增)。
- 属性值、引用值或列表值,如 Volume 或 OutputBus(带有 @ 前缀)。
为简便起见,我在 Python 中编写了一个通用实用程序函数。该函数负责生成与传给 ak.wwise.core.object.set 的对象定义对应的必要 JSON 代码。事实上,无需理解这个函数就可以使用它。下面的示例中就使用了该函数。
def Object(type, name, *children, **properties):
# 基本定义
definition = {
"type" : type,
"name" : name
}
# 获取关键字参数并将其转换为属性
for key, value in properties.items():
definition['@'+key] = value
# children 可为其他定义或文件导入指令
if len(children) > 0 and isinstance(children[0], str):
# 在发现第一项参数为字符串时,我们将其视为文件路径
definition["import"] = {}
definition["import"]["files"] = []
for file in children:
definition["import"]["files"].append({
"audioFile" : file,
})
else:
# 否则,将其视为一组 children 定义
definition["children"] = children
return definition
还要注意的是,这里使用了 Python 参数系统。其中前两个参数是固定的,而其他参数会自动转换为 children 或音频文件导入定义。最后,该函数还使用了关键字参数来定义属性。
下面我们来介绍一个创建 Music Segment 的专用函数。它重复使用了上面所说的函数:
def MusicSegment(name, *children, **properties):
# 为 Music Segment 创建定义
return Object("MusicSegment", name, *children, **properties)
现在,我们可以使用这个函数来创建 Music Segment 定义(如下所示):
MusicSegment("Segment1",
R"C:\MyAudio.wav",
Volume = -3)
如果还想拥有多条 Music Track,则可使用:
MusicSegment("Segment1",
MusicTrack("TrackA",
R"C:\TrackA.wav",
Volume = -2
),
MusicTrack("TrackB",
R"C:\TrackB.wav",
Volume = -2
)
)
Music Playlist Container 的创建与之并无太大区别。不过,这次我们需要指定播放列表本身。注意,在以下示例中,我们在容器上设置了PlaylistRoot(Playlist Item 树)。另外,我们还会在不同的 Music Playlist Item 上设置 LoopCount 和 Segment:
MusicPlaylistContainer(
R"MPL1",
MusicSegment("Segment1", R"C:\Segment1.wav"),
MusicSegment("Segment2", R"C:\Segment2.wav"),
PlaylistRoot =
MusicPlaylistItem(
MusicPlaylistItem( Segment = root + "\MSC\MPL1\Segment1"),
MusicPlaylistItem( Segment = root + "\MSC\MPL1\Segment2"),
LoopCount = 0
)
)
最后,为了创建 Music Switch Container,我们要将 Switch 与子对象关联起来。对此,可通过 MusicSwitchContainer 函数中的前两个参数来实现。建议查看一下实现代码,其中包含了将 Switch 与子对象关联起来的细节。
MusicSwitchContainer(root + R"\MSC",
# 音乐容器订阅的 Switch Group
[
R"\Switches\Default Work Unit\SWG_1",
R"\Switches\Default Work Unit\SWG_2"
],
# 关联条目
{
R"SW_1_1.SW_2_1": R"MPL1",
R"SW_1_2.SW_2_1": R"MPL1",
R"*.SW_2_2": R"MPL2",
},
# 子对象
MusicPlaylistContainer("MPL1", ...),
MusicPlaylistContainer("MPL2", ...),
最后,在 WAAPI 中将所有这些定义传给 ak.wwise.core.object.set。整个定义树决定了如何创建音乐结构。
下一步?
大家不妨试用一下 Wwise 2023.1 中新增的 WAAPI 功能。我们想知道各位对此有什么看法,同时希望这些功能可以带来帮助。各位希望用这些功能来做什么呢?我们一直都很想了解大家如何使用我们的产品。
各位希望将来在 WAAPI 中增添哪些功能呢?请在“试用反馈”类别的问答版块给我们留言。
如有兴趣学习 WAAPI 但不知从哪里入手,可参阅以下链接:
- 快速了解 WAAPI (https://www.audiokinetic.com/zh/library/edge/?source=SDK&id=waapi_gettingstarted.html) – 支持的不同编程语言的概述和示例。
- WAAPI 参考(https://www.audiokinetic.com/zh/library/edge/?source=SDK&id=waapi_functions_index.html) – 了解各种 WAAPI 函数的详细信息。
- ak.wwise.core.object.set (https://www.audiokinetic.com/zh/library/edge/?source=SDK&id=ak_wwise_core_object_set.html) – 详细了解 ak.wwise.core.object.set。
- 导入音频文件和创建结构 (https://www.audiokinetic.com/zh/library/edge/?source=SDK&id=waapi_import.html) – 了解如何创建 Wwise 结构。
- Wwise 对象参考 (https://www.audiokinetic.com/zh/library/edge/?source=SDK&id=wobjects_index.html) – 了解 Wwise 对象及其属性。
评论