MIDI 2.0 UMP June 2023 updatesについて

半年前から「アップデートがある」と言われていたMIDI 2.0ですが、6/12にようやく最終版が確定して公開されました。MIDI 2.0という名称は変わりませんが、UMP(MIDIメッセージのフォーマット)の仕様への大掛かりな機能追加に加え、SMF2と呼ばれるMIDI Clip Fileフォーマット仕様の追加、MIDIバイスの初期化まわりのプロトコルに対する非互換変更も加えられています。

概要はmidi.orgの記事に書かれているので、そちらを参考にすると良いでしょう。ひとつ注意点として、このページはブログ記事っぽい作りですが、実際には仕様のアップデートがあるたびに更新されているので、古い内容が今回の更新で消えたことになりますし、次に変更があったらまた消える可能性があるので、今後参照するときはarchive.orgのページなどを確認したほうが良いかもしれません。

www.midi.org

ここを見ている人は知っているかもしれませんが、わたしはMIDI 2.0 UMPをサポートするCのライブラリ(cmidi2)とKotlinのMIDI 2.0ライブラリ/エコシステム(ktmidimugene-ngaugene-ngkmmk)を公開しているので、現在はこれらのライブラリを今回のアップデート(以降June 2023 updates)に追随させる作業に取り組んでいます。本当は今ktmidi 新しいMIDI Clip Fileの再生機能などを実装してからこのエントリをまとめるつもりだったのですが、まだ標準化されていないファイルフォーマットの楽曲構成に沿った変更を大きく加えなければならないことが見えてきたので、先にこちらをまとめることにしました。

このまとめは2部作で、今回はMIDI 2.0 UMPのアップデートについてのまとめになります。MIDI Clip Fileフォーマットに関するまとめが続く予定です(本編を公開する時点で未着手)。

長くなると思うので、このエントリをまとめる前に変更点を箇条書した自分用メモも共有します。

UMP-2023-06-Updates.md · GitHub

MIDI 2.0 UMPガイドブック」の内容に対する影響

わたしがM3や技術書典で「オーディオプラグイン研究所」名義で発行しているMIDI 2.0 UMPガイドブックは、当然ながらこの変更が加えられる前に書かれたものなので、今回のアップデートは反映されていません。とりあえずは本書のテクニカルサポートとして、このエントリでJune 2023 updatesの内容を詳しく説明していきます。同書には改訂版を発行するタイミングで反映するでしょう(改訂版発行をお約束する意図ではありません。基本的にはこのエントリでまとめてしまえば情報としては完結するはず…)

同書の内容として重要な変更が2点あります:

  • 2.6 「UMPとプロトコル」で言及している「プロトコルネゴシエーション」がまるごと廃止されました。今後はUMPに新しく追加されたプロトコル選択の仕組みを使います。
  • UMPのユーティリティ メッセージ(No-op、JR Clock、JR Timestamp)からグループをあらわすフィールドが消失しました。もともと意味があったフィールドとはいえないので、この変更による影響はほぼないでしょう。

その他は「追加機能」ばかりなので、同書の内容は引き続き意味のあるものとして読むことができます。

xamaritans.booth.pm

(紙版は先日の技術書典14でほぼ完売してしまったので、現在は電子版のみ取り扱っています。)

SMF2をサポートする新しいメッセージ種別の追加

UMP June 2023 updatesの主目的のひとつは、SMF2ことMIDI Clip Fileフォーマットの実現に必要なメッセージの追加です。これはほぼ「SMF仕様にあってMIDI 1.0仕様にないもの」と同等です。ただし、SMFのメタイベントにはMIDI Clip Fileのコンセプトに馴染まない部分があり、UMP June 2023 updatesには反映されていない重要なフィールドも存在します。

