PyWwise: WAAPIをPython風に

オーディオプログラミング

最初に

みなさん、こんにちは。マテウス・ビラノです。アセットパイプライン、開発ツール、ゲームプレイシステムを専門とするゲームオーディオエデュケータ兼デベロッパとして、主にオーディオの分野で活動しています。私の名前に聞き覚えがある方は、おそらくWwise Up On Airの動画(英語)をご覧になったからだと思います。

ゲームオーディオのプロフェッショナルや愛好家がWwise Authoring API(WAAPI)や PyWwise Pythonパッケージに関して知識を深めるお手伝いをできればと思い、このブログを寄稿しました。最初に一般的なWAAPIのユースケースを紹介し、次にPyWwiseを深掘りします。このブログ記事を読み終えた方が、自信を持ってPythonでWAAPIツールを構築できるようにすることを目標としています。

はじめに、このような機会を設けてくださったAudiokineticに感謝の気持ちを伝えたいと思います。コラボレーションできることをいつも光栄に感じており、これからもゲームオーディオコミュニティに新しいコンテンツを提供していきたいと考えています。Audiokineticは最高のパートナーです。

 

推奨されるソフトウェア

このブログに沿って作業するには、以下のソフトウェアが必要です。

技術的には統合開発環境(IDE)やVisual Studio Codeをはじめとするコードエディタならどれでも使用できますが、私はPyCharmにこだわっています。PyCharmはPythonに特化しており、非常に使いやすいためです。Pythonのインストールや設定にも対応しています。

 

Pythonを選ぶ理由

PyWwiseはPython向けに特別にプログラミングされています。私がPythonを選ぶ理由は、次の3つの点に優れているためです。

  • コードの読みやすさ
  • 組み込みライブラリ
  • コミュニティサポート

Pythonをはじめて使用する場合でも心配する必要はありません。ここで紹介する例は初心者にも分かりやすくなっており、Pythonの公式ウェブサイトには参考資料(記事、おすすめの参考書、ドキュメントなど)が豊富に用意されています。

 

WAAPIとは

Wwise Authoring API(WAAPI)は特定のプログラミング言語に依存しないインターフェースであり、ツールやスクリプトを使用してWwiseとやり取りすることができます。開発ツールの作成で幅広く使用されており、自動化検証インテグレーションタスクに関連することが一般的です。

実例を示すために、シンプルなツールのアイデアをいくつか紹介します。

  • インポータでオーディオファイル名に基づきコンテナを作成する。
  • スクリプトでオブジェクト名の中にあるスペースを下線に置き換える。
  • バッチスクリプトでテールの無音部分を切り取って最適化する。
  • クリーンアップツールでSoundBank内に存在しないWwiseオブジェクトを削除する。

WAAPIはWwise Authoring Query Language(WAQL)やWwiseアドオンコマンドと実に綿密に連携します。これら3つを習得することで、オーディオツールを作成する際に新たな可能性を大きく広げることができます。

 

PyWwiseとは

PyWwiseは、無料で提供されるオープンソースのPythonパッケージであり、Audiokineticのwaapi-clientパッケージの最上位に構築されています。PyWwiseを使用して、Wwiseとのやり取りをすべて処理できます。このブログでは、waapi-clientを「raw WAAPI」と呼びます。

 

きっかけ

私はこれまでのキャリアにおいてWAAPIを頻繁に使用し、あらゆる規模のオーディオチームにさまざまなカスタムツールを提供してきました。当然のことながら、raw WAAPIの欠点にも気づきました。

  • JSONオブジェクトを頻繁に使用する必要があるため、しばしば非常に複雑になります。
  • コードのコンパイル技術のサポートが不足しているため、コンパイルに時間がかかる場合があります。
  • インラインドキュメントが提供されていないため、代わりにウェブブラウザーが必要になります。

