「MIDI 2.0 MIDI-CIガイドブック」刊行によせて

atsushieno/ktmidiMIDI-CIの機能をあらかた実装していた経験をもとに、しばらく時間をとってM3 2024春および技術書典16に向けて新刊「MIDI 2.0 MIDI-CIガイドブック」を執筆していた。並行してktmidiでMIDI 2.0トランスポートのプラットフォームMIDIアクセスAPIサポートを追加していたこともあって(これについては先日書いた)、これでMIDI-CIを含むMIDI 2.0関連技術は(USB-MIDI 2.0など個別のトランスポートを除いて)だいたい理解したと思う。

MIDI 2.0 MIDI-CIガイドブック

目次等はこちらから:

androidaudioplugin.org

そういうわけで、今回は同書の宣伝として、本書はどんな知見を提供する意図で書かれているものなのかを解説したい。

MIDI 2.0の一部とされるMIDI-CIは、具体的に何をしたいのか、何ができるのか、よくわからない仕様だった。従来のMIDI 2.0仕様の解説を見ても「双方向的なやり取りができる」とか「MIDI2はMIDI1と後方互換になっている」など、あまり具体的ではない「ふわっとした」ものが大半だった。これはもちろん従来の解説が理解不足で書かれていたという意味ではなく、MIDI-CIで規定されるProfile ConfigurationやProperty Exchangeの具体的な応用が未整備だったために、基本的にMMAの発表をそのまま流すしかなかったはずなのだ。

MIDI 2.0の技術を採り入れることで具体的に何ができるようになったのかを説明できる人は、実のところ今でもあんましいないと思う。実態とはかけ離れた説明になってしまうこともある。わかりやすい例が「MIDI 2.0になると256チャンネルが利用可能になる」だ。202x年代のわれわれにとって、MIDIはすでに楽曲配信フォーマットではないし(MMAは楽曲のパッケージとしてのSMF2をまだ仕様策定できていない)、1つのチャンネルで1つの音色を割り当てて256音色使える!といっても響かない。純粋なMIDI 2.0対応デバイスなら不要だが、MPEデバイスは1音色で1グループ16チャンネルを消費したりする。MIDI 2.0なんて使わなくても、DAWを使えばトラックごとにチャンネルの限界を意識せずに打ち込みが可能だし、そもそもMIDI 2.0は256チャンネルを出力として利用可能にするための仕様ではない。これはFunction BlockとUMPエンドポイントのコンセプトについて理解していれば分かるかもしれないが、多分そんな人は滅多にいない。かくいう自分も最近までちゃんと理解していなかったし、MIDI 2.0が「こういう」仕様として公開されたのは2023年なので、MIDI 2.0の概要が公表された2019年当初から今のように理解していたらそれは予知能力者だ。

MIDI-CIで楽器固有のプロファイルを実装した楽器には専用のコントローラーを割り当てることができる」というのもよく言われるけど、具体的なプロファイル仕様が存在せず、存在しないものを実装した楽器も無いのでは単なる画餅だ。いや正確に言うと、「ドローバーオルガン」と「ロータリースピーカー」の仕様は存在する。でもそれだけだ。他は従来のMIDI 1.0時代の仕様(GMやMPEなど)をMIDI-CIプロファイルとして組み込んだ程度のものしかない。

一方で、MIDI-CIプロパティは完全に単なる画餅というわけでもない。プログラムリストやコントローラー情報といった具体的なプロパティ仕様は標準として策定されていて、これらは実装さえあれば今すぐにでも相互運用できるといえる。現状で最先端と言えそうなのはKORGのKeyStageだと思うけど、KORGが公開しているKeyStageサポートファイルのProperty Exchange MIDI Implementationを見ると、標準の音色情報としてのProgramListはサポートされていて、どんな接続先でもやり取りできる一方で、標準のコントローラー情報であるAllCtrlListなどはサポートされていない(代わりにX-ParameterListという独自のパラメーターリストが実装されており、要するにこれを実装しているKORG製品としかやり取りできない)。

(これは多分良い側面と悪い側面があって、MMAによって標準化されれば全てのMIDI 2.0ソフトウェアが対応するデフォルトの選択肢として強力な存在になるし、逆にMMAの鈍重な委員会仕様策定が進むまではMIDI 2.0技術を採用しても現実的に使い物にならない可能性がある。)

プログラムリストを取得できるとかコントローラーの名前とかパラメーターの値域を知ることができるなら、それは進化なのだけど、オーディオプラグインの世界ではプリセット名やパラメーター名は当然のように取得できるので、あるオーディオプラグインのフォーマットの範囲で完結するやり取り(たとえばDAWプラグインやり取り)であれば、MIDI-CIは何も付加的な価値をもたらさない。MIDIに基づく技術でも、MIDNAMを使えばかなりの情報が得られる。MIDNAMは全てではないが多くのDAWで採用されている。MIDI 2.0よりは普及しているし今すぐ使える。(追記: もちろん「MIDIプロトコルに乗せて相互運用できる」レベルではないので、MIDI-CIの完全な代替技術にはならない。限られた文脈での代替技術の話)

…といったようなことが、本書を書いていく過程でわかるようになった。これはMIDI-CI仕様と呼ばれるドキュメントだけを見ても理解できないと思う。Common Rules for MIDI-CI Profilesというプロファイル共通仕様、Common Rules for Property Exchangeというプロパティ共通仕様を理解して、その上で各論的に規定される個別のプロファイル仕様やプロパティ仕様を知ることで、ようやく具体的な目的と何が(仕様上)実現できているのかを把握できる。仕様書というものは正しさファーストで書かれているので、それとは別に、前例などを紹介しつつ、なるべく理解しやすくまとめたものがあるべき、というスタンスで本書は書かれている。「Get and Set Device Stateプロパティ」という仕様を説明無しで出されても大抵のMIDIユーザーには理解不能だと思うけど、「オーディオプラグインでも実装されている『状態』の保存と復元に相当する機能をMIDI-CIで実現したやつだ」と説明すれば理解できる可能性が高くなる。本書にはそういう説明をいろいろ盛り込んでみた。

