Giter Club home page Giter Club logo

smfspec's Introduction

SMF (Standard MIDI Files) の構造

更新履歴

更新履歴

SMFとは

MIDIを使った演奏情報を一定の形式(フォーマット)のファイルにまとめたものを (ここでは)「MIDIデータファイル」と呼んでいるが、 そのMIDIデータファイルの、世界でもっとも一般的な形式がSMF(Standard MIDI Files)である。 ファイルの拡張子は ~.MID であることが多いが、 ~.SMF であることもある。

SMFは基本的に 「この時間(タイミング)に、この音を強さ**でオンにする」 といったレベルの情報の集まりでしかなく、 「ここを2回繰り返す」とかいった「ちょっと高級な情報」は扱えない。 よって、SMFは曲データを作成しているときの保存フォーマットとして用いられることは少なく、 専ら曲データ完成後の配布用フォーマットとして用いられている。 実際、一般的なシーケンサソフトでは、 独自フォーマットとSMFのどちらでもデータを保存することができ、 前者は通常のデータ保存に、後者は(シーケンサソフト外にデータを渡す)エクスポート的な機能として用いているはず。

以下の表記について、その他

  • 数値は、10進か16進かは特に区別せずルーズに記述している :-)。 ただし、 16進には prefixとして "0x"をつけていることがある。 (例: 0xFFなど。C言語での表記と同じ)
  • SMFにおいては、数値データはほぼすべてbig endianの即値で表される。 例えば 0x12345678 という4バイトの数値は、 0x12 0x34 0x56 0x78という順で格納される。 (唯一の例外はMIDIイベントの0xEnで、 これだけはlittle endianで格納される)
  • 基本的に「(プログラム側で)理解できないコードは、無視する」 ことができるようなデータ構造になっているため、 SMF関連のプログラムを作成する際には、そのように設計すべき。 そうしておけば、将来SMFに拡張があった場合でも、 そのプログラムを使って問題が生じるようなことにはならない。

SMFのおおざっぱな構造

ものすごく大雑把に書くと、以下のものを順番に単純に結合(concatenate)したもの、 ということになる。

以下サンプル。テンポ120で、四分音符でドレ、全音符でミを鳴らすSMF。

000000 4D 54 68 64 00 00 00 06 00 01 00 02 00 30 4D 54  / MThd.........0MT 
000010 72 6B 00 00 00 0B 00 FF 51 03 07 A1 20 00 FF 2F  / rk......Q....../ 
000020 00 4D 54 72 6B 00 00 00 18 00 90 3C 7F 30 3C 00  / .MTrk.....・0<.. 
000030 00 3E 7F 30 3E 00 00 40 7F 81 49 40 00 00 FF 2F  / .>.0>[email protected]@.../ 
000040 00                                               / .

また、フォーマット1の場合、「トラックデータその1」 (Conductor Track)には演奏以外のデータ (テンポ等の各種メタイベントSysExイベント) を格納し、実際の演奏データは「トラックその2」以降に格納するのが一般的。

以下、ヘッダ以下の具体的なデータ構造を記す。

ヘッダについて

説明 "MThd" 実データの大きさ (現状、必ず6) フォーマット (0000/0001/0002) トラック数 (2byte数値) 時間単位 (デルタタイムの意味の指定)
データ例 4D 54 68 64 00 00 00 06 00 00 00 02 00 30

ヘッダにおける、実データの大きさについて

ヘッダに含まれる実データの大きさを4byteのbig endianで格納する。 ヘッダでの実データの大きさは、 フォーマットに2byte、トラック数に2byte、時間単位で2byteなので、合計6byte。 結局、ここには (SMF1.0では) 常に 00 00 00 06 が格納されることになる。

フォーマットについて

