ReaWwiseの開発 パート2 - 実装

Wwiseの使い方やツール

本ブログは2部に分かれています。前半はReaWwiseのプロダクション前についてでしたが、後半は拡張機能の開発について説明したいと思います。

ユーザインターフェースのデザインが完成に近づき、WAAPI-Transferのコードベースの仮定を検証する複数のイテレーションが終わったところで、プロジェクトの技術的な基盤を決定する段階となりました。

プログラミング言語

REAPERは複数のスクリプト作成のプログラミング言語に対応しています(Lua、EEL、Python、C++)。中でもC++を選んだ理由は以下の通りです:

  • Audiokinetic社内にC++の専門知識がある
  • 既存コードを再利用できる
    • WAAPI C++クライアント
  • REAPERのLuaの好みなど、REAPERと合わせて使われている言語に対するディペンデンシがない
  • Pythonスクリプトのインタプリタのような実行時のディペンデンシに依存しない
  • WAAPI-Transferなど、ほかのオープンソースプロジェクトからの再利用の可能性
  • ほかのReaScriptに対するディペンデンシがない(GUIに関する可能性あり)

フレームワーク

使用する言語が決まったところで、このプロジェクトに最も適したフレームワークを検討しました。以下を基準に判断しました:

  • クロスプラットフォームであること(Windows、Mac)
  • コミュニティから確実にサポートされていること
  • テーマ設定やカスタマイズが可能であること
  • 最近のプログラミングパターンやパラダイムを使用していること
  • 社内コード標準に従っていること

WDL/ネイティブ

これがC++ベースのREAPER拡張で最も広く使われている方式のようです。OSがホストするウィンドウの範囲内でユーザインターフェースを構築することができます。またMacでWin32 APIのサブセットを使用するための、SWELLを提供します。これはクロスプラットフォームの観点で重要です。

私たちは開発の初期段階に、一部のアイデアを検証するためにWAAPI-Transferコードベースを使いました。この時、SWELLが対応していないWin32 APIをかなり使用していることにすぐ気づきました。これによりクロスプラットフォーム対応の面でいくつかの問題が生じ、一部のグラフィックコンポーネントを使用できないことが分かりました。

SWELLでMacのネイティブコンポーネントを使う場合、MacとWindowsのルック&フィールが完全に一致しないということになります。

もう1つの欠点として、Win32のAPIは難易度が高いことで知られています。これはC言語で書かれており、ずいぶん前からあります。さらに後方互換性を保つ必要があるため、多数の古いアーティファクトが残っています。

JUCE

JUCEはオーディオ関連のアプリケーションやプラグインを作成するために広く使われている、クロスプラットフォームのC++フレームワークです。現代的で一貫性のあるAPIで、カスタマイズ性に優れ、コミュニティのサポートも絶大です。詳細は後述しますが、CMakeのためのネイティブサポートもあります。

私はJUCEベースでオープンソースの既存REAPER拡張を見つけることはできませんでした。ただしコードスニペットは確かにいくつかあり、JUCEをREAPER拡張に実装することは可能で簡単であることが証明されていました。

WAAPI-Transferコードベースで少し作業をすすめた結果、新しいUXがあまりにも違うため、UIの大部分を書き直す必要があることが明らかになりました。JUCEコンポーネントの描写はアクセスが簡単でオーバーライドすることができるため、JUCEのバニラコンポーネントをデザインで計画した通りにすることは容易でした。

イベント処理のシステムはWin32のイベント処理システムを直接使うよりも直感的です。イベントはコンポーネント内で管理されます。ウィンドウのメッセージループはJUCEが抽象化してくれるため、複雑な管理が不要です。

JUCEのもう1つの長所はオブジェクト指向のプログラミングがGUIアプリケーション開発と相性がよいことです。個々のGUIコンポーネントをオブジェクトとして簡単に表現することができ、それぞれの機能が関数として表されます。Win32 APIではそこまで自然に行えません。コンポーネントがstructとして定義されています。JUCEを使用した時のコードの整理方法と同じようにするためには、より注意が必要です。

プロジェクトの整理と構造

プロジェクト構造の説明に移る前に、CMakeを簡単に紹介したいと思います。

CMake

ReaWwiseプロジェクトにはビルドファイルが含まれません。代わりにCMakeを使用します。CMakeでは、私たちがプロジェクトのビルドをどうしたいのかを汎用的に決めることができます。次にCMakeを使い、プラットフォーム別のビルドファイルを生成することができます。