「SMFにあってMIDI 1.0になかった情報」を列挙します。

  • MIDIイベント間のデルタタイム(時間差分)とその計算基準値: デルタタイムは時/分/秒ではなく、「4分音符1つをxxxカウントとした場合(xxxは設定次第、480とか960とか192といった数値)のティック数」のように表現されます。これは、音符の長さを時/分/秒で表現するとテンポが変わるたびにこの数値が変わることになって、音楽の正しい解析と再生が妨げられるためです。UMPには時/分/秒に基づくJR Timestampしかありません。
  • メタイベント: これにはさらにいくつかのカテゴリがあるので、分けて説明します:
    • テンポ: これは演奏に直接影響します
    • 拍子(Time Signature)やコード情報(Key Signature)、マーカー(楽曲中の時間位置に紐付けられた目印)など、演奏に影響するともいえないが楽曲中で何度も指定される補助情報
    • 楽曲タイトル・作曲者等の情報などのメタ情報で、一般的には一度しか出現しないもの
    • End of Track: 楽曲ファイルのデータの区切りとなるステータスコード

SMFのメタイベントは、MIDI 1.0メッセージとしては「システムリアルタイム」メッセージに割り当てられていた0xFFを識別子として使用していました。そのため、MIDIメッセージングのための仕組み(たとえばMIDIバイスアクセスAPI)とは排他的な存在だったといえます。2020年版のUMP仕様はデバイス間のメッセージングに用いられるパケットのフォーマットを規定するために策定されたので、この中にSMFのメタイベントのようなものが含まれることはありませんでした。

Delta ClockstampsとDCTPQ

UMP June 2023 updatesには、このデルタタイムを表現する新しいメッセージDelta Clockstamps(DCS)と、このデルタタイムの基準値となるDelta Clockstamp Ticks Per Quarter note 略してDCTPQと呼ばれるメッセージがユーティリティメッセージ(message type = 0)に追加されました。ステータスコードはそれぞれ0304です。

DCTPQ and DC DCTPQに指定すべき数値は、SMF headerにおける"division"フィールドが「正の値であった場合」と同様です。MIDI 1.0の世界では、一般的には「3でも4でも5でも割り切れる数値」が用いられていたようです。480を指定すれば32分音符と48分音符が表現できます。960を指定すれば64分音符までいけます。筆者はMIDI以前の時代から192を使って64分音符まで表現できるようにしていました(255までしか使えなかった時代の名残…!)。

ちなみに、SMF headerのdivisionが「負の値であった場合」は、デルタタイムはSMPTEに基づいて計算されることになっていましたが、これはJR Timestampでカバーできるともいえるでしょう(JR Timestampにはたかだか2秒強しか格納できないので、時間に応じたパケット分割が必要になってしまうのが面倒ではありますが)。

DCの値もSMFにおける(7ビットエンコーディングする前の)デルタタイムの数値と同等と考えて良いです。JR TimestampやDCTPQと異なり20ビットあるので注意してください。6/13時点での仕様書にもこの辺を混同したサンプルパケット図が含まれていたりします(報告済)。

ちなみにMIDI 2.0 UMPガイドブックの姉妹書ともいえる「MIDI 2.0エコシステム構築術」の 3.3.2 「パケットから音楽データ構造まで」の節には、ktmidiに含まれるMIDI 2.0用楽曲データにおいて、筆者のJR Timestampを「乗っ取って」このDelta Clockstamp相当の機能を実現するやり方が書かれています。

Flex Dataメッセージ (0xD)

新しい可変長データのメッセージ種別

SMFメタイベントの多くは可変長テキストを指定するものです。メタイベントの識別子テキスト(01h)、著作権表記(02h)、トラック名(03h)、楽器名(04h)、歌詞(05h)、マーカー(06h)、キュー(07h)が該当します。

UMPには既に可変長パケットとしてSysEx7(メッセージ種別0x3)、SysEx8とMixed Data Set(メッセージ種別0x5)があり、メタイベントは特に0x5とは相性が良さそうにも見えますが、UMP June 2023 updatesでは新しく0xDというメッセージ種別コードでFlex Dataメッセージというコンセプトを新しく導入しました。結果的に、0x5にサブコードを追加するよりもスッキリとしたメッセージ体系になっています。Flex Dataは以下の図のような128ビットのパケットになります。

Flex Data message format