現在、0, 1, 2の3種類が定義されている。それぞれフォーマット0, フォーマット1, フォーマット2と呼ばれる。

  • フォーマット0
    1本のトラックに全チャンネルの情報を押し込める形式。 トラックが1つしかないため、対応プレーヤの制作が容易。 またプレーヤ・シーケンサ間の互換性も保ち易くなるため、 市販のSMFデータは大抵がこの形式。

  • フォーマット1
    同期演奏される複数のトラックで構成される。 大抵はシーケンサのいちトラックに対して SMFデータのいちトラックが対応するようになる。 つまり、一般的に「トラック」 と呼ばれるものが順番にバイナリ化されて concatenate (結合) されたフォーマットと考えればよい。 そのため、 トラック構成を保存しつつSMF化したい場合はこの形式が用いられる。 通常はこのフォーマット1を用いればよいと思う。

  • フォーマット2
    複数トラックで構成される。パターン情報を保存し、 パターンを切り替えつつ演奏させるなんて場合に用いる。 フォーマット1がトラックを垂直に並べ、 それぞれを同期させて演奏させるフォーマットであるとするなら、 フォーマット2はパターン情報を水平に並べ、 順番に演奏させるようなものといえるだろう。
    ただし筆者はフォーマット2に対応したシーケンサソフトを知らない。 通常フォーマット0か1だけを扱っていても特に問題となることはない (・・・と、思う)。

時間単位について (デルタタイムの意味の指定)

  • 正数である場合 (MSB(第15bit目)が0の場合; こちらが一般的)
    デルタタイムを、小節・拍等を基準として表現する。 この場合、このフィールドには4分音符あたりの分解能が入る。 例えば、全音符の分解能が1920だったなら、 ここに入るデータは 1920÷4 = 480 = 0x01E0 となる。

  • 負数である場合 (MSB(第15bit目)が1の場合)
    デルタタイムを、実時間のタイムコードで表現する。上位1バイトに「SMPTEフォーマットの負数」が、下位1バイトに「フレームあたりの分解能」が入る。
    (以下は MIDI 1.0 規格書の4-4ページの内容を若干修正のうえ抜粋)

ビット15が1の場合、ファイル中のデルタ・タイムは、SMPTEやMIDIタイムコードと一致する方法で、秒の分数に相当する。
14から8までのビットは、4つの標準的なSMPTEとMIDIタイムコードのフォーマットに相応して、 -24, -25, -29, あるいは-30の4つの値のうちの一つ (-29は30ドロップ・フレームに相当) を含み、秒ごとのフレーム数を表す。
これらのマイナスの値は、2の補数形式でストアされる。
第2バイト(プラスでストアされる)は、フレーム内の分解能である。 典型的な値は4(MIDIタイムコード分解能), 8, 10, 80(ビット分解能), あるいは100であろう。
この方式は、タイムコード・ベースの正確な分解能を可能にするが、 25フレーム/秒でフレームごとの分解能を40に指定することによって、トラックをミリセカンド・ベースにすることも可能にする。
ファイル中のイベントが30フレーム・タイムコードのビット分解能でストアされている場合、 ここで指定する値は0xE250である。

トラックデータについて

データ例 4D 54 72 6B ** ** ** ** ** ** ~
説明 "MTrk" 実データのデータ長 実データが続く

実データ (正しくはMTrkイベントと呼ぶ) においては、「デルタタイム」 + 「イベント」の組がトラック末尾まで続く。

そして、「イベント」には、以下の3種類がある。

以下、それぞれの構成要素について記す。

デルタタイムとは

次に続くイベントまでの時間を表す時間情報のこと。ヘッダーブロックで時間単位の項目を正数で指定した場合 (=デルタタイムを小節・拍等を基準として表現する場合)、 デルタタイムは(直前のイベントから)次に続くイベントまでの時間差を表す。
例えば全音符の分解能が480だった場合、デルタタイム120というのは 「四分音符分の時間(の後)」を意味する。
同様に、 もしトラックの最初のイベントがトラックの先頭に発生する際のデルタタイムや、 2つのイベントが同時に発生するような場合(の2つ目のイベント)につくデルタタイムは、 0となる。