以下のようにCMakeを使うメリットはいくつかあります:

  • ビルド環境ごとにビルドファイルを別々に維持する必要がありません
  • システムライブラリを探すためのヘルパー関数があります
  • さまざまなシステムのビルドファイルを生成することができます
  • ファイル操作やシェルコマンドの実行など、その他のタスクを実行することができます

プロジェクト構成

当初から、懸念事項をできる限り分離したいと考えました。このためコードベースを以下の4つのメインプロジェクトにまとめました:

ReaWwise (src/extension)

このプロジェクトにREAPER固有のコードがすべて含まれます。拡張のエントリーポイントを定義し、拡張APIへの必要なすべての呼び出しを行います。さらにWAAPIをほかの拡張機能が使用できるよう、Luaに公開するコードも含まれます。このプロジェクトは共有ライブラリとしてコンパイルされ、REAPERにインポートする実際の拡張ファイル(reaper_reawwise.dll)を出します。

WwiseTransfer_Standalone (src/standalone)

このプロジェクトにReaWwiseをスタンドアロンアプリケーションとしてローンチするためのコードが含まれます。主にGUIの開発とテストで使用します。REAPERへの接続はありません。REAPERから来るはずのデータはモックです。

WwiseTransfer_Shared (src/shared)

このプロジェクトにアプリケーションのすべてのコアコンポーネントやGUI要素が含まれます。コードは静的ライブラリとしてコンパイルされ、拡張やスタンドアロンのコードがディペンデンシとして使用します。

WwiseTransfer_Test (src/test)

このプロジェクトにテスト関連のコードがすべて含まれます。

ディペンデンシ

サードパーティ

  • Catch2:テスティングフレームワーク
  • JUCE:アプリケーションフレームワーク
  • rapidjson:WAAPIクライアントのディペンデンシ
  • reaper-sdk:REAPER APIヘッダ
  • trompeloeil:モッキングフレームワーク

最後のディペンデンシとしてAkAutobahn(WAAPIクライアントのC++実装)と、Wwise SDKのいくつかのヘッダがあります。これらはコードベースの一部ではなく、Audiokinetic Launcherから別途入手します。

下図はこれらのプロジェクトやディペンデンシがどのように互いにリンクしているのかを表しています:

reawwise_1

アプリケーションアーキテクチャ

このセクションでは、ReaWwiseのメインコンポーネントをいくつか紹介しながらその仕組みについて考えます。

プレビューパネル

プレビューパネルを駆動するメインコンポーネントはDawWatcherオブジェクトです。DawWatcher が定期的にDawContextに対しクエリを行い、プレビューパネルの内容を更新すべきかどうかを確認します。またWwiseからも、プレビューに影響するかもしれない変更があった場合は通知されます。プレビューパネルを更新する必要があると、DawWatcherはレンダリングするアイテムをDawContextからフェッチし、オブジェクト情報をWwiseからフェッチします。次にすべてを一緒にコンパイルしてプレビューを作成し、それをプレビューパネルに送信します。

DawContextはDAWを表したインターフェースです。DawWatcherなどのWwiseTransfer_Shared内のコードは、ここからファイルパス、セッション情報、セッションステートなどの情報をREAPERから取得します。WwiseTransfer_Shared内のコードは、REAPERを意識しません。またDawContextが別のDAWやアプリケーションとつながるような実装も可能です。

ReaWwiseにおいて、DawContextの具体的な実装がReaperContextです。拡張APIの呼び出しがすべてここにあります。

以下はプレビューパネルの更新プロセスの簡略化されたシーケンス図です。

reawwise_2

Wwiseへの転送

ImportTaskオブジェクトが、ReaWwiseとWwiseの間の転送を駆動するメインコンポーネントです。WaapiClientオブジェクトを使用し、Wwiseに対していくつかの呼び出しを行います。また転送の詳細を記録し、転送の終わりに詳細レポートを作成します。ImportTaskはReaWwiseのメッセージスレッドをブロックしないよう、非同期で実行されます。

WaapiClientはWwiseとのやり取りを行うコンポーネントです。WAAPIのリクエストや回答の管理をすべて抽象化し、AkAutobahnクライアントの上のレイヤーとして機能します。WaapiClientWatcherは、WAAPIとの接続を監視するためにWaapiClientと合わせて使うコンポーネントです。Wwiseとの接続が維持されているか、定期的に確認します。

以下はインポートプロセスの簡略化されたシーケンス図です。

reawwise_3

アプリケーションステート

ReaWwiseはJUCE ValueTreeを使用し、コンポーネント間で共有する必要のあるステート情報やデータを格納しています。ツリー形式のデータ構造であり、int、string、boolなどの多くの基本データタイプを格納することができます。独自のデータタイプには、特別なコンバージョンテンプレートを使います。コンポーネントはリスナーとして登録することにより、ValueTreeのデータ変更が通知されます。

ReaWwiseではGUIコンポーネントがValueTreeの変更に聞き耳を立て、自身を適宜更新します。こうすることでコンポーネント同士を完全に切り離すことができます。またビジネスロジックと表示方法のロジックも分離させることができます。以下はReaWwiseのテキストフィールドが、その内容の妥当性に応じてエラーステートを表示する流れを示す簡略化されたシーケンス図です:

reawwise_4

テスト

開発フェーズ中にいくつかのテスト方式を実験しました。最初に試し、今でもプロジェクトに残っているのがユニットテストです。2つの目アプローチはエンドツーエンドテストでしたが、最終的に断念しました。

エンドツーエンドテスト

エンドツーエンドテストについて考えたきっかけは、ADC21のJoel Noel氏の講演です。JUCEアプリケーションのための新たなエンドツーエンドテストのフレームワークとして、JUCE End to endを発表しました。どのようなJUCEアプリケーションでも、javascriptで制御できるようにするツールです。どのようなJUCEコンポーネントでも、クエリできるようになります。例えばアプリケーション内のボタンのクリックをトリガーした上で、確認のポップアップが表示されたかどうかをチェックすることができます。

フレームワークとしてうまくいきましたが、最終的にすべてのコンポーネント(Reaper、ReaWwise、およびWwise)が一緒にシームレスに自動で動くようにすることは難しく、不可能に近かったのです。

まとめ

この連載では2部に分けて、アプリケーション開発のさまざまな面を紹介しました。製品コンセプトの形成、ユーザエクスペリエンスの検討、インターフェースの設計などにはじまり、ReaWwiseの技術的な判断やソフトウェアアーキテクチャの選択にいたるまで、掘り下げて紹介しました。

ReaWwiseの内部構造についてさらに知りたい方は、GitHubのaudiokinetic/ReaWwiseで公開中のコードをご覧になることをおすすめします。

アンドリュー・コスタ

ソフトウェアデベロッパ、R&D

Audiokinetic

アンドリュー・コスタ

ソフトウェアデベロッパ、R&D

Audiokinetic

8年前からソフトウェアデベロッパとしてコンテンツクリエイターのためのツールを開発。ソフトウェアと音楽制作に情熱を注いでいる。2021年よりReaWwiseの主要デベロッパのうちの1人として、Audiokineticで開発に携わっている。

コメント

Replyを残す

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

ほかの記事

A Wwise Unity Cheat Sheet

今日のトピックは、Wwise Unity...

2.2.2021 - 作者 マス・マレティ・スナロプ(MADS MARETTY SØNDERUP)

Wwiseのオーディオオブジェクト 作成からプロファイリングまで簡単な9ステップ

Wwiseの新しいObjectベースのオーディオパイプラインが気になるけれど、どこから始めていいのか、悩んでいませんか?Windowsで、Wwiseを使ってAudio...

24.6.2021 - 作者 ダミアン・キャストバウアー(Damian Kastbauer)

WAAPIとTTSでVOの仮アセットを自動作成する方法

はじめに...

17.6.2022 - 作者 Huang Chao (黄超)

WAAPIとPythonを利用したチーム作業の紹介とその例

20.12.2022 - 作者 ユージン・チェルニー

AudioLinkと共に挑む冒険

11.4.2024 - 作者 ピーター・ドレッシャー (PDX)

Unreal EngineのAudioLinkを使用する方法

はじめに...

2.7.2024 - 作者 合田 浩(HIROSHI GODA)

ほかの記事

A Wwise Unity Cheat Sheet

今日のトピックは、Wwise Unity...

Wwiseのオーディオオブジェクト 作成からプロファイリングまで簡単な9ステップ

Wwiseの新しいObjectベースのオーディオパイプラインが気になるけれど、どこから始めていいのか、悩んでいませんか?Windowsで、Wwiseを使ってAudio...

WAAPIとTTSでVOの仮アセットを自動作成する方法

はじめに...