お読みになる前に

この文章は1998年に書いたものです。
ところどころ不遜な表現、イライラしたり、読みづらい箇所があるかと思いますが、若気のいたり(?)ということで、お許しください。お役に立ちましたら「ええがね」を押してください。(Facebookの「いいね」ではありませんので、どこにもシェアされません。)

ええがね(183)

はじめに

SMF(Standard MIDI File)のフォーマットを詳しく書いたものを探している人が多いようで、ここではSMFフォーマットの中身を簡単に説明します。
まだ、わかりにくい説明だと思いますので徐々に充実させていければと思っています。

ただし、16進数の知識はあらかじめ持っているものとします。
16進数は “10H” というように “H”を末尾につけます。2進数は11111111Bというように、
最後に “B” をつけます。まあ、2進数なんてみればわかりますよね。

SMFのフォーマット

SMFには、Format 0/1/2 の3つのフォーマットがあります。
Format 0 はすべてのチャンネルのデータを1トラックに全て収めたものです。
Format 1 はマルチトラック対応のフォーマット。
楽譜でいえば、楽器のパート毎にきれいに分けられて、記述されいるのがFormat 1 で、楽器の分け隔てなく、全てのパートを同じ五線譜にごっちゃになって書かれているものが、Format 0です。ふーむ、こんな例えでいいのかな。
Format 2ってのはシーケンスパターンをそのまま保存する・・・といったような、私もよくわかっていない仕様なんですが、対応機種が少ないので、ここでは無視します。

SMFのフォーマット

SMFの構造

SMFの構造は、意外と簡単です。
SMFは大きく分けると1つのヘッダのブロック(ヘッダ・チャンクと申します)
と複数のトラックのブロック(トラック・チャンクと申します)に分けることができます。
ヘッダチャンクは、その曲の基本的情報が詰まっています。詳細は後で述べましょう。
トラックチャンクってのは、名前の通り、1トラックごとの情報がつまっています。

SMFにはFORMAT 0/1/2 と3つのフォーマットがありますが、
FORMAT 0 は・・・そう、トラックチャンクは1つしかございません。(Format 0 はすべてのチャンネルのデータを1トラックに全て収めたものですから)
FORMAT 1/2 は使用しているトラックの数だけトラックチャンクがございます。

ヘッダチャンク

では、早速SMFファイルのデータ構造を見てみましょう。
読者の皆さんは、いくつかのSMFファイルとバイナリエディタを用意して、実際にSMFの中身を見てみるといいですね。

さて、SMFファイルはヘッダチャンクから始まります。ヘッダチャンクの構造を下に示します。下の数字(16進数)は一例ですので、データによって変わります。ヘッダチャンク

チャンクタイプ(4byte)

チャンクタイプはこれから始まるチャンクのタイプを記号で表したもの。 マジックナンバーってやつですね。 ヘッダチャンクは“MThd”となっている。これを数値化してやれば、 4DH, 54H, 68H, 64Hとなる。ので、どんなSMFファイルも先頭の4バイトは、 上のように決まっているはず。

データ長(4byte)

チャンクタイプの次にデータ長が4バイトをしめます。 データ長っていうのは、これから先、このチャンクが何バイト続くかを示したものです。 4バイトであらわすことができるが、ヘッダチャンクは必ず [00H, 00H, 00H, 06H] (このチャンクは6バイトですって意味ですね)となります。 だって、この後に続くのは、フォーマット(2byte) + トラック数(2byte) + 時間単位(2byte) で、合計6バイトと決まっているんだもん。 じゃあ、なんで4バイトもあるのさって私に聞かないでくださいね。 多分、将来の拡張用だったり、トラックチャンクに合わせたりしてるんでしょうね。

フォーマット(2byte)

SMFフォーマットの0か1か2を2バイト利用して、 あらわします。フォーマット0なら、[00H, 00H]、フォーマット1なら、[00H, 01H]となります。

トラック数(2byte)

データの全トラック数をあらわします。フォーマット0なら、 無条件で、[00H, 01H]となるのがおわかりでしょうか? (フォーマット0は1トラックオンリーですよね)

時間単位

要するに分解能ってやつです。 じゃあ、時間単位なんて書かずに分解能って書いちゃえばいいじゃんって思うのですが、 だって虎の巻にそう書いてあるんだもん。(笑)
って冗談はさておき、 みなさんはふだん音楽を作る時に、 「何小節の何拍目」といった感じで、タイミングを調整しますね。 音楽を作る時に「演奏開始時から2分43秒000のタイミングで・・・」 なんてタイミングの取り方をしている人はまずいないと思います。

