SMFの構造

SMF(Standard Midi File)はMIDIの演奏データを扱う際に標準的に扱われているファイル形式です。
.midまたは.midi拡張子のファイルがそれです。

MIDIではイベントを所定のタイミングで演奏者(電子楽器や音源など)に送信して音楽を演奏する訳ですが、そのタイミングを定義した楽譜となるデータを用意する必要があります。
一方で、さまざまなソフトウェア間で共通で扱える形式が望ましいです。
このデファクトスタンダードとして生まれたのがSMFです。

WindowsMediaPlayerをはじめ、さまざまなプラットフォームでSMFを再生できます。
MIDI再生用の高レベルAPIも用意されており、再生目的ならそちらを利用したほうが良いでしょう。
しかし、MIDI再生に対して独自のエフェクトやテンポの変更、ループ再生などを行おうとした場合、上記のAPIでは対応できなくなる場合が存在します。
そのような場合にはSMFの構造を把握した上で色々と処理する必要が出てきます。

今回はこのような用途を念頭においてSMFの構造について解説していきます。

■SMFの全体像
SMFは大まかに以下のような構造になっています。

 [“MThd”][ヘッダ長][ヘッダデータ]
 [“MTrk”][トラック長][トラックデータ]
 [“MTrk”][トラック長][トラックデータ]
  ・・・

1行目にはSMFのヘッダ、2行目以降にはSMFの演奏情報が格納されたトラックが入ります。
このファイル構造はチャンクと呼ばれる構造になっており、WAVEファイルもこの形式を用いています。

■ヘッダの構造
ヘッダの構造は以下のようになります。

 [“MThd” (4byte)]
 [ヘッダ長(必ず6) (4byte)]
 [フォーマット(0~2) (2byte)]
 [トラック数 (2byte)]
 [時間単位 (2byte)]

最初の4byteはチャンクタイプ、次の4byteにはデータ長が入ります。
チャンクタイプは必ず”MThd”となります。
データ長にはチャンクタイプとデータ長を除いたヘッダの全体サイズが入ります。
したがって、必ず6になります。
SMFかどうかはチャンクタイプとデータ長が前述の値になっているかどうかで判断できます。

フォーマットにはSMFのフォーマット情報が入ります。
フォーマットは0,1,2の3種類が定義されています。

 フォーマット0
  単一のマルチチャネルトラック
 フォーマット1
  複数同時に再生されるシーケンストラック
 フォーマット2
  複数のシーケンス的に独立した単一のトラックパターン

このうちフォーマット2はほとんど使用されていないため、通常はフォーマット0と1の場合のみ考慮すればよいかと思います。

トラック数にはMIDI演奏者となるチャネルの数が格納されます。
MIDIでは1チャネルが1つの演奏者に相当し、最大で16チャネルまで同時に演奏することができます。

時間単位にはMIDIイベントを送信する時間間隔の分解能が入ります。
この値が大きいほどより細かなタイミングでMIDIイベントを送信できるようになります。

■トラックの構造
トラックの構造は以下のようになります。

 [“MTrk” (4byte)]
 [データ長 (4byte)]
 [イベント]
 [イベント]
 [イベント]
  ・・・

先頭4byteには必ず”MTrk”が入ります。
次の4byteにはトラックのデータ長が入ります。これはトラック全体サイズ-8となります。
データ長以降にはイベントが連なっています。
このイベントは以下の構造になっています。

 [デルタタイム(1~4byte)][イベント]

デルタタイムにはイベントを発生させるまでの待機時間が入ります。
このデルタタイムは1~4byteの可変長となり、以下のような構造になります。

 [0AAAAAAA]
  → AAAAAAA
  or
 [1AAAAAAA][0BBBBBBB]
  → AAAAAAABBBBBBB
  or
 [1AAAAAAA][1BBBBBBB][0CCCCCCC]
  → AAAAAAABBBBBBBCCCCCCC
  or
 [1AAAAAAA][1BBBBBBB][1CCCCCCC][0DDDDDDD]
  → AAAAAAABBBBBBBCCCCCCCDDDDDDD

