自 Wwise Authoring Query Language (WAQL) 的第一个版本发布以来已经有几年了。在此之后,几乎没什么改动。最大的改动就是把 WAQL 集成到了 Wwise 2022.1 的 Project Explorer Search。在 2023.1 版本即将发布之际,我们在此说说对查询语言所做的优化。
如果不知道什么是 WAQL,建议先阅读另一篇博文:https://blog.audiokinetic.com/zh/introducing-waql/
在深入探讨之前,我们先来总结一下为 Wwise 2023.1 所做的一些努力。在过去几年里,我们一直在设法将 Wwise 设计工具中的一些陈旧数据模型转换为所谓的“通用对象模型”。简单来说,藉此可采用相同的方式来处理 Wwise 中的所有对象。比方说,每个对象都有属性(如 volume)或对其他对象的引用(如 output bus)。对此,我们可以向 WAQL 或 WAAPI 暴露各种通用函数而无需创建专用函数。比如,WAAPI 中没有 setVolume 函数。要设置音量,可调用 ak.wwise.core.object.setProperty 或 ak.wwise.core.object.set,并将 volume 作为属性名称来传递。
我们在对象模型中加入了一个相对较新的概念,就是“对象列表”。借助对象列表,可以在任意对象内存储不定数量的对象或对象引用。比如,Actor-Mixer Hierarchy 下的每个对象现在都对应有 RTPC 对象列表和 Effect Slot 列表。跟音量一样,我们可以通过 ak.wwise.core.object.set.
设置 RTPC 对象或效果器。
我们来详细说说效果器和 WAQL。在 2022.1 及更早版本中,可通过以下方式查询所有插槽 0 上带有 EQ 的对象:
$ where effect0.pluginname : "EQ"
这时会返回工程中所有第一个效果器的插件名称包含 "EQ" 的对象。不过,要是查询所有效果器插槽,代码就会变得很冗长:
$ where effect0.pluginname : "EQ" or effect1.pluginname : "EQ" or effect2.pluginname : "EQ" or effect3.pluginname : "EQ"
在 2023.1 中,我们将移除这四个固定效果器插槽并代之以对象列表。那么,如何通过 WAQL 查询对象列表中的效果器呢?为此,必须在 WAQL 中添加对对象列表的相应支持。
引入列表函数
在 WAQL 中,可在 where 语句、select 语句或返回表达式中使用不同类型的访问器。下面列出了各种访问器类型:
访问器类型 |
返回 |
示例 |
属性 |
变量(整数、浮点数、布尔值、字符串) |
Volume、Pitch、IsLoopingEnabled |
引用 |
Wwise 对象 |
Output Bus、User Aux Send 0、Attenuation |
对象列表(新增) |
一组 Wwise 对象 |
RTPC、效果器、子对象、下级对象 |
JSON 对象 |
带键值的 JSON 对象 |
duration.max, duration.min, loudness.integrated |
鉴于 WAQL 本身支持对象列表,我们可以利用其执行很多操作。比如,我们加入了一项与列表有关的改进,来让函数对列表中的所有条目执行同样的操作。
我们先来说说 count() 函数。count() 函数会返回列表中的条目数。该数值可与另一数值进行比较。比如:
$ where effects.count() > 0
并且,count() 函数还可将可选条件作为参数。藉此,可计算符合特定条件的条目数。下面我们来试试在效果器列表中搜索带有 EQ 的对象:
$ where effects.count(effect.pluginname : "eq") > 0
除此之外,也可使用 any() 函数来编写此查询。对此,只要有一个条目符合条件,函数就会返回 true。这样更加高效:
$ where effects.any(effect.pluginname : "eq")
以下函数都可以用在列表上:
函数 |
说明 |
示例 |
count |
返回列表中与指定条件相符的条目数(若无相符条目,则返回 false)。条件语句是非强制的。 |
$ where children.count() > 3 |
any |
有任何条目符合条件,即返回 true,否则返回 false。若为空,则返回 false。条件语句是非强制的。 |
$ where rtpc.any() |
all |
若列表中的所有条目均与指定条件相符,则返回 true,否则返回 false。若为空,则返回 false。条件语句是强制性的。 |
$ where children.all(type = "Sound") |
first |
返回与指定条件相符的第一个条目。条件语句是非强制的。 |
$ where effects.first(effect.pluginname :"EQ") != null |
last |
返回与指定条件相符的最后一个条目。条件语句是非强制的。 |
$ where effects.last(effect.pluginname :"EQ") != null |
take |
获取指定数量的条目,并在新的列表中返回对应条目。 |
$ select children.take(2) |
skip |
跳过指定数量的条目,并在新的列表中返回其余条目。 |
$ select effects.skip(1) |
at |
返回列表中指定索引处的条目。 |
$ select effects.at(0) |
where |
在新的列表中返回与指定条件相符的条目。 |
$ select effects.where(effect.pluginname :"EQ") |
详细说说效果器
接下来,我们详细说说效果器。在 2023.1 中,我们将声音引擎端的最大效果器数量由 4 个增加到了 255 个。在 Wwise 设计工具端,只在特定情况下对系统资源(如内存)有一些限制。对此,我们可以把效果器存储在列表中。下面展示了 Sound 对象上所应用效果器的数据模型的 UML 图:
除此之外,我们还将引入名为 EffectSlot 的新对象类型。EffectSlot 是存储 Bypass 和 Render 属性并将 Effect 引用存储到 ShareSet 或 Custom 效果器的微对象。
Sound 对象不仅会存储 EffectSlot 对象列表,还会存储 BypassEffects 属性(一次性旁通 Sound 上的所有效果器)。
事实上,我们可以通过以下方式查询工程中的所有 EffectSlot 对象:
$ from type effectslot
扩展返回表达式
在结合 WAAPI 使用 WAQL 时,可借助返回表达式查询所返回对象的特定数据。比如,使用以下 WAQL 查询来返回工程中的所有 Sound 对象:
$ from type sound
结合使用返回表达式的 JSON 数组:
["name", "volume"]
以 JSON 格式返回以下表格:
name |
volume |
Silence |
0 |
Ambient_Day_Element_01_01 |
0 |
Ambient_Day_Element_01_02 |
0 |
waterdrop_05 |
-4 |
Ambient_Water_River |
-20 |
现在,假设我们想获取这些 Sound 对象的 RTPC。对此,可使用以下表达式来实现(Wwise 2022.1 中就能做到):
["name", "volume", "rtpc"]
不过对于 rtpc 列,每行都会显示类似以下的内容:
[
{
"id": "{1D1905C4-18F9-4506-AD8B-A0CDEC396F4D}"
}
]
这是一组 JSON 对象,其中每个对象都包含 RTPC 条目的 ID。只有在已经知道这些 GUID 或额外查询 GUID 的更多信息时,这些 ID 才有用。在默认情况下,在 WAQL 返回 Wwise 对象时,会同时返回对象的 ID 和名称。在此,RTPC 对象没有名称,所以只会返回 ID。
假设我们想获取与 RTPC 关联的属性名称以及驱动 RTPC 的控制输入名称。在 2023.1 中,对此可借助新的语法来通过复合函数返回新的 JSON 对象:
["name", "volume", "rtpc.{controlinput, propertyname}"]
对于最后一列,会返回以下内容:
[
{
"controlinput": {
"id": "{CEC3FD56-5B7C-44AF-B635-5C3A0C36825E}",
"name": "Distance_to_Camera"
},
"propertyname": "Volume"
}
]
同样,我们同时获得了 ControlInput 引用的 id 和 name(默认值)。若不想获取控制输入的 ID,则可使用以下一组表达式:
["name", "volume", "rtpc.{controlinput.name, propertyname}"]
我们会获得以下结果,看上去更加简洁明了:
[
{
"controlinput.name": "Distance_to_Camera",
"propertyname": "Volume"
}
]
现在问题是使用返回表达式作为 JSON 对象中的键值来访问该列的代码太过冗长。比方说,要访问第一行的 RTPC 字段,需要使用以下 Python 代码:
myRTPCs = results[0]['rtpc.{controlinput.name, propertyname}']
为了解决这一问题,我们可以通过关键字 as 引入别名的概念,以此来重命名表达式:
["name", "volume", "rtpc.{controlinput.name, propertyname} as rtpcs"]
现在代码缩短了,看起来就像这样:
myRTPCs = results[0]['rtpcs']
列表的组合
最后,我们来说说列表的组合。比如,我们先获取工程中的所有 Event:
$ from type event
借助以下表达式,获取与各个 Event 关联的所有原始 WAV 文件:
["name as event", "children.target.[this, descendants].where(type=\"Sound\").originalRelativeFilePath as originals"]
结果如下:
event |
originals |
Ambient_Region_PineForest |
[ |
Ambient_River |
[ |
为什么会这样呢?当中有很多概念。下面我们来剖析一下这个表达式:
children |
返回 Event 的 children(即 action)。 |
.target |
选择每个 action 的 target 对象。通常指向 Actor-Mixer Hierarchy 中的对象。 |
.[this, descendants] |
针对每个 target 构建并选择新的数组。该数组为 target 本身 (this) 及其 descendant 的组合。 |
.where(type=\"Sound\") |
对对象进行筛选来只保留 Sound 对象。注意,因为是在 JSON 值内,所以对双引号进行了转义。 |
.originalRelativeFilePath |
针对每个 Sound 选择相对于 Originals 文件夹的原始 WAV 文件路径。 |
其他列表
下面列出了可在 WAQL 中使用的列表:
专用列表:
- 多个对象中的效果器(新增)
- 多个对象中的 RTPC
- Random/Sequence Container 中的 Playlist(新增)
- Audio File Source 中的标记点(新增)
- Actor-Mixer Hierarchy 对象中的元数据
- Music Switch Container 中的条目和参数(新增)
- Dialog Event 中的条目和参数(新增)
- Music 对象中的 Stinger
- Music Segment 中的 Cue
- Music Track 中的 Sequence
- Music Sequence(即子音轨)中的 Clip
通用列表:
- children
- ancestors
- descendants
- referencesTo
结语
这里有很多东西要消化。如想了解更多信息并试用,可以使用 WAQL Playground (https://github.com/ak-brodrigue/waql-playground)。为了支持文中的新功能,我们已经做了相应更新。不妨下载 Wwise 2023.1 并在自己的工程中试用。这样可以更加直观地学习和了解该设计工具版本。
WAQL 旨在方便用户掌控并查看自己的数据。文中所说的新功能让这一理念更加容易实现。
记住,各位可直接在 Project Explorer、List View 和 Toolbar 的 Search 字段以及 Schematic View 和 Query Editor 中使用 WAQL。
如想进一步了解 WAQL 和对象模型,请参阅 WAQL 参考 (https://www.audiokinetic.com/en/library/edge/?source=SDK&id=waql_reference.html) 和 Wwise 对象参考 (https://www.audiokinetic.com/en/library/edge/?source=SDK&id=wobjects_index.html)。
评论