本書ではMIDI-CI仕様に沿った具体的なSysExメッセージのやり取りについても解説しているし、MIDI-CIツールを使ってその具体的なやり取りを確認できることも示している。本書にひと通り目を通せばMIDI 2.0よくわからん」っていう人が「MIDI 2.0完全に理解した」と言えるくらいにはMIDI 2.0が理解できると思う。

本書はMIDI-CIガイドブックという書名ではあるけど、実際にはUMP仕様の一部となったプロトコル制御まわりの話も付録としてまとめてあって(しかも割と量がある)、ここには2023年6月のアップデートで追加されたFunction Blockなどのコンセプトの解説なども含まれていて、MIDI 2.0デバイスを構成するとはどういうことかというレベルの疑問もそれなりに解決できる内容になっていると思う。

3月の開発記録 (2024)

あっという間にQ1が終わってしまいましたが進捗どうでしょうか? うちは完全にダメです(時候の挨拶)

今月は開発記録として出せるものが全然無くて自分でも驚きなのですが、主に4月のM3 2024春・5月の技術書典16向けに準備している新刊の作業ばかり進めていました。そして半分くらい台湾で過ごしていて、その半分くらい風邪でダウンしていて(と言っても寝込んでいたのは3日くらいなのですが)、その間は全然執筆が進まなかったので、執筆合宿(?)としてもイマイチでした。ですが、まあ執筆とそのための調査を主な作業にしていたので、それなりの密度のものが書けているとは思います。

新刊は「MIDI 2.0 MIDI-CIガイドブック」の予定です。「MIDI 2.0 UMPガイドブック」と対を成す内容で、当然のように(?)世界初のMIDI-CIに関する書籍になる予定ですが、4月のうちに世界のどこかで誰かが先を越す可能性は0ではないです。まだ「3/32までには初稿が終わるかも?」というレベルですが、現時点で90ページあるので、図版のTODOなどを消化していくと最終的には100ページ超えくらいにはなるでしょう。

仕様の解説をしている「仕様編」と、MIDI-CI実装の使い方についてまとめた「実装編」から成る予定です。

現時点でコードは1行も出てきていないので、一応非プログラマー向けの技術書と言えなくもない内容です。とはいえMIDI-CIの長いSysExバイトストリームを直接編集できるミュージシャンが多いとも思えず、MIDI-CI自体もエンドユーザー向けの技術とは言い難いので、「MIDI 2.0を使いこなしたい人向け」になるとは思えず、「MIDI 2.0をきちんと理解したい人向け」みたいな位置付けの本になりそうです。

1月の時点でMIDI-CI Property Exchangeに関する覚書を公開しましたが、当時は割といろいろ調べてそれなりの量の情報をまとめて書いたつもりだったのが、この本の関連章を書き終えてみると「MMA/AMEI定義プロパティは何も調べてなかったな」とか「だいぶ表層的なところだけまとめているな」といった印象になったので、だいぶ情報の多い内容になったと思います。実装編は、2月に英語でまとめたJUCE CapabilityInquiryDemoと自分のktmidi-ci-toolをベースに、すでに仕様編で解説した部分を削ぎ落としつつ書き直しています(スクショだけ使いまわしていますが)。英語のまとめでも書いているのですが、JUCEにはMIDI Message Reportの機能がサポートされていないので、自分の実装だけが頼り…みたいな部分もありました。

それと2月末には「勉強会をやる予定です」と書いていたのですが、勉強会の企画を動かしていたのが、自分以外全く動きがなくなってしまい、自分の独断ではコミュニティとしては何も進められないので、多分何もやらないと思います。何かやるなら自分の独断で動かせるコミュニティとして作り直すべきだと思いますが、自分にはそこまでコミュニティ活動をやるインセンティブは無いので、やらないと思います。

ktmidi-ci improvements

今月の開発作業のほとんどはktmidi-ciの細かい改善部分です。

  • MIDI 2.0 UMP対応: 今までMIDI 1.0レベルでのみ実装していたのですが、MIDI 2.0 UMPプロトコルでも意味のある実装になるように手を加えました。といってもUMPのグループに対応しただけです。
  • APIの整備: ktmidi-ciのAPIはこれまでktmidi-ci-toolの機能の際限ない拡張に対応しながら設計をひっちゃかめっちゃかにしつつ成長してきたのですが、ライブラリとして使えるようにまともなAPIにしないと実用性が無い(ktmidi-ci-toolとして終わってしまう)ので、APIと内部実装を整備しました。特にまともなテストが書ける状態になったのが大きな改善点です。
  • JSONSchema対応: まだ「スキーマを定義できる」のレベルでしかないですが、JSONスキーマの設定項目が増えました。今後MMA/AMEI定義のプロパティなどに対応する日が来たら有用でしょう。

今月はProfile Configurationまわりの関連仕様をいくつか読み込んだこともあって、ProfileHost/ProfileClientにはもう少しCommon RulesのためにAPIを仕込める余地があるなあとか、ktmidiもjuce_midi_ciもProperty Exchange APIには特にresIdまわりで根本的な欠陥があるなあとか、いろいろ考えることがあったのですが、まだ具体的にAPIやktmidi-ci-toolをどう変えていくかというところまでは考えがまとまっていないところです。どちらかといえばMIDI-CI仕様そのものに手が加えられるべきと思っているので、いわゆるembrace and extendするしかないか…? みたいなことを考えています。