最上位ビット(MSB)が0ならそこでデルタタイムのデータは終了です。
そうでない場合はデルタタイムのデータが続きます。
取得したA~DビットのMSBを除外して詰めたものがデルタタイムの値となります。

このデルタタイムを用い、以下の式により待機時間[ms]が算出できます。

 待機時間 = デルタタイム × テンポ / 1000 / 時間単位

デルタタイムがこのような形式になっている理由は、SMFのファイルサイズ削減のためです。
もしデルタタイムが常に4byte固定なら、データサイズが無駄に大きくなってしまいます。

イベントには以下3種類が存在します。

 [MIDIイベント] or [SysExイベント] or [メタイベント]

MIDIイベントは各チャネルに対して送信するメッセージです。
SysExイベントはシステム固有イベントです。
メタイベントはテンポの変更や歌詞などの情報です。

MIDIイベントは以下のような構造になっています。

 [ステータスバイト(1byte)][データバイト1(1byte)][データバイト2(1byte)]
  or
 [ステータスバイト(1byte)][データバイト1(1byte)]

データバイトはステータスバイトの上位4bitによって1または2つに変わります。
ステータスバイトは以下の構成になります。

 [1AAABBBB]
  A – ステータス
  B – チャネル

MSBが1に固定されている理由は、ステータスバイトが省略される可能性があるためです。(ランニングステータス)
ステータスバイトが省略される場合、MSBは必ず0になります。

データバイトの個数は以下のように定められています。

 1AAAが0x8 or 0x9or 0xA or 0xB or 0xEのとき
  →2個
 1AAAが0xC or 0xDのとき
  →1個
 1AAAが0xFのとき
  →SysExイベント or メタイベント

1AAAが0xFのときはMIDIイベントではなく、後述するSysExまたはメタイベントである可能性があります。
SysExイベントかメタイベントのどちらであるかの判定は以下のようになります。

 1AAABBBBが0xF0のとき
  →SysExイベント
 1AAABBBBが0xFFのとき
  →メタイベント

SysExイベントの構造は以下の通りです。

 [イベント長(1~4byte)][0xF0(1byte)][イベントデータ(イベント長byte)]

データ長はデルタタイムと同様の方式で格納されています。
0xF0はSysExイベントを表す固定値です。
送信するSysExイベントは0xF0とイベントデータのかたまりになります。

メタイベントの構造は以下の通りです。

 [イベントタイプ(1byte)][イベント長(1~4byte)][イベントデータ(イベント長byte)]

イベントタイプにはメタ情報の種類が入ります。
この種類に応じてイベント長が以下のように定められています。

 イベントタイプが0x01~0x07 or 0x7Fのとき
  →イベント長は可変長
 イベントタイプが0x20のとき
  →イベント長は1
 イベントタイプが0x2Fのとき
  →イベント長は0
 イベントタイプが0x51のとき
  →イベント長は3
 イベントタイプが0x54のとき
  →イベント長は5
 イベントタイプが0x58のとき
  →イベント長は4
 イベントタイプが0x59のとき
  →イベント長は2
 イベントタイプが上記以外
  →エラー

上記で定められたイベント長に反する場合は不正なファイルとみなすことが出来ます。
イベント長はSysExイベント長とデルタタイム同様の形式で入っています。

トラックの末尾には必ずイベントタイプが0x2Fのメタイベントが入ります。
これはトラック終了イベントです。

以上駆け足になってしまいましたが、このSMFの構造を理解することで、SMFからMIDIイベントを解析してMIDIデータを再生できるようになります。
次回はSMFから実際にMIDIデータを読み込むプログラムについて解説したいと思います。

■参考サイト
SMF 【 Standard MIDI File 】
http://homepage3.nifty.com/sabanisoft/pgroom/midi.txt
SMF (Standard MIDI Files) の構造
SMF フォーマット解説
EternalWindows MIDI / MIDI再生サンプル(フォーマット0のみ)