SMFフォーマットでは、 この2種類(「何小節何拍」と「何分何秒何フレーム」)の指定ができます。 どちらを指定するかは、2バイトのうちの上位バイト(図の左側ですね) の最上位ビット(第7ビット)によって決定されます。 0なら前者が採用され、1なら後者が採用されます。 上位ビットが80H以上ならば、後者ということになりますね。 (10000000Bを16進数に変換すると80Hだから) ま、大抵のデータが前者を使っていますので、気にする必要はないですね。 上の例では[01H, E0H]となっていますので、素直に[01E0H]と解釈しましょう。 (わざわざひっくりかえしたりしない) 01E0Hを10進数に直すと480。要するにこの例では「分解能480のデータ」であることを示しています。

トラックチャンク

さあ、早くも最終章です。(笑)
このトラックチャンクが重要なんですよね。右のスクロールバーの下がり具合でその重要さが分かります。

それでは、下に例を示します。
トラックチャンク

チャンクタイプ(4byte)

トラックチャンクのマジックナンバーは「MTrk」[4DH, 54H, 72H, 6BH]です。 トラックチャンクは必ずこの4バイトから始まります。

データ長(4byte)

ヘッダチャンク同様、この後に続くデータの長さを4バイトであらわします。 上の例では[00H, 00H, 5BH, 6AH]を素直に00005B6AHバイトと解釈して、23562バイト(多分) となります。

データ本体

さ、いよいよ本番です。けっこう最初は ?? な感じですが、 慣れてくるとバイナリエディタに並んだ16進数をポカンと見てるだけで、 どんなデータか想像出来るようになります。(ほんとか?) 詳しくは以下に説明します。

トラックチャンクのデータ構造

トラックチャンクのデータ本体は無数のイベントが集まって出来ています。 「1チャンネルのC4を鳴らせ」・・・で、これは立派な1イベントです。 SMFファイルは、[タイミング]と[イベント]が組になっています。 わかりやすくいうと「いつ」「何をするのか?」がセットになったものが、 何百何千と続いているということです。この「いつ」の部分を「デルタ・タイム」 と呼びます。

デルタタイムの表現方法

デルタタイムは可変長の数値で表現します。 可変長というのはその名前の通り「長さを変えることができるデータ」です。 これまでに出てきた数値表現はみんな「固定長の数値表現」でした。「各チャンクのデータ長は4バイトであらわす」 という具合に2バイトとか4バイトって決まっていました。 しかし、いつもいつも4バイトも必要になるわけでもなく。 例えば、ヘッダチャンクのようにデータ長が6バイトで済んでしまうのなら、 なにもデータ長の部分で[00H, 00H, 00H, 06H]って4バイトも使わなくても、 [06H]の1バイトだけで終わらせることができますね。

デルタタイムに「固定長数値表現」を当てはめると、 データが冗長になってしまい大変です。”0″をあらわすのに[00H, 00H, 00H, 00H] なんて4バイトも使う余裕はありません。 なぜならデルタタイムは1イベントに1回出現するからです。 1音なるごとに1回出現(設定)するのです。1曲あたり何万というイベントがあります。 その度に4バイトも使ってたら、大変なデータ量になりますよね。 逆に1バイトで済ませてしまうと、精度の高い(どころか普通の)音楽は表現できません。

そこで考え出された(?)のが、「可変長数値表現」です。 可変長数値表現は1バイトのうち、 最上位ビットを除く7ビットを数値を表現するのに利用します。 最上位ビットは次のバイトもデータバイトであるか? をあらわすためのフラグに利用されます。

それでは下に表でまとめます。これを見れば一発で理解できるかも?
固定長数値表現

む~。わかんない?わかんないですよね。これじゃあ。

えー、先ほど申しましたように、「各バイトの最上位ビットはフラグに利用する」 ということから、7ビットで表現できる数値までは1バイトで表現できることになります。 2進数でいえば、00000000Bから01111111Bまでですよね。 16進数になおせば、00Hから7FHまで。
じゃあ、80Hはどうあらわすかというと、もう1バイト余分に使います。 そこで上の表を128の列を見てください。 128を普通に16進数表現すれば、80H。2進数は10000000Hとなりますが、 可変長数値表現の場合、最上位ビットが使えないので、上位バイトの最下位ビットに繰り上げをします。 また上位バイトの方では次のバイトもデータバイトである事を示すために、 最上位ビットに1を立てます。逆に最上位ビットがが0ならば、 データバイトはこのバイトで終わりという意味になります。 128を可変長数値表現に直した場合の下位ビットがそれにあたりますね。

可変長数値表現わかりましたか? 説明するのも難しいですね~。ふぅ。

デルタタイムに話を戻します。 1つのイベントを表現する場合、まずデルタタイムを設定します。 デルタタイムは「直前のイベントからの時間」をあらわします。 デルタタイムはヘッダチャンクで設定した「時間単位」の設定に左右されます。 もし「時間単位」が [00H 60H](96)に設定されていれば、 デルタタイム30H(48)は八分音符をあらわします。 デルタタイムが00H(0)であれば、 直前のイベントと同時にそのイベントが実行されるという意味になります。 和音を表現する時はこれにあたりますね。

