今日SMFに近い演奏データのデータフォーマットをMIDI 2.0 UMPベースで実装しようとしていて気付いた小ネタ。いろいろ増やしてMIDI 2.0 UMPガイドブックの改訂版を出す場合はその時に取り込もうと思っています。(改訂版は技術書典サイトとboothではアップデートを出せるので、新たに購入する必要がないようにするつもりです*1。)
MIDI 1.0におけるSMF(MIDIファイル)には、可変長のデータを保存する命令としてSYSEXとメタイベントが利用できます。SYSEXはF0h〜F7hに、メタイベントはFFhから指定長のデータを保存できます。メタイベントにはメタイベント種別があり、MIDIイベントとしては存在しない次の各命令に使えます。
メタイベントID | 意味 |
---|---|
0 | シーケンス番号 |
1 | テキスト |
2 | 著作権表示 |
3 | シーケンス名またはトラック名 |
4 | 楽器名 |
5 | 歌詞 |
6 | マーカー |
7 | キューポイント |
0x20 | MIDIチャネルプレフィックス指定 |
0x2F | トラック終端指定 |
0x51 | テンポ |
0x54 | SMPTEオフセット |
0x58 | 拍子指定 |
0x59 | 調指定 |
0x7F | シーケンサー固有イベント |
ところでメタイベントの識別子になるFFh
には、実はMIDIイベントが存在しており(システムリセット)、自由に使える領域ではありません。SMFでこれを使えるのは、単に概念として演奏データファイル中にシステムリセットが出現することがあり得ないためです。
MIDI 2.0でメタイベントを表現することを考える場合、MIDI 1.0イベントとMIDI 2.0 UMPは根本的に前提が異なる形式になっているので、半ばゼロベースで構造を考えなければいけません。MIDI 1.0時代のSMFの構成は次のようになっています。
- SMFヘッダ
- フォーマット指定 (format 0, 1, 2)
- デルタタイム分解能指定 (division)
- SMFトラック
MIDI 2.0にはUMPとしてJR Timestampメッセージがあるので、デルタタイム指定の代替として機能するかもしれません。ただしJR Timestampのタイムスタンプ値は16ビットで1/31250秒単位のフレーム数を指定するので、たかだか2秒くらいしか表現できません。全音符・全休符などを表現するには複数のJR Timestampメッセージが必要ということになります。これはいまいちなので別の表現形式のほうが適切なのかもしれません(未検討)。
話を少し本題に戻しましょう。SMFにしか存在しない概念であるメタイベントをMIDI 2.0 UMPの上に構成するにはどうすればよいでしょうか? MIDI 2.0には次の3通りの可変長データ表現があります。
- SYSEX7
- SYSEX8
- MDS(混合データセット)
SYSEXはあくまでSYSEXなので、わかりやすく区別するためにはMDSを使うのがシンプルな解に思えます。しかしMDSにはSYSEX7やSYSEX8に無い特性があり、メタイベントには適していません。なぜでしょうか? その答えはMDSが分割送信できるとされていることにあります。
MDSはMIDIケーブルを通じてファームウェアアップデートを実行するような場面で活用できるように設計されたものですが、分割送信できるデータはいつ終端が届くかわからず、先頭と末尾の間にJR Timestampが含まれる可能性もあります。テンポ変更や拍子指定などのイベントでは正確なタイムスタンプが重要ですが、MDSではデータを最後まで受信しないと完全なMDSを取得できません。UMPをストリーミング処理していると、MDSを最後まで受信した頃にはもう処理時間が変わってしまっている可能性があります。UMPをバッファリング処理しないとメタイベントを正しいタイミングで取得・処理できないというのは、効率的ではありません。
あとは、UMPで予約されている領域を活用する方法もありますが、UMPの基本設計のひとつとして、可変長データを固定長データのフォーマットに変形する仕組みがあります。これを崩さずに可変長データを送信するのであれば、8ビットをフルに使えるSYSEX8を使うのが一番無難でしょう。適当にManufacturer IDを使う手もありますが、ユニバーサルSYSEXでSMFのシステムリセットのようにファイルへの保存があり得ないような命令の領域を使えば、無難にメタデータを格納できるのではないでしょうか。MMAのユニバーサルSYSEXのリストを眺めてみると、non-realtimeの7Bh〜7Fhの辺りは問題なく使いまわせそうです。
そういうわけで、SMFにおけるメタデータを表現するなら、おそらくSYSEX8を使ってユニバーサルSYSEXの一部領域を乗っ取りつつ、既存のパケットフォーマットを使い回すのが良さそうだというのが、現時点での自分の理解です。
*1:約束するというものではなく、債務者たるわたしが進んでこれを履行するときは債務の履行になる特殊な債権債務関係()と理解してください