上記の実データの説明でも分かるように、 前のイベントと次のイベントの時間間隔を定義するために、 デルタタイムは常に必要である。

ちなみにヘッダーブロックで規定するように、 デルタタイムは拍子(に基づく時間情報)を意味するだけではなく、 SMPTEタイムと共にトラックをレコーディングする場合の"秒"の分数 (分解能) を意味することもあるが、後者の表現を用いることは稀であり、 無視して良いと思う。 (映画なんかでSEを挟む場合、こちらの表現を用いることがある、らしい)

可変長形式について

デルタタイムは、以下のような可変長形式で表される。

  • 1byte(8bit)の内、数値に7bit、 次のバイトもデータバイトが続くかどうかのフラグに1bit用いる。 フラグにはMSB(第7bit)を用いる。
  • 最大4バイト。つまり、実際に表現できる数値の最大値は228-1。 これだけあれば、通常の音楽に用いる時間表現には十分。 (デルタタイム 0FFFFFFF というのは、500BPM(拍/分)という速いテンポにおいて、 2 x 106個の96分音符の長さは3日間分にあたり、デルタタイムとしては十分な長さである。)
  • 以下具体例。左が数値(16進)で、右が可変長形式での表現(16進)。
00000000 → 00
00000040 → 40
0000007F → 7F
00000080 → 81 00
00002000 → C0 00
00003FFF → FF 7F
00004000 → 81 80 00
00100000 → C0 80 00
001FFFFF → FF FF 7F
00200000 → 81 80 80 00
08000000 → C0 80 80 00
0FFFFFFF → FF FF FF 7F

MIDIイベントとは

いわゆる演奏データのこと。 MIDIのチャンネルメッセージそのままと考えて差し支えない。

以下、具体例。

  • 8n kk vv (3byte) または 9n kk 00
    ノートオフ。チャンネル番号nで鳴っているノート番号kkの音を消音する。

  • 9n kk vv (3byte; vvは0以外)
    ノートオン。チャンネルnでノート番号kkの音をベロシティvvで発音する。 vvが0の場合は、ノートオフと同じ意味となる。

  • An kk vv (3byte)
    ポリフォニックキープレッシャー。 チャンネルnで発音中のノート番号kkの音に対し、ベロシティvvの プレッシャー情報を与える。 (今押さえている鍵盤を、新たにベロシティvvで押さえ直す)

  • Bn cc vv (3byte) (特定条件下で、4byteになる時があるので注意。詳細後述)
    コントロールチェンジ。 チャンネルnで、コントローラナンバーccに、値vvを送る。

  • Cn pp (2byte)
    プログラムチェンジ。 チャンネルnで、プログラム(音色)をppに変更する。

  • Dn vv (2byte)
    チャンネルプレッシャー。 チャンネルnに対し、プレッシャー情報vvを送信する。 (ポリフォニックキープレッシャーの、チャンネル内全音版)

  • En mm ll (3byte)
    ピッチベンド。チャンネルnに対し、ピッチベンド値llmmを送信する。このイベントのデータに限り、さりげなくlittle endianなので注意。

    ピッチベンド値llmmは、他のMIDIイベントの2byte目以降と同様に、

    • llとmmはどちらも7bitの値を採る
    • 正数のみ記載可能である

    ため、例えばピッチベンドが-8192,0,8191に対応する値llmmはそれぞれ 0x0000, 0x4000, 0x7F7F となる。(ピッチベンド値に 8192のゲタを履かせた形になる)

チャンネルメッセージとは

これらチャンネルメッセージは、 厳密にはチャンネルモードメッセージチャンネルボイスメッセージに分かれる。

チャンネルモードメッセージとは

コントロールチェンジ120~127を使用するメッセージ(0xBn 0x78 ~ 0x7F) のことで、要は音源制御系のメッセージである。 (All Sounds Off とか、Reset All Controllers など)

チャンネルボイスメッセージとは

