コンテンツへスキップ
ものがたり
戻る

【サルベージ】 オーディオプラグインフレームワークを設計する

昔からいろいろな思いつきを書き溜めたまま放置してしまって、そのままお蔵入りになることが多いのだけど、これは一度考え方のたたき台的に出しておいたほうが良いかと思ったので公開することにした。書いたのは最後の2段落以外は2020年11月なのだけど、最近CLAPも話題に出てきたのでその流れでも読めるかもしれない。

以下本文。


これは自分が現在開発しているAAP (android-audio-plugin-framework) の設計方針を見直すために書いている。

オーディオプラグインフレームワークの乱立問題

オーディオプラグイン規格をきちんと作り出すのはたぶん簡単ではない。

オーディオプラグインの実行環境とSDKの分離思考

従来は、オーディオプラグインフレームワークとはランタイムとSDKの両方を曖昧に含む関係だったが、JUCEやWDL/iPlug2、DPFなど、オーディオプラグインフレームワークそのものではなく、オーディオプラグインを開発できるSDKがポピュラーになっている。Carlaのように複数のプラグインフレームワークやファイルフォーマット(sf2/sfzなど)に対応する機構もある(が、まだポピュラーとまではいけない)。新しいプラグインのランタイムも、これらにプラグインバックエンドやホストバックエンドを追加すれば対応できるので、フレームワークが乱立することそのものについての弊害はある程度縮小している。

これを前提として考えると、新しいオーディオプラグインフレームワークを構築するのは、必ずしもそこまで無価値ではない。

初期段階ではそのフレームワークのAPIが安定していることよりも、それらの開発フレームワークのサポートが重要であると考えられる。一般的に、長期的に開発されていてポピュラーなSDKはAPIが安定している。

安定的なAPIと安定的なABI

プラグイン開発者あるいはホスト開発者として懸念すべきは、「API」の安定性だ。APIとABIの維持に関する各ステークホルダーのインセンティブをまとめておく。

バージョン1.0をリリースしてsemantic versioningを意識する段階になったら、プラグインのAPI自体が破壊的変更を要求しないようにしなければならない。もっとも、破壊的変更のニーズは常に存在する。LV2設計者はrun()にオーディオバッファを渡す仕様にせずにconnect_port()でポインタ上の接続を確立してrun()に何も渡さない仕様にしたことを後悔しているが、このようなレベルで非互換問題が生じる(これを生じさせたい)可能性は常にある。VST3の仕様が策定された時に、プラグインがプロセス分離された空間でないとロードできないiOSやAndroidのような環境が出現することは想像できなかっただろう。

(AAPではNdkBinderとAIDLの制約上、個別のashmemポインターをParcelFileDescriptorとして送受信せざるを得ないので、LV2で反省しているようなことが実現できるわけではないし、JUCEサポートにおいてはashmemポインターが動かないことを前提としている。)

現状AAPは破壊的変更の過ちを犯すルートにある(APIの追加が破壊的変更になっている)。VST-MAのCOMライクなクエリーインターフェースのほうが賢いこともある。LV2でこれをやろうと思ったら拡張を使うしか無いし、全てのホストに拡張に対応させるしかない。

一方でVST3のクエリーインターフェースはまだるっこしいのでVST3そのものが採用されない、みたいな側面はあった。このあたりはABIが課題になるような低レベルではなく、一段上の、APIの破壊的変更があり得るeasing API SDKを用意することで対応するのが適切かもしれない。

GUIとの連動・分離

WindowsではWin32 API、MacOSではCocoaでほぼ統一できるが(ホントかな…CarbonとCocoaを同一視してるレベルみたいな気もしてきた。まあとりあえずいいか)、LinuxデスクトップではGtk2/Gtk3/Qt4/Qt5で乱立していて、しかも相互運用性が無かった。現在はX11を使いそれ以上のフレームワークを使わないのがトレンドになっている。結果的に、オーディオプラグインのGUIフレームワークはプラットフォーム標準やUI技術から乖離した、貧弱なゲームUIフレームワークに近いものになってしまっている。

これは伝統的なオーディオプラグインの立ち位置であり、モダンなUIに合わせてプラグイン機構を構築できる可能性も十分にある。そのためにはプラグイン機構とUI機構の十分な分離が必要になる。これを強制的に実現できているのはLV2のみだ。LV2の場合、オーディオプラグインコード(classなど)を直接参照することもできないため、ポート経由でUIの更新を反映するしかないため、この分離点をより強力に(AOPのように)制御できる。AUやVST(あるいはそれを前提にしたJUCE)ではこれが自然にはできない。

とはいえ、LV2でもGUIがX11の貧弱なUIフレームワークしか使えない事態に変わりはない。LinuxのGUIで相互運用性がないというのは、具体的にはGUIのmain loopの設計の相違で複数のGUIフレームワークが両立し得ないという状態だ。X11を使っているのは最大公約数としての消極的な対応でしかない。真面目に解決するなら、プロセスレベルで分離した上で、UIをホストとの間で制御する仕組みを構築する必要がある。

プロセスを分離するとパフォーマンスに影響が出るが、そもそもUIスレッドとオーディオスレッドは分離していなければならないし、UIスレッドにリアルタイム要件は無い。UIからオーディオパラメーターを操作するときはアトミックであることが求められるし、オーディオのプロパティはUIプロセスからオーディオプロセスへのリクエストプロトコルによってクエリできればよい。


この記事を共有:

前の記事
12月の活動記録(2021)
次の記事
VitalをAndroidで動かす