Wwise 2023.1 对 WAAPI 的更新

音频编程 / 新版本 / Wwise 技巧和工具

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。比方说,以下场合就有使用:

除此之外,将 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

img1

每次移除对话框都要在 WAAPI 函数中提供相应选项,以便对文件自动执行 Check OutMark 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 视图并为其创建了新的选项卡:

img2

好消息是 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-servergenerate-soundbanktab-delimited-import 时,我们直接在 WwiseConsole 中添加了对版本控制的支持。有了这些,便可进一步拓展自动化流程,从而实现真正的功能区隔。

扩大 WAAPI 覆盖范围

下面,我们来详细说说如何扩大 WAAPI 覆盖范围。在过去几年中,我们对 Wwise 中的诸多数据模型进行了重构,以使其具有通用性并可通过现有 WAAPI 函数访问。在这个版本中,我们将重点放在了 Interactive Music Hierarchy 上。比方说,之前无法在 WAAPI 中配置某些音乐对象。对此,我们在 2023.1 中暴露了与音乐对象相关的内容:

  • Music Switch Container 中的关联参数
  • Music Sequence Container 中的 Playlist 根对象
  • Music Stinger 中的 TriggerSegment 引用
  • 音乐对象中的 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 时,会将对象定义传给函数。对象定义至少包含以下字段:

此外,对象定义还可选择包含以下字段:

  • children:定义一组其他对象定义。 
  • import:定义要将哪些音频文件导入到对象中(2023.1 新增)。
  • 属性值、引用值或列表值,如 VolumeOutputBus(带有 @ 前缀)。

为简便起见,我在 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 上设置 LoopCountSegment

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 但不知从哪里入手,可参阅以下链接:

 

伯纳德 罗德里格 (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)

借助 Wwise Motion Source 稳健设计振动效果

还记得你第一次体验到妙不可言的振动效果是什么时候吗?当时可能你刚买了 Nintendo 64 Rumble Pak,满心期待地准备玩《Golden Eye》–...

15.4.2019 - 作者:马克西米连·西马德·波里尔 (Maximilien Simard Poirier)

“零代码”开发小游戏—UE4蓝图与Wwise结合的设计思路 — Part 1

大家好,我叫伍岚珊(Coffee...

6.1.2020 - 作者:伍岚珊

人人都能用 WAAPI(一)概述

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

29.9.2020 - 作者:汪洋

Wwise 2023.1 新增功能

Wwise 2023.1 现已推出并可通过 Audiokinetic Launcher 下载。下面来简要介绍一下该版本中都有哪些新增功能。...

7.7.2023 - 作者:Audiokinetic (音频动能)

Wwise Spatial Audio 2023.1 新增功能 | 减弱相位偏移效应

在今天的博文中,我们将深入探讨在对给定环境进行声学建模时可能会遇到的一种有趣声学现象:相位偏移。我们先简单了解一下相位偏移的基本物理特性,然后再展示 Reflect 在 Wwise 23.1...

25.1.2024 - 作者:艾伦•李 (Allen Lee)

更多文章

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

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

借助 Wwise Motion Source 稳健设计振动效果

还记得你第一次体验到妙不可言的振动效果是什么时候吗?当时可能你刚买了 Nintendo 64 Rumble Pak,满心期待地准备玩《Golden Eye》–...

“零代码”开发小游戏—UE4蓝图与Wwise结合的设计思路 — Part 1

大家好,我叫伍岚珊(Coffee...