ハトネコエ Web がくしゅうちょう

プログラミングとかAndroid

SMF ( Standard MIDI File ) Format1 のバイナリを読んでみた

曲を作るためにお世話になっている MIDI
そういえばこれってどういうふうにできているんだろう、ってふと気になりました。

それでこの記事
SMF(Standard MIDI File)フォーマット解説 | 技術的読み物 | FISH&BREAD
を読んでいましたら、「意外と簡単?!」という気がしてきましたので、解読してみることにしました。

DominoMIDI編集ソフト)で、バイオリンの音でドを全音符ぶん鳴らすMIDIファイルを作成し、レッツトライです。

f:id:nekonenene:20170225194904p:plain

上のように作ったMIDIファイルを FavBinEdit で開いてみました。

4D 54 68 64 00 00 00 06 00 01 00 02 03 C0 4D 54 
72 6B 00 00 00 17 00 FF 03 00 00 FF 51 03 06 8A 
1B 00 FF 58 04 04 02 18 08 00 FF 2F 00 4D 54 72 
6B 00 00 00 2B 00 FF 03 00 00 FF 21 01 00 00 B0 
79 00 00 B0 00 00 00 B0 20 00 00 C0 28 00 B0 07 
64 9E 00 90 3C 64 9E 00 80 3C 00 9E 00 FF 2F 00

ヘッダー

まずはヘッダーを見てみましょう。

4D 54 68 64 00 00 00 06 00 01 00 02 03 C0

の部分です。

開始宣言

4D 54 68 64
これを、ASCIIコード表に沿って直すと MThd
たぶん MIDI Type header を略したのかなあ、と思いますが
自信ないですので、情報をお持ちの方いらっしゃいましたらお教えください。

ともかく、 4D 54 68 64 の並びが来たら、「ここからヘッダーだよー」っていうサインになります。

ヘッダーの長さ

00 00 00 06
「ヘッダーの長さは 6 Byte だよー」と言っています。

つまり 1行目
4D 54 68 64 00 00 00 06 00 01 00 02 03 C0 4D 54

00 01 00 02 03 C0
までがヘッダーの部分ですね。

(補足)
1Byte = 8bit なので、6Byte = 48bit です。(2進数で表現したときに数字が48個並びます)
16進数1つあたりのデータ量は 4bit(2 ^ 4 = 16 なので)だから、
16進数の場合では12個並ぶところまでが 6Byte です。

MIDIフォーマット

00 01
MIDI format = 1 であることを示しています。
SMF ( Standard MIDI File ) のファイルフォーマットは Format0, Format1, Format2 しかないので、
必然的にここの値は 00 00 ~ 00 02 までしか存在しません。

Wikipedia先生いわく

ヘッダチャンクとトラックチャンク1つのみで構成されるフォーマット0、複数トラックを持つフォーマット1、 マルチシーケンスでシーケンスパターンを指定するフォーマット2の3種類である。このうちフォーマット2は、現在ほとんど使われていない。

とのこと。
( https://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%BF%E3%83%B3%E3%83%80%E3%83%BC%E3%83%89MIDI%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB )

私の感覚だと Format1 がもっとも一般的かなあと思います。
たいていのDTMソフトでのMIDI出力は Format1 で出力されていたかと思います。

トラック数

00 02
トラック数が 2個であることを示しています。

あとで説明する、 MTrk で始まるところが 2か所あると言い換えることができます。

解像度

03 C0
これを10進数に直すと 960 です。 解像度は960 です。

解像度というのは、1拍を何分割して表現できるか、ということです。
仮に解像度が2であれば、1拍(四分音符)を2分割するのが最大。
八分音符より短い音符を置くことはできませんし、音の長さはすべて八分音符単位。
「ほんのちょっとだけ鳴らす長さを短くしたい」などできません。

解像度が高いほど、「ほんのちょっとだけ鳴らす長さを短くしたい」とか「音の始まりを少しだけずらして和音を生演奏っぽく」などできるわけです。
480 とか 960 の設定値を見ることが多いですかね。

このファイルのヘッダー部分は以上です。

トラック(コンダクタートラック)

トラックはヘッダーに書いてある通り 2個ありますが、
まず 1個目の部分を見ていきましょう。

4D 54 
72 6B 00 00 00 17 00 FF 03 00 00 FF 51 03 06 8A 
1B 00 FF 58 04 04 02 18 08 00 FF 2F 00 

この部分です。

この1個目のトラックは「コンダクタートラック」と言う場合もあるもので、テンポや拍子の設定などをおこなっています。

開始宣言

4D 54 72 6B
これを、ASCIIコード表に沿って直すと MTrk
MIDI Track の略なんでしょうかね?

ともかく、4D 54 68 64 で「ここからトラックについての記述が始まるよー」というサインになります。

トラックの長さ

00 00 00 17
10進数に直すと 23 です。
「ここから 23 Byte 続くよー」と言ってくれています。

トラックのタイトル

00 FF 03 00

各トラックにはトラック名を付けることができます。
「Drums」などとタイトルを付けておくと、MIDIファイルを開いた人がなんのトラックなのかわかりやすいからです。
ただ、演奏には関係のない情報なので無くても構いません。

最初の「00」は「0Tick進んだところで信号を送るよ」って合図で(Tickについてはあとで説明します)、
「FF 03」は「トラック名を設定するよ」って合図で、その次にトラック名の長さを書きます。
この場合では「00」だから 0 Byte、ですのでそのあとに文字が続いていません。

この場合、トラック名を設定していないのだからバイナリを編集してこの部分を丸ごと削っても問題ありません。
ただし、そうするとトラックの長さも変わりますからそこの編集も忘れないようにしましょう。
(それを忘れると、再生できないMIDIファイルになってしまいます)

つまり、コンダクタートラックの
4D 54 72 6B 00 00 00 17 00 FF 03 00 00 FF 51 03 06 8A
の部分は、
4D 54 72 6B 00 00 00 13 00 FF 51 03 06 8A
と直してしまってかまいません。
こうすることで、無駄な 4Byte を削ることができます。(現代ではまったく意味をなさないレベルのファイル容量削減ですね!)

Tick

Tick は時間の単位です。
解像度が 960 の場合は、960Tick が1拍の長さです。

960Tick(1拍)を表すには、960は16進数で「3C0」だから「03 C0」とでもなるのかな、と思っていると、
そうではありません。「87 40」と表現します。

ポイントは、「960 ÷ 128 = 7 あまり 64」であることです。

Tickの値がどれだけ大きくてもいいように、Tickは何Byteでも表現できるようになっています。
しかし何Byteでも表現できるということは、どこかが終わりだとわからなければいけません。
ですので、「80」より小さい値を終わりと決めています。(つまり「7F」以下)
逆に「80」以上は「Tickを表現する値はこの次も続くんじゃ」と表していることになります。

この意味は2進数で考えるとわかりやすくて、16進数の「7F」は2進数では
01111111
16進数の「80」は2進数では
10000000
なので、勘のいい人は「先頭1ビットがフラグね」とわかるかと思います。

「7F」(10進数で言うところの127)より大きな値である128は以下のように書きます。
81 00
2進数に直すと
10000001 00000000
128 * 1 + 0 = 128
128進数で表現されていると言えるでしょう。

16800Tickならば、
81 83 20 と表します。
(128 ^ 2) * 1 + 128 * 3 + 32 = 16800

テンポの指定

00 FF 51 03 06 8A 1B

最初の「00」は「0Tick進んだところで信号を送るよ」って合図で、
つまり先ほどのトラック名の設定と同じタイミングで信号を送るよ、ってことになりますね。

「FF 51」は「テンポを設定するよ」って合図で、その次の「03」で「そのために 3Byte 使うよ」と伝えています。
そして値は「06 8A 1B」、10進数に直すと 428571 です。

428571、この大きな値は……? と思ったら、どうやらこれ、1拍あたりの秒数を指しているそうです。(単位はマイクロ秒)

BPM=120 のときの1拍の長さは 0.5 秒(500000マイクロ秒)であるから、
BPM=140 のときの1拍の長さは
0.5 * 120 ÷ 140 = 0.428571428… 秒
なるほど、たしかに 428571 マイクロ秒で間違いありませんね。

拍子の指定

00 FF 58 04 04 02 18 08
4分の4拍子であることの指定になります。

最初の「00」は「0Tick進んだところで信号を送るよ」って合図、
次の「FF 58」が「拍子を設定するよ」の合図、
次の「04」より、拍子の設定に 4Byte 使うことがわかります。

その4Byteが「04 02 18 08」の部分です。
「04 02 18 08」について順番に説明していきます。

拍子の指定1つ目

まずは最初の「04」。これは拍子の分子にあたります。
4分の3拍子なら「03」、4分の5拍子なら「05」が入ります。

拍子の指定2つ目

次の「02」。これは分母です。しかし、"4分の" なのに 2 っておかしいですよね?
これは 2 ^ (-2) (2のマイナス2乗)が4分の1であることがポイントです。
このマイナス2の2が、ここでの値なのです。

ですから、8分の6拍子でしたら、
ひとつめの部分は6拍子なので「06」、
ふたつめの部分は “8分の” なので、2のマイナス3乗が8分の1ですから「03」となります。

拍子の指定3つ目

そしてその次、「18 08」の部分。ここはなんのためにあるのか私よくわかっていません。

まず「18」の部分、10進数で言うと24であるここは、
1拍あたりのMIDIクロック数を示しています。*1

MIDIクロック」という言葉がわからなかったので調べたところ、
ここ( http://www.dipss.com/dipss/dtmkouza/whatsdtm/3-3.html )によると、

2台のシーケンサーMIDI接続されていて同時に演奏する場合、どちらもインターナルクロックで動いてしまうとバラバラの演奏になってしまいます。そこで、1台をインターナルクロックに、もう1台をエクスターナルクロックに設定してやります。すると、エクスターナルクロックに設定した方の機器はもう1方の機器が出すテンポに合わせて演奏するようになります。これが同期演奏です。

とあります。
つまるところ、複数のMIDIバイス間でのテンポ感を共有するものっぽいんですけど、よくわかってないです。
少なくとも、単純にMIDI再生するだけにおいてはどんな値でも良いっぽくて、ここを倍の「30」とかに変えても、なんの変わりもなく再生されます。

拍子の指定4つ目

最後の「08」、ここは
「4分音符1つと同じ長さになる32分音符の数」を指しているそうです。

「8以外になにか設定する値ある?!!」と思います。かなり不思議です。

トラックの終わり(End of Track)

00 FF 2F 00

最初の「00」は先ほどまでと同じく、0Tick経過後に、あとに続く信号を流すことを意味しています。
次に続く「FF 2F 00」が、「このトラックはここで終了します(End of Track)」という信号です。

この部分がなくても再生できるMIDIプレイヤーもありますが、
Windows Media Player などで再生できなくなりますので、しっかりと付けてあげましょう。

以上がコンダクタートラック部分の説明です。


長くなりましたので、残りの演奏トラック部分の説明は、また時間のできたときにおこないます。

【※追記 2017/03/06】
続き書きました!

広告を非表示にする