この辺が実現するかは未定です。MIDI-CIはだいぶ実利に乏しいというか「Manufacturer IDを持たない開発者に対して十分に開かれておらず、クローズド仕様のゆえに不備も少なくないMMAの仕様よりは、まだMIDNAMみたいな既存仕様のほうが良いのでは…?」みたいなことを思っています。

2月の開発記録 (2024)

Linuxデスクトップ環境がずっと死んでいて消耗していますが、今月はかろうじて生き延びました…(!?)

2月はAndroid 15というでかいリリース(developer previewですが)があった関係でだいぶ揺り動かされました。先にそっちから書きます。

Android MidiUmpDeviceService対応

数日前にAndroid 15のMIDI 2.0対応についてここに書きました。

atsushieno.hatenablog.com

AAPはイベントメッセージのフォーマットとしてUMPを採用しているし、任意のインストゥルメントプラグインMIDI 1.0のMidiDeviceServiceとして公開できる仕組みを構築しているわけで、MidiDeviceServiceのUMP対応が出てきたら、当然やらないわけにはいきません。

resident-midi-keyboardを開発したときのコンセプトのひとつは「AAPと連携してUMPをやり取りできる仕組み」であり、従前から作り込んでいたので、特にAPIとしてほぼMidiDeviceServiceと同じであれば、24時間以内に全部対応できるでしょ…!と意気込んで着手したわけですが、なぜかうまく動かない…原因は↑で書いたドキュメンテーションのバグだったわけですが、泣く泣く24時間以内リリースはあきらめました(!?)

「新API公開から24時間以内にリリース」は自分がXamarin.Androidの開発で昔いつもやっていたことだったので(もちろんそれが許されるくらいのプロダクトの規模感だったときの話ですが)、ちょっと懐かしいノリですね。

いずれにせよ、↑で書いた通り原因は判明したので、resident-midi-keyboardにはUMP対応がクライアント/サービスともに追加され、AAPはMidiUmpDeviceService化できるようになりました。Androidオーディオチームのリーダー曰く、MidiUmpDeviceServiceをサポートした世界初の事例だそうです。そりゃそうだ…!

ktmidi-ci-toolにも仮想MIDIポートとして使えるMidiDeviceServiceがあるので(Android OSとしては動的に作成できる仮想ポートの類は無いので、あくまで固定のソフトウェアMIDIポートです)、こちらもMidiUmpDeviceServiceを追加したのですが、MIDI 1.0の時と同じように「自分自身のvirtual MIDIポートと繋いだらinitiatorとreceiverのテストができるのでは…?」と考えて接続しようとしても、「同一ポート」への接続を試みていることになってしまうので(実際そうだし)、UMP接続ではメッセージがdropされてしまうようです。自分ではEchoUmpDeviceServiceみたいなやつを作って、in portに来たものをそのままout portsに送りつけています(EchoというかForwardというべきかもしれない)。

ktmidi-ci-toolのほうは、「UMP対応!」と言うためにはgroupの扱いがきちんとできている必要があって、ここはgroupの概念が存在しないMIDI 1.0しかサポートしてこなかったので、その対応を進めているところです…といいたいところですが、まず先送りしていたMIDI-CIサポートAPIの整備から着手しているところです。まあUMPサポートがあるだけでも大きいので、現状のまま出してもいい気がしてきましたが…(!?)

ktmidi 0.8.0-pre work

MidiAccessのUMPポート対応

ktmidiは「MIDI 2.0をサポートするライブラリ」ですが、これにはプラットフォームMIDI APIのサポートが含まれていません。UMPはApple OSとAndroid 13以降とLinux kernel 6.5以降 & alsa-lib 1.5.10以降でしかサポートされていなかったので、ずっと先送りにしていました。Android 13もUSB MIDI 2.0デバイスのみの対応だったので、自分には関係ない話でした。

Androdi 15でこの状況が変わったので、MidiAccessAPIでは、MidiPortDetailsに暫定的にUMP用ポートか否かを判別するプロパティが追加されています。Android APIでいえばMidiDeviceInfo.typeALSAでいえばsnd_seq_client_infoに追加されたtypeに相当する情報です。resident-midi-keyboardのはktmidiのAPIMIDIバイスを列挙して接続するので、

ALSAなら普段使いのデスクトップですぐできるだろ?と思われそうですが、AlsaMidiAccessが利用しているalsaktはプラットフォーム上に存在するlibasound.soをロードして使うjavacppのライブラリだったので、まずこれをalsa-libをstatic linkしてlibjnialsa.so(JavaCPPが自動生成するネイティブライブラリ)を構築するように変更する必要があります。

これが以外と筋が悪く、libasoundはシステム上に存在するconfigをロードして使わなければならないところ、alsa-libがstatic linkされている場合にこのconfigファイルはどこに存在するのか/バンドルしていいものなのか…といった問題があり、現状ではconfigのロードで問題が出るくらいなら従来のlibasound.soを動的にロードする方式に戻すか…となっています。そうなると、システム上に存在するlibasound.soUbuntu 22.04ならlibasound2-devが提供しているもの)がalsa-lib 1.5.10以降でない環境では、たとえkernelが6.5以降が動いていても、まだUMPが使えないことになります。

そんなわけで悩みどころなのですが、今Linuxデスクトップ環境が絶賛死亡中なので、いつ着手できるかわからない状態です。マウスコンピューターを捨ててMSI機を復活させるしか無いか…

rtmidiバインディングとの格闘(現在進行形)