6/18追記: 図にchannelフィールドが含まれていなかったのを修正しました

Flex Dataには固定長のバイナリメッセージも存在しますが、Flex Dataの可変長データは(少なくともJune 2023 updatesでは)全てテキストデータです。テキストメッセージの大きな特徴のひとつはnull-terminatedである、すなわち\0が出現したらそれはデータの終端を意味するといえる、ということです。Flex Dataには、SysEx7/SysEx8/MDSのようなサイズ情報のフィールドが存在しませんFlex Dataの処理系は、\0が出現するまで、パケット分割番号などを気にすることなく文字列をバッファリング(あるいはキャパシティの許す限り格納)するだけでよいことになります。

(実はこれは本当にそう言えるのか疑わしいところがあるのですが、それは後で説明します。)

実際、SysEx8やMDSのパケット分割と統合はUMP処理の最も面倒な部分であり、「パケットが順番通りに到達しなかったら…」のようなことを考慮しなければならないことになります。UMPにデバイスのROMアップデートバイナリなどを送信する場合は、MDSのような並列送信を可能にする仕組みにも意義がありますが、Flex Dataのテキストメッセージはせいぜい歌詞が送信される程度なので、長大なデータ送信の最適化まで考慮する必要はないわけです。

Flex Dataメッセージの共通フィールド

Flex DataのformatはSysEx7/SysEx8/MDSではステータスバイトに割り当てられていた情報と同じで、0〜3でパケット分割の状態を示します(0 = 単一パケット、1 = 開始パケット、2 = 続行パケット、3 = 終端パケット)。

addressは他のメッセージには無い概念ですが、0のときはそのメッセージはチャンネルに送られ、1のときはグループ全体に送られる、と規定されています。SMFメタイベントはもともとチャンネルを宛先とするメッセージではなかったのですが、歌詞などは特定のチャンネル(パート)に対して指定すべきものであった(複数の歌唱パートがあれば複数の歌詞指定が必要になり得る)といえるでしょう。UMPではメインヴォーカルとバックコーラスに別々の歌詞を指定できることになります。

status bankは、その次に来るstatusフィールドのカテゴリーを示します:

  • 0: setup and performance: テンポや拍子など、音楽的な意味を持ち、演奏中に何度も指定値が変わり得るメッセージです。formatは(June 2023 updatesの時点では)どれも0です
  • 1: metadata text: 演奏情報ではなく楽曲のメタデータとなるテキスト
  • 2: performance text: 歌詞など、演奏中に消費される情報となるテキストです

(SMFのメタイベントの分類でほぼ同じ説明を書いていますが、同一ではありません)

Flex Dataメッセージの5バイト目〜16バイト目の意味は、status別に規定されます。以降、各論的に説明していきます。

テンポ指定 (bank = 0, status = 0)

status bank = 0, status = 0のFlex Dataはテンポ指定です。データ部の最初の32ビットでテンポを10ナノ秒単位で指定します。これはSMFにおけるテンポ指定の100倍の精度です。SMFでは1マイクロ秒単位で、500,000がBPM = 120のときの4分音符の長さとして使われていました。

もちろん、どれだけ高い解像度でテンポを指定しても、処理系がその通りに再生してくれるとは限りません。

拍子 (Time Signature) 指定 (bank = 0, status = 1)

status bank = 0, status = 1のFlex DataはTime Signature指定です。SMFのメタイベントで指定していたものと同じです。ちなみに(SMFも同じですが)分子と分母を素朴に指定するのではなく、分母には「2のn乗」のnを指定するかたちになり、「そういう枠にはまらない音楽」については0を指定します。つまり2の累乗しか実現できませんし、0が出現することがあり得ます。ついうっかり単純に分母として割り算すると(そんな事態があればですが)0除算エラーになります。

分子、分母、"number of 1/32 notes" を1バイトずつ指定します。その最後のやつは意味があるのか?と思われそうですが、UMP仕様の説明ぶりからして、SMF仕様の策定時にすでに何らかのデバイスとの後方互換性目的のためだけに存在していたようです:

The Number of 1/32 Notes field expresses the number of 1/32 notes in 24 MIDI Clocks. This is copied from SMF 1, where it is stated:
    
    "This was added because there are already multiple programs which allow the user to specify that what MIDI thinks of as a quarter-note (24 clocks) is to be notated as, or related to in terms of, something else."

メトロノーム (bank = 0, status = 2)

UMPにはメトロノームを指定するメッセージが追加されています。シーケンサーが送ってきたり、受信デバイス上のメトロノームを設定したりというユースケースが想定されています。SMF2に含まれることはないでしょう。パラメーターの説明は省略しますが、primary divisionに加えてsubdivisionで裏拍のパターンも指定できます。

調号指定 (Key Signature) (bank = 0, status = 5) とコード名指定 (bank = 0, status =6)

UMP Flex Dataにはkey signatureを指定するメッセージが追加されています。DAWではコードトラックのような概念が一般的に存在していますが、SMFにはそのような概念がありませんでした。

コード名のほうは、オルタードや分数コード等も表現できることもあって、だいぶ情報量が多くなっていますが、16バイトに収まっています。フィールドの詳細な説明は割愛します。

ルート音などを指定するフィールドで注意すべきこととして、A, B, C, D, E, F, Gが1, 2, 3, 4, 5, 6, 7に割り当てられています。0〜6ではないですし、C, D, E, F, G, A, Bではありません

テキストメッセージ

Flex Dataの残りはテキストメッセージです。status bankの説明で言及したとおり、曲中に変化しないメタデータテキストと、演奏中に何度も指定される演奏テキストがあります。

(1) status bank = 1: メタデータテキスト

status 内容
00 未知のメタデータテキスト
01 プロジェクト名
02 曲名
03 MIDIクリップ名
04 著作権表記
05 作曲者名
06 作詞者名
07 編曲者名
08 発行者? 出版社名? (publisher name)
09 主要演奏者名
10 並奏者名
11 録音/実演日
12 録音/実演場所

(2) status bank = 2: 演奏テキスト

status 内容
01 未知の演奏テキスト
02 歌詞
03 歌詞の言語
04 ルビ
05 ルビの言語

特筆すべきテキスト種別は無い…といいたいところですが、いくつか気をつけるべき点があります。まずメタデータテキストの種類によっては、複数回指定されるものがあり得るということです。著作権表記や作曲者名、編曲者名などは複数行出現し得ます。

次に、歌詞とそのルビの文字列には、実は\0が含まれることがあり得ます。「テキストが\0で終わるというのはウソ」という冒頭の伏線はここで回収します。歌詞の中に出現する\0はmelisma = メリスマを表します。

musescore.org

ひとつのシラブルに複数のノートがスラーで繋がっているような場合が考えられるでしょう。

UMP仕様ではこの\0で表現されるmelismaはformat = 2こと継続パケットでのみ出現しうる、となっています。継続パケットで続きが存在することが既知であれば、その中で\0が出現してもメリスマであると断定できますが、format = 1のときは\0は出現できないことになっています。

筆者はこの仕様には疑問の余地があると考えています(これも報告済)。format = 1のときに出現する\0はむしろformat = 2のときと同じでmelisma以外はあり得ず、意味が曖昧になるのはformat = 0とformat = 3の場合です。これらのformatの場合でも、歌詞がmelisma終わることは考えられないので、最後の非\0コードまでは有効な歌詞、それ以降は空白データ、それ以前の\0はmelisma、と判断することは可能でしょう。

歌詞の中で改行が出現する場合の扱いもやや特殊です。改行はCRのみです。そして続いてLFが出現した場合、改段として扱われます。

SMFにあってFlex Dataメッセージにないもの

Flex Dataの説明もこれで最後になりますが、以下のものはSMFにあってFlex Dataにありません。

  • インストゥルメント名
  • マーカー
  • キューポイント

キューポイントは「(ここから)ソロ」みたいな意図でテキストを残すものとして規定されていますが、DAW等で使われているとも考えにくく、シンプルに削除されたのでしょう。