ラッパー関数を作成することで、そうした問題を修正できる」と考える人もいるでしょう。確かにその通りです。それはまさに、私がPyWwiseを使用する前までさまざまなプロジェクトやクラアントで繰り返し行ってきたことです。問題がお分かりになりましたでしょうか?

結局はボイラープレートコードを記述することに疲れてしまったのです。私は生産性の高いコードを実際に記述するよりも、JSONでアクロバティックなことを行うために多くの時間を費やしていることに気が付きました。私を含めたすべての人が、このようなことをする必要がなくなることを願いました。

私がプロジェクトの谷間にようやくゆっくりとした時間を過ごしていた時、PyWwiseと出会いました。情熱を持って意欲的に取り組むことができるプロジェクトでした。raw WAAPIで求められていた、パワーアップした機能を備えた究極のラッパーがPyWwiseです。

 

初期設定

それでは設定をはじめましょう。PyCharmを開いて新しいプロジェクトを作成します。添付のスクリーンショットをご参照ください(すべてバージョン2025.1をベースにしていますが、おそらく後続のバージョンと大きく違わないでしょう)。

welcome-to-pycharm

python-new-project

python-package-pywwise

準備ができたら、コーディングにすすみましょう。それぞれの例は、以下の構成になっています。

  1. タスクの概要説明
  2. raw WAAPIを使用したタスクの実装方法
  3. PyWwiseを使用したタスクの実装方法

 

具体的な例

例1:Hello, Wwise!

簡単なものからはじめましょう。Wwise Logsビューにログメッセージを書き込みます。WwiseのLogsビューをご存じない方のために、このビューを開いた時の画面を以下に示します。

logs-view-open-Wwise

Wwiseに接続中であれば、1つの関数を呼び出すだけでメッセージ付きの新しいログ項目を追加できます。そのためには、いくつかのデータの入力(引数)が必要です。

  • “message”:新しいログエントリのテキスト
  • ”severity”:新しいログエントリの重要度(タイプ)。 “Message”、“Warning”、“Error”、“Fatal Error”のいずれかを指定します。
  • “channel”:新しいログエントリのチャネル(タブ)。“soundbankGenerate”、“conversion”、“copyPlatformSettings”、“waapi”、“projectLoad”、“general”、“sourceControl”、“lua”のいずれかを指定します。

まずraw WAAPIを使用した実装例を見てみましょう。

# Raw WAAPI

from waapi import WaapiClient

# 最初にWwiseに接続します。
client = WaapiClient()

# 次にデータを入力してJSONオブジェクトを作成します。
args = {"message": "Hello, Wwise!", "severity": "Message", "channel": "general"}

# その後に作成したJSONオブジェクトを使用して関数を呼び出します(関数名を指定)。
client.call("ak.wwise.core.log.addItem", args)

# 最後に接続を切断します。
client.disconnect()

以下に示すようにたくさんの文字列があることに気が付きます。 

  • データ入力名(“message”、“severity”、“channel”)
  • 入力データ自体(“Hello, Wwise!”、“Message”、“general”)
  • 呼び出すWAAPI関数の名前(“ak.wwise.core.log.addItem”)

ここにはエラーが入り込む余地があります。IDEではタイプミスを確実に見つけることができません。PyWwiseはユーザの代わりに文字列やJSON オブジェクトの作成を処理して、このようなワークフローの問題に対処します。 

# PyWwise

from pywwise import new_waapi_connection, ELogSeverity, ELogChannel

# 最初にWwiseに接続します。
ak = new_waapi_connection()

# 次に関数を呼び出すだけです。JSONは不要です。
ak.wwise.core.log.add_item("Hello, Wwise!", ELogSeverity.MESSAGE, ELogChannel.GENERAL)

# 最後に接続を切断します。
ak.disconnect()

