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

Android 15のMIDI 2.0サポートについて

2/16(PST)にAndroid 15 DP1が公開され、“Virtual MIDI 2.0 Devices” のサポートが追加されました。これについてMIDI 2.0の最新の状況(自分が知りうる公知のもの)を踏まえて解説します。この機能を要望したのは自分なので1、たぶん自分が説明するのが一番正しいはず…

承前: Android 13のMIDI 2.0サポートからどうなった?

AndroidでMIDI 2.0の機能が初めて追加されたのは2年前のAndroid 13です。正式版がリリースされたとき、このブログでその詳細を解説しました。

atsushieno.hatenablog.com

内容を箇条書きでまとめるとこんな感じです:

ここから2年経って現在に至るわけですが、ソフトウェアMIDI 2.0デバイスのサポートが実際に追加された以外の部分は、半分くらいは「ハズレ」になった感じです。

え、そんなに外れるような予想を出したの??と思われそうですが、もちろん理由があります。2023年6月に、MIDI 2.0仕様は大きく変容したのです。これについては6月に詳しくまとめました。

atsushieno.hatenablog.com

ここで重要なのは「Protocol Negotiationがなくなった」という部分です。↑の話はProtocol Negotiationを使うことで論理的に1つのMIDIポートをMIDI 1.0 bytestreamとMIDI 2.0 UMPの両方で切り替えて使い回せる前提だったのが2、そうではなくなってしまったわけです。代わりに導入されたUMP Stream ConfigurationメッセージはUMPとして規定されているので、MIDI 1.0でも2.0でもUniversal SysExであればよかったProtocol Negotiationとは異なり、そのポートが最初からUMPトランスポートであることが前提となりました。

2022年の時点では、MIDI 2.0を包括的にサポートしているプラットフォームAPIはAppleのCoreMIDIとAndroidのUSB限定MIDI 2.0ポートのサポートしかなく、各プラットフォームでどうMIDI 2.0接続をデザインするかは不透明な状態でした。おそらく各社がMMA(MIDI協会)で相談・総合接続の検証を行っていく過程で、「ポートはMIDI 1.0トランスポートかUMPポートか『あらかじめ』規定しておく」という合意が形成されたように(外からは)見えます。

ちなみにもうひとつ「外した」と思われる「MIDI-CIサポートも追加してくると思う」ですが、ktmidiでMIDI-CI実装をゼロから作ったので、将来的には代わりにコレを使ってもらえればと思います。まだAPIが未整備…

MidiUmpDeviceService: 唯一のAPI変更点

上記のまとめでは「新機能を仕切り直してMIDI-CIサポートも追加してくると思う」と書いたわけですが、MIDI-CIの面倒なProtocol Negotiationがそもそもなくなったので、プラットフォームのAPIでMIDI-CIは不要になりました。結果的に、Android 15で追加されたのは、新しいMidiUmpDeviceServiceという、MidiDeviceServiceの兄弟分のようなクラス1つのみです。使い方は基本的にMidiDeviceServiceとほぼ同じです。MidiUmpDeviceServiceから派生した実装クラスを作成し、AndroidManifest.xml<service>android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE"<meta-data>付きで登録し、対応するump_device_info.xmlのようなファイル(リソース名前は任意)をres/xmlに置くだけです。

特に、MidiDeviceServiceの中で使われるMidiReceiverMidiSenderMidiInputPortMidiOutputPortなどのクラスは全て共通です。なぜなら、(わたしが「外れた」予想の説明で書いた通り)MIDI 1.0のトランスポートの上にUMPを流しても、単なるByteArrayを送受信することにしかならないので、APIを変える必要が無いからです。

ただし、AndroidManifest.xml<service>のMIDIデバイスのメタデータXMLの内容は微妙に異なっているので気をつける必要があります。具体的には、(1) MidiDeviceServiceではmeta-data属性で指定していましたが、MidiUmpDeviceServiceではproperty属性で指定し(この属性の存在を知らなかったのですが、API Level 31で追加されていました)、また(2) MidiDeviceServiceのときは<input-port><output-port>をそれぞれ記述していたのに対し、MIDI 2.0のUMPポートは双方向なので、<port>という要素を使うことになります。Android 15では、ここからinputとoutputのペアが自動的に作成されることになります。

特にproperty属性を使う点に注意してください。2/21現時点ではMidiUmpDeviceServiceのAPIリファレンスにある記述が正しく、android.media.midiパッケージのOverviewにある記述は間違っています(自分はここでハマりました)。

あとonGetInputPortReceivers()のreturnが地味にArray<MidiReceiver>ではなくList<MidiReceiver>になっています。わたしはこれがArrayだったのは良くないと思っているので、地味に良い変更だと思います。Arrayだとポート数がService生成時に固定になってしまうので、将来的に動的ポート生成をサポートしようとしても整合しなくなる可能性があります。(とはいえまだdeveloper previewなので、単なる実装での見落としとして「元に戻される」かもしれません。)

仮想MIDIデバイスの可能性

MidiUmpDeviceServiceを使うと、これまでUSBに限られていたMIDI 2.0デバイスが、理論上はAndroid APIでリーチできる任意のデバイスまで広がったことになります。まあこれは半分は言い方の問題で、これまででも任意のAndroid Serviceを提供することで、android.hardware.usbのAPIを使ってオレオレUSB MIDI 2.0サポートを実装することもできたでしょうが、公式にAPIを与えられたServiceならおよそ誰が作ったものでも相互接続できるようになるでしょう。

「仮想MIDIデバイスを作れる」と言われても、ソフトウェアMIDIシンセサイザーを作る人以外は関係ないのでは…と思われるかもしれませんが、実際にはもう少し適用領域があります。

MIDI 2.0 UMPのクライアントアプリケーションの作り方は、Android 13の頃と変わりません。MidiManager.getDevicesForTransport()でUMP対応デバイスを取得し、あとはMIDI 1.0の頃と同じAPIで、ただしMIDIメッセージのByteArrayにはMIDI 1.0バイトストリームではなくUMPを流す、というだけです。

MIDI 2.0アプリケーションを開発するのにMIDI-CIの機能は必須ではありませんが、MIDI-CIが必要であれば、C++アプリならJUCE 7.0.9から存在しているjuce_midi_ciモジュール(解説)、Kotlinなら上述のとおり筆者のktmidiでそのための機能を提供しています…と言いたいところですが、ktmidi-ci-toolの開発の過程でAPIはひっちゃかめっちゃかなので、API整備を待ったほうが良いでしょう。

Footnotes

  1. こういうのをアレオレ(「アレはオレがやった」)って言う界隈があるそうですね

  2. そういう目的で存在しているとは仕様上は書かれていないですが


この記事を共有:

前の記事
2月の開発記録 (2024)
次の記事
1月の開発記録 (2024)