3月は何の因果か自分の時間が取れる日がここ数ヶ月よりはあったので、溜まっていたコーディングタスクを少し片付けました。(原稿執筆などのタスクに少ししわ寄せが行っているのですが、そもそも原稿が最優先タスクになるのもおかしい話ですし…?)
AAP APK InstallerとJava API for GitHub 2.0に至る改善
AAPは技術的課題が山積みのまま他のタスクに追いやられて手が付けられない状態が何ヶ月も続いているのですが、今月はリリースされたAndroid API Level 36での動作を確認しようと、AAP APK Installerでプラグインをインストールして試そうとしたのですが、正常にインストールできるものが限られていて調査本編どころではなかったので、このままイマイチ体験を放置し続けられないと思い、重い腰を上げてインストーラーそのものをいろいろ改善していました。インストールの失敗はアプリ上のログで見られるようになり、インストール状況も目視できるようになったりと、最低限の改善を施しています。
それよりも大きいのは、公開当初から問題だったAPKのダウンロードがきちんとストリーミング処理で行えるようになったことです。これは、このアプリで使っているJava API for GitHubというライブラリの実装がダウンロード処理をバッファリングしていたために、100MBもいかないうちにOOM Killerによって殺されていたという問題に対処したものです。これでどのアプリケーションも、少なくともダウンロードを完了してPackageInstallerに制御を渡すところまでは完遂できるようになりました。
GH4Jの開発元(昔はJenkinsのひとがつくっていたのですが今はノータッチ)ではデスクトップ中心で使われていたのか、この辺の問題を解決すべきとは思っていなかったようで(記録はされていて反応もしているのに対処がなかった)、こちらから自分のコードで使っているHTTPクライアントのカスタム実装を教えたら、本体に取り込みたいという話になって、いつの間にか全体的にバッファリング処理が削られていって新バージョン公開(予定)という流れになっています。
AAP: NDK r18 fixes、OB-X-AEとaap-juce-ob-x-ae
aap-coreをAPI Level 36やNDK r18でビルドして更新する作業も必要になっていて、NDK r18には実際にNdkBinderが削除されてビルドが壊れるという大問題が発生して、結局Androidチームに報告してビルドできるようになった && 対処してもらうことにしたりと、まあまあ面倒がありました。
それがひと段落したので、aap-lv2までひと通り更新をかけた後、aap-juceにもアップデートの修正をかけていたのですが、OB-XdやVitalといったProjucerベースのプロジェクトが面倒になって、今回はついにOB-XdはOB-X-AEとして(ADLplug-AEみたいなもん)自前でCMake化してビルドするアプローチに変更することにしました。CMake化したのでついでにCLAP版もビルドしています。
OB-Xdの場合は、本家の更新が3年以上止まっているのと、Linux版jucerみたいなのがあって面倒だったことがあり、CMake化は改善しかなかったといえるでしょう。ただこれをAAP化したaap-juce-ob-x-aeはまだCIビルドが通っていないようです(途中でこっちどころではなくなってしまった)。気が向いたら直して続けると思います。
ちなみに自分がこの辺をいじっていた頃、なぜかTAPコミュニティでもOB-Xd のforkが同時に複数本生えたりしてして(たぶん偶然)、fork開発者同士で相互に話題になっていました。OB-Xd に一体何が起こったのか…w
ktmidi 0.11.0とlibremidi-panama
先月も書いているのですが、AAPより喫緊の課題をかかえているのはMIDIデバイスアクセスに根本的な問題のあるktmidiのほうで、JavaCPPを捨ててPanamaに移行するタスクを早めに終わらせる必要がありました。今月Java 24がリリースされて、jextractのほうもそんなに変わっていなそうだったのと、Jarにきちんとネイティブライブラリをバンドルしてロードできるものとして実績があるプロジェクトとしてJNEというライブラリを発見したので、改めてlibremidi-panamaを使ってLibreMidiAccessのAPIを実装することにしました。
このバインディングの対象となるlibremidiが実にトリッキーなライブラリで、anonymous struct / unionがjextractのコード生成の失敗に繋がるのですが、jextractも本当は処理できるはずなのに古いLLVM実装に固執して新しいLLVMでは失敗し続けるという問題があって、一筋縄ではいかず、最終的には組み込んでいるlibremidiのAPIを変更してバインディングを自動生成しているのですが、一応動くようにはなりました。
そこからさらにFFMのAPIで実装するところでいろいろハマったのですが、この辺は多分FFMに慣れれば何とかなるのでしょう。FFMは意外とメモリ境界をちゃんと把握していて、アクセスできない領域のチェックはしっかりしているんだなあと思わされました。
ちなみにjextractがバギーなコード生成しかしないなら、Panamaバインディング生成程度はもうAIコーディングできるんじゃね?とか思いながら試しにRoocode + Mistralとかで書かせてみたりしたのですが、やはりPanamaまわりの知見は浅く、コンパイルさえ通れば実行時にどうなっても知らんみたいなコードしか生成されないので、あきらめました。多分他のAIコーディングプロバイダーで試しても同じだろうなと思います(未検証)。
libremidi-panamaでktmidi-ci-toolとkmdspとkmmkとcompose-audio-controlsが問題なくLibreMidiAccessで使えることがわかったので(MIDI 2.0サポートはこれらのアプリ次第ですが)、libremidi-javacppからlibremidi-panamaに差し替えてktmidi 0.11.0をリリースしました。PanamaはJava 22以降が必要になるのですが、まあこれは致し方ないでしょう。
0.11.0に合わせてKotlinも2.1.20に更新しようかと思ったのですが、Kotlin 2.1へのアップグレードは大幅なABIの破壊的変更になりますし、Kotlin 2.1で警告から禁止になるようなpattern matchに依存しているGradleプラグインが壊れて動かなくなる事例も観測されたので、まだ時期尚早とみて見送っています。既にkotlinx-coroutines 1.10.xなどはKotlin 2.1 ABIに移行してしまっているので、Kotlin 1.8時代のABIを維持するためにKotlin 2.0.xを使い続けることができるかどうかは、エコシステム全体の動向次第と言わざるを得ないでしょう。同じような話をKotlin Fest 2024でやったなあ…
何はともあれ、LibreMidiAccessが安定することは、MIDI 2.0方面の開発ひいてはAAPの開発そのものにも大きな影響があります。LibreMidiAccessで実現できるMIDI 2.0プレイヤーのためにソフトウェアMIDI 2.0音源の仕組みを作る意義があり(uapmd)、デスクトップでのMIDI 2.0アプリケーションの動きをAAPに…というフィードバックループの構造になっているわけです。AAP本体の開発が進んでいるように見えなくても、自分の中ではこれらはマイルストーンが繋がっています。
Compose Multiplatformのデスクトップpointerイベントの追従
ktmidiのアップデートに合わせてcompose-audio-controlsも各種依存パッケージを最新版に更新していて、そこで気づいたのですが、Compose for Desktop (JVM) のマウスイベントの扱いが開発当時と変わっていて、カーソルが領域内に入っただけでノートオンになってしまうような使い物にならない挙動になっていたので、コードを見直してまともに動作するようにしました。compose-audio-controlsもデスクトップ上ではkmdspでの表示用コンポーネントとしてしか使っていないので、CMPのどのバージョンから挙動が変わっていたのかは確認もしていませんが。
Compose Multiplatformはまだこういう破壊的な挙動の変更が出てくるので油断ならないところです。現状だとkmdspがwasmで表示されない問題があるのですが、これもWasmビルドのパッケージングに破壊的変更があったということでしょう(こっちはalphaなので破壊的変更が十分想定されるべきもの)。Wasm版はまだIMEもサポートされていないレベルなので、今後どう収拾をつけるつもりなのか、まだ(悪い意味で?)目が話せません。まあこれはオーディオアプリケーションのC++ GUIあるある案件でもあります(先月も書きましたがVitalAudio/visageなんかもそう)。