2月はJavaCPPの使い方もいろいろ調べ直していました。JavaCPPは元来Java用のバインディング生成機構で、ビルドもMavenが前提になっているところが割とあり、gradle-javacppが公式といえど情報が少ないやつです。特にGradleプラグインは「いつ」必要になるのか不鮮明で、今のところ「バインディングライブラリ用のGradleプラグインと、そのライブラリを参照するモジュール用のGradleプラグインが別々にあり、参照する側のモジュールでは直接参照しないアプリケーションでもこのプラグインが必要になる」という理解で動かしています。.NETでいえばPCLのプロファイルの概念が曖昧なまま、要件としては(JavaCPP固有のレベルで)存在している感じです(と書くとどれだけ面倒なやつか伝わるかも)。

このプラグインをちゃんと使わないと、パッケージの参照解決で「存在し得ない」プラットフォームネイティブコードを含むパッケージが要求されるようになったり(Linux上でWindows用jarを要求されたりする)、逆に実行時に必要なjnirtmidi.soを含むjarがビルド時に参照として解決されずにUnsatisfiedLinkErrorが発生したりすることになります。

あと、これだけいろいろ調べて解決してきたのに、「rtmidi-javacppをMaven Centralから引っ張ってきたやつはjarにクラスが含まれていないかのように失敗し、MavenLocalから引っ張ってきたやつはうまくビルドできる」みたいな問題がまだ発生していて、こんなに問題を引き起こすならrtmidi-jnaを復活させるほうがまだ筋が良いか…?みたいになっています。が、librtmidi.*をバンドルする等の問題が発生するはずなので、それもそれで手を出しにくい…

rtmidi cinteropバインディングとの格闘(現在進行形)

rtmidi-javacppはKotlin/JVMの問題なのですが、rtmidiはKotlin-Nativeでも問題になります(!) Kotlin-Nativeのライブラリも、参照関係をstatic linkしないと実行時に動的に必要になる…というのでstatic linkするように作り変えました。これで特にApple OS上ではうまく行っているはずなのですが(iOSはそもそもrtmidiがサポートするようになったのが最近なので未確認)、Linux上でこのバインディングを使うアプリケーションでALSAのリンクを動的に解決できず失敗する…みたいな問題が起きています。多分どこかに簡単に対応できるやり方があるんだけど多方面に問題が出すぎていて未解決です。とにかく人手が足りない…(!?)

misc. AAP work

Gradle/AGPとの格闘

2月はまず、ずっとリリースできていなかったAAP v0.8.0をリリースするための作業から始まりました。一般向けにはこれで終わりです。この節の残りは実際に何をやっていたかを記録しておきます。(開発記録なので!)

0.8.0のコードは11月上旬からあまり変わってなかったのですが、リリース作業が面倒でやっていなかったわけではなくて、AAPのaarをプロジェクト内でimplementation(project(...))で参照しているとビルドできるのにMavenパッケージとしてimplementation("org.androidaudioplugin...")で参照しているとオーディオファイルが正常にロードできないという謎の問題がリリースを阻んでいました。

AAP 0.7.8のリリース時には、AAPの全派生プロジェクトでaap-coreをgit submoduleにしてaarをプロジェクト内参照する構造にするという前提で、控えめのアップデートを行っていたのですが(0.7.8に追従させたパッケージは少ないです)、このアプローチではaap-juceの特にProjucerを前提としたプロジェクトが移行できませんでした。仕方ない、根本的にビルドシステム(GradleとAGP)に踏み込んで問題を解決するか…という意気で、GradleやAGP (AOSP) のソースを、主にPrefabやCMakeのサポートを中心に読んで過ごしました。

問題の一端は、project(...)で参照解決しているとデバッグビルドが流用され、Maven(MavenLocalを含む)経由で参照解決するとリリースビルドが使われる、というGradleの挙動にありました。そしてリリースビルドでのみ想定通りに動作しなかったのは、Cコードの未定義動作がLLVMの最適化フラグによって挙動が変わった結果であって(LLVMのvectorizationが問題のトリガーになっているところまで突き止めました)、問題が再現するかどうかはデバッグビルドかリリースビルドかに依存する、という「よくある問題」のいち類型でした。project(...)で参照していたらデバッグビルドになる、とは一般的には理解されていないと思いますが、そんなハマり方があるんですね…

なお0.8.0のWhat's newはすでに11月に書いていたとおりです(これ以上書くことがない…)

NdkBinderのcallbackとの格闘

AAP拡張機能の新バージョンで目玉だったはずのホスト拡張機能のサポートですが、ホストとプラグインが別々のアプリケーションだとBinderがcallbackオブジェクトを渡してくれないという問題が残っていて、これが情報が多くなくて「BnCInterfaceをJava APIでいうところのStub代わりに使えばよい」というところまで辿り着くのにしばらくかかりました。NdkBinderもだいぶ自分が草分け的に使っているので、情報がほぼ無いんですよね…

3月の予定

3月は割と日本にいないので進捗は悪いだろうなと思います。あとM3用(あとたぶん技術書典用)の新刊も書かないとですし。あと、ひさびさにオーディオ勉強会をやることになると思って画策しています(やるなら4月)。

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サポートからどうなった?

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

atsushieno.hatenablog.com

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

  • USB-MIDI 2.0プロトコルの規格が固まったので、Android 13でプラットフォームのレベルで実装された
  • MIDI 2.0のサポートは既存のMIDIポートの接続でもできる(UMPストリームをMIDI 1.0メッセージの代わりに流せばいいだけ)
  • OSが認識できる((MidiManager.getDevicesForTransport()で取得できる))ソフトウェアMIDI 2.0デバイスはまだ作成できない(のでissuetrackerで要望を出した)
    • 後方互換性の問題で単なる機能追加はできない
  • GoogleAndroidオーディオチームがこの辺の新機能を仕切り直してMIDI-CIサポートも追加してくると思う

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

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

