ResidentMIDIKeyboard: Android用の常駐型MIDIキーボード

ずっとAndroid用のコードを書いているくせにAndroidアプリをリリースすることが全く無かったのですが、ちょっとオーディオプラグインプロジェクトの負担を減らすために(!?)、compose-audio-controlsの応用で常駐型MIDIキーボードアプリを作ったので、ひさしぶりにGoogle Play Storeアップロード体験してみるかと思ってリリースしてみました。といってもまだOpen Testingです。一応数日様子を見て、変更なく落ち着いたら正式リリースするつもりです(そうしない理由は特に無い)。

play.google.com

ソースも公開しています。

github.com

機能

Use anywhere: システムアラートウィンドウとして動作するので、「どこからでも」使用できます(compose-audio-controlsのDiatonicMidiKeyboardで実装した機能をSystem Alert Windowで使っているだけです)。

connect to MIDI OUT, get connected as MIDI IN: Android MIDI APIでデバイスとして認識されるMIDI OUTデバイスに接続して、MIDIメッセージを送りつけることができるMIDIキーボードです。MIDI OUTデバイスが無ければ何もできません。何もなければソフトウェアMIDIバイスをインストールしましょう。

あと、いったんリリースしてみて、このアプリはもしかして「MIDI入力デバイス」としてMIDIキーボード(ハードウェア)の代わりに使えたほうが便利なんじゃないか(DAWの上にオーバーレイ表示して意味があるのはそのDAWから入力デバイスとして接続したときだろう)、と考えたので、そうなるように機能追加しました。具体的には、このアプリはMidiDeviceServiceとしても動作するようになっています(MIDI OUTのほうはクライアントとして繋ぐだけなので「デバイスである」必要はない)。Android MIDI APIちゃんとサポートしているDAWなど任意のアプリであれば、MIDI INデバイスとして接続できます。

MIDI 2.0: MIDI 1.0でもMIDI 2.0でもメッセージを送りつけることができます。Android MIDI APIで利用できる任意のMIDI出力デバイスに接続します。MIDI2対応のデバイスは実質AAP MidiDeviceServiceしかないと思います。

AAPのプラグインはすべてちょっと設定するだけでMidiDeviceServiceとして動作するので、AAPとして動作するOB-XdやOdin2やVital(oid)やSurge-XT、sfizz、(aap-)fluidsynth、MDA-LV2の各種シンセといった音源が全部このキーボードから接続して動作確認できます(ただしパラメーターもstateも全て初期状態です)。Android MIDI APIに準拠していれば何でも良いですが、MIDI 2.0モードに対応しているのは現状AAP Instrumentsのみです。まあそれでもper-note pitchbendに対応しているわけではないです。あとaap-juceはどうもMIDI2モードのときにpitchbendの変換処理がおかしいっぽいので、この辺は要修正です(!)。

supported MIDI messages: キーボード コンポーネントからnote on, note off, pitchbend(横方向ドラッグ), per-note pitchbend(縦方向ドラッグ)、key pressure = PAF (デバイスポインタープレッシャー) をサポートしています。per-note pitchbendはMIDI 2.0モードを有効にする必要があります。ちなみにper-note pitchbendを受け取って音を変更できるデバイス/ソフトウェアをわたしは何一つ知りません。

最新版でコントロールパラメーターもknob経由で送信できるようになっています。CC、RPN、NRPN、Per-Note Controllers (Assignable/Registered)、Program Changeなどのほか、キーボードと役割がかぶるPAFやPer-Note Pitchbendなども送信できます。RPN/NRPNはMSB/LSB/DTE(MSB)の3つがセットです(DTE LSBまではちょっと画面に収まらなくなりそうなのであきらめました)。ものによっては値を連続的に送ってほしくないやつがあると思うので、無効化スイッチも付けました。スイッチを有効にしたときにも送られるので実用上これでいいでしょう。

Embedded UI: システムアラートウィンドウだけでなく、SurfaceControlViewHostを使ったリモートUIとしても使えます。ライブラリとして参照することなく、自分のアプリにこのMIDIキーボードを組み込めることになります。ただ実装してみたものの、Binderレベルでのプロトコルの互換性が保証されているわけでもないですし、ちゃんとバージョン管理できるaarを使ったほうがずっと安全ですね。このアプリがインストールされているときだけ、アドインとして呼び出してoptionalに使うべきものです。

補足

実装してみて初めて気づいたのですが、Androidで動作するDAWの大半が、実はAndroid MIDI APIをサポートしていないようです。Audio Evolution Mobile、Caustic3、Cubasis LE 3、FL Studio Mobile、n-Track Studioなどを試しましたが、USB MIDIやBLE MIDIを個別にサポートしている程度で、みんなMidiManagerからデバイスリストを取得していないのではないか…?という感じです。何でじゃろ…C++で実装していてそっちのほうがやりやすかったとか…?(そんなわけ無い気がする) ちなみにJUCEベースのZenBeatsやHelio Workstationは、JUCEのレベルでMIDI INとMIDI OUTがあべこべになっているので、確認する意味もないです。

動作確認にはMIDI IN接続を選択できるDreamhound StudiosのMIDI Keyboardを使いました。MIDIキーボードをMIDIキーボードに接続…(!)

mastodon.cloud

そんなわけで「これで打ち込み作業がやりやすくなったのでは…?」から「なんか便利に使える機会がないな…?」となった機能ですが、これがあればアプリ側にMIDIキーボードになるUIが無くても(あるいは「まともに使える」UIが無くても)困らなくなります(!?)

あと今回初めてGoogle Play Developer ConsoleでアップロードできるaabをGitHub Actionsでビルドするようなワークフローを組んでみたのですが(そもそも以前にGoogle Play Developer Consoleにアクセスしていたときはaabという概念そのものが無かった)、APK/AAB署名をやってくれるactionもあるし、Play Storeが要求するversionCodeを自動的にGitHub Actionsのbuild numberで置き換えてくれるプラグインもあるし、楽にできるようになっているな…となりました。気が向いたらGoogle Play Developer Consoleへの自動アップロードも組み込もうと思います。

今回はJetpack Composeを使ってキーボードを実装してSystem Alert WindowやSurfaceControlViewHostに載せてみたわけですが、こういう使い方をしている人はあまりいないので色々追加で調べることになりました。この辺の話はまたの機会に…

ちなみにresidentというのはMS-DOSの常駐型 = TSRのresidentから取ってます(terminateはしない)。たぶん現代的にも妥当なネーミングなんじゃなかろうか…