大家好,我是溪夜。
在《人人都能用 WAAPI(一)概述》中,我们用思维导图对 WAAPI 进行了重新归纳,并在配置好开发环境后,一起用 Python 写了几个简单的小程序,体验了 WAAPI 强大功能的冰山一角。
从第二篇起,我们会开始介绍 WAAPI 的所有分支。为了不当一个讨厌的“文档翻译官”,我会努力用简单化的语言来描述 WAAPI 的功能,并在最后把 API 组成简单的示例程序。
考虑到 Audiokinetic 官网无代码框,我在 GitHub 创建了一个仓库,其中包括了本系列文章中所有的代码,地址为:https://github.com/zcyh147/Everyone-can-use-WAAPI
阅前须知:
- 把 API 逐条列出讲解的主要目的是为了快速阐述其功能,补充文档叙述中的一些不足,以帮助大家快速对 WAAPI 建立印象。在这里并非有意重写文档,所以建议看完本文后按照第一篇文中所提的方法,用自己喜欢的整理方式重新整理一次。
- 本文并不会把每个参数全都翻译并复述一遍功能,这里均假设读者已学会了概述中的 JSON 阅读方法,能够自行根据我的介绍查阅 WAAPI 参数及返回值等文档信息,并希望读者在阅读时能准确的找到在线文档中对应的章节以便于及时参考。
- 为了照顾初学者,我尽量把注释写到每一行,以便快速入门。
- 学会 RTFM(Read The Fucking Manual),因为接下来的文章比较严肃。如发现某些概念看不懂或找不到,请善用 Wwise 文档强大的搜索功能或通过 Google 检索所需答案。
- 因为 WAAPI 的功能也是随版本更新逐渐添加的,建议安装最新版 Wwise 进行学习。当报错信息提示 “The procedure URI is unknown.” 时,就代表你的当前 WAAPI 版本并不支持此 API。
本文目录:
[toc]
wwise.core 概览
作为 WAAPI 中最大的模块(以我在上一篇提出的分类逻辑),wwise.core
承载着最多的功能。除了它与 soundengine
模块外,执行类剩余的几个模块的功能相对简单,所以 wwise.core
会成为我们投入精力最多的一个部分。
简单的看一下上面的思维导图,不难看出本模块的功能主要针对 Wwise 音频设计部分,而不是对声音引擎。这也代表如果你有一些对 Wwise 操作级的优化想法,这会是对你最为重要的章节。
注意:图中划线的部分代表 WAAPI 中已被新功能替代的部分。
audio(导入音频并创建对象)
功能简介
audio
分支下有 import
和 importTabDelimited
,根据名字容易看出这是负责导入音频资源的功能。这两者的区别是前者只能导入音频并创建对象,后者为根据“制表符分割文件”中所定制的规则导入音频并执行相关操作。
所谓制表符分割文件导入文件,即在音频文件导入时使用一个制表符分割的文本文件读取提前规定的被导入相关属性(例如音频文件在本机的储存位置、Wwise 中它要所产生对象在各个层级〔各种容器、Event、Output Bus〕的位置、设定属性值和引用、Switch 指派子元素等)。
这种文本文件可使用 Excel 或者其它电子表格工具生成,其中的第一行所填写的内容定义了每列的标题所定义的属性,每列中的内容决定了在 Wwise 中建立什么内容。
应用场景
不难看出,audio
分支是最重要的资源导入分支,也是大部分实用性 WAAPI 程序的起点。这类工具的功能致力于帮助减少人工导入大量文件时带来的繁琐操作,同时根据所定义的信息能够自动完成层级、属性、流播放等设定。
如果想通过 import
导入音频文件,就需要再文件名上定义其在工程中的各种位置和属性。为此,要建立一个词典对应文件名中的代号,以减少文件名过长带来的报错可能。
因为操作系统对于文件名和路径的长度有所限制,所以 Wwise 中的文件名长度要受此约束,理论只支持最大256字符长度文件名的资源导入。再加上系统路径所占用的字符数,最后往往给不到用户那么多可用字符,也会直接带来错误。
我们也可利用 importTabDelimited
通过制表符分割文件完成导入工作,因为需在 Excel 中定义符合 Wwise 可识别信息规则的制表符分隔 txt 文件。对于这个步骤其实也可进行优化。例如可通过 VBA 给声音设计师创建一个在 Excel 中快速对制表符分割文件结构构建的程序,或使用 Python 创建一个更完备的 GUI 程序以完成制表符分割文件的快速创建等。
audioSourcePeaks(音频源峰值获取)
功能介绍
此分支下有 getMinMaxPeaksInRegion
和 getMinMaxPeaksInTrimmedRegion
,其功能为获取音频峰值并返回相关信息。两者的区别是前者返回的是用户所选区域的峰值,后者是对修剪过后的音频取峰值。注意这个峰值不是唯一的 Max Peak,而是根据 numPeaks 参数指定数量的 Min/Max Peak。
在 getMinMaxPeaksInRegion
的参数中,可看到 timeFrom
和 timeTo
,这两者用来确定取值区域的开始和结束时间。getMinMaxPeaksInTrimmedRegion
则无需指定开始结束时间,会直接对 SFX 对象裁剪后的部分进行求值。audioSourcePeaks
两个功能的返回值都是使用 Base 64 编码的16位有符号 int 数组。所以要经过 Base 64 解码,按照给定的格式(fmt)解析字节流,再换算成 dB。
应用场景
本人对 Wwise 插件开发了解还不够,但对一段音频取这么多最大/最小峰值点的操作看起来并不是普通的需求,这两个功能提供的最多点位可多达 14294967295。如有需求存在要在 Wwise 内获取音频片段的 Peak 点的操作,可使用此两者进行计算。
object(对象操作)
功能介绍
create, copy, delete, move(对象基本操作)
这四个功能是最基本的对象操作,分别对应对象的创建、拷贝、删除、移动。所需参数都是围绕着对象名、类型、它现在的位置、之后的位置等跟对象有关的基本参数。create
与 audio
功能导入的音频时创建的对象的覆盖范围略有不同,create
能够创建文档中 Wwise Objects Reference 中列出的海量对象,audio
在导入音频的时候则只能创建与音频相关的对象(各类容器、Event 等)。create
的功能非常广泛,从创建简单的的 Sound SFX 对象或 Work Unit,创建复杂容器并指定元素,甚至能够创建效果器和声源插件等。
setReference, setRandomizer, setAttenuationCurve, setName, setNotes, setProperty(设置对象的各种属性)
create
创建对象时只能设置即少量属性(比如本小节中提到的六个 API 里,只有 Notes 能在对象创建的时候被设置),所以我们需要通过上面的这些功能进一步设置对象属性。
其中 setReference
为设置引用,如想要为一个 SFX 对象设置输出 Bus,就需要为这个对象添加对某个 Bus 的引用。setRandomizer, setAttenuationCurve, setName, setNotes
这些功能为语义本身功能,设置随机、衰减曲线、名字、备注。setProperty
是真正用来设置大量属性的命令,在文档的 Wwise Objects Reference 中我们可以看到大量有关对象的信息,点开某一个,你就可以看到其能被设置的所有属性名。
举例来说,如果想对 Mastering Suite 中的某项属性进行设置,只需提供它的 GUID 或路径这类位置信息,再把 “property” 键对应的值修改为要修改的属性名,最后提供合适的值即可完成属性修改。
get, getTypes, getPropertyInfo, getAttenuationCurve, getPropertyAndReferenceNames, isPropertyEnabled(获取对象的各种属性)
如果想获取现有工程的各种数据,就需要用 get
这几个功能。get
是基于 query(查询)的功能,它能够获取到大量工程信息,例如 ID、名字、占用空间大小、路径、播放长度等,设定好合适的查询参数即可完成查询。就像我们在概述篇提到的,它可同时传入 Arguments 和 Options 两个参数,在使用时可按需设置 Options 来控制查询的返回结果。
这里解释一下其所使用的两个参数的含义。
其中 Arguments 中含有 from 和 transform 两个子参数:
- from 为查询的数据来源,即以名字、GUID、路径,类型等作为限制条件来搜寻对象。除了对单个对象 GUID 的查询,其中还有些条件比如
ofType
或者search
能够找出符合条件的一整类对象。 - transform 比较有趣,功能是对已选择的对象进行变换。比如我们现在在 from 中设定选择了某个层级下的 Random Container,此时在 transform 中再添加 select parent,就会转而选择上级对象。transform 是 from 的一种重要补充,能够方便的对已设定的 from 条件进行父级、子级和引用等的查询。
Options 中含有 return 和 platform 两个子参数,注意 Options 是可以留空的:
- return 指定了对象被查询后返回什么内容,留空则默认返回 GUID 和对象名。
- platform 指定了查询什么平台下的结果,留空的话会查询默认平台。
那为什么还要有 getTypes, getPropertyInfo, getAttenuationCurve, getPropertyAndReferenceNames, getPropertyNames
呢?
其实这是 WAAPI 设计时对功能分工的不同,get
更针对对象的各种属性进行查询。另外四个查询更偏向对象的元属性,getTypes
返回当前所有注册的对象类型,getPropertyInfo
返回对象属性的具体信息(例如取值范围等),getAttenuationCurve
返回特定对象的衰减曲线设置,getPropertyAndReferenceNames
返回对象拥有的属性和当前引用名称。isPropertyEnabled
可用来检测一个属性是否处于激活状态。
应用场景
对象操作的可应用之处非常多,例如可通过 get
系获取转码后音频文件的空间占用情况、获取对象的属性和引用状况、查询工程中所有的平台、所有正在使用的 Output Bus 等。如果对于 Wwise 的 Queries 功能有所了解,用这个功能会很顺手。
getInfo 和 log.get(工程信息和日志获取)
功能介绍
这是两个简单的信息获取功能,getInfo
用于获取当前工程全局信息,例如 Wwise 版本、平台信息、WAAPI 版本等。log.get
用于获取的日志,Logs 窗口中的那几个选项卡就对应本 API 中的参数 channel
。
如果想实现在自己的程序里动态更新日志的状态,则需要用订阅类的功能,有关这部分我们会在之后的章节里进行讨论。例如订阅类中的 ak.wwise.core.log.itemAdded
,就是在有对象被添加时返回信息。
应用场景
在对工程全局信息和日志有需求时,可根据需求使用这两个功能。因为 log.get
能获取最新的日志,可以在 WAAPI 程序中某些步骤后加入这项功能,从而把每次的日志保存到数据库或进行预想的分析,以获得更为定制化的反馈报告。
profiler(分析器功能)
功能介绍
startCapture, stopCapture(Capture 开关)
单纯的控制 Profiler Capture 的开始与停止,无需参数即可直接调用。
getBusses, getCursorTime, getRTPCs, getVoiceContributions, getVoices(获取 Profiler 中特定时间点的信息)
其中 getBusses
、getRTPCs
、getVoices
为同一类功能,通过传入一个特定的时间作为参数,即可查询 Profiler 此刻的 Bus、RTPC ID、RTPC 值、相关声部等信息。getCursorTime
可获取当前 Profiler 光标的位置,并返回时间。getVoiceContributions
需要通过管线 ID 和 Profiler 的时间位置,可查询影响干声路径的音量和高低通等参数。
注意,其中的 getBusses
和 getVoices
也可传入两个参数,第二个参数用来指定查询范围。
应用场景
不难看出,startCapture, stopCapture
可集成到工具上进行手动或自动控制,方便进行比较复杂的捕捉需求。例如使用 Unity WAAPI 客户端,把一些分析的功能做进去,在 Unity 中的控制采集开关。get
类功能可根据需求获取分析数据,再根据需求进行分析之用。
project.save(保存工程)
功能介绍
保存当前工程。
应用场景
单纯代替 Ctrl + S 过于简单,比较有用的场景可以是在追求稳定的时候根据触发条件自动保存工程。比如在批量导入上万文件或实行其他批处理操作时,为了工程安全,或许可在每导入100个甚至10个文件时就调用此功能保存一次,以避免任何潜在的不安全。
remote(远程连接到游戏)
功能介绍
connect, disconnect(连接,断开连接)
对于看过 Wwise 一分钟的小伙伴,应该看过侯老师亲自配音的“如何使用 WAAPI 将 Wwise 连接到您的游戏”(https://www.bilibili.com/video/BV11E411P7GW),在视频中就是通过 connect
完成了连接操作。注意,调用 connect
时需要提供主机 IP 并可选提供端口号,这也是为什么下面存在 getAvailableConsoles
,如果有连接到不同的游戏引擎中的需求,就可通过此功能先获取可连接的客户端列表再进行操作。
getAvailableConsoles, getConnectionStatus(查看当前可用的控制台和获得当前连接状态)
getAvailableConsoles
用来获取当前可用的控制台,即查看当前有哪些跟 Wwise 可连接的游戏引擎工程,并返回编辑器名字、IP、端口、平台等信息。getConnectionStatus
则是获取当前连接状态,只有跟游戏引擎连接完成后,才会返回它们的控制台信息和连接状态。
应用场景
remote
中的连接和断开连接功能,就像 Wwise 一分钟里所讲可以直接设置到游戏中的菜单上,从而进行点击调用来达到快速连接游戏。或者通过 StreamDeck 和 Metagrid 这样的工具快速触发 Python 脚本来激活远程连接,能够有效减少操作浪费的时间。
对于 getAvailableConsoles
和 getConnectionStatus
,如果大家记得 profiler
部分,相信能想到把这两个紧密相关的功能结合,可以把 Wwise 分析器的数据搬到自己的程序中。比如自动化提取想要的信息,或者简单的在自己的程序里告知用户当前连接状态如何,以实现比较完善的 Wwise 外工作流。
soundbank(SoundBank 生成与内容设置)
功能介绍
generate
意如其名,负责生成 SoundBank。我们知道,日常生成 SoundBank 时除了全部生成外,还可勾选特定的 SoundBank、平台、语言来生成,同理,使用 generate
时也需要设置这些参数。getInclusions
可以返回 SoundBank 内包含所有 Event 的列表,其中包括 GUID 和 Event 所含的层级结构。setInclusions
负责设置 SoundBank 内包含的内容,可以对内容添加、删除、替换。
应用场景
soundbank
类中如果想对不存在的 SoundBank 进行内容设置,需先通过 create
进行创建 SoundBank 对象和层级,之后才能继续设置包内内容。
至此,一套完整的创建工作流就产生了,audio
类负责导入音频并创建音频层级结构,create
类可以创建并构建各种其他层级和 Event 等,最后由 soundbank
类负责收尾创建 SoundBank。这套工作流可用于根据外部设定的结构文件自动化创建,或在 Wwise 外的控制程序上通过 soundbank
检视工程内部结构。
switchContainer(Switch 容器内操作)
功能介绍
在讨论 create
类时我们谈到它可以创建各种容器,其中就包括 Switch Container。那为什么这里又出现了 switchContainer
呢?
众所周知,Switch Container 指派了控制当前容器的 Switch/State Group 后,需要对每一个引入的 Switch / State 指派容器中的对象,这样才让能 Switch Container 有实际的工作意义。
WAAPI 中把对 Switch Container 中进行指派的这类操作独立出来,使用 switchContainer
类来进行相关操作。addAssignment
和 removeAssignment
分别为添加与去除 Switch 指派的功能,只需要提供 Switch Container 中对象的 GUID 和目标 Switch / State Group 中的 Switch / State 的 GUID 即可轻松完成删减。getAssignments
是一个非常简单的功能,传入给定 Switch Container 的 GUID,会返回当前容器下所有子对象的 GUID 和指定的 Switch / State 的 GUID。
应用场景
switchContainer
的应用场景很简单,可作为自动化导入工具流的补充,对导入时创建的容器进行 Switch 元素指派。
transport(走带条控制)
功能介绍
create, destroy, executeAction
create, destroy
为一对功能,前者是在当前工程中创建一个走带条对象(Transport Object),后者是销毁这个走带条对象。因为是针对对象的操作,所以创建时需要对象的 GUID,销毁时需要走带条对象的 ID。
什么叫走带条对象?其实在 Wwise 日常的工作中我们无时无刻不在重复这两个动作。
例如选中 Event A,走带处的控制焦点会变成控制它的状态,即为其创建了一个走带条对象。此时选中其他对象,这个走带条对象销毁,创建下一个。如果点击了走带条上的 Pin,这个走带条对象就会持续存在,而其他对象的走带条对象创建被阻滞。
对于 Soundcaster 来说,每个 Session 其实就是一堆走带条对象的集合。executeAction
功能如其名,可以控制走带条对象的播放行为,它具有五种 Action 可供使用。
getList, getState
getList
为获取当前工程内所有走带条对象,在 WAAPI 中走带条对象创建后如没经过 destroy
手工销毁,可通过此功能查看当前还存在的走带条对象。getState
为获取走带条对象的状态,在 executeAction
为走带条对象指定播放状态后可通过此命令进行查询。
应用场景
transport
类的功能应用场景比较简单,如果想在 Wwise 外的程序里创建简单的播放行为控制功能,可通过其进行实现。这个外部的程序可以是自己的外置集成工具,也可以是通过 WAAPI 在声音引擎中对 Wwise 中的声音进行预览,甚至在 Wwise 外可复刻一个 Soundcaster。
undo(撤销)
功能介绍
beginGroup
和 endGroup
的概念对于接触过 ReaScript 的人可能更容易理解一些,功能类似 ReaScript 中的 Undo_BeginBlock() 和 Undo_EndBlock()。即执行完 beginGroup
后到执行 endGroup
之前所有的操作都会被保存为一个 Undo Group。其中 beginGroup
可以被堆叠使用。cancelGroup
为撤销最后一个 Undo Group 内的所有操作。
应用场景
Undo Group 是把一串行为一次性撤销的设计,当然,Undo Group 理应也可以作为 Redo 来使用,不过 WAAPI 中目前只有撤销功能,所以想实现 Redo 还需等待 API 更新。从应用场景来说,undo
类功能可为 Wwise 外工具增添撤销功能。
总结
如果按照顺序读下来感到有些遗忘是正常的。这也是为什么在开头我建议大家看完这篇文章后自己对 WAAPI 进行一次重构的原因。
要把 WAAPI 做成自己的武器库,应该按照如下步骤进行:
- 整理出你自己的 WAAPI 框架,无论是详细的笔记还是简略的思维导图
- 发散思维,思考自己要解决音频设计与整合工作中的哪些痛点
- 抽象出头脑中原型的逻辑,并寻找到可使用的 API
- 完成功能代码
实例
单纯的介绍是枯燥的,下面通过实例体验一下 WAAPI 的应用。
因示例更注重功能代码实现,故不会像真实项中一样进行交互设计或者加入 GUI。所以这部分代码实际上是从程序里抽象出的核心功能代码,如果想要应用到自己的程序上,只需为其设计交互层即可。
在这里我会尽量覆盖到 wwise.core
分支下的 API,少量没用上的功能大家可自行研究,使用原理上区别不大。
以下代码的重点是参数调用方法,当遇到奇怪的 Bug 时仔细看看你写的和示例的区别,应该就能找到问题所在。
为了方便使用,我把每个功能都封装到了函数里,可修改概述中 Functions(执行类)示例的执行代码以上在本机试用。
Wwise 在 macOS 下的目录结构与 Windows 不同,因为其运行在 Wine 中,在 macOS 端进行尝试的同学请注意一下文件路径的问题(点击菜单中的 Project,在最近访问的工程处可查看 Wine 中 Wwise 的具体目录)。
1. 导入 Sound SFX 并建立层级结构并设置一些属性,随后为这个 Sound SFX 创建 Event 并设置动作类型。最后创建 SoundBank,并添加之前创建的 Event,并生成这个 SoundBank
快速导入音频文件到 Wwise 中并建立所需的音频层级,甚至建立 Event,这无疑会极大提高音频整合时的效率。在下面的实例中我们需要手工的在参数中填写文件路径、属性、层级等相关信息,在实际工作中可以发挥想象力,把工具中这部分繁琐的工序也解决掉。
比如通过规范的文件名代号、与数据库联动、正则表达式等方法让文件在导入前有足够的元数据准备,再通过你所使用的编程语言自动把这种格式化好的文件名格转化为 WAAPI 导入文件所需设置 JSON 参数,即可真正实现把音频快速导入 Wwise 工程的目的。
选定 API
- 导入文件:
import
- 设置属性:
setProperty
- 创建 Event 并添加声音、创建 SoundBank:
create
- 组装并生成 SoundBank:
setInclusions
、generate
编写代码
1 | # 1. 导入文件并建立音频层级 |
2. 获取当前工程全局信息、Sound SFX 大小、SoundBank 大小
选定 API
- 获取全局信息:
getInfo
- 获取 Sound SFX 转码后大小:
get
- 获取生成的 SoundBank 路径:
get
编写代码
1 | # 1. 获取全局信息 |
3. 简易实现 Soundcaster 中 Session 和播放功能
Soundcaster 的功能主要由两部分组成,其一是保存一组走带条对象数据方便复用(即 Soundcaster Session),其二是在播放时可控制 Game Sync 及对象的属性、M/S 等。
因为第二部分中有些功能比较复杂,在示例中我们只完成第一个部分。
注意:WAAPI 中的 Transport Object 创建后无法同 Wwise 工程保存,如果想复现 Soundcaster Session 功能,需要将每组 Session 的 GUID 单独保存以供下次重建 Transport Object。
选定 API
- 走带条控制:
transport
编写代码
1 | # 1. 为对象创建 Transport Object(X 为自定编号或标记,以下简称为走带条对象),创建结束后会返回走带条对象的 GUID。为方便管理,我们把对象名跟走带条对象的 GUID 放在同一个字典里作为函数返回值 |
4. 抓取 Profiler 数据生成报告
选定 API
- 开始连接:
startCapture, stopCapture
- 抓取信息:
getBusses, getCursorTime, getRTPCs, getVoiceContributions, getVoices
编写代码
1 | # 1. 开始 Profiler 数据抓取,并返回抓取开始时的时间 |
接下来讲什么?
下一篇文章中,我们会讨论执行类中剩下的模块,包括 UI 操作模块 wwise.ui
、WAAPI 模块 wwise.waapi
、Debug 模块 wwise.debug
及声音引擎模块 soundengine
。