1月の開発記録(2023)

1月はひさびさに開発作業に専念できた気がします。

rebranding AAP: Audio Plugins For Android

今月からAndroid Audio Plugin FrameworkをAudio Plugins For Androidに改名しました。GitHubリポジトリもatsushieno/android-audio-plugin-frameworkからatsushieno/aap-coreに変えてあります。

改名自体に喫緊の理由は無く、名前の先頭に他所の商標が入っていると問題になりそう、っていう予防的な対策です(喫緊ではないもののずっと気になっていた問題ではあります)。GoogleAndroid関連商標っぽく見えるので、公開イベント等で話す時に名前を出しにくくなるともったいないですし、今ならそんなにブランディングの変更も大して面倒ではないと思って着手しました。後ろにFor Androidを付けるのは割と一般的な回避策だと思います(Mono for AndroidWindows Subsystem for Android)。略称としては引き続きAAPを使いますし(AP4A、APfAでもいいけど)、コード上の命名に変更はありません。ここを変えるとしたら大々的に非互換変更を加えるときでしょう。

昨年末から少しずつプロジェクトとして回していくための作業が進行しつつあります。

AAP: previewing presets & MIDI settings

JUCEやLV2のプラグインは、ものによってはMIDIのプログラムチェンジに対応して音色…に対応するプリセット…を切り替える機能を実装しているものもありますが、そうはなっていないプラグインも多いです。それでも、プラグインAPI経由でプリセットを変更する機能が実装されているものは少なくありません。また、エフェクトプラグインについては、MIDIプログラムチェンジでプリセットを切り替えるというものはほぼありません。

モバイル環境でオーディオプラグインのパラメーターを細かく調整するのは作業として面倒ですが、プリセットを選択する程度であればカジュアルにドロップダウンリストのようなものを表示するだけなので、現実的な解だと思います。そういうわけで、とりあえずはaaphostsampleにプリセット選択機能を実装してみました。今月はBYODを移植したので、BYODのプリセットを選んでいろんなディストーションを適用して遊べるようになったのですが、割といい体験になったと思います…ちゃんと動けば。JUCEのプリセットサポートはややクセがあって(プログラムチェンジみたいに番号で選ぶようになっている)、AAPでもやや無理をしている部分があるので、割とちゃんと動かないことが多く、今後見直す必要がある課題みたいになっています。

aaphostsampleは、今は完全にやっつけプレビュー機能しかもっていませんが、MIDIマッピング設定などをshared preferenceで保存できるようになりましたし、今後はだんだん管理機能のようなものを追加していくことになると思います(aaphostsampleである必要は全く無いですが)。

ちなみにaaphostsampleでプリセットを選択できる機能をスクショしてmastodonに流したのですが(twitterにもforwardされる)、その数日後にJUCEのAudioPluginHostにも同じ機能が実装されましたね。いいところはどんどんパクってほしいですね(自信過剰)

new AAP ports

今月はしばらく前に移植を試みて動いていなかったJUCEプラグインが2つ動くようになりました。

(1) aap-juce-os251: OS-251のAAP移植が動くということはすなわちreact-juceプラグインが動くということです。最初に移植したときに起動時エラーで落ちていて、1年以上放置していたのですが、多少JUCE on Androidの罠がわかるようになってきて(主にこの 辺りなど)、今年改めてAAPの最新版に更新して改めてビルドして既知の起動時エラーも対処してみたら動くようになりました。

(2) aap-juce-byod: BYODはRTNeuralの応用事例であり、ギターエフェークターのリアルな実用例としても大いに移植のしがいがあるプラグインです。RTNeuralで使われているxsimdが古くてAndroid上でSIMD最適化が行われていなかったので、RTNeuralで最新のxsimdを使えるように多少コードの修正を加えて、Android上でも使えるようになりました。これも実は半年くらい前に試しに移植してみて動かなかったやつです(今でもUI起動前にクラッシュするのでちゃんと動くとは言い難いですが)。

去年は新しいプラグインをほとんど増やしていなかったので、だいぶ豊作な印象があります。実のところ、去年移植が全然増えなかったのは、これ以上増やしても大変っていうこともまああるのですが、あまり新しいJUCE OSSプラグインが出てこなかったということもあります。このBYODとOi Grandadくらいしかパッと思いつきません。LV2プラグインもそんなに増えているわけではないですが、master meとかが新しいかな。プラグインというとちょっと違うけどIldaeilも新顔ですね。

AAP parameters and presets on MidiDeviceService

MidiDeviceServiceとなったオーディオプラグインでは、入力がMIDIメッセージのみになってしまうので、パラメーターやプリセットの指定がそのままではできません。これがDAWであれば、MIDI mappingを使ってDAWMIDI入力メッセージをパラメーター設定やプリセット指定に置き換えたりできるわけで、同じような機能がMidiDeviceServiceにもほしくなります。特にAAP V2プロトコルではMIDI2が入力として使えるので、マッピングポリシーさえ事前に規定しておけば、それなりにストレートなやり方でマッピングが実装できます。

というわけで、AAP拡張機能の一部として、以下のようなマッピングを調整できるようにしました:

  • CCをパラメーター設定として使う
  • Assignable Controller (NRPN) およびPer-Note Assignable Controllerをパラメーター設定として使う
  • ユニバーサルsysex8で所定のビットパターンをもつものをパラメーター設定として使う
  • プログラムチェンジをプリセット設定として使う

一方で、VST3のようにマッピングを強制した結果オーディオプラグインでまともにMIDIのプログラムチェンジが受信できなくなったり、ノートとパラメーターチェンジが別々のシーケンスになって順序がおかしくなったりする問題の原因にもなるので、プラグイン開発者が「このメッセージは受信したら独自に処理する」と宣言しておくことで、MidiDeviceServiceのようなホスト側がそれらを「マッピング対象としない」ことが可能になっています。