MidiUmpDeviceService: 唯一のAPI変更点

上記のまとめでは「新機能を仕切り直してMIDI-CIサポートも追加してくると思う」と書いたわけですが、MIDI-CIの面倒なProtocol Negotiationがそもそもなくなったので、プラットフォームのAPIMIDI-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.usbAPIを使ってオレオレUSB MIDI 2.0サポートを実装することもできたでしょうが、公式にAPIを与えられたServiceならおよそ誰が作ったものでも相互接続できるようになるでしょう。

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

  • 筆者の事例のひとつとして、ktmidi-ci-toolでは仮想MIDIポートを公開しており、これ自体は他の「MIDI-CIデバイス」に接続して操作するためのアプリケーションですが、MIDI-CIのリファレンス実装として、他のMIDI-CIクライアントアプリケーションがこのアプリケーションに「接続」して使うことも可能になっています。(ktmidi-ci-toolはKotlin Multiplatformアプリであり、LinuxMacOSでも同様に動作します。)
  • MIDIキーボードアプリでたまに見られるのは、仮想MIDI入力ポートも作成しておいて、そこに届いたメッセージをそのまま自らがクライアントとして接続しているMIDI出力に送信するというものです(たとえばvmpk, kmmk
  • UMP用の仮想ポートを作成して、MIDI 1.0デバイスにUMPから変換して送信するような機能も実装可能でしょう。MidiDeviceServiceもMidiUmpDeviceServiceも動的にポートを追加することは出来ませんが、MIDI1のデバイスであればUMPのグループを別々に扱うことで1つのUMPポートから16デバイスまではアプリケーション側で対応可能でしょう(デバイス名等をクライアント側に認識させるにはUMP StreamメッセージのEndpoint Discoveryを送ってEndpoint Name Notificationを受け取る必要がありそうです)

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整備を待ったほうが良いでしょう。

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

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

1月の開発記録 (2024)

2024年最初の月次報告書です(かしこまった言い方)

ktmidi 0.7.0, and ktmidi-ci-tool first preview release

先月ずっと開発していたktmidi-ci-toolというかMIDI-CI基盤ですが、いつまでも遊んではいられない(!?)ので、今月Mavenパッケージのレベルでktmidiからktmidi-ciを新たに切り離して、それがProfile Configuration, Property Exchange, Process Inquiryの3つの柱(PIがthree Psに該当するのかは怪しいところですが)を実装できたところでfeature completeとしてリリースに踏み切りました。

ktmidi-ciのAPIはktmidi-ci-toolの開発過程でだいぶひっちゃかめっちゃかになって、今でもend user developers向けのAPIとしては全然整備されていないのですが、これは「そういうもの」と明示して、とりあえずktmidi-ci-toolを公開することを最優先課題として動いています。この場合の「公開」は、ざっくり書くと

というボリューム感で、1週間くらいはこのリリース作業そのものに追われました。特にUnderstanding MIDI-CI Toolsは「そろそろMIDI-CIをちゃんと理解したい人なら誰でも参照する資料」みたいなやつがほしいと思って書いたやつなので(同じノリで書いたやつとしてはAndroid+JUCE+CMakeのやつとか、日本語だとlanguage server protocolの解説記事とか)、今後もMIDI-CIの説明が必要になったときに適宜指していこうと思います。

日本語版をまだ書いていないので(JUCE Advent Calendarの記事で満足しちゃったかも)、英語を読めないと置いてきぼりですが、日本語で読みたい人はDeepLなりChatGPTなりにでもかけてください。

この過程でMMAが(関係者が開発しているため)midi.orgでしょっちゅう言及しているMIDI 2.0 Workbenchなんかも試してみたんですが、JUCE CapabilityInquiryDemoの実装のほうが圧倒的に実用性が高いので、そっちを使ったほうが理解が早いと思います(というかworkbenchのほうは「実装検証用ツール」以外の側面ではほぼ無ですね)。ktmidi-ci-toolは仮想的なMIDI-CIデバイスを作れるCapabilityInquiryDemoと「同等」にするのを目指して作ったので、だいたいの機能は同様に使えます。

post-release momentum

ktmidi 0.7.0というリリースを出したのは「とりあえずひと段落つける」ためでしたが、ktmidi-ciのAPIはひっちゃかめっちゃかな状態で他人に使ってもらえる状態ではないので、一旦整理する必要があると思っています。本当はMIDI 2.0サポートをMidiAccessに追加してktmidi-ciでもUMPをサポートしたかったのですが、ALSA以外で使えるのはCoreMIDIくらいしかなく、CoreMIDIをサポートするにはJVMもNativeも自分で実装しなければならず、ktmidiにばかりかまけているとAAPが進まなくなる…という感じで先送りにしたので(圧倒的に開発の人手(?)が足りない…)、この辺も今後のTODOです。まあMidiAccessなしでUMPをサポートする方法も無いわけではないですが(実利に乏しい)。

ktmidi-ci-toolはアプリとしてだいぶプリミティブなCompose Multiplatformプロジェクトになっていて、まともなアーキテクチャも適用しておらず(最初Essentyを使おうとしたのですが、状態管理実装がAPIレベルでしかなさそうだったので、ドキュメントなり資料なりが増えるまで見送り)、androidx側でLiveDataとかをKMPに移植しそうなふいんきもあるので少し待とうと思っています。あとKotlin/Wasmがもう少し整備されるかもしれないKotlin 2.0待ちなところもあります。KotlinConf 2024が5月下旬なので、そこまで出ないかも。

あとProcess InquiryのMIDI Message Reportの機能をもう少し拡充して仮想MIDIレジスタステートマシンにしてしまえば、「汎用MIDI 2.0コントローラー」のようなものも作れると思います。Roland/YAMAHA/KORGあたりが自前で作っていそうな気もしますが(!) 特にKORGはKeyStageでPEの実装まで持ってるはずだし。PEの実装は割と大変で、AppleのCoreMIDIにも含まれていないやつです。これは「CoreMIDIの中でJSONやりたくない(JSON処理系を参照したくない・取り込みたくない)」が理由かもしれませんが(!?)

Android audioチームも同様のライブラリを作っている可能性が割とありますが、たぶんAndroid専用になるでしょうし、Kotlinかどうかはわかりません。

what is new in this month

ktmidi-ciとktmidi-ci-tool、この1ヶ月で何が変わったのかを集めてみましたが、だいぶ多いので箇条書きで終わらせます…

  • Responder UIの内容が空っぽだった(要するに何の機能も無かった)のを作った
  • MidiCIResponderのProfile Configurationサポートが実装されてなかったので作った
  • PEのメッセージに含まれる高度な機能を全部作った: columns, subscriptions, body chunking, pagination, mutualEncoding, partial updates(これはクライアント側だけあった)
  • Profile Details Inquiry/Replyサポートが無かったので実装した
  • Process Inquiryを実装した
  • 単なるdata classだったMessageクラスでのコード共通化(common parts, serializerなど)
  • zlib+Mcoded7のためにzlib実装をDesktop/Androidだけ何とか使えるようにした
  • 設定のロード/セーブを実装した(セーブは手動)
  • ktor-ioの参照を外して(ByteOrder等の自前実装)Wasmビルドを追加した
  • MidiCIInitiatorとMidiCIResponderをMidiCIDeviceに融合した(一部古いものが残っている)
  • InitiatorScreenで複数のMIDI-CIデバイスを表示するようにした(もしあれば)
  • Android用の仮想ポートとしてMidiDeviceServiceを追加した
  • PEでDeviceInfoをやり取りしてInitiator側で表示できるようにした

AAP 2023 Year in Review and 2024 Roadmap

そんなわけで1月もほぼktmidi-ciの開発に費やしてしまったのですが、2月はAAPに少しずつ戻ってこようと思っています。まずは例年なら年末に公開していたYear in Reviewの記事を書きました。去年はそれまでと比べてだいぶAAPの作業を多めにやっていたので(勉強会も開催していなかったし)、進捗が大きい…

https://atsushieno.github.io/2024/01/28/aap-2023-year-in-review.html

(先月はAndroidでのオーディオプラグインAPIのリアルタイム拡張機能についても記事を書いていて、今月は上記ktmidiの記事2本も上げたので、英語ブログの更新が多い…)

それから、これも例年なら年初に公開というか更新していた2024 Roadmapを出しました。

https://github.com/atsushieno/aap-core/issues/191

2023年初には20項目くらいあったのですが、去年はGUIを消化したりMIDI2 UMPで拡張機能メセージングを整理した関係で割と多めに課題を解消できていて、今年は14件でスタートです。

あと「ほぼktmidi-ciの開発に費やしていた」とかしれっと書きましたがちょっとウソで、Tracktion Engine on Androidの開発相談を受けてちょいちょい調べ物をしたりしていました。tracktion_engineのDemoRunnerをAndroidでビルドできるようにしたので、これに手を加えてこのRoadmapにあるaap-juceのTE対応タスクも完了となる可能性が高いです(楽観的)。TE、Androidでは非常に重いやつがあるのですが、OboeからOpenSLESにしたら軽くなったとかいう謎報告も受けていて、まだ調べ物をする必要があるやつかもしれません(仕事ではないので今はやってない)。

2月の予定

そんなわけで、2月はたぶんAAPメインに戻ってくると思います。ただM3 2024春にサークル参加することになったので、もしかしたらそっちの作業を何かやっているかもしれません。MIDI-CI本くらいは出そうと思っています(2月でなくてもいい)。

MIDI-CI Property Exchangeに関する覚書

この覚書の端緒

2023/12/2にjuce-midi-ciに関する記事を書いたやつの延長戦で(?)、atsushieno/ktmidiMIDI-CI実装をアップデートして機能追加している。この記事にも書いたが、JUCEのCapabilityInquiryDemoは割と機能豊富なので、自分のMIDI-CI responder実装のdogfoodingで重宝している。

記事を書いた時点では、そもそもMIDI接続に使用しているrtmidiバインディングのビルドが安定せず(各種OSでのArm64サポートが期待できないrtmidi-jnaから新しく作っているrtmidi-javacppに移行しつつあって、Kotlin/Gradleでの実例が乏しく難易度が高いし、仮想MIDIポートのサポートも調整が必要だった)、運良く接続できた時だけ稼働させて調べて書いていた。先週ようやくMIDI-CIサポートが「開発可能」な状態まで来たので、MIDI-CIの関連しようとにらめっこしながら実装を追加している。

記事でも書いたが、MIDI-CIはバージョン1.2で大幅な仕様変更が加えられた。Protocol Negotiationは削除されてUMP仕様の一部となり、代わりにProcess Inquiryが追加された。Function Blockのコンセプトが追加されてプロファイルの適用対象のコンテキストの新たな要素となった(MIDI-CIのメッセージバイト列に破壊的変更はない)。

Common Rules

MIDI-CIには、無視できない関連仕様がいくつか存在する。MIDI-CI仕様を読み込んで実装できるのは、MIDI-CIのメッセージフォーマットのバイナリシリアライゼーションくらいで、それらのメッセージがアプリケーション間でどのような手順に基づいて実装されるかは、実は別の仕様に記述されている。具体的には、まず次の2つが重要だ:

  • M2-102: Common Rules for MIDI-CI Profile
  • M2-103: Common Rules for MIDI-CI Property Exchange

これらの仕様がいかに重要であるかを説明するために(といっても本稿はProperty Exchange(以下PE)に関する覚書なのでPEについてのみ追求する)、まずMIDI-CI仕様本体の規定内容を把握しておこう。MIDI-CI仕様本体では、PEについては次のメッセージが規定されている:

  • Property Exchange Capabilities InquiryおよびReply to it
  • Get Property DataおよびReply to it
  • Set Property DataおよびReply to it
  • SubscriptionおよびReply to it
  • Notify

そして、これらがそれぞれヘッダーと本体をもつ、ということくらいしか規定されていない。プロパティを取得したり設定したりするためには、そのプロパティのメタ情報が必要になるが、MIDI-CI仕様そのものには、これが含まれていない。MIDI-CIの機能を使うなら、「プロパティ情報のリスト」もまたPEによって対象デバイスに問い合わせる必要がある(他にもたとえばProfile Detailsに基づいてできなくもないだろう)。個別のシステムに特化してプロパティ情報が既知である(つまりMIDI-CIでやり取りする必要がない)プログラムでもない限り、MIDI-CI仕様のPEに関する規定だけでは何も出来ないというわけだ。

Common Rules for PEの仕様では、このヘッダーにどのような項目が含まれているべきかが具体的に規定されている。たとえば、ヘッダーは必ずJSONシリアライズされ、本体は通常はJSONシリアライズされる(他のmime typeも利用できる)、Get Property Dataのヘッダーには取得したいプロパティ名を{"resource": "xyz"}のように指定する、Reply to Get Property Dataの本体にはプロパティリストが含まれる(そのJSONフォーマットのスキーマもある)、といったことが規定されている。MIDI-CI仕様本体には「JSONで送受信する」ということすら規定されていない。

Common Rules for PEの仕様にはバージョンがあって、これは仕様書の箇所によってはProperty Exchangeのバージョンとも呼ばれている。PEのバージョンはReply to PE Capababilitiesメッセージにも含まれていて、MIDI-CI仕様では特定のPEのバージョンのみを想定した内容にはなっていない。つまり、MIDI-CI仕様のバージョンが変わらなくても、Common Rules for PEのバージョンが上がると、同じPEの仕組みの上でも異なるアプリケーション プロトコルに基づくやり取りが発生する可能性が(将来的には)ある。逆に、MIDI-CIの仕様はバージョン1.2で大きく変化したが、Common Rules for PEの仕様は根本的には変わっていない。2024年初現在でも2020年12月発行のバージョン1.1が最新だ。

MIDI-CI実装上の考慮事項としては、PEの実装とプロパティメタデータの扱いは境界を分けておいて、Common Rules for PEに準拠する部分はプロパティメタデータ操作インターフェースの実装のひとつとして作っておくと良さそうだ。

プロパティ操作機能の有無と互換性の確認

MIDI-CI Initiatorにとって、接続したResponderでPEがサポートされているかどうかを確認するには、2つのアプローチが考えられる:

  • 自分が発行したDiscovery Messageに対するReply to Discovery MessageのフィールドCapability Inquiry Category Supportedフィールドで、PEのフラグが立っていなければ、それはそのResponderがPEをサポートしていないことを意味する。ただしこれはDiscoveryが先行していることが前提なので、最初からMUIDまで把握している(あるいはMUIDを特に解釈しない)システムでは、このアプローチは成り立たない。
  • PE Capabilities Inquiryを送信して、Reply to PE Capabilitiesで応答が返ってくることを確認する。応答がないことも考えられるし、応答はNAKかもしれない(PEをサポートしていないことになる)。

Property Exchange Foundational Resource

Common Rules for PE仕様(バージョン1.0)のセクション7(Resources and Resource Data in a Device)では、プロパティのリストを取得するためのResourceListをサポートすることが必要とされている。そのリソース定義の詳細はセクション12にまとめられている。

また、M2-105-UM: Foundational Resources: DeviceInfo, ChannelList, JSONSchemaという仕様では以下のプロパティも規定されていて、これらをサポートすることが強く推奨されている("strongly recommended"):

  • DeviceInfo : そのデバイス/エンドポイントの情報。SysExでやり取りされるmanufacturer ID、family ID、model ID、version ID(software revision)などのフィールドのほか、文字列で定義される名前もフィールドとして規定されている
  • ChannelList : デバイスが用意・想定しているチャンネルの構成。チャンネルの「クラスター」やプログラム名などが取得できることになっている
  • JSONSchema : そのデバイス/エンドポイントのリソースのJSONスキーマ定義

プロパティのメタ情報を使わないシナリオ

Common Rules for PEが無ければプロパティのリストすら取得できないことになるので、一般的なMIDI-CIの機構では、Common Rules for PEの実装は必須であるようにも見える。しかし、すでにResponderのPEのメタ情報が既知で(どんなプロパティが存在するのかがわかっていて、どのチャンネルを利用できるかもわかっている場合)、値の取得/設定/変更通知のみが必要である、と事前にわかっているのであれば、Common Rulesに基づくやり取りは、必須ではないと考えられる。

メッセージチャンクと巨大なプロパティ情報の並列処理

MIDI-CI仕様(本体)では、PEのSysExパケットを分割してプロパティの本体(body)を送受信するメッセージチャンクの仕組みが規定されている。MIDI 2.0 UMPのMDS (mixed data set)と同様、複数のGet Property Data / Set Property Dataが並列して処理できるようになっている。これはSysExパケットそのものを分割して(F7で終わらないF0メッセージとして)送受信するかどうかとは別の話だ。

PEの並列処理を可能にするために、PEのbodyを含むメッセージではリクエストIDが渡される。これが7ビットしかないため、並列処理最大数は127になる。この数値はProperty Exchange CapabilitiesでInitiator側の最大値を渡せて、そのReply to ...のメッセージでResponder側の最大値が返されることになる

リアルタイム処理とプロパティヘッダの解析

Common Rules for PEでは、headerもbodyもJSONが使われることがほぼ前提になっていて、特にheaderはJSON以外のフォーマットを使うことが想定されていない。PEのメタデータの取得そのものはPEにおいて必須ではないが、Get Property Data やReply to Get Property Dataといったメッセージでも、このJSON前提のPEヘッダーが必要になることに変わりはない。JSONの解析は明らかにリアルタイム処理向きではない。

もっとも、Common Rules for PEのヘッダーで利用できるJSONは、JSON仕様よりもはるかに厳しい字句上の制約が加えられている ("5.1.1 JSON Header Data Property Additional Rules"):

  • プロパティ値はnumber, boolean, stringのみ
  • stringプロパティには最大長が必ず指定される
  • プロパティのkey名はcamelCaseで最大20文字のASCII文字列
  • リクエスト(Get Property DataやSet Property Data)のヘッダーの最初のプロパティはresourceでなければならない
  • レスポンス(Reply to Get Property DataやReply to Set Property Data)のヘッダーの最初のプロパティはstatusでなければならない
  • ヘッダーに空白文字を含めることはできない
    • 値にもnon-ASCIIな改行文字は含まれないため、必ず1行で完結しなければならない

Common Rules for PEヘッダーの解析はサイズ上の制約から事前にアロケーションを済ませたメモリ領域だけに展開でき、空白文字等の利用も許さないので、リアルタイム解析を可能にするべく最大限配慮した設計になっているともいえるが、realtime readyな言語で実装した上で詳細を検討すべきところだろう。

ちなみに本体のほうはmedia typeを問わないため、単なるapplication/octet-streamでもよいが、application/jsonやzlib+Mcoded7のようにリアルタイム性能を損なうようなデータ処理を要するmedia typeが指定される可能性もある。

12月の開発記録 (2023)

12月、あっという間に過ぎていきました。開発記録はほぼ一つのことしかやっていないので短めです。シンプルにいきます。

ktmidi midi-ci-tool

今月初頭にJUCE 7.0.9で追加されたMIDI-CIサポートについてというエントリを書きましたが、当該エントリにもある通り、これを書いた時点ではktmidiのMIDI接続まわりで期待通りの動作ができていなくて、じっくり機能を調べることができていませんでした。その後ほどなく、実際には仮想MIDIバイス接続さえ出来てしまえば何とかなることがわかって、JUCEのCapabilityInquiryDemoとの間で相互接続できるようになって、開発が一気に進捗するようになりました。

上記エントリでも言及しましたが、ktmidiのMIDI-CIサポートには、MIDI-CIのInitiatorとResponder(ざっくりクライアントとサーバーのようなものだと思って大丈夫です)のagentのようなものが実装されていましたが、あくまでproof of conceptとしてしか存在していなかったので、APIとしての利用価値はほぼ無いものでした。それでは存在している意味がないので、ここはひとつJUCEに倣ってCapabilityInquiryDemoのようなツールをktmidiだけでも作ってみよう、と思って、新しいサンプル ユーティリティをCompose Multiplatformで開発しています(未完成):

ktmidi midi-ci-tool with juce CapabilityInquiryDemo Initiatorの機能はむしろResponderより後に作り始めたものだったのですが、UIを含むアプリケーションとしての機能はむしろResponderより先にざっくり出来上がってしまいました。Responder側の機能は昨日ようやく開発が始動した感じです。年始の暇な時間に作り上げようと思っています。

Compose Multiplatformで作っているので、デスクトップとAndroid、あわよくばCompose for WasmでWebも…という感じなのですが、現状ktmidiが依存しているKtorがまだWasmに対応していないので、Web版はまだすぐには出来なそうです。Android版は…Googleが新しいAPIを作っていれば && MIDI-CIがそこに入っていれば必要ないという話があるのですが(!)、現状まだ無いので、ここが最大の使い所かもしれません。atsushieno/aap-coreに統合すれば、AAPの拡張機能の問い合わせをMIDI-CIでできるようになる可能性がまあまああります。

(AAPの開発の進捗は今月はほぼゼロです。)

MIDI-CIの実装に際しては、MIDI-CIと関連仕様についていろいろ覚書を貯めているので、これはいずれ公開しようと思っています(今月出すつもりだったけどプライベートで忙しくて無理でした)。

Kotlin MultiplatformでMIDI 1.0/2.0ライブラリを開発している話

ktmidiの開発が進んでいた頃、20日にKotlin/Swift愛好会(たまに参加している)があって、直近まで登壇枠が開いていたので、そんなら…と思ってktmidiの話をしてきました。

speakerdeck.com

内容はほぼプラットフォーム別バックエンドをどう作るとかMaven Centralでの発行どうする?みたいな部分に集中してしまいましたが、参加者のみなさんの質問の大部分(いろいろ来てありがたかったところです)は「Kotlin/Swiftはリアルタイム性ないぜ〜」って話したところに集中していました。上記MIDI-CIやMIDI 2.0の話はほぼゼロです(!)