上記以外のチャンネルメッセージのことで、要は発音系のメッセージである。

コントロールチェンジ Bn が4バイトになる場合

チャンネルモードメッセージの中で、 「MIDIモードをモード4にする」操作を行うときにのみ、 コントロールチェンジ(0xBn)の構成バイトが4byteになるので注意。 それ以外のコントロールチェンジはすべて3byte構成になる。

例: MIDIモードをモード4 (OMNI OFF / MONO) にする
  B1 7C 00    (MIDIチャンネル2を、まずOMNI OFFにする)
  B1 7E 00 04 (MONO; 4byte目に使用するチャンネル数(ここでは4つ)を記載する)

例: MIDIモードをモード2 (OMNI ON / MONO) にする
  B1 7D 00    (MIDIチャンネル2を、まずOMNI ONにする)
  B1 7E 00    (そして、MONO ONにする) 

ランニングステータスについて

直前のMIDIチャンネルメッセージのステータスバイトと同じステータスバイトが連続する場合、 2回目以降のステータスバイトを省略することができる。 これをランニングステータス(ルール)と呼ぶ。

ステータスバイトとは、 MIDIのチャンネルメッセージで書いたところの、 0x8nとか0x9nとか0xAnとかのバイト部のこと。

例えば、以下の実データ例において、 太字で表した部分はランニングステータスルールにより省略可能である。 (なお一番左のデータはデルタタイムである。念のため)

00 90 3C 7F : ノート番号3C(ド)をベロシティ7Fで発音
60 90 3C 00 : ノート番号3C(ド)を消音
00 90 3E 7F : ノート番号3E(レ)をベロシティ7Fで発音
60 90 3E 00 : ノート番号3E(レ)を消音
00 90 3C 7F : ノート番号3C(ド)をベロシティ7Fで発音
00 90 40 7F : ノート番号40(ミ)をベロシティ7Fで発音
00 90 43 7F : ノート番号43(ソ)をベロシティ7Fで発音
60 90 3C 00 : ノート番号3C(ド)を消音
00 90 40 00 : ノート番号40(ミ)を消音
00 90 43 00 : ノート番号43(ソ)を消音
00 C0 00 : 音色をピアノに変更

ランニングステータスが適用されているかどうかは、 ステータスバイトに相当する部分のMSB(第7bit)で判定できる。 (0なら適用、1なら未適用)

ノートオフの表現について

ノートオフを表現する場合、8n9nによる2つの表現方法があるが、 当然9nを用いた方がランニングステータスを適用しやすくなる。すなわち、SMFのデータを小さくすることができる。

一方で、ランニングステータスを用いると、 曲の途中から再生するようときに、省略されたステータスをプレーヤが復元できない、 といったこともあり得るので、SMFデータ作成者は、 ランニングステータスを用いない方法でもデータを作成できるようにしておくべき。 またこの場合、先ほどとは逆にノートオフには8nを用いた方が、 ノートオフの意味合いをより明示できるだろう。

SysExイベントとは

主にMIDIシステムエクスクルーシブメッセージを指定するのに用いられる。 SysExイベントには、F0で始まるものとF7で始まるものの2種類がある。

F0で始まる場合とF7で始まる場合とで、記述方法に若干の違いがあり、

F0で始まる場合、

  • 送信されるデータにはF0が含まれる (つまり、下表の w, y, z が送信される)
  • データ長にはF0の分を含めない (つまり、y + z 分の長さが収まる)
  • データの最後は必ずF7で終了する
  • 一般的には、エクスクルーシブメッセージを転送するのに用いる
  • 例: F0 05 aa bb cc dd F7 (これで、F0 aa bb cc dd F7 が転送される)

F7で始まる場合、

  • 送信されるデータにはF7が含まれない (つまり、下表の y の部分のみ送信される)
  • データ長にはF7の分を含めない (y の長さが収まる)
  • 一般的には、任意のデータを転送するのに用いる
  • 例: F7 04 aa bb cc dd (これで、aa bb cc dd が転送される)