実装当時はparameters拡張機能の一部としていましたが、プログラムチェンジの設定は明らかに「パラメーター」ではないので、今は「midi拡張機能」の一部としています。もともとmidi2拡張というのがあったのですが、イベント処理の内部実装が全てMIDI 2.0 (UMP)による実装になったので、拡張もへったくれもない状態で、それなら今回ついでにMIDIイベント処理に関する拡張機能として再利用しちゃえ、ということでこうなりました。

LMN-3-DAW-Android

2年前にJUCE+Android+CMakeのネタを書いてから、JUCE on Androidに詳しい輩とみなされるようになっていて(実際にはそんなにJUCE開発の経験は無いんですが)、ちょろちょろとメールで相談がやってくる日々です。今月は、LMN-3-DAWAndroidで動かしたいんだけど…みたいな相談がやってきたので、(自分では開発に関わるつもりは無かったので)とりあえずブートストラップとしてプロジェクト構造があればいいかと思って、1日でAndroid版を作ってみたら、案外そのまま起動できるところまでいったので、そのまま渡した(教えた)ら、翌日には公式リポジトリ持っていかれていました。

ただ引き渡してからが問題で、Windowsでビルドできない…という話になって、(もう1年以上Windowsをまともに起動していない)、調べ方だけいろいろ伝えて、某オーディオDiscordでも喧々囂々の議論が行われていたのですが(といっても自分はほぼ眺めていただけ)、あの記事は思いのほか勘違いされている(たとえばパッチが無いと動かないような修正を加えていると思われている)ということがわかり、ちょっとエゴサしてみるとforumやらdiscordやらでリンク出されては「難しくて読めねえ」みたいな感じの反応ばかり出ている状態だったので(!?)、気が向いたらそのうち何とかしよう…と思いました。

最近も「MIDI入力が期待通りに取れないんだけど…」みたいな相談を受けては「そもそもJUCEは入出力を取り違えているレベルでまともにAndroid MIDIを扱っていないので…」みたいな話をしていて、これもそのうちJUCEを直すしかねーかな…みたいな感じになっています(自分のコードで使う予定が全く無いのでモチベーションが低い)。

このプロジェクト、DAWのエンジン部分にはtracktion_engineが使われていて、AAPも組み込めれば利用事例になるといえばなるんですが、USBで接続するハードウェアコントローラーが無いとまともに使えないので(デスクトップ用にはコントローラーのエミュレーターになるJUCEアプリもあるのですが、Androidでそのまま使えるわけがない)、とりあえず今のところ実験用に使う予定はないです。

AAP APK Installerとパッケージングの全般的な統一

今年はAAPをある程度は実用性のあるオーディオプラグインのソリューションとして使えるようにしていきたいと思っているのですが、パッケージが多数のリポジトリに分散していて全く試せる状態になっていなかったので、何かしらのベータテスティング的なデプロイメントを実現しようと考えました。それで紆余曲折を経て、AAP APK Installerという仕組みが出来上がりました。AAP専用にするのはちょっともったいないので、AAP以外でも使えるようにある程度一般化して、自分でパッケージカタログ等を用意すれば何でもインストーラーに渡せるようなActivityを含むライブラリにもなっています。詳しくはzennのほうに書いています。

zenn.dev

それでAAPプラグインのカタログを自分でまとめていて、パッケージ名などが割とバラバラで散逸していたので、この機に全部統一的な名前にしようと考えて、全てorg.androidaudioplugin.ports.lv2.*org.androidaudioplugin.ports.juce.*みたいな名前にして、アプリケーション名も全部 "AAP xxx" の形式にしました。これで関連パッケージが全部インストール済みアプリケーションの先頭に AAP xxx としてまとまるのでスッキリしました。

あとaap-lv2-*リポジトリ群、aap-juce-*のCMakeビルドのリポジトリ群、aap-juce-*のProjucerビルドのリポジトリ群、の3つのカテゴリーで、それぞれGitHub Actionsのワークフローが同一の設定をほぼ使い回せる程度に共通化されていたので、Reusing workflowsの機能を使って全部まとめることにしました(現在進行形で移行中)。

aap-juce hosting fixes, and Helio Workstation now works

AAP 0.7.4をリリースしてから気づいたのですが、aap-juce-plugin-hostが何やらインスタンス生成で固まるようになっていました(自動テストしたいんだけどできないやつ)。AudioPluginHostは現状aap-juceの唯一的な実装例なので、これが動かないのでは見本として体裁が悪いので、ちゃんと調べて直すことにしました。結論からいえばAndroidContext.bindService()がメインスレッドで動くようになっていたのとJUCEのjava.lang.Threadサポートのバグのコンボだったわけですが、無事また使えるようになりました。

ホスティング実装が直ったので、この機会にと思ってHelio WorkstationAndroid版へのAAP統合をアップデートして試してみたのですが、相変わらずオーディオルーティングのところでオーディオポートが検出されないという問題が直っていなかったので、これも調査してみて、どうやらjuce::BusesPropertiesの設定を含むjuce::AudioPluginInstanceしか認識されないということがわかったので、バス設定を適当にでっち上げるようにしたら(!?)、ちゃんと実行できるようになりました。

実行はできるものの、オーディオクオリティは全然良くないので、その辺は今後の調査次第という感じですが、そもそもHelioに最初から含まれているプリセットプラグインでも音飛びが生じているので、AAPの問題とHelioの半々という感じです。まだまだ問題はありますが、AAPが見込んでいるオーディオプラグインフォーマットらしい使い方が、これでようやく示せたのではないかと思います。何とか実用できるところまでもっていきたいところですね。