イベント

デルタタイムを設定したら、次はイベントです。 イベントは必ずデルタタイムとセットになっていて、 [デルタタイム(いつ?)][イベント(何をする?)][デルタタイム][イベント]と 交互に繰り返されて、データ本体を構成します。

イベントは次の3つに分けることが出来ます。

MIDIイベント
SysExイベント
メタイベント

それでは、この3つを順番に説明します。

MIDIイベント

MIDIイベントは単純にMIDIチャンネルメッセージをあらわしたもの。 ぶっちゃけた話、演奏データのことです。 音源のマニュアルの後ろの方に詳細が載っているのですが、 ここでも簡単に説明します。

MIDIイベントは、ステータスバイトとデータバイトを利用して表現します。 例えば「C5の音を鳴らせ」という命令であれば、 「鳴らせ」というのがステータスバイト、「C5の音を」というのがデータバイトになります。 ステータスバイトは最上位ビットが 1 (80H-FFH)になっています。 データバイトはその逆で 0 (00-7FH)になっています。 ボリューム等の設定の多くが制限値を127(7FH)にしているのはこういうことからなのです。

それでは代表的なチャンネルメッセージを紹介します。

メッセージの名称 データ 効能
ノート・オフ 8nH kkH vvH
(n=対象チャンネルナンバー
kk=ノートナンバー
vv=ベロシティー
音を止める。ベロシティーは鍵盤から手を離す時の速さを表す。
ノート・オン 9nH kkH vvH
(n=対象チャンネルナンバー
kk=ノートナンバー
vv=ベロシティー)
音を鳴らす。ベロシティーは鍵盤を弾く際の速さを表す(音の強さってことです)
コントロールチェンジ BnH ccH vvH
(n=対象チャンネルナンバー
cc=コントロールナンバー
vv=データ)
各種コントローラの設定ができる。

MIDIイベントは上記のように3バイトのデータが組み合わさっています。 例えば「8チャンネルのC4(60=3CH)をベロシティ90で鳴らせ」 というメッセージは[98H 3CH 5AH]という具合です。 ただし、同じチャンネルメッセージが連続して続く場合は、ステータスバイトを省略できる ランニング・ステータス・ルールが適用できます。 例えば、同一チャンネルのノートオンが連続している場合はステータスバイトの 9nHが省略できるというわけです。 このようなルールがあるので、[8nH kkH vvH]とするノートオフを、 [9nH kkH 00H](ベロシティ0のノートオン)として、 第一バイトを省略して、データ量を削減するシンケーサもあります。

SysExイベント

システムエクスクルーシブメッセージを表すイベントです。 SysExイベントは以下のような2種類のフォーマットが存在します。

SysExメッセージ

SysExイベントにはF0ステータスとF7ステータスの2種類がありますが、 実際にはF0ステータスを使うのが好ましいとされてます。 エクスクルーシブメッセージの詳細は音源等のマニュアルの後ろに掲載されているはずですので、 そちらをご覧ください。

SysExイベントがMIDIイベントと異なるのは、 ステータスバイトの後に可変長のデータ長が格納される点です。

メタイベント

メタイベントは、拍子やテンポ、著作者情報や歌詞等の情報を格納します。 すべてのメッセージが重要ではなく、歌詞等は無視するシンケーサが多いですね。 逆にテンポやトラックエンドの情報はシンケーサにとってかかせない情報になります。

メタイベントではステータスバイトにFFHが利用されます。 フォーマットは以下の通り
メタイベント

イベントタイプは次のようなものがあります。代表的なものを挙げます。

データ 機能 効能
FFH 00H 02H Sequence Number シーケンス番号。FORMAT 0/1では利用されない
FFH 01H len text Text コメントなどのテキスト
FFH 02H len text Copyright Notice 著作権表示
FFH 03H len text Sequence/Track Name シーケンス名・トラック名
FFH 04H len text Instrument Name 楽器名
FFH 05H len text Lylic 歌詞
FFH 2FH 00H End of Track トラックチャンクの終わりを示す
FFH 51H 03H tempo(3byte) Set Tempo テンポ。

4分音符の長さをマイクロ秒単位で表現。
FFH 58H 04H nn dd cc bb Time Signature 拍子
nn=分子
dd=分子(2の負のべき乗で表す)
cc=メトロノーム間隔(四分音符間隔なら18H)
bb=四分音符あたりの三十二分音符の数
FFH 59H 02H sf ml Key Signature キー(調)を表す
sf=正・・シャープの数 負・・フラットの数
ml=0・・・長調 1・・・短調

3つのイベントの種類を紹介しましたがいずれも、 イベントの前にデルタタイムを設定することを忘れないようにしましょう。

【参考文献】もっと詳しく知りたい方は・・・
SMFリファレンスブック
新井 純 著 リットーミュージック ¥2,800

  • Facebook
  • twitter
  • Hatena