MIDI Clip Fileで使われることを前提として考えると、インストゥルメント名の位置付けは割と不透明です。「トラック名」が楽器を表すのが一般的ともいえますし、ひとつのトラックが複数の楽器を演奏する可能性もあって特に時間で変化する可能性もあると考えると、メタデータテキストなのか演奏テキストなのかも自明ではありません。扱いに困ったので削除、という見方もできるでしょう。

マーカーも筆者はSMFでそれなりに活用していたのですが(たとえばSMFプレイヤーにマーカーを選択してジャンプする機能を実装して打ち込み作業を楽にしていました)、これはMIDI Clip Fileではないほうのフォーマット(後述)で代替されるべき情報であると考えられます。追記: midi.orgの関連スレ

UMP Streamメッセージ (0xF)とプロトコル確立の変更

Protocol Negotiationの廃止とUMP Endpointの誕生

MIDI 2.0はMIDI 1.0システムの上で構築できるようにプロトコルが設計されていて、2020年当初のMIDI 2.0システムでは、MIDI 1.0からMIDI 2.0への切り替えはMIDI 1.0でも表現できるUniversal SysExに基づくMIDI-CIのレベルで、Protocol Negotiationメッセージによって行われていました。June 2023 updatesでは、MIDI-CIからこのProtocol Negotiationが消失し、その機能はUMPに新しく追加された「UMP Endpointに接続調整する」仕組みによって置き換えられました。

「なぜ?」というのは仕様書には全く書かれていないのですが、このようなドラスティックな仕様の変更を引き起こす理由はいくつか考えられます。

  • 後述するFunction Blockという概念が新しく追加され、これがMIDI-CIを伴わないUMPのレベルで完結できるため、プロトコル設定のメッセージングもUMPのレベルで完結するようにして見通しを良くした可能性はあります。
  • MIDI-CIに基づく接続が確立されていないとMIDI 1.0と2.0の切り替えが行えないというのは、実のところ不必要な制約でした。筆者はMIDI 2.0をサポートしているプロジェクトを複数持っていますが、そのいずれもMIDI-CIに基づくProtocol Negotiationをまともに実装していません(そもそもMIDI 1.0接続のAPIを前提にしていて、一方的に旧仕様におけるSet New Protocolメッセージを一方的に送り付け、返信は受信しない、といった動作)。MIDI 1.0/2.0プロトコルの切り替えが意味を成すのはMIDI 2.0をサポートするデバイスのみなので、UMPで切り替えを指示できればそれで十分なのです。

Function Blocksのコンセプト

UMP June 2023 updatesでは、新しくFunction Blocksというコンセプトが導入されました。この"function"は関数ではなく機能と捉えるのが適切でしょう。function blockはMIDI 2.0 UMPにおける「グループ」を複数まとめてひとつのメッセージングの送受信対象として扱おうというものです。

UMP June 2023 updatesの仕様書には、「MIDI出力に送信するキーボードに1つ、トーンジェネレーターに2つ、DAWのトランスポートコントロールに1つ」の4つのグループを1つのFunction Blockとしてまとめる例が挙げられています。これが「ひとつの(仮想的な)シンセプラグイン」を表していると考えると、比較的すっきり理解できるでしょう。

Function Blocksがどのように確立されるか、既存の(あるいは廃止された)MIDI-CIのどの概念に対応するものなのか、といった議論は、MIDI 1.0ポートのサポートなども含めてそれなりのボリュームのある変更となっており、UMPの「パケット」に関する変更点を解説しようという今回のまとめの趣旨からはやや外れるので(そういう話はMIDI-CI updatesをまとめることがあればそこでまとめられるべき)、今回はFunction BlocksをサポートするUMP Streamメッセージの説明に登場する、グループをさらにまとめる論理単位である、という以上は踏み込まずに説明します。

UMP Streamメッセージ (0xF)