ここで残されている文字列は、実際のメッセージ“Hello, Wwise!”だけです。そのほかは実際のPythonオブジェクトに置き換えられます。これらは完全に自己文書化されて、自動コンパイルに対応しています。例えば、以下はPyCharm内部に直接組み込まれているak.wwise.core.log.add_item関数のドキュメントです(関数名の上にカーソルを置くと表示されます)。

hello-wwise-pywwise

お気づきかもしれませんが、オブジェクトのいくつかに「E」のプレフィックスが加えられています。それらはenumeration(列挙型)と呼ばれます。列挙型は、一連の事前定義された値であると考えてください。例えば列挙型のELogSeverityの場合は、MESSAGEWARNINGERRORFATAL_ERRORの4つの値が表示され、ユーザはその中から1つだけを選ぶことができます。PyWwiseによって提供される列挙型は、自動コンパイルに加えてエラーチェック(タイプミスなど)にも対応しています。

pywwise-general

pywwise-general-typo

最後にPyWwiseコードスニペットを実行すると、“Hello, Wwise!”の新しいログエントリがWwiseに直接表示されます。簡単ですよね?

pywwise-log-entry-Wwise

次にWwiseオブジェクト(サウンド、コンテナ、バスなど)に関する例を見てみましょう。WAAPI対応のツールやスクリプトのほとんどは、Wwiseオブジェクトの検索、作成、削除、変更に関連しています。

例2:Wwiseオブジェクトの検索

ループ対象に設定したサウンドオブジェクトをすべて検索して、次にそれぞれでストリーミングを有効化します。Authoring Query Language(WAQL)を使用すると、オブジェクトを非常に簡単に検索できます。ストリーミングプロパティの設定では、以下に示す3つのデータの入力が必要です。

  • "object":新しいプロパティ値を設定するオブジェクト
  • "property":新しい値の設定対象となるプロパティの名前
  • "value":プロパティの新しい値

このブログ記事ではクエリ言語については詳しく説明しませんが、WAQLを習得することを強くおすすめします。WAAPI(rawまたはPyWwise)を使用するプロジェクトでは、Wwiseオブジェクトのクエリは非常に一般的な操作です。ただし、この例では非常に簡単なクエリを使用することにします。

早速raw WAAPIを使用してはじめましょう。

# Raw WAAPI

from waapi import WaapiClient

# Wwiseに接続します。
client = WaapiClient()

# ループ対象に設定されているサウンドオブジェクトを検索します。
args_get = {"waql": '$ from type Sound where IsLoopingEnabled = true'}
objs = client.call("ak.wwise.core.object.get", args_get)

# 各オブジェクトで繰り返し、ストリーミング対象に設定します。
for obj in objs["return"]:
  args_set = {"object": obj["id"], "property": "IsStreamingEnabled", "value": True}
  client.call("ak.wwise.core.object.setProperty", args_set)

# 完了。
client.disconnect()

PyWwiseを使用すると、JSONが不要になって文字列の量が最小限に抑えられるため、プロセスがかなり簡素化されます。

# PyWwise

from pywwise import new_waapi_connection, Sound

# Wwiseに接続します。
ak = new_waapi_connection()

# ループ対象に設定されているサウンドオブジェクトを検索します。
objs = ak.wwise.core.object.get('$ from type Sound where IsLoopingEnabled = true')

# 各オブジェクトで繰り返し、ストリーミング対象に設定します。
for obj in objs:
  ak.wwise.core.object.set_property(obj.guid, Sound.is_streaming_enabled, True)

# 完了。
ak.disconnect()

お気づきになったかもしれませんが、このスクリプトは拡張性の高いツールのよい例です。サウンドブジェクトの数に関係なく対応できます。1個でも何億兆個でも全く問題ありません。

sound-objects-script

sound-objects-scriptable-after

例3:Wwiseオブジェクトの作成