w x y z
F0なSysEx F0 データ長 (= y + zの長さ) 転送データ (= w + y + z)
F7なSysEx F7 データ長 (= yの長さ) 転送データ(= y)


いずれの場合も、データ長は可変長形式で格納される。 (従って、SysExイベントにはランニングステータスは適用できない。 データ長のMSB(第7bit)が可変長形式適用で1になっていると、ランニングステータスとの区別が付かないため)

F7で始まるSysExイベントは、リアルタイムバイトやソングポインター、MIDIタイムコード、システムリセットなど、 通常の方法では送れないデータを直接送るための裏技として用いられるが、 シーケンサを変えた場合にこのデータがどう処理されるかは分からない等互換性維持の面で問題があるため、通常はF0で始まるエクスクルーシブメッセージ送信のみを用いるのが無難である。
(実際、余程のことがない限りF0のみを使用していても問題とはならないはず)

メタイベントとは

メタイベントは、シーケンサやSMFに便利な、演奏データに含まれない情報 (テンポ、曲タイトル等)を納めるのに用いるイベント。 基本的に、ステータスバイトFFで始まり、 次にイベントタイプを表すバイトが続き、 さらに可変長形式で格納されたデータ長が続き、 最後にデータ自体が続く (SysExイベントのF7の場合と似ている)。

もしデータがなければデータ長は0となる。SysExイベントと同様に、ランニングステータスの適用はできない。(データ構造上は適用できそうなものだが、仕様書で明確に適用不可と明記されている)

すべてのプログラムがすべてのメタイベントをサポートしなければならないということではない。

現在定義されているメタイベントは、具体的には以下のものがある。

ステータスバイト メタイベントタイプ データ長 (可変長形式) データ 意味 備考
FF 00 02 ssss シーケンス番号
FF 01 len text テキスト
FF 02 len text 著作権表示
FF 03 len text シーケンス名(曲タイトル)・トラック名
FF 04 len text 楽器名
FF 05 len text 歌詞
FF 06 len text マーカー
FF 07 len text キューポイント
FF 08 len text プログラム名 (音色名) RP-019
FF 09 len text デバイス名 (音源名) RP-019
FF 20 01 cc MIDIチャンネルプリフィックス v1.0
FF 21 01 pp ポート指定 SMF非標準
FF 2F 00 --- トラック終端
FF 51 03 tttttt テンポ設定
FF 54 05 hr mn se fr ff SMPTEオフセット
FF 58 04 nn dd cc bb 拍子の設定
FF 59 02 sf mi 調の設定
FF 7F len data シーケンサ特定メタイベント


  • FF 00 02 ssss : シーケンス番号
    フォーマット2で、パターンを識別するために用いる。 通常フォーマット0,1では用いない。

  • FF 01 len text : テキスト
    任意の長さ・内容のテキストを記述することができる。 ただし、著作権表示シーケンス名/トラック名楽器名歌詞マーカーキューポイントプログラム名デバイス名といった用途には、専用のメタイベントタイプ(0x02~0x09)が別途用意されているのでそちらを用いるべき。
    (余談: メタイベントタイプの 0x01~0x0Fは、 この手のテキストイベントのために予約されている)

    例えば、トラックのいちばん初めに、意図する音源名やオーケストレイション、 その他ユーザがそこに置きたいと思う情報を書いておくと良い。

    なお互換性のことを考えると、このイベント中のテキストは、 印刷可能なASCII文字を使用することが推奨されている。 (まあイマドキそんなことを気にする必要もないとは思いますが・・ 以下、 いちいち書きませんがテキスト関連のメタイベントすべてに同じことが当てはまります)

  • FF 02 len text : 著作権表示
    曲に関する著作権表示を記述する。 この表示には (C)の文字、 著作物発行年と、著作権所有者名とが含まれなければならない。 このイベントは第1トラック (Conductor Track)の最初のイベントとして、デルタタイム0で置かれなければならない。 また、ひとつのMIDIファイルに幾つかの楽曲がある時には、 すべての著作権表示をこのイベントに置いて、 かつそれがファイルの先頭に来るようにしなければならない。

  • FF 03 len text : シーケンス名(曲タイトル)・トラック名
    以下の場合には、シーケンス名(つまり、曲のタイトル)となる。

    その他の場合には、トラック名となる。

  • FF 04 len text : 楽器名
    そのトラックで用いられる楽器の種類を記述する。

  • FF 05 len text : 歌詞
    歌詞。 一般的には、(歌詞の)各音節が別々の歌詞イベントとして記述され、 そのイベントが始まるタイミングでシーケンサ等が歌詞を表示することになる。

  • FF 06 len text : マーカー
    リハーサル記号やセクション名のような、 シーケンスのその時点の名称を記述する。 ("Introduction" とか "A", "B" など)
    このメタイベントを使用する場合、置き場所としては 通常フォーマット0のトラック、 もしくはフォーマット1のファイルの第1トラック (Conductor Track)となる。
    (余談: 日本ファルコム社のYs2 EternalのMIDI曲では、 このメタイベントを 無限ループ用のマーカーとして用いているようです)

  • FF 07 len text : キューポイント
    曲データ中、このメタイベントの挿入されている位置で、 その曲以外の進行を記述するのに用いる。 (曲中の進行の記述には、マーカーを用いる)
    例えば「車が走り出す」「画面がフェードアウトする」など。

  • FF 08 len text : プログラム名 (音色名)
    直後に続く「プログラムチェンジ」と 「バンクチェンジ」で表している音色名を記載する。 (非GM音源だと結構使いでがあるでしょ?)
    このメタイベントはトラック中のどこにおいても構わないが、 プログラムチェンジやバンクセレクトと共に用いるべきである。

  • FF 09 len text : デバイス名 (音源名)
    このメタイベントがあるトラックが、 どのデバイスに配置されるのかということをテキストで表すものだが、 大抵は音源の名前を入れることになるだろう。 (区別できれば結局どんなテキストでも構わないわけですが)
    このメタイベントは、1トラックにつき1回だけ、 最初に音源にデータが送られる前に記述されるべき。 (データが送られた後で音源が決まるんじゃ、意味ないですしね)
    ちなみにこのメタイベントは、 あくまで16ch以上のデータを想定して考案されたものなので、 「音源名」に拘らず「ポート番号」や、 もっと極端な話「FM」「WAVE」とか、そういったのもアリ。

  • FF 20 01 cc : MIDIチャンネルプリフィックス
    MIDIイベント以外 (つまりSysExイベントメタイベント)のイベントに対して、 MIDIチャンネルを指定する際に用いる。
    例えば、 エクスクルーシブメッセージをMIDIチャンネルに対応させたり、楽器名をMIDIチャンネルに対応させたり、 フォーマット0を使用時にMIDIチャンネルとトラック名を対応させたりする場合に用いる。
    なおこのイベントによる設定は、再度このイベントが現れるか、 任意のMIDIイベントが現れるまで有効。

  • FF 21 01 pp : 出力ポート指定 (現在のSMFでは未定義)
    このメタイベントは現在のSMF Version 1.0では定義されていないが、 32ch対応のMIDI音源の台頭に伴い、 いわゆるデファクトスタンダードとして普及している模様。
    利用法として、現状以下の3パターンが確認されているが、 どのパターンで作成されているかはデータだけでは識別不能。

    • pp=0~3が、それぞれポート1~4に対応している場合
    • pp=0がミュート、1~4がそれぞれポート1~4に対応している場合
    • pp=0がポート1、pp=1がポート2、pp=2がポート1&2両方、 pp=3がミュート

    日本国内(のフリーソフトの類)では基本的に 1. のパターンを想定しておけば問題ないと思われる。

    このイベントの効力は次に同イベントが出現するまで続く。 当然ながら初期状態(無指定時)ではポート1が選択されている。

    (余談: SMF ver1.0 specに完全に沿った形で32chなデータを表現する (つまり、このメタイベントを用いない)場合、以下の方法が考えられる。

    1. フォーマット2を使う
    2. 出力ポート毎に、個別のSMF(ファイル)を出力する
    3. テキストイベント等 メタイベント FF 09 (デバイス名) で、トラック毎に 出力ポート 出力デバイス名 を明記しておく

    ただし、1.~3.はいずれも演奏には当然「対応プレーヤ」が必要となる・・・

  • FF 2F 00 : トラック終端
    そのトラックの終端を表す。 これがあることによって トラックの正しい終結点が明確になり、 トラックが正確な長さを持つようになる。
    このイベントは仕様上省略不可となっているので注意。
    (余談: 逆に、メタイベントに関しては、 このメタイベントと次に出てくるテンポのメタイベントさえ実装すれば、 極端な話それ以外(のメタイベント)は実装上無視しても、 演奏自体は正しいものとなる)
    (蛇足: FF 2* 系は、基本的に「トラック全体に関わる設定項目」 を想定しているように思われます)

  • FF 51 03 tttttt : テンポ設定 (単位は μsec / MIDI 四分音符)
    このイベントでテンポ変更を指示する。
    tttttt (3byte) には、四分音符の長さをマイクロ秒 (μsec) で表したものを格納する。
    例えば、BPM=120 (1分あたり四分音符が120個)の場合、 四分音符の長さは 60 x 106 / 120 = 500,000 (μsec)。 これを16進にすると0x07A120。従って、メタイベントは

FF 51 03 07 A1 20

となる。

( 余談: 割算した結果が割り切れない場合、大抵のプレーヤは 「有効桁数以下切り捨て」として扱っている → データ作成時も切り捨てていることを想定している、 ようです。 個人的には有効桁数以下0捨1入した方がより正確なテンポ値に近づくと考えますが、 自身のMMLコンパイラでそのように実装した結果プレーヤでの表示が1ずれたりしちゃって、 「何でテンポ値がずれるの?」 というFAQに追われる羽目となってしまいました(^^;)

  • FF 54 05 hr mn se fr ff : SMPTE オフセット
    トラックデータの先頭にこのメタイベントをおくことで、 トラックデータの演奏開始時刻を指定することができる。 時間は、MIDIタイムコードと全く同様に、 SMPTEフォーマットでエンコードされなければならない。

  • FF 58 04 nn dd cc bb : 拍子記号
    拍子記号は、4つの数字で表現される。
    nnとddは、そのまま拍子記号の分子と分母を表す。 ただし分母は2のマイナス乗で表現する。 つまり2=4分音符、3=8分音符、4=16分音符・・・となる。
    ccは、メトロノーム1拍あたりのMIDIクロック数を表している。 24MIDIクロックで4分音符を表す(後述)ため、 例えば4分音符ごとにメトロノームを鳴らす場合、 cc=0x18 (=24)となる。
    bbは、MIDI4分音符(24MIDIクロック)の中に入る32分音符の数を表す。 大抵bb=8となる。

例:
4/4拍子だと、FF 58 04 04 02 18 08
3/4拍子だと、FF 58 04 03 02 18 08
6/8拍子だと、FF 58 04 06 03 18 08

いずれも、cc=0x18(=24)、bb=8の場合。 (余程のことがない限り変更する必要はないと思いますが)

  • FF 59 02 sf mi : 調号
    調号を指定する。
    sfには、♯や♭の数が入る。正数なら♯の数、負数なら♭の数を表す。 ハ長調(つまり♯も♭もない)の場合は0。 (このあたりの詳細は、楽典などで確認のこと。)
    miは長調か短調かを表すフラグで、0なら長調、1なら短調。

  • FF 7F len data : シーケンサー特定メタイベント
    特定のシーケンサーのための特別な要求のためこのイベントタイプを用いる。 データバイトの最初の1バイトはメーカーIDである。 その後、独自フォーマットによるデータが続く。 このようなものであるため、 SMFデータの互換性を考えるならこの機能を使うことはないだろう。

SMFの実際

テンポ120で、四分音符でドレ、全音符でミを鳴らすSMF。

ランニングステータスが多用されていることに注意。 また、ランニングステータスの効果を高めるために、 ノートオフとして 9n kk 00 (ベロシティゼロでノートオン) を使用している。

000000 4D 54 68 64 00 00 00 06 00 01 00 02 00 30 4D 54  / MThd.........0MT 
000010 72 6B 00 00 00 0B 00 FF 51 03 07 A1 20 00 FF 2F  / rk......Q....../ 
000020 00 4D 54 72 6B 00 00 00 18 00 90 3C 7F 30 3C 00  / .MTrk.....・0<.. 
000030 00 3E 7F 30 3E 00 00 40 7F 81 49 40 00 00 FF 2F  / .>.0>[email protected]@.../ 
000040 00

これを具体的に読み解くと、以下のようになる。

ヘッダ
4D 54 68 64          "MThd" 
00 00 00 06          ブロック長(6) 
00 01                フォーマット(1) 
00 02                トラック数(2) 
00 30                四分音符の分解能(0x30=48)  


トラック1のデータ (Conductor Track)
4D 54 72 6B          "MTrk" 
00 00 00 0B          ブロック長(0x0B=11) 
00 FF 51 03 07 A1 20 テンポ(120)
00 FF 2F 00          トラックエンド  


トラック2のデータ (演奏トラックその1)
4D 54 72 6B          "MTrk"
00 00 00 18          ブロック長(0x18=24) 
00    90 3C 7F       ベロシティ127でノート3Cをノートオン 
30       3C 00       48tick後、ノート3Cをノートオフ 
00       3E 7F       直後に、ベロシティ127でノート3Eをノートオン 
30       3E 00       48tick後、ノート3Eをノートオフ 
00       40 7F       直後に、ベロシティ127でノート40をノートオン 
81 40    40 00       192tick後、ノート40をノートオフ 
00    FF 2F 00       トラックエンド

MIDI 1.0 規格書の4-12ページ以降に、より細かい実際のサンプルあり。

筆者が辿ったハマリ道

  • ランニングステータスは、 MIDIイベントのみに有効
    つまり、SysExイベント(F0, F7)や、 メタイベント(FF) では使えない。 後者の方は(FFの直後に来るのが00~7Fなので)ランニングステータスを適用できそうなものだが、 仕様書に適用不可と明記されている。 以下は MIDI 1.0 規格書の4-7ページより抜粋。

SysEx イベントとメタ・イベントは、その時点で有効になっているすべてのランニング・ステータスを無効にする。 ランニング・ステータスは、これらのメッセージには適用されないので、使用してはいけない。

  • SysExイベント(F0, F7)のデータ長は、可変長形式で格納する
    とはいえ実際には、大抵この手のデータは127バイト以下なので、 ただの1バイトデータだと思いこんでいても何とかなってしまうことが多い。

  • 曲中のテンポ情報(FF 51 03)は、Conductor Track(最初のMTrk)にすべてまとめる
    これをやっておかないと一部シーケンサ (MacのPerformer; バージョンは分からず)なんかで誤動作することがあるらしい。 以下は MIDI 1.0 規格書の4-5ページより抜粋。

フォーマット0のファイルでは、テンポはトラック中のいたるところにばらまかれており、 テンポ・マップ・リーダーは、その間にあるイベントを無視しなければならない。 フォーマット1では、テンポ・マップは第1トラックにストアされなければならない。

参考文献

E-mail: yyagiとvolatile をピリオドでつないで、末尾に @gmail.com をつけてください。

smfspec's People

Contributors

yyagi8864 avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.