以上のように、UMPには新たにMIDI-CIでやっていたようなメッセージを新たに追加する必要が出てきました。MIDI-CIはUniversal SysExで実現していたのですから、これらの新しいメッセージもUMPとして表現する必要は必ずしもないのですが、UMP Endpointに関するメッセージもFunction Blocksに関するメッセージも、特定のチャンネルに結びつけて処理すべきものでもなく、SysEx7/SysEx8ではない独自のメッセージ種別を新たに定義したほうが、仕様として見通しが良いのは確かでしょう。

そういうわけで、かどうかは(このまとめはMMAやAMEIの関係者ではない筆者が書いているだけなので)わかりませんが、June 2023 updatesでは新たに0xFというメッセージ種別が規定されました。

UMP Stream message format パケット分割フォーマットはいつも通りの0〜3(完結・開始・継続・終了)です。あとはステータスコードしかないので、内容としては非常にスッキリしています。

UMP Streamメッセージに規定されているのは次のものです。

status メッセージ
0x00 エンドポイント探索リクエス
0x01 エンドポイント情報の報告
0x02 バイス識別子の報告
0x03 エンドポイント名の報告
0x04 プロダクトインスタンスIDの報告
0x05 ストリーム設定リクエス
0x06 ストリーム設定の報告
0x10 Function Block探索リクエス
0x11 Function Blockの報告
0x12 Function Block名の報告
0x20 Start of Clip
0x21 End of Clip

最後の2つ以外は、MIDI-CIで規定されていたようなリクエストとレスポンスのメッセージを規定したものとして読めます。前述した通り、これらのメッセージについて説明するためには、そもそもMIDI-CIなどがどのようなメッセージのやり取りをするものなのかを説明しなければならないので、この内容の詳細な説明は割愛します。MIDI-CI仕様にひと通り目を通してからこの部分を読むと理解が早まるでしょう。

Start of ClipとEnd of Clip

UMP Streamメッセージの最後の2つ、Start of ClipとEnd of Clipは、ここに出現するとやや唐突な印象がありますが、何も難しいことはなく、End of ClipはSMF仕様に含まれていたEnd of Trackに相当するものだと考えればよいでしょう。Start of ClipはSMFには無かった概念ですが、新しいMIDI Clip File (SMF2) 仕様においては、ヘッダーブロックと演奏ブロックを区分する目印として不可欠の存在となります。

MIDI 2.0メッセージの追加

ここまでの変更に比べるとだいぶささやかではありますが、MIDI 2.0のチャンネルボイスメッセージにもいくつか追加項目があります。

  • Controller 84 (Portamento) のデータ部分はノート番号なのですが、この「値」は32ビットでどのように表現されるのが正しいのか自明ではない、という議論があったようです。32ビットの先頭7ビットが使われるということが明記されました。(一貫性のあるMIDI 1.0 to 2.0 Translationを実装することを考えれば、これはほぼ自明だったともいえます)
  • CC 126とCC 127のOmni Mode On/Offについても、同様の明確化が図られました。
  • Registered Controller (MIDI 1.0におけるRPN)にはPitch Bend Sensitivityが定義されているのですが、Per-Note Pitchbendについてはどうなっているのか?という問題が2年前から提起されていたようです。今回新たにPer-Note用のRPN #00/07 としてSensitivity of Per-Note Pitch Bendが追加されました。先頭7ビットが100セント(半音)単位での整数部、残り23ビットが小数部です。

MIDI Clip File編に続く(たぶん)

UMP仕様のアップデートに関する解説は、(だいぶ端折りましたが)これでひと通り終了です。本当はここにMIDI Clip Fileのセクションを追加して終わりにしたかったのですが、ここまでですでに10000字位になっていて、いくらなんでも長すぎるので、改めて書こうと思います。ただ、そんなに長い仕様でもないので、まとめても今回の1/10くらいで終わるショボい内容になりそうなふいんきもあります。

先に要点だけ書いておくと、MIDI Clip FileはSMF2を名乗って拡張子も*.midi2と規定するくせに、SMFの代替になるわけではなりませんMIDI Container Fileという仕様が別途策定作業中にあって、MMAの企業メンバー以外には非公開です。