ここではサウンドオブジェクトを作成して、ボリュームルーピングストリーミングのパラメータを設定しましょう。サウンドの出力バスも設定します。オブジェクトを作成するには、以下に示すようにいくつかのデータの入力が必要です。

  • “name”:新しいオブジェクトの名前
  • “type”:作成するオブジェクトのタイプ。Wwise Objects Referenceに記載されているオブジェクト名のいずれかにする必要があります。
  • “parent”:新しいオブジェクトの作成先となる、既存のオブジェクトのGUID、プロジェクトパス、一意の名前
  • “onNameConflict”:同じ親や名前がすでに存在するオブジェクトの場合のアクション。“replace”、“rename”、“merge”、“fail”のいずれかを指定します。

パラメータについては、プロパティリファレンスの2つのタイプがあります。プロパティは、通常ブール値(例:True)または数値(例:-6.0)を保存するパラメータです。リファレンスは通常、別のWwiseオブジェクト(例:“/Master-Mixer Hierarchy/Default Work Unit/Master Audio Bus/Sfx”にあるバス)のプロジェクトパスGUIDが保存されるパラメータです。パラメータを設定するには、以下のデータが必要です。

  • “object”:パラメータを設定するプロジェクトパス、GUID、オブジェクト
  • “property”または“reference”:設定するパラメータの名前。Wwise Objects Referenceを参照する必要があります。
  • “value”:パラメータの新しい値

このケースのraw WAAPIとPyWwiseを比べてみましょう。

# Raw WAAPI

from waapi import WaapiClient

# 新規接続。
client = WaapiClient()

# データが入力されたJSONオブジェクト。親オブジェクトは、Actor-Mixer HierarchyのDefault Work Unitです。
parent = "\\Actor-Mixer Hierarchy\\Default Work Unit"
args_new = {"name": "MyCoolSfx", "type": "Sound", "parent": parent, "onNameConflict": "replace"}

# 関数の呼び出し(関数名を指定)。
obj = client.call("ak.wwise.core.object.create", args_new)

# Volumeプロパティを設定。
args_volume = {"object": obj["id"], "property": "Volume", "value": -6.0}
client.call("ak.wwise.core.object.setProperty", args_volume)

# IsLoopingEnabledプロパティを設定。
args_loop = {"object": obj["id"], "property": "IsLoopingEnabled", "value": True}
client.call("ak.wwise.core.object.setProperty", args_loop)

# IsStreamingEnabledプロパティを設定。
args_stream = {"object": obj["id"], "property": "IsStreamingEnabled", "value": True}
client.call("ak.wwise.core.object.setProperty", args_stream)

# OutputBusリファレンスを設定。
path_bus = "/Master-Mixer Hierarchy/Default Work Unit/Master Audio Bus/Sfx"  # このバスが存在している必要があります。
args_bus = {"object": obj["id"], "reference": "OutputBus", "value": path_bus}
client.call("ak.wwise.core.object.setReference", args_bus)

# 完了。
client.disconnect()

PyWwiseで同じように実装した場合、かなりすっきりして見えます。 

# PyWwise

from pywwise import new_waapi_connection, ProjectPath, EObjectType, ENameConflictStrategy, Sound

# 新規接続。
ak = new_waapi_connection()

# ProjectPathの関数によってActor-Mixer Hierarchy内のDefault Work Unitが提供されます。
parent = ProjectPath.actor_mixer_hierarchy(default_work_unit=True)

# この関数によって新しいオブジェクトが作成され、オブジェクトに関する情報が返されます。
# これは必要な情報であるため、変数に保存しましょう。
info = ak.wwise.core.object.create("MyCoolSfx", EObjectType.SOUND, parent, ENameConflictStrategy.REPLACE)

# オブジェクトの情報を使用して、オブジェクトへの直接紐づけを作成します。
# これにより、オブジェクトでプロパティやリファレンスを簡単に設定できます。
sound = Sound(info, ak)
sound.volume = -6.0
sound.is_looping_enabled = True
sound.is_streaming_enabled = True
sound.output_bus = ProjectPath("/Master-Mixer Hierarchy/Default Work Unit/Master Audio Bus/Sfx")

# 完了。
ak.disconnect()

以上です。取り組んだ例の中では、これがおそらく最も便利です。私自身の多くのツールを含め、Wwiseの自動化用のツールは数多くありますが、これらのツールではこのセクションで示したスニペットと同様のコードが使用されます。

 

結論

PyWwiseは単に構文を簡単にするだけではなく、以下に示すように安全で簡単なコード記述を実現します。

  • 文字数が少ないため、入力ミスが減る
  • オートコンプリート機能を備えた列挙型
  • 完全なインラインドキュメント

ドキュメントを再確認する時間が減り、重要事項に取り組む時間が増えます。

それでは、すべてのチームがPyWwiseに切り替えるべきでしょうか?いいえ、必ずしもそうではありません。チーム独自のWAAPIラッパーがあり、問題なく機能している場合は、そのまま使い続けても構わないと思います。しかし、新たにプロジェクトはじめる場合や、コードベースを大幅に簡素化しようと考えている場合は、PyWwiseが最適かもしれません。

 

最後に

PyWwiseの学習について、ご満足いただけると嬉しいです。PyWwise GitHub Wikiには、初心者だけでなく、WAAPIの経験者にも役立つ例やガイダンスが数多く用意されています。コントリビュータの助けを借りて、PyWwiseのメジャーアップデートのたびに定期的に更新されています。

PyWwiseに関するご質問がございましたら、 遠慮なくご連絡ください。またバグを見つけた場合や、改善の余地があると思われる点について PyWwise GitHub Issuesのページに投稿していただけますと幸いです。プルリクエストも大歓迎です。

このブログ記事をお読みいただきありがとうございました。またお会いしましょう。

マテウス・ビラノ

マテウス・ビラノ

マテウス・ビラノはテクニカルオーディオを専門とするゲームデベロッパとして、システムデザインとツール開発に重点的に取り組んでいます。彼が手掛けた作品には、Until Dawn、Glassbreakers、Baldur’s Gate 3などがあります。マテウスはオープンソースプロジェクトにも継続的に参加して、ゲーム開発に関する講義を行ったり、「Lonyeld」のアーティスト名でLoFiミュージックを制作したりしています。

コメント

Replyを残す

メールアドレスが公開されることはありません。


ほかの記事

コマンドアドオンで実現できる、ワークフローの改善

継続的なワークフロー改善の努力を...

20.8.2019 - 作者 ベルナール・ロドリグ(Bernard Rodrigue)

WwiseとREAPERの連携(パート1)WAAPI Transfer

WAAPI...

27.5.2020 - 作者 二コラ・ルキッチ(Nikola Lukić)

誰でも使えるWAAPI パート1: 概要

こんにちは。ヤン・ワン(別名シー・イェ)です。 私がWAAPI(Wwise Authoring...

23.2.2021 - 作者 トーマス・ワン(汪洋)

Wwise 2021.1向けAuthoringプラグイン | パート 1: 経緯と目標

14.4.2021 - 作者 ミシェル・ドネイ(MICHEL DONAIS)

新WAQL(Wwise Authoring Query Language)の紹介

9.6.2021 - 作者 ベルナール・ロドリグ(Bernard Rodrigue)

傾いた2Dビュー用にカスタマイズされた、Wwiseリスナー位置投影システム

はじめに Jater (Ruohao)...

14.1.2025 - 作者 Ruohao (Jater) Xu

ほかの記事

コマンドアドオンで実現できる、ワークフローの改善

継続的なワークフロー改善の努力を...

WwiseとREAPERの連携(パート1)WAAPI Transfer

WAAPI...

誰でも使えるWAAPI パート1: 概要

こんにちは。ヤン・ワン(別名シー・イェ)です。 私がWAAPI(Wwise Authoring...