今月は前のめりにまとめた完全自分用の開発作業記録です。今月は上旬にちょっとDSPまわりの勉強をしていた以外は、だいぶAndroidAudioPlugin関連の開発に時間をつぎ込めた感じです。
s:/aria2web/aqua/
割とどうでも良い前フリから。もう半年以上前ですが、aria2webというsfizzサンプラーにARIA extension GUIをWebViewとしてロードできる仕組みを実現したLV2プラグインを作りました。
プロジェクト名がイマイチだったので*1、いずれAAPに取り込んでWeb UI化するときに名前を変えようと思っていたので、今月ようやく着手しようと思った時点でaquaに改名しました。
XML namespacesを適切に利用した拡張管理
そういうわけで、先月末にいよいよGUI統合に取り掛かりたいと書いていたやつですが、実は未だに何もない状態です。先に片付けるべき課題があったためです。具体的には、プラグイン個別のGUIに対応するより先に、GUIのないプラグインの汎用パラメーターコントローラーリストのようなUIを(特にWebUIで)作るべきだろう、と考えました。
そのために実際に必要になるのは、パラメーターの種別に応じたUIコントローラーであり(値の範囲が整数なのに小数値を入力したくはないわけです。あるいはそもそもLV2 Atomのように数値でない場合とか)、そのためにはまずパラメーターのプロパティが規定される必要がありました。LV2で言えばport propertiesというコンセプトです。LV2であればlv2:portProperty
とかunits:unit
などがこれに相当します。
ポートのプロパティは拡張可能でなければなりません。拡張可能な情報を素直に実装するとしたら、適切な名前解決が重要です。
さし当たって、それまでdefault
, minimum
, maximum
属性として規定されていた<port>
要素のプロパティはurn:org.androidaudioplugin.port
名前空間に属することになりました。これまでAAPは自由に拡張可能としてきたわけですが、C APIレベルでの拡張性しか実装していなかったので、ポートのプロパティのようにメタデータ(aap_metadata.xml
)を拡張するやり方が未整備でした。
この拡張を実装するにあたって問題になったのが、AAPの内部でtinyxml2を使ってメタデータを処理していたのですが、tinyxml2はXML namespacesを処理できないことでした。マジか…他に何が使えるんだ…と思って調べてみると、PugiXmlもRapidXMLもダメ、使えるのはexpat(APIが完全に古い), libxml2(glib依存が厳しい), xerces-c++(でかい)…といった感じでした。ここは20世紀か?という感じの古参ばかりですが、あまり深入りしたくなかったので、Android側でのメタデータ解析は全てAndroid APIでやることにして、デスクトップのみlibxml2にして回避しました。ちなみにJUCEに含まれるXMLのAPIもダメです。
LV2みたいにTurtle文法を選択してパーサの選択肢が無くなるよりXMLにしたほうが絶対良いでしょ、と考えていた(いる)わけですが、C++がこんなにxmlnsをサポートしていないパーサばかりだとは思っていなかったので、自分がC#やらKotlinやら使ってコードを書いていたのはずいぶんスポイルされているんだなあ、となりました。しかし、うーん…さすがに2021年からC++でXMLパーサを実装するのはどうかな…
ちなみに今月はこのポート プロパティの基盤を実装しただけで、それを反映するGUIの構築には着手していない状態です。
flatten LV2 dev. environment
aap-lv2が12月にリポジトリを分割して開発しやすくなったので、aria2webをaquaに変更する過程で気づいた、sfizzで期待通りにオーディオデータが生成できなかった問題に着手しました。が、中で使われているLV2 toolkit (serd + sord + lv2 + sratom + lilv)がバイナリ参照になっていて、デバッグ時に中まで追えずに苦労していました。その度にデバッグのためにリポジトリの構成を変更してLV2 toolkitを一緒にビルドしてはコミット前に戻す、みたいなことを繰り返していたのですが、こんなことをするくらいなら全部aap-lv2のリポジトリでビルドすべきだ、と考えるに至りました。
実際これを避けていたのは(1)公式のビルドスクリプトがwafだったので公式に可能な限り準拠したやり方にしたかったのと、(2)Androidの4アーキテクチャ分 x この数のネイティブライブラリビルドの増加はCIビルド時間に有意に悪影響が出ると考えていたからなのですが、wafをスキップして直接CMakeでまとめてビルドしてみると個々のライブラリのビルド時間はそれほど長くも無かったので、全部フラットにビルドすることにしました。これでデバッグ体験はかなり良くなっています(現状やるのはわたしだけだと思いますが)。
ちなみにこの作業自体は前述のaquaをAAPに取り込んでWebUIの実験台に使おうと思ってまずsfizzの動作確認…となってオーディオ側の問題に気づいて着手したのですが、前述の通りGUI統合に入る前にポートのプロパティを…となってこれも未着手です。
Hera (Juno 60 emulator) port to AAP
今月はRolandからJuno 60のプラグインが出て話題になりましたが、ほぼ同時期にADLplugやsfizzの開発者がHeraという新しいJuno 60エミュレーターのJUCEのプロジェクトを公開していました。そういうつもりはないんだけど完全に開発者の追っかけみたいなムーブになっている…
既存のエミュレーターにJUCEのMPEサポートを加えて作ってみた、というコンセプトで作られているようです。RolandのほうはMPE対応はしていなさそうですね(featuresを見ているだけですが)。
CMakeベースのJUCEプラグインだったので、ちょうどCMakeでInstrumentとして機能するやつがほしかったんだ…! と思って、その日のうちにAndroidに移植しました。
aaphostsampleで開くとちゃんと音が出るので、最近ではOB-Xdと並んで簡単に動作チェックする時に活用しています。移植作業もEqとChowPhaserを移植していたのを応用しただけです。Heraとは全然関係ないのですがChowPhaserの作者から「Androidに移植してるの?? 試せる??」みたいなメールをいただいて「すまん…JUCEのaudio inまわりがおかしいからまだだ…!」みたいなお返事を書くことに…
Helio workstation with AAP
AAPのプラグイン移植はずいぶんバリエーションが増えてきたのですが、ホスト側はさすがに使い回せるアプリがほとんど無く、自作のaaphostsample、JUCEのAudioPluginHost、あとtracktion_engineを使うaugeneが一度も演奏できたためしがない…という状況でした。
JUCEを使ったDAWのひとつにHelio Workstationというのがあるのですが、これがTabletのみを意識しているとはいえAndroidでも動作すると謳っているので、これにAAPサポートを追加したいというのがひとつのプロジェクト完成形の姿でした(任意のDAWで任意のプラグイン移植が使える状態といえるわけです)。
ただhelio-workstationのプロジェクトは、Android用のプロジェクトなどが既にProjucerで生成された後の構造をいじって作られているようなので、既存のaap-juce移植のモデルにはうまくフィットしませんでした。何回か失敗した後で、スクラッチからHelio向けに独自のアプローチでビルドプロセスを構築することで(といってもどっちも自分しかやっていないのですが)、ようやくAAPサポートを追加できました。
ほぼオリジナルのコードに手を加えること無く実現したので、無理にビルド手順を一般化しないほうがいいのかなあと思い始めています。昨日も公開されたばかりのVitalを移植しようとしたのですが、このプロジェクト構造も一般的ではないのでAndroidビルドを追加する時点でかなり無理っぽさが出ています。いずれやるとしても何日かかかりそう…(本家でやってほしさある)
ちなみに現時点で、プラグインの一覧が取得できるものの名前の表示がイマイチ(これはオリジナルがそう)、オーディオグラフの設定画面ではオーディオ出力ポートが出てこないので生成結果を一切音声出力できないっぽく見える、その割に実はプラグインのインスタンスも生成できていてノートに合わせて音が生成できる、ただし出てくる音がノイズまみれの謎音…みたいな状態なので、実用性はまだ無いです。
プラグインインスタンス生成の安定化
AAPで初期からずっと困っていた最大の不安要素のひとつが、プラグインのインスタンスを複数回生成するとプラグイン側のサービスが落ちてホストも一緒に落ちる、という問題でした。これだとちょっと使うだけですぐホストが落ちるので使用体験を語れるほどのレベルにも至っていない感じだったので、port propertiesが落ち着いたところでがっつり取り掛かりました。
いろいろ試行してみてわかったのは、どうやらandroidaudioplugin.aarをbuild.gradle
上でmavenLocal()
から解決していると発生するが、デバッグするためにsettings.gradle
をいじってプロジェクトの一部にandroidaudioplugin
を追加してbuild.gradle
上でproject(':androidaudioplugin')
として参照すると発生しない(!)ということでした。
何でこの話を書こうと思ったのかというと、事実関係を確認する前提が割と罠だらけなので記録しておきたかったわけです(自分用メモなので!):
mavenLocal()
でパッケージを解決する時は、ローカルでビルドして修正を加えているバージョンが参照されているかどうか、build.gradle
の内容から念押しする必要がある(settings.gradle
で追加しても自動的にproject(':...')
で解決するわけではない)- CIでビルドしても通るようにsubmoduleの
android-audio-plugin-framework
をmake
でビルドしているので(前述のaap-lv2リポジトリ分割について書いたエントリを参照)、別のソースツリーでandroidaudioplugin.aarやandroidaudioplugin-lv2.aarに手を加えたものをアプリで確認するなら、maven localのパッケージを上書きしないようにビルドする必要がある - debugビルドとreleaseビルドの両方でmaven-publishされる構成が変わるので、自分の期待通りのものがaarとして発行されているか、
build.gradle
内のpublishToMavenLocal
タスク関連の記述をきちんと確認する
今回の問題は急場しのぎ的にreleaseビルドのaarをpublishToMavenLocal
で~/.m2/repository
以下にデプロイしないように変更したら直りました。それ以上のビルドの細かいオプションのどこに違いがあったのかは、まあやる気が出たときに…。関連するネタとしては、JUCEプラグインをAndroidに移植しているときにJNI_OnLoad()
がreleaseビルドでだけ衝突する問題が発生したことがあって、その時はldに-fltoが渡されていたのが引き金になっていた、というところまで調べたりしました。これもそれ以上の原因は追及していなかったり…。
いずれにせよ、この処置を施した後は、プラグイン インスタンスの生成まわりで不定期的にクラッシュする場面はほぼ無くなったので、ようやく安心してある種のアプリでは使えるようになったんじゃないかと思います。
今後の方針
現時点で直しておきたいのは(1)Frequalizerを含むJUCEのエフェクター系プラグインが全滅している問題(JUCEのバグ)と(2) HelioでプラグインをロードしたときにAudio Outが適切に認識されない問題(AudioPluginHostでは出てくるのでたぶんHelioのバグ)、の2点で、着手するならこの辺でしょう。
あとは今月ようやく2021年版ロードマップ(2021年中に全部やるとは言ってない)を作ったので、その中からおそらくGUIまわりを中心に消化していくことになるでしょう。
とはいえ4月にM3 2021春があってそっちに向けて何かしらまとまったものを作りたいので、開発はしばらく休止かもしれません。あと多分何かしら別の原稿を書くことになると思いますし。はーどうしよ…
*1:aria2webだとただの変換処理系みたいに見えるので。実際、最初はそうだったのですが…