WAQL 2.0

音频编程 / Wwise 技巧和工具

自 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.setPropertyak.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
$ where effects.count(effect.pluginname:"eq") > 0

any

有任何条目符合条件,即返回 true,否则返回 false。若为空,则返回 false。条件语句是非强制的。

$ where rtpc.any()
$ where children.any(type = "Sound")

all

若列表中的所有条目均与指定条件相符,则返回 true,否则返回 false。若为空,则返回 false。条件语句是强制性的。

$ where children.all(type = "Sound")

first

返回与指定条件相符的第一个条目。条件语句是非强制的。

$ where effects.first(effect.pluginname :"EQ") != null
$ where children.first().type = "Sound"

last

返回与指定条件相符的最后一个条目。条件语句是非强制的。

$ where effects.last(effect.pluginname :"EQ") != null
$ where children.last().type = "Sound"

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 图:

img1

除此之外,我们还将引入名为 EffectSlot 的新对象类型。EffectSlot 是存储 BypassRender 属性并将 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 引用的 idname(默认值)。若不想获取控制输入的 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

[
"SFX\\Ambient\\Ambient_Background\\treesVillage_001.wav",
"SFX\\Ambient\\Ambient_Background\\treesVillage_001.wav",
"SFX\\Ambient\\Ambient_Background\\BAS_amb_pineforest_night_background_loop_01.wav",
"SFX\\Ambient\\Ambient_Background\\BAS_amb_pineforest_night_background_loop_01.wav"
]

Ambient_River

[
"SFX\\Ambient\\Ambient_Emitters\\Water_RiverLoop.wav"
]

为什么会这样呢?当中有很多概念。下面我们来剖析一下这个表达式:

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 ExplorerList ViewToolbar 的 Search 字段以及 Schematic ViewQuery 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)。

伯纳德 罗德里格 (Bernard Rodrigue)

伯纳德 罗德里格 (Bernard Rodrigue)

Audiokinetic开发总监Bernard Rodrigue 是 Audiokinetic 的开发总监。他自 2005 年加入 Audiokinetic 后,一直积极参与 Wwise 的基础研发。现在,Bernard 仍在带领团队从事 Wwise 的提升和扩展研发,比如 Interactive Music 等等。

 @decasteljau

评论

留下回复

您的电子邮件地址将不会被公布。

更多文章

使用Wwise/Unreal Engine 4/Unity 3D进行脚步材质管理

在前期制作之初,声音设计师需要设定很多系统的原型,而且他们不是总有音频程序员帮忙。 幸运的是,Wwise结合现今的引擎,比如Unreal Engine 4和Unity 3D,能提供很大帮助。 ...

5.4.2017 - 作者:塞巴斯蒂安 盖拉 (Sebastien Gaillard)

简化 WAAPI

假如你之前从来没用过 Wwise Authoring API (WAAPI),我觉得不妨接着往下读,说不定对你有帮助呢。 我也知道,对于非编程人员,WAAPI...

21.8.2018 - 作者:亚当·克罗夫特(ADAM T. CROFT)

利用命令扩展改进工作流程

持续改进工作流程 ...

1.4.2019 - 作者:伯纳德 罗德里格 (Bernard Rodrigue)

连通 Wwise 和 REAPER – 第 1 部分:WAAPI Transfer

也许大家对 WAAPI Transfer 并不陌生,但我还是觉得有必要详细地说一说。WAAPI Transfer 是一款开源的 REAPER 扩展插件,方便将音频素材直接从 REAPER 导出到...

18.5.2020 - 作者:尼古拉·卢基奇 (Nikola Lukić)

人人都能用 WAAPI(一)概述

大家好,我是溪夜。 去年下半年我接触到了 WAAPI(Wwise Authoring API),作为头脑不怎么灵光的非专业程序员,看到 WAMP、JSON...

29.9.2020 - 作者:汪洋

人人都能用 WAAPI(二)wwise.core 分支

大家好,我是溪夜。 在《人人都能用 WAAPI(一)概述》中,我们用思维导图对 WAAPI 进行了重新归纳,并在配置好开发环境后,一起用 Python 写了几个简单的小程序,体验了 WAAPI...

29.10.2020 - 作者:汪洋

更多文章

使用Wwise/Unreal Engine 4/Unity 3D进行脚步材质管理

在前期制作之初,声音设计师需要设定很多系统的原型,而且他们不是总有音频程序员帮忙。 幸运的是,Wwise结合现今的引擎,比如Unreal Engine 4和Unity 3D,能提供很大帮助。 ...

简化 WAAPI

假如你之前从来没用过 Wwise Authoring API (WAAPI),我觉得不妨接着往下读,说不定对你有帮助呢。 我也知道,对于非编程人员,WAAPI...

利用命令扩展改进工作流程

持续改进工作流程 ...