Building a GNU-Autotools-based Project for iOS: Part 1

The open-source movement started way before the rise of the mobile scene, leaving enormous great free libraries and components lying around. Lots of the reusable codebases are usually only “a build away” from becoming mobile-ready. One of the most popular build systems for the open-source projects is GNU Autotools.

Recently I tried to build a few renowned C/C++ open-source projects for iOS, only to find that:

  1. Nothing works out of the box;
  2. The scattered info on the Internet is often inconsistent or out of date.

… familiar pattern for a platform with breaking changes every so often, keeping everyone busy and unable to document things tightly. But here is my $0.02. Hope it’ll help folks who got bruises out of trials-and-errors.

In Part 1, I’ll focus on building static libs for iOS.

The Environment

Check my setup before getting excited. You know why, having tried and failed upon many StackOverflow tips.

  • macOS 10.14.6
  • iOS 13.1
  • Xcode 11.1

If you can’t get my solution working for your own projects, e.g., when it’s based on a more recent environment, it’s quite possible that you need a next-gen tip.

The Working Solution

For the impatient, here is the build script to place in the root folder of your autotool-based project

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#! /bin/sh

#
# Build for iOS 64bit-ARM variants and iOS Simulator
# - Place the script at project root
# - Customize MIN_IOS_VERSION and other flags as needed
#
# Test Environment
# - macOS 10.14.6
# - iOS 13.1
# - Xcode 11.1
#

Build() {
# Ensure -fembed-bitcode builds, as workaround for libtool macOS bug
export MACOSX_DEPLOYMENT_TARGET="10.4"
# Get the correct toolchain for target platforms
export CC=$(xcrun --find --sdk "${SDK}" clang)
export CXX=$(xcrun --find --sdk "${SDK}" clang++)
export CPP=$(xcrun --find --sdk "${SDK}" cpp)
export CFLAGS="${HOST_FLAGS} ${OPT_FLAGS}"
export CXXFLAGS="${HOST_FLAGS} ${OPT_FLAGS}"
export LDFLAGS="${HOST_FLAGS}"

EXEC_PREFIX="${PLATFORMS}/${PLATFORM}"
./configure \
--host="${CHOST}" \
--prefix="${PREFIX}" \
--exec-prefix="${EXEC_PREFIX}" \
--enable-static \
--disable-shared # Avoid Xcode loading dylibs even when staticlibs exist

make clean
mkdir -p "${PLATFORMS}" &> /dev/null
make V=1 -j"${MAKE_JOBS}" --debug=j
make install
}

echo "HI"

# Locations
ScriptDir="$( cd "$( dirname "$0" )" && pwd )"
cd - &> /dev/null
PREFIX="${ScriptDir}"/_build
PLATFORMS="${PREFIX}"/platforms
UNIVERSAL="${PREFIX}"/universal

# Compiler options
OPT_FLAGS="-O3 -g3 -fembed-bitcode"
MAKE_JOBS=8
MIN_IOS_VERSION=8.0

# Build for platforms
SDK="iphoneos"
PLATFORM="arm"
PLATFORM_ARM=${PLATFORM}
ARCH_FLAGS="-arch arm64 -arch arm64e" # -arch armv7 -arch armv7s
HOST_FLAGS="${ARCH_FLAGS} -miphoneos-version-min=${MIN_IOS_VERSION} -isysroot $(xcrun --sdk ${SDK} --show-sdk-path)"
CHOST="arm-apple-darwin"
Build

SDK="iphonesimulator"
PLATFORM="x86_64-sim"
PLATFORM_ISIM=${PLATFORM}
ARCH_FLAGS="-arch x86_64"
HOST_FLAGS="${ARCH_FLAGS} -mios-simulator-version-min=${MIN_IOS_VERSION} -isysroot $(xcrun --sdk ${SDK} --show-sdk-path)"
CHOST="x86_64-apple-darwin"
Build

# Create universal binary
cd "${PLATFORMS}/${PLATFORM_ARM}/lib"
LIB_NAME=`find . -iname *.a`
cd -
mkdir -p "${UNIVERSAL}" &> /dev/null
lipo -create -output "${UNIVERSAL}/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ARM}/lib/${LIB_NAME}" "${PLATFORMS}/${PLATFORM_ISIM}/lib/${LIB_NAME}"

echo "BYE"

The Expected Results

Assuming you are at the project root, run the script and you should get:

  • The static libs for arm64 family and iOS simulator under ./\_build/platforms/<ARCH>/lib
  • The universal binary for all architectures combined

Running a lipo check on the universal binary should give you something like these:

1
2
$ lipo -info /path/to/mylib/_build/arm/lib/libmylib.a
Architectures in the fat file: /path/to/mylib/_build/arm/lib/libmylib.a are: arm64 arm64e
1
2
$ lipo -info /path/to/mylib/_build/x86_64-sim/lib/libmylib.a
Non-fat file: /path/to/mylib/_build/x86_64-sim/lib/libmylib.a is architecture: x86_64
1
2
$ lipo -info /path/to/mylib/_build/universal/libmylib.a
Architectures in the fat file: /path/to/mylib/_build/universal/libmylib.a are: x86_64 arm64 arm64e

The Lessons Learned

I feel obliged to write down the major gotchas that may help in the future.

Compiler Executables

I lost most of my time to this. I started out by using the autotools compiler environment variables this way:

1
CC=clang

To my surprise, I then always ended up with builds holding x86_64 instead of arm64. The correct way is now shown in the solution above. Before getting there, I was on the wrong track: Fiddling with the architecture triplets that I copied from the Internet, unsure whether or not they could be trusted. I’ve tried numerous triplets, i.e., arch-vendor-os, to no avail. GNU is not big on documentation. The closest standard triplet lists I could find are:

These proved useless in my situation.

To make it worse, config.guess always gives me the wrong x86_64 as well:

1
2
$ ./config.guess
x86_64-apple-darwin18.7.0

Knowing that autotools is just a wrapper over the real compilers, I’ve also tried to understand how the compiler works behind autotools. I created an Xcode project and watched the IDE build log, hunting for clues on magic flags. This proved useless as well, including the intriguing clang flag -target arm64-apple-ios13.1, not to be confused with the --target flag for configure.

The cross-compile idea also made me tweak the build/host/target combination over and over, only to find that having --host alone will suffice, the rest is implied by the assigned toolchain variables and flags.

Bitcode

Since iOS 9, enabling bitcode is required for library providers. However, to enabling bitcode without causing compiler errors such as:

1
ld: -bind_at_load and -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES) cannot be used together

I have to put in a trick to signify the build machine version, which works around a supposed libtool bug:

1
export MACOSX_DEPLOYMENT_TARGET="10.4"

Shared libs

Without adding --disabled-shared, autotools generate both dynamic and static libs for the project. This turns out to cue Xcode to try to load shared libs first (CRASH) even when I have not specified -l for the dylibs. So --disabled-shared is mandatory for using static libs.

Conclusions

Autotools try to hide away compiler and OS details behind the magic configure command and its obscure options. In the Desktop era, the dirty work was done so well that all I needed was following the default trilogy steps. After this trip, I realized how much it takes to support a new platform on the build system end. Although the new build systems like CMakes improve the cross-platform build environment, the mobile devs inevitably bump into dinosaur autotools-based codebases, rigged with traps. Patience in research is the only cure, IMHO.

In Part 2 (schedule TBD), I’ll build a Framework for iOS using autotools.

References

十条你得知道的 Wwise Launcher 用法(2019 版)

注:本文基于 2018 年初在 Audiokinetic 公众号上发表的文章《十一条你可能不知道的 Wwise Launcher 用法》,由作者针对软件更新做了改动,不影响操作的截图沿用旧版本

本文基于以下环境

  • Wwise Launcher: 2019.5.0.905
  • Wwise: 2019.1.0.6947
  • macOS Mojave 10.14.5

从 2016 年起,你在 Audiokinetic 官网点击下载 Wwise 的时候,实际上下载的是 Wwise Launcher(下文简称 Launcher),后来你就习惯用 Launcher 来下载更新 Wwise 了。你还学会了用它管理本机上的多个工程和 Wwise 版本,对吧?但对新手,Launcher 可能还有不明显的功能和注意事项对你会很有用,本文初版一年多后,我们再来梳理一下吧!

1. Wwise Launcher 有权威参考资料吗?

如今我们有了详细的官方图文文档,可从 Launcher 的页菜单项``Wwise Launcher Documentation`直达,本文中的许多内容已可在文档中找到。不过在本文写作时,文档只有英文版,零星信息尚未与最新软件版本同步,因此下文还会给出必要的向导。英文自信的同学扫读本文即可。

2. 程序员:哪里有 Wwise 集成/整合方面的例程?

我们可以通过 Launcher 来安装 Wwise 的IntegrationDemo。如果我们已经安装了 Wwise,但是没有找到这个 Demo,则需要打开 Launcher,找到WWISE页上我们的目标版本,然后修改安装。

勾选左边选项框中的SDK (C++),和右边的目标平台。

之后点击Install...开始安装。安装结束后切换到SAMPLES页,选中正确的 Wwise 版本(图中为2017.1.4),便可以看到IntegrationDemo了。

如果想运行 Demo,则直接点击Run IntegrationDemo;如果想打开对应的 Wwise 工程检查或修改声音设计内容,那么点击Open in Wwise

如果想打开源码工程,则点击左边按钮的下拉菜单,之后打开 Demo 所在文件夹,

通过文件系统打开对应平台的子文件夹,找到并打开 IDE 工程文件。

在研究 IntegrationDemo 的时候,推荐对照Wwise SDK Help文档来学习,特别是声音引擎集成纵览一节。

不论我们的项目用的是自研引擎还是商业引擎,IntegrationDemo都是我们学习 Wwise 整合代码最好的帮手。这是因为IntegrationDemo是 Wwise SDK 的一部分,Audiokinetic 会持续维护测试这组例程和对应的 Wwise 工程;每当有重要的新功能时,该 Demo 中一般会加入新功能演示。

更重要的是,假如我们的项目出现了奇怪的问题,开始怀疑是不是 Wwise 的 bug 时,可以首先尝试用 IntegrationDemo 来对照重现问题,Demo 提供的简化环境经过了反复测试,有助于隔离发现应用端的整合问题。Demo 中重现不了的问题往往会暴露出用户端的使用不当。

2. 我们公司大部分机器在内网,怎么安装 Wwise?

我们可以用某台外网机安装需要的 Wwise 组件,然后通过 Launcher 制作离线安装包给内网机使用:

添加插件为单独步骤,在制作离线包时注意勾选所有需要的插件。

做好的离线包除了组件文件夹bundle外,会自带一个配套的 Launcher 安装包:

最后将离线安装包发送到各台内网机上,在内网机上分别装好包里自带的 Launcher,再打开 Launcher 定位到离线包来安装。

注意:Windows 版的 Launcher 只能安装 Windows 版的设计工具,Mac 版类似。

3. 我下载安装了 Wwise,但是我的工程在生成 SoundBank 时报错,说最多只能使用 200 个声音文件,怎么办?

这说明我们的工程没有添加合适的 Wwise 授权码。我们需要打开 Launcher,登录我们的 Audiokinetic 账号,在PROJECTS页找到我们的 Wwise 工程,点击钥匙状按钮,接着我们有两个选择:

1)注册新项目,并等待 Audiokinetic 商务联系人批准项目注册并接收系统邮件取得授权码。

这时我们会来到官网项目注册页面,需要根据向导填好所有信息并提交申请。之后如果确信申请通过了但没有收到系统邮件,则最好检查一下垃圾邮箱。

2)如果我们的项目确定已经注册了,只是没有导入授权码,则需要注意:每个 Wwise 项目在 Audiokinetic 官网上都有若干管理员(Wwise Project Leader),他们一般是我们自己项目团队的成员。可以联系管理员把我们的 Audiokinetic 账号加入该项目,之后便可以在 Launcher 里工程的Set Project License菜单中找到对应的项目授权码并授权项目了,做授权码导入操作时要确保关闭已经打开的工程

4. 如何安装 Unity 集成?

假设我们的电脑上已经装了 Unity,并且创建了 Unity 工程,而我们现在想给其中某个 Unity 工程安装 Wwise 集成。这时我们需要来到 Launcher 的UNITY页,点开顶部菜单的浏览按钮:

选中目标 Unity 工程并确认后,在UNITY页的工程列表里就能看到这个工程了。接下来我们有两个选择:

1)直接从官网下载 Unity 集成并同时安装,

2)先下载离线安装包,

然后通过 Launcher 手动安装。

注意,无论用哪种方法,安装期间必须关闭所有打开的 Unity 编辑器。如果需要定制 Unity 集成,可以在离线安装包中解压出源码包,再重新编译 Unity 集成

Unreal Engine 的安装方法与此类似,在UNREAL ENGINE页中完成。

5. 新手怎么学习 Unity 和 Unreal Engine 集成?

学习 Unity 集成有两个选择:

1)通过 Launcher 安装运行 Unity 集成示例,再对照官方文档学习。

2)通过官方免费的认证课程系列 中的 Wwise-301 (以及 Wwise-251 中的一部分)学习,对 Wwise 新手而言这一般意味着你要先学习入门课程 Wwise-101,但可能很多过来人都会告诉你,这是值得的,别问我为什么知道 ^_*

学习 Unreal 集成, 我们也有两个选择:

1)通过 Launcher 的UNREAL ENGINE页安装运行 Unreal 集成示例,再对照官方文档学习。

2)通过 Launcher 安装运行 Wwise 的空间音频 Demo:Wwise Audio Lab

6. 我网速慢,看在线文档不方便,如何在本地查看 Wwise 的音频设计和程序整合文档?

我们可以通过 Launcher 安装离线文档,注意要选择想要的平台。

之后就可以在WWISE页的对应 Wwise 版本下找到各个平台对应的多语言离线文档了。

容易忽略的是:Unity 和 Unreal Engine 集成的离线包中自带了离线文档(.chm.html包),解包时不要错过哦~

7.我感觉碰到了一个 Wwise 的 Bug,该怎么上报?

我们可以去?页的About,点击Report a Bug...,接着根据向导提供必要信息确认上传即可,支持上传截图、Wwise 工程 zip 包和普通 zip 包。汇报成功后,你会看到类似下图的截图,别问我怎么知道的 :P

8. Launcher 在安装中出现错误或者操作失败怎么办?

我们可以去?页的About Wwise Launcher...

找到 Launcher 的完整日志,

然后想办法汇报给 Audiokinetic 的项目技术支持或社区问答论坛,或者如果你确信是 Bug,参见上一条。

最常见的问题是网络问题,出这类问题的时候如果开了 VPN,则可以尝试关闭了 VPN 再使用 Launcher。

有一类特殊问题是通过 Launcher 安装和升级 Unity 集成的时候失败了,这时怎么办呢?

首先我们需要根据Unity 集成版本说明来确认要安装的 Wwise 版本对应支持的 Unity 版本。

如果版本都是对的,则需要打开 Unity 编辑器观察控制台里的错误信息。Wwise Unity 集成安装时需要运行 Unity 程序来做一些初始化或者升级工作,这个过程中 Unity 工程内部可能错误,但这些错误并不会在 Launcher 界面上显示出来,要在 Unity 编辑器中查看。

9. 向 Wwise 工作人员提技术问题时,怎样让沟通更准确高效?

我们在项目档期紧张时遇到 Wwise 相关的技术问题,常常撒腿就找官方技术支持。但是巧妇难为无米之炊,没有客观详实的诊断信息,支持人员也只能来回提问试探,沟通效率可能会不理想。为了高效沟通,我们可以善用 Wwise 的 Profiler(性能分析器)来记录问题过程,利用 Launcher 来制作诊断包,在提问时将诊断包发送给官方技术支持。

Windows 版本的 Launcher 整合了 Wwise 中的辅助工具Wwise Project Zipper的功能,支持 Windows 和 macOS

可以将 Wwise 工程及性能分析器日志记录 (profiler session)定制内容后打包。

之后便可以前往官网项目的技术支持频道将 zip 包作为附件发送给 Audiokinetic 的技术支持了。这样的做法通常可以为我们省去好几轮前期沟通。

10. 怎样了解 Audiokinetic 的新闻和最新技术?

我们打开 Launcher 的首页,便可以看到最新的 Audiokinetic 的英文版新闻和技术博客。

首页上还有社区问答论坛的最新提问,可以从中学习其他用户的经验。

如果要看中文版,现在可以直接点击 Launcher 首页右上角的语言列表切换。

后记

越来越多的集成开发环境包括 Unity 和 Unreal Engine 利用独立于编辑器的 Launcher(启动器)作为控制中心程序来管理工程和资源。这样做,一来可以方便用户管理多个引擎版本和工程,避免和操作系统文件管理器中铺天盖地的文件夹和文件类型缠斗,甚至发生零散文件操作引起的意外;二来可以整合引擎开发商提供的一系列服务。在 Wwise 工作流程中,Wwise Launcher 正在扮演类似的角色,并且还在成长中。

十条你可能不知道的 Wwise Launcher 用法

本文基于以下软件版本:

  • Wwise Launcher: 2017.11.30.720
  • Wwise: 2017.1.4.6407
  • macOS Sierra 10.12.6 (16G1114)

从 2016 年起,在 Audiokinetic 官网点击下载 Wwise 的时候,你实际上下载的是 Wwise Launcher(下文简称 Launcher),到现在你应该已经习惯用 Launcher 来下载更新 Wwise 本身甚至管理本机上的多个工程和 Wwise 版本了,但 Launcher 可能还有一些不太明显的功能和注意事项对你会很有用,我们今天就来梳理一下吧!

1. 程序员:哪里有 Wwise 集成/整合方面的例程?


我们可以通过 Launcher 来安装 Wwise 的IntegrationDemo。如果我们已经安装了 Wwise,但是没有找到这个 Demo,则需要打开 Launcher,找到WWISE页上我们的目标版本,然后修改安装。

勾选左边选项框中的SDK (C++),和右边的目标平台。

之后点击Install...开始安装。安装结束后切换到SAMPLES页,选中正确的 Wwise 版本(图中为2017.1.4),便可以看到IntegrationDemo了。

如果想运行 Demo,则直接点击Run IntegrationDemo;如果想打开对应的 Wwise 工程检查或修改声音设计内容,那么点击Open in Wwise

如果想打开源码工程,则点击左边按钮的下拉菜单,之后打开 Demo 所在文件夹,

通过文件系统打开对应平台的子文件夹,找到并打开 IDE 工程文件。

在研究 IntegrationDemo 的时候,推荐对照Wwise SDK Help文档来学习,特别是声音引擎集成纵览一节。

因为IntegrationDemo是 Wwise SDK 的一部分,Audiokinetic 会持续维护测试这组例程和对应的 Wwise 工程;每当有重要的新功能时,该 Demo 中一般会加入新功能演示,所以不论我们的项目用的是自研引擎还是商业引擎比如 Unity/Cocos2d-x,IntegrationDemo都是我们学习 Wwise 整合代码最好的帮手。

更重要的是,假如我们的项目出现了奇怪的问题,开始怀疑是不是 Wwise 的 bug 时,可以首先尝试用 IntegrationDemo 来对照重现问题,Demo 提供的是高度简化和反复测试过的环境,有助于隔离发现应用端的整合问题。Demo 中重现不了的问题最终往往会证明来自应用端的使用不当。

2. 我们公司大部分机器在内网,怎么安装 Wwise?


我们可以用某台外网机安装需要的 Wwise 组件,然后通过 Launcher 制作离线安装包给内网机使用:

做好的离线包除了组件文件夹bundle外,会自带一个配套的 Launcher 安装包:

最后将离线安装包发送到各台内网机上,在内网机上分别装好包里自带的 Launcher,再打开 Launcher 定位到离线包来安装。

3. 我下载安装了 Wwise,但是我的工程在生成 SoundBank 时报错,说最多只能使用 200 个声音文件,怎么办?


这说明我们的工程没有添加合适的 Wwise 授权码。我们需要打开 Launcher,登录我们的 Audiokinetic 账号,在PROJECTS页找到我们的 Wwise 工程,点击钥匙状按钮,这时我们有两个选择:1)注册新项目,并等待 Audiokinetic 商务联系人批准项目注册并接收系统邮件取得授权码。

这时我们会来到官网项目注册页面,需要根据向导填好所有信息并提交申请。之后如果确信申请通过了但没有收到系统邮件,则最好检查一下垃圾邮箱。

2)如果我们的项目确定已经注册了,只是没有导入授权码,则需要注意:每个 Wwise 项目在 Audiokinetic 官网上都有若干管理员(Wwise Project Leader),他们一般是我们自己项目团队的成员。可以联系管理员把我们的 Audiokinetic 账号加入该项目,之后便可以在 Launcher 里工程的Set Project License菜单中找到对应的项目授权码并授权项目了,做授权码导入操作时要确保关闭已经打开的工程

4. 如何安装 Unity 集成?


假设我们的电脑上已经装了 Unity,并且创建了 Unity 工程,而我们现在想给其中某个 Unity 工程安装 Wwise 集成。这时我们需要来到 Launcher 的UNITY页,点开顶部菜单的浏览按钮:

选中目标 Unity 工程并确认后,在UNITY页的工程列表里就能看到这个工程了。接下来我们有两个选择:1)直接从官网下载 Unity 集成并同时安装,

或者 2)先下载离线安装包,

然后通过 Launcher 手动安装。

注意,无论用哪种方法,安装期间必须关闭所有打开的 Unity 编辑器

5. 新手怎么学习 Wwise Unity 和 Unreal Engine 集成?


学习 Unity 集成可以通过 Launcher 安装运行 Unity 集成示例,再对照官方文档学习。

学习 Unreal 集成, 我们有两个选择:1)可以通过 Launcher 的UNREAL ENGINE页安装运行 Unreal 集成示例,再对照官方文档学习。

2)也可以通过 Launcher 安装运行 Wwise 的空间音频 Demo:Wwise Audio Lab

6. 我需要根据特定 Wwise SDK 库版本来重新编译 Wwise Unity 集成,怎么办?


有时候我们等不及官方 Unity 集成补丁,而想基于某个已经下载好的原生 SDK 补丁库来重新构建 Unity 集成, 这时我们需要的是 Unity 集成的源码包,可以在 Launcher 的UNITY页下载离线安装包来取得(见4. 如何安装 Unity 集成?)。

离线包的格式为 .tar.xz,一般我们需要通过 Launcher 安装集成来解出源码包来。如果想手动解包,则需要安装可以解压XZTAR格式的程序。比如在 macOS 上,我们可以通过Homebrew来安装xz这个包,之后即可通过命令行xz -d 来解包了。源码包的文件一般以_Src.zip结尾。

重新编译 Unity 集成的方法见官方文档

7. 我网速慢,看在线文档不方便,如何在本地查看 Wwise 的音频设计和程序整合文档?


我们可以通过 Launcher 安装离线文档,注意要选择想要的平台。

之后就可以在WWISE页的对应 Wwise 版本下找到各个平台对应的多语言离线文档了。

8. Launcher 在安装中出现错误或者操作失败怎么办?


我们可以去?页的About

找到 Launcher 的完整日志,

然后想办法汇报给 Audiokinetic 的项目技术支持或社区问答论坛

最常见的问题是网络问题,出这类问题的时候如果开了 VPN,则可以尝试关闭了 VPN 再使用 Launcher。

有一类特殊问题是通过 Launcher 安装和升级 Unity 集成的时候失败了,这时怎么办呢?

首先我们需要根据Unity 集成版本说明来确认要安装的 Wwise 版本对应支持的 Unity 版本。

如果版本都是对的,则需要打开 Unity 编辑器观察控制台里的错误信息。Wwise Unity 集成安装时需要运行 Unity 程序来做一些初始化或者升级工作,这个过程中 Unity 工程内部可能错误,但这些错误并不会在 Launcher 界面上显示出来,要在 Unity 编辑器中查看。

9. 提出技术支持问题时,怎样让沟通更准确高效?


我们在项目档期紧张时遇到 Wwise 相关的技术问题,首先想到的是请求官方技术支持支援。但是巧妇难为无米之炊,没有客观详实的诊断信息,支持人员也只能来回提问试探,沟通效率可能会不理想。为了提高沟通效率,我们可以善用 Wwise 的 Profiler(性能分析器)来记录问题过程,利用 Launcher 来制作诊断包,在提问时将诊断包发送给官方技术支持。

Windows 版本的 Launcher 整合了 Wwise 中的辅助工具Wwise Project Zipper的功能(macOS 版暂时没有此功能),

可以将 Wwise 工程及性能分析器日志记录 (profiler session)定制内容后打包。

之后便可以前往官网项目的技术支持频道将 zip 包作为附件发送给 Audiokinetic 的技术支持了。这样的做法通常可以为我们省去好几轮前期沟通。

10. 怎样了解 Audiokinetic 的新闻和最新技术?


我们打开 Launcher 的首页,便可以看到最新的 Audiokinetic 的英文版新闻和技术博客。

首页上还有社区问答论坛的最新提问,可以从中学习其他用户的经验。

如果要看中文版,目前可以先点击 Launcher 首页任何链接来到 Audiokinetic 官网,然后切换语言为简体中文。

后记


越来越多的集成开发环境包括 Unity 和 Unreal Engine 利用独立于编辑器的 Launcher(启动器)作为控制中心程序来管理工程和资源。这样做,一来可以方便用户管理多个引擎版本和工程,避免和操作系统文件管理器中铺天盖地的文件夹和文件类型缠斗,甚至发生零散文件操作引起的意外;二来可以整合引擎开发商提供的一系列服务。在 Wwise 工作流程中,Wwise Launcher 正在扮演类似的角色,并且还在成长中。

快速试听 Wwise 互动音乐的过渡

本文探讨的所有问题和解决方案都基于 Wwise 2017.1.x。不对后续版本负责。

起因:听一下过渡好难

利用 Wwise 的互动音乐功能可以用小段音乐编组搭建出动态和互动的曲式和织体,但搭完了之后你还是得用耳朵试听乐段间的过渡,确保衔接在音乐意义上是无缝的。截至 Wwise 2017.1,不少人在这里遇到了麻烦,见下图:

这个例子来自 Wwise 自带的示例工程Sample Project(可以从Wwise Launcher 的SAMPLES页下打开),在 Music Playlist Editor 中,Stealth这个 Music Playlist Container 中有一个 Sequence Continuous 模式的 Music Segment 编组,图中正在试听前两个 Music Segment 间的过渡。这时,由于Stealth采用的过渡规则中,Source 段用了Exit source at Exit Cue这条同步规则,见下图:

Exit Cue 一般靠近曲子尾声,所以你必须把Stealth_Seg_01从头听到 Exit Cue 处才能开始试听和Stealth_Seg_02间的过渡,如果曲子很长,这样就很浪费时间;还有更惨的:当要听的过渡在一个很长的连续列表的中间靠后时,即便同步点是 Next Bar 这样近距离的,每次你还都得从最顶上听起。如下图:

要听Seg 02b (B)到下一段的过渡时,你每次都得从Seg 01a (A)听起。上图里还比较仁慈,每个段落都只循环了一次,万一有些会循环有限次,万一播着中间接了个电话,… 你懂的。

于是你又下意识地跑到 Music Segment Editor 界面中想对着波形迅速定位到过渡点附近听,结果发现一次又只能试听一个 Segment,见下图:

所以还是快不起来。

在别的情况下,也会有类似问题:

  • 确认单个 Music Segment 的循环无缝时。
  • 试听 Music Switch Container 下的状态切换时。

需要强调的是,麻烦大小和设计有关,短小的 Music Segment 可能几乎感觉不到。

面对这些情况,你的需求大概是这么三条

  1. 能从播放列表中直接挑出任何一对 Music Segment 来听过渡,不管它们在什么地方。
  2. 能定位到过渡点附近开始听。
  3. 能试听单曲循环的首尾衔接。

换句话说:如今主流媒体播放器能做到的一些事情。

上图中,如果你想听SayArchitect这两首曲子间的过渡(假设有的话),那么只需要鼠标双击Say这首,然后用底部进度条定位到尾部收听即可。如果要测自循环,则只需要把循环模式调到单曲循环就行了(注:Wwise 中由于有同步点的问题,常规的进度条还不够)。

相比之下,我们发现:Wwise 虽然提供了搭建非线性音乐结构的一整套工具,但其 UI 的预览功能不支持线性媒体播放器中的列表定点播放体验。 当然,反过来大多数线性媒体播放器也不支持 Wwise 提供的众多非线性播放功能。

在 Wwise 在 UI 上做出改进之前,我们暂时还得面对这个问题。

好消息是,“解决方法“(Hack)还是有的,虽然都不完美!
我们来看看到 Wwise 2017.1 版本为止,目前已知的三种方法。我把它们按从直观到抽象排了序。

方法一:手动拼接法

看到标题你估计已经有点失望了。没错,这个方法很简单粗暴:通过复制粘贴把想要一起听过渡的几个 Clip 放到 Music Segment Editor 的多轨界面上,手动确保各个 Clip 的 Exit Cue 和 Entry Cue 彼此对齐,最后通过光标直接定位到过渡点附近收听播放。如果要测某一轨的无缝循环,那就把该轨上的内容复制一遍紧贴在自己的尾巴上。

实际操作中有个麻烦:在 Music Segment Editor 下对齐来自两个 Music Segment 的 Clip 时,只有一个 Segment 的 Exit Cue 和 Entry Cue 能显示出来,另一个的则看不到。所以你可能要另想办法来对齐同步点,比如用 Clip handle 临时裁剪掉看不到 cue 的 Music Segment 的 Pre-Entry 或者 Post-Exit 部分,不然很难肉眼对齐。

这个方法的优点是:

  1. 概念简单,接近 DAW 里面的操作习惯。
  2. 能通过交互来精确定位到过渡点附近。
  3. 可以试听任何 Music Segment 甚至单个 Clip 的组合。

但缺点很明显:

  1. 用来拼接 Music Segment 的临时操作会改动 Music Segment,污染了设计本身。事后还得清理现场。虽然可以创建专门的“测试段落”,但就要维护这个多轨测试对象,并不轻松。
  2. 很难快速测试多轨 Music Segment 间的过渡。上面说的 Clip handle 操作对各轨可能都要做一遍。
  3. 手动操作繁琐,对齐容易出错。

这个方法大约只适合粗略测单轨循环的情况。如果非要走这条路,倒还不如直接在 DAW 里面对素材做这些工作来得简单。

方法二:快进播放法

这个是 Wwise 201 认证教程 中推荐的方法,海内外的一些设计师都有这样用的,见下图:

将父级容器的播放速度调大,那么播放时过渡涉及的 Music Segment 就能快进到过渡点附近。这时候如果想精听,则可以降回原速,除非你告诉我专业人士的耳朵都是 4 倍速的!

这个方法的优点是:

  1. 概念简单。
  2. 对设计的污染少。只动一个播放速度参数,用后还是很容易调回来的,因为默认值一般都是 1。

但是缺点仍然很明显:

  1. 在 Music Playlist Editor 中,依然无法直接收听任意一对 Music Segment 的过渡,只能从头顺序播放。
  2. 这是一种渐进操作,无法一步定位到过渡点附近。
  3. 操作上对反射神经有一定要求 …

方法三:Seek 法

这个技巧是海外设计师 Aaron Brown 分享的。基本原理是立足于 Event 及其 Seek(即寻址跳转)这个 Action。跟前两种方法相比,它更能满足本文开始分析的三条需求,但实际操作要绕点路。

Aaron Brown 的原始分享可以在 Wwise 的非官方 Facebook 群Wwise Wizards & Witches中找到,但是他只给了粗略的示意图,如下图所示:

但我实操后发现在 Wwise 2017.1 中上面的方法并不能凑效,要修改一些做法。下面详细讲解一下。

原理

这个方法是希望避免肉眼定位和等待,实现一键定位到音乐的过渡点附近,按照原速收听过渡。

Wwise 的 Event 中有 Seek 这个 Action 可以定位到播放文件中的指定位置。前两种方法其实也做了这个定位,只不过是用肉眼和手工操作来确保的:开始试听过渡的播放点必须要在同步点之前。而针对这点,Seek Action 只需设好跳转位置的数值就可以。并且,它还有服务于互动音乐的一条诱人的特性

  • Seek Action 的 Seek Percent(按百分比跳转)模式下,跳转是相对于 Entry Cue 和 Exit Cue 进行的。

打个比方,不管这些 Cue 设定在 Music Segment 的什么位置,即使我们把跳转设在 99% 处(即 Seek Percent 为 99%),它也绝对不会超出 Exit Cue 而误入 Post-Exit 段;对于单曲循环的情况,跳转百分比位置是相对循环区间来算的。有了这个条件,我们就可以放心定义跳转点来一键空降到过渡点附近了。

不过 Seek Action 还有一条重要的限制

  • Music Playlist Container 和 Motion 对象不支持 Seek。

而 Music Segment 不能控制过渡,只能依赖其父级容器,所以在 Event 中实现过渡的唯一希望就是 Music Switch Container 了。你大概明白了吧?我们要使用 State 切换来模拟所有的过渡情况,在切换状态之前执行 Seek 动作来直接跳转到过渡点之前的邻近位置,就能达到“直接”播放过渡段的目的。

State 切换要模拟的情况包括:

  1. 工程自身互动音乐设计中的状态过渡。
  2. Music Playlist Container 里面的相邻 Music Segment 间过渡。
  3. Music Segment 单曲循环的首尾衔接。

看起来好像很复杂,但其实只需要把要做过渡的 Music Segment 提取出来,分别关联一个 State,指派给一个测试用的 Music Switch Container 就可以了。

下面我们通过一个实例来说明做法。

做法示例

我们还是以 Wwise 安装包自带的Sample Project为例来说明 Seek 法的具体操作。简单起见,我们就挑选下图中选中的两个 Music Segment 来举例:

这两个 Segment 也是Stealth这个 Music Playlist Container 中顺序播放的相邻对象(见本文第一张图),采用的过渡同步点为 Exit Cue。

首先,创建一个专用的 Music Switch Container,把Steath_Seg_01Steath_Seg_02复制到它下面。见下图:

创建新的 Music Switch Container 是有原因的:在指定 Music Switch Container 的状态路径的时候,路径对应播放对象只能是该 Music Switch Container 的直接子对象,也就是说之前位于Stealth下面的对象如果不挪出来就无法直接关联到 State 上面去;而如果复制到原 Music Switch Container 下面,就又会污染设计。所以比较好的做法是直接创建一个新的 Music Switch Container。

这个专用容器的过渡规则一般只需要用默认的Any to Any规则就可以,但要注意默认会播放源段的 Post-Exit 和目标段的 Pre-Entry,不启用淡变。当然,你完全可以按需要来定制整条规则。这里我们重点强调 Exit Cue 的情况。

接着,创建一个新的 State Group,然后为两个 Music Segment 各创建一个 State。见下图:

下一步,回到测试专用的 Music Switch Container,设置好状态路径,让上面的 State 和 Music Segment 一一对应。见下图:

最后,我们创建测试事件。里面依序包含如下 Action:

  • 我们让第一个 Set State Action(状态初始化)比 Play Action 稍早一点执行,确保在播放之前状态已经初始化成源状态,播放源 Segment。这里我们把 Play Action 的 Delay 设为0.01, Set State 的 Delay 为 0
  • Seek Action 也比播放略提前,Seek 模式为Seek Percent,位置为90%。你可以视需要修改这个位置。注意:Aaron Brown 在 Wwise 2016.2 中采用 Seek All 这个 Action,Scope 设为 Global,但经测试发现在 Wwise 2017.1 中这个做法无效,要使用 Seek 才行。
  • 后一个 Set State Action 也就是状态切换的动作比播放稍晚一点,确保不会覆盖第一个 Set State 操作,导致源 Segment 没能播放起来。所以这里的 Delay 设为0.02

现在测试一下这个 Event,看是不是能一步到位试听过渡?

如果要试听一个 Music Segment 单曲循环的首尾衔接,则可以把问题转化为“从这个段落过渡到它自己的副本”,唯一需要改变的就是要复制源 Music Segment,把它作为目标段落即可。

你可能会问:“我就用同一个 Music Segment,给它关联两个不同的 State 不行吗?”然而 Wwise 中,基于 State/Switch 过渡时前后必须为两个不同的对象,音乐引擎才会启动过渡行为。所以必须给原 Music Segment 做一个副本,才能通过切换 State 来测试自循环过渡。

为了简单,示例中我们就地在工程已有的 Work Unit 中创建试听用的临时对象和其它元素。这样做还是污染了现有设计的,因为 Work Unit 对应 XML 文件,是 Wwise 工程的设计内容实体。所以比较好的做法是创建测试专用的 Work Unit,这样就不会污染,且很容易一键删除所有测试元素。注意,测试对象只要保证不打到 SoundBank 里就不会影响游戏的实际性能。

进一步讨论

为了试听一个小小的过渡,Seek 法看起来并不直观,需要好几步操作,这是它最明显的缺点。但是我们可以看到 Seek 法有独特的优势:

  1. 它可以满足我们的三条需求。
  2. 它不需要人工肉眼对位,也不依赖反应。这点别的工具很难做到。
  3. 设好的 Event、Music Switch Container 和 State 可以作为一套可复用的测试框架保留下来,用同一套框架甚至同一个事件测试各种过渡,只用反复改变事件 Action 列表中的 两个 State 就好。
  4. 整个流程可以通过 Wwise Authoring API(WAAPI)自动化脚本来加速。

除以上三种方法之外,还可以用 WAAPI 做后端来写一个简单的媒体播放器播放列表界面来达到传统的体验。

小结

本文针对很多人提到的“听一下过渡好难”的痛点,总结了一下在不编程或自己写第三方 UI 的情况下,现有能加速试听过渡的方案,希望能给大家一点帮助,权当抛砖引玉,欢迎大家指正和探讨其它可能的技巧和方案。

GDC17 音频见闻 - 空间音频 Spatial Audio(1)

《Gears of War 4》中的环境声学预处理技术

继 Binaural 和 Ambisonics 之后,今年 GDC 上的空间音频热门关键字多了混响(Reverberation)、声障和声笼(Obstruction & Occlusion)这些环境声学概念,标志着对沉浸音频体验发起的又一次冲锋。这些游戏音频界的旧瓶里最受瞩目的新酒之一当数微软研究院和《战争机器4》(GoW4)开发组的 Project Triton,即通过预先演算声波传播物理参数来辅助人工设计的环境声学效果方案。本次的演讲实际上是微软已启动六年之久系列研究的首次产品化成果。演讲幻灯片已放出。以下尝试稍加总结。

成果

这乍一看很学术范的技术主要是为了解决游戏环境声学设计中的一大痛点:传统设计方法依赖在游戏地图中手工标注或绘制声学区域,再挂接音频效果器来实现室内混响和声障/声笼效果;音频中间件如 Wwise 的效果器控制参数一般是够用的,但在游戏中手工标注的工作量巨大且容易出错,实际开发工期紧,很难打磨调优。

不巧,玩家极易通过生活经验察觉这类设计错误,后果严重,参见《刺客信条:大革命》中曾出现的全局 bug。音频设计界早就希望能像物理渲染界一样自动化这个过程,为创意设计赢得时间。

演讲中放出的最终解决方案:

  • 离线预处理:在游戏地图中自动选取采样位置,对各个位置上的发声体-听者结对(emitter-listener pair)根据几何数据自动做 3D 声波传播模拟并生成原始声学数据库;
  • 解析:从原始声学数据自动提炼出一组感知参数值:直达波能量、反射波能量、反射波衰减率(wetness)和混响衰减率;
  • 接入音频中间件:运行时通过查询数据库获取感知参数值,用来控制 Wwise 中的声障/声笼滤波器和辅助总线上的混响效果器,产生实时互动的声学效果。

用这种方法,设计师不再需要手动标注环境声学区域,可以专注于艺术效果设计,即通过感知参数来控制音频效果输出。结果不但错误少,且游戏中的实时性能也达到甚至优于项目初始要求。

一些细节:

  • 声障值和声笼值分别用初始能量(直达声比例)和反射能量来代表;
  • 区分室外和室内情况,方法是通过在室外设置理想边界,并测量从玩家位置发声后能抵达这条边界的能量占总能量的比例来推算室内-室外比,以此来做到平滑过渡;
  • 原始声学数据计算用 100 台机器需要约 4 小时计算时间,初始数据为 50 TB,做解析后降为 100 MB。

经历

很多学术报告大概也就到此为止了,让一部分听众感觉高山仰止,另一部分不知所云。然而这次两位主讲人–微软研究院的 Nikunj Raghuvanshi 和 Coalition 工作室的 John Tennant –继续披露了研发和产品化的详细经过,以机器人领域的恐怖谷现象为纲,讲述了从真实到艺术真实的探索(微信游戏音频群里尾巴老师语),体现了对基础研究和产品化过程深刻的理解,是演讲中我个人最喜欢的部分。

从 2011 年的 V1.0 到 2014 年的 V2.0 直到最终版,项目经历了标准的恐怖谷历程:

  1. V1.0 中初尝预处理甜头后,发现一些问题亟待解决,比如区分室内外和消除混响效果中的浑浊;
  2. 于是寄希望于自动化和仿真路线,火力全开,将模型复杂化:从 FDN 混响换成卷积混响,声学数据因此变成冲激响应,在增加室外效果器组分支的同时还增加了对早期反射和后期混响的区分,导致要控制 12 个混响单元;
  3. 不幸跌入恐怖谷,为了自救而后退一步,着重解决艺术真实问题,最后简化了模型,按时保质完成了产品化。

一些真实和艺术真实的折衷处理:

  • 冲激响应路线很难控制质量:录音和测量人员不同,因此高度依赖配准,为后期调试带来困难,终弃;
  • 混响效果受输入声音动态范围影响,将输入的真实动态范围做了艺术限制之后,清晰度得到提升;
  • 与传统影视建立的艺术效果标准相比,物理计算中得出的直达声能量以及衰减时间,结合游戏具体玩法后,会出现不合心理期望的情况,终弃。

有意思的是,上面的道理很多其实是马后炮:是在主动作出简化混响模型的决定后从实践中领悟到的,又一次说明奥卡姆剃刀原则的普适性。

未来

Triton 项目的未来计划包括在预处理模型中加入:

  • 直达分量方向性、早期反射、户外回声;
  • 动态几何结构,比如活动的门窗和物理毁坏。

巧的是,Triton 和 Wwise 不谋而合,这几条正是 GDC17 上 Wwise 2017.1 版本展出的新 Spatial Audio 功能的一部分,请看下篇。

相关资料

第一天

之前玩公众号,但发现在引用外链和离线写作方面有限制。很多时候我期待的是自由外链引用、离线为主、一键发布、迭代迁移方便的博客流程。

GitHub 的静态站点系统 GitHub Pages 支持按照软件开发流程来管理博客,通过纯文本标记格式比如 Markdown 写作存为本地文件,再一键发布到远程。

所以来 GitHub 试试。