2013.3.7
前へ
次へ
ホームページトップへ戻る

復活!CP/M ワンボードマイコンでCP/Mを!
CP/MがTK−80互換のワンボードマイコンの上で復活します
ND80ZVとMYCPU80の上でCP/Mが走ります

[第338回]


●MIDIファイルを解析

前回わが愛する糟糠の妻の協力により作成いたしました四季の歌のMIDIファイルのサイズは1275バイトでした。
むむ。
そのくらいのサイズならば。
なんとか解析できるかも。

さっそくロードしてメモリダンプを実行いたしました。
こういう場合にND80ZV(ND80Z3.5)のND80Zモニタは実に便利です。
下はそのときのログファイルです。
あ。
これはE−80(仮称)ミニコンに移植しましたND80Zモニタで作業したときのログファイルです。

logfile nd80zlog\02181822.txt open

ND80ZVに接続しました
0001 0000 - z
1000 00C3 - 
*** nd80z3 basic ****
>/ld siki-b.mid,9000
loading SIKI-B.MID ...04fb(1275)bytes loaded,from 9000 to 94FA
>dm 9000,94fa
9000  4D 54 68 64 00 00 00 06-00 01 00 04 01 E0 4D 54  MThd..........MT
9010  72 6B 00 00 00 1A 00 FF-51 03 0A 2C 2A 00 FF 58  rk......Q..,*..X
9020  04 04 02 18 08 00 FF 59-02 01 00 83 60 FF 2F 00  .......Y....`./.
9030  4D 54 72 6B 00 00 01 F1-00 B0 00 00 00 B0 20 00  MTrk.....ー...ー .
9040  00 C0 00 00 B0 07 64 00-B0 0A 40 00 90 48 64 83  .タ..ー.d.ー.@..Hd.
9050  00 90 48 00 60 90 48 64-81 40 90 48 00 30 90 47  ..H.`.Hd.@.H.0.G
9060  64 81 40 90 47 00 30 90-45 64 86 00 90 45 00 81  d.@.G.0.Ed...E..
9070  40 90 47 64 83 00 90 47-00 60 90 47 64 81 40 90  @.Gd...G.`.Gd.@.
9080  47 00 30 90 45 64 81 40-90 45 00 30 90 43 64 86  G.0.Ed.@.E.0.Cd.
9090  00 90 43 00 81 40 90 42-64 81 40 90 42 00 30 90  ..C..@.Bd.@.B.0.
90A0  43 64 81 40 90 43 00 30-90 45 64 81 40 90 45 00  Cd.@.C.0.Ed.@.E.
90B0  30 90 47 64 81 40 90 47-00 30 90 45 64 83 00 90  0.Gd.@.G.0.Ed...
90C0  45 00 60 90 43 64 81 40-90 43 00 30 90 42 64 81  E.`.Cd.@.C.0.Bd.
90D0  40 90 42 00 30 90 40 64-8C 00 90 40 00 83 00 90  @.B.0.@d...@....
90E0  47 64 83 00 90 47 00 60-90 47 64 81 40 90 47 00  Gd...G.`.Gd.@.G.
90F0  30 90 45 64 81 40 90 45-00 30 90 43 64 81 40 90  0.Ed.@.E.0.Cd.@.
9100  43 00 30 90 45 64 81 40-90 45 00 30 90 43 64 81  C.0.Ed.@.E.0.Cd.
9110  40 90 43 00 30 90 42 64-81 40 90 42 00 30 90 40  @.C.0.Bd.@.B.0.@
9120  64 83 00 90 40 00 60 90-40 64 83 00 90 40 00 60  d...@.`.@d...@.`
9130  90 40 64 86 00 90 40 00-81 40 90 48 64 83 00 90  .@d...@..@.Hd...
9140  48 00 60 90 48 64 81 40-90 48 00 30 90 47 64 81  H.`.Hd.@.H.0.Gd.
9150  40 90 47 00 30 90 45 64-81 40 90 45 00 30 90 43  @.G.0.Ed.@.E.0.C
9160  64 81 40 90 43 00 30 90-45 64 81 40 90 45 00 30  d.@.C.0.Ed.@.E.0
9170  90 48 64 81 40 90 48 00-30 90 47 64 8C 00 90 47  .Hd.@.H.0.Gd...G
9180  00 83 00 90 48 64 83 00-90 48 00 60 90 48 64 81  ....Hd...H.`.Hd.
9190  40 90 48 00 30 90 47 64-81 40 90 47 00 30 90 45  @.H.0.Gd.@.G.0.E
91A0  64 83 00 90 45 00 60 90-47 64 81 40 90 47 00 30  d...E.`.Gd.@.G.0
91B0  90 48 64 81 40 90 48 00-30 90 47 64 83 00 90 47  .Hd.@.H.0.Gd...G
91C0  00 60 90 47 64 81 40 90-47 00 30 90 43 64 81 40  .`.Gd.@.G.0.Cd.@
91D0  90 43 00 30 90 40 64 83-00 90 40 00 60 90 40 64  .C.0.@d...@.`.@d
91E0  83 00 90 40 00 60 90 42-64 83 00 90 42 00 60 90  ...@.`.Bd...B.`.
91F0  47 64 83 00 90 47 00 60-90 45 64 81 40 90 45 00  Gd...G.`.Ed.@.E.
9200  30 90 43 64 81 40 90 43-00 30 90 42 64 81 40 90  0.Cd.@.C.0.Bd.@.
9210  42 00 30 90 43 64 81 40-90 43 00 30 90 40 64 8C  B.0.Cd.@.C.0.@d.
9220  00 90 40 00 83 60 FF 2F-00 4D 54 72 6B 00 00 02  ..@..`./.MTrk...
9230  AA 00 B0 00 00 00 B0 20-00 00 C0 00 00 B0 07 64  ェ.ー...ー ..タ..ー.d
9240  00 B0 0A 40 00 90 34 64-81 40 90 34 00 30 90 39  .ー.@..4d.@.4.0.9
9250  64 81 40 90 39 00 30 90-3C 64 83 00 90 3C 00 60  d.@.9.0.<d...<.`
9260  90 39 64 81 40 90 39 00-30 90 34 64 81 40 90 34  .9d.@.9.0.4d.@.4
9270  00 30 90 39 64 81 40 90-39 00 30 90 3C 64 81 40  .0.9d.@.9.0.<d.@
9280  90 3C 00 30 90 37 64 81-40 90 37 00 30 90 34 64  .<.0.7d.@.7.0.4d
9290  81 40 90 34 00 30 90 37-64 83 00 90 37 00 60 90  .@.4.0.7d...7.`.
92A0  34 64 81 40 90 34 00 30-90 2F 64 81 40 90 2F 00  4d.@.4.0./d.@./.
92B0  30 90 34 64 81 40 90 34-00 30 90 37 64 81 40 90  0.4d.@.4.0.7d.@.
92C0  37 00 30 90 33 64 83 00-90 33 00 60 90 36 64 83  7.0.3d...3.`.6d.
92D0  00 90 36 00 60 90 39 64-83 00 90 39 00 60 90 3B  ..6.`.9d...9.`.;
92E0  64 83 00 90 3B 00 60 90-34 64 81 40 90 34 00 30  d...;.`.4d.@.4.0
92F0  90 37 64 81 40 90 37 00-30 90 3B 64 81 40 90 3B  .7d.@.7.0.;d.@.;
9300  00 30 90 37 64 81 40 90-37 00 30 90 34 64 86 00  .0.7d.@.7.0.4d..
9310  90 34 00 81 40 90 34 64-81 40 90 34 00 30 90 37  .4..@.4d.@.4.0.7
9320  64 81 40 90 37 00 30 90-3B 64 83 00 90 3B 00 60  d.@.7.0.;d...;.`
9330  90 34 64 81 40 90 34 00-30 90 37 64 81 40 90 37  .4d.@.4.0.7d.@.7
9340  00 30 90 3B 64 83 00 90-3B 00 60 90 34 64 83 00  .0.;d...;.`.4d..
9350  90 34 00 60 90 37 64 81-40 90 37 00 30 90 3B 64  .4.`.7d.@.7.0.;d
9360  81 40 90 3B 00 30 90 34-64 81 40 90 34 00 30 90  .@.;.0.4d.@.4.0.
9370  37 64 81 40 90 37 00 30-90 3B 64 83 00 90 3B 00  7d.@.7.0.;d...;.
9380  60 90 34 64 81 40 90 34-00 30 90 39 64 81 40 90  `.4d.@.4.0.9d.@.
9390  39 00 30 90 3C 64 83 00-90 3C 00 60 90 34 64 81  9.0.<d...<.`.4d.
93A0  40 90 34 00 30 90 39 64-81 40 90 39 00 30 90 3C  @.4.0.9d.@.9.0.<
93B0  64 83 00 90 3C 00 60 90-34 64 83 00 90 34 00 60  d...<.`.4d...4.`
93C0  90 3B 64 81 40 90 3B 00-30 90 39 64 81 40 90 39  .;d.@.;.0.9d.@.9
93D0  00 30 90 37 64 81 40 90-37 00 30 90 36 64 81 40  .0.7d.@.7.0.6d.@
93E0  90 36 00 30 90 33 64 81-40 90 33 00 30 90 34 64  .6.0.3d.@.3.0.4d
93F0  81 40 90 34 00 30 90 39-64 81 40 90 39 00 30 90  .@.4.0.9d.@.9.0.
9400  34 64 81 40 90 34 00 30-90 39 64 83 00 90 39 00  4d.@.4.0.9d...9.
9410  60 90 3C 64 81 40 90 3C-00 30 90 39 64 81 40 90  `.<d.@.<.0.9d.@.
9420  39 00 30 90 34 64 81 40-90 34 00 30 90 39 64 81  9.0.4d.@.4.0.9d.
9430  40 90 39 00 30 90 3B 64-81 40 90 3B 00 30 90 39  @.9.0.;d.@.;.0.9
9440  64 81 40 90 39 00 30 90-37 64 81 40 90 37 00 30  d.@.9.0.7d.@.7.0
9450  90 36 64 81 40 90 36 00-30 90 34 64 81 40 90 34  .6d.@.6.0.4d.@.4
9460  00 30 90 33 64 81 40 90-33 00 30 90 34 64 81 40  .0.3d.@.3.0.4d.@
9470  90 34 00 30 90 37 64 81-40 90 37 00 30 90 33 64  .4.0.7d.@.7.0.3d
9480  81 40 90 33 00 30 90 34-64 81 40 90 34 00 30 90  .@.3.0.4d.@.4.0.
9490  36 64 81 40 90 36 00 30-90 37 64 81 40 90 37 00  6d.@.6.0.7d.@.7.
94A0  30 90 39 64 83 00 90 39-00 60 90 3B 64 83 00 90  0.9d...9.`.;d...
94B0  3B 00 60 90 34 64 83 00-90 34 00 60 90 37 64 81  ;.`.4d...4.`.7d.
94C0  40 90 37 00 30 90 36 64-81 40 90 36 00 30 90 34  @.7.0.6d.@.6.0.4
94D0  64 86 00 90 34 00 83 60-FF 2F 00 4D 54 72 6B 00  d...4..`./.MTrk.
94E0  00 00 18 00 B9 00 00 00-B9 20 00 00 C9 00 00 B9  ....ケ...ケ ..ノ..ケ
94F0  07 64 00 B9 0A 40 83 60-FF 2F 00 00 31 00 31 00  .d.ケ.@.`./..1.1.
>0000 00C3 - 
リモート接続を終了しました
logfile closed at Mon Feb 18 18:23:30 2013

下は、上のログファイルをプリントアウトして、そこに解析した内容を書き込んで作成したメモです。


使用済みのA4用紙の裏にプリントアウトしています。
資源保護の精神です。

こうやって実物で確認をしてみますとMIDIファイルは割と分かりやすい構造をしています。
少し分かってきますと、このような16進数ダンプをそのまま見て、おおよそのことは解読しながら先へ進んでいくこともできそうです。

●MIDIファイルの構造

MIDIファイルはトラックごとに区切られた構造をしています。

●ヘッダートラック

一番最初にはヘッダートラックがあります。

9000  4D 54 68 64 00 00 00 06-00 01 00 04 01 E0 4D 54  MThd..........MT

ヘッダートラックの先頭にはMThdという文字が書かれています。

その文字に続く4バイトはトラックに含まれるデータのバイト数(この4バイトの次のデータからトラックの終わりまで)です。
MIDIファイルで扱う数値は16進数ですが、2桁以上を格納する場合に、Z80や8080などのように下位バイトを先に置く(リトルエンディアン)のではなく、それとは逆に普通に読むのと同じ順番に、つまり上位バイトから先に格納します(ビッグエンディアン)。

私は昔リトルエンディアン(little endian)のことをリトルインディアンだと思っていました(汗)。
one little,two little,three little indians…。
ケツから先に進むのは小さいインディアンの作法かと…。

ご存知の方も多いかと思いますが、ネーミングのもとはガリバー旅行記なのだそうですね。
玉子を大きいほうの端(big end)から割るグループと小さいほうの端(little end)から割るグループとの対立をえがいたエピーソードに由来するのだそうであります。
ガリバー旅行記は子供のころに読んだきりですが、そんなお話は知りませんでした。
でも私はだいたい真ん中あたりから割りますけれど。

トラックのサイズは4桁の固定長ですが、これとは別のところで可変長の数も出てきます。
可変長の数の扱いはちょいとややこしい方法でおこないます。
それは後で説明することにいたしまして、ここでのデータ数は6になっています。
ヘッダートラックのデータサイズは常に6バイト(00 00 00 06)のようですが、違う場合もあるのかも知れません。

●フォーマット

その後ろの、つまりデータの先頭の2バイトがファイル形式(フォーマット)です。
フォーマット0〜フォーマット2があるようですが、普通はフォーマット0(00 00)かフォーマット1(00 01)のようです。

フォーマット1はヘッダートラックのほかに2つ以上のトラックがあります。
フォーマット1の最初のデータトラックは演奏に必要なデータとかファイル識別のための情報(速さとか曲名とか)を入れるためのもので、それに続くトラックが楽譜のパートに相当するのだと思います。

フォーマット0はヘッダートラックのほかにはデータトラックが1つあるだけというシンプルな形式です。
シンプルですけれど、1つのトラックにいわば指揮者のための情報と演奏者のための情報が一緒につまっているだけ、その中身は複雑になります。

フォーマット情報2バイトの後ろには、ヘッダートラック以外のデータトラックの数が入ります。
ここでは4(00 04)になっています。
ということは演奏者(MIDI音源)に送るためのデータトラックは4−1=3ということになります。

しかし、前回お見せしました四季の歌の楽譜の画像を見ていただきますとわかりますが、2つのパートしかありません。
画像ではTr.1とTr.2になっています。
またBASICで1バイトずつに区切ってゆっくりと送信した結果も先にトラック1が演奏され、続いてトラック2が演奏されましたが、トラック3は演奏されませんでした(そのようでした)。
その疑問の答えは後になってわかりました。
最後のトラック(アドレス94DBH〜)の中身は空っぽでした。
楽譜作成プログラムの設定で、パッカーション用のトラックにチェックが入っていたため、(実際には使われていない)空のトラックが作成されてしまったのでした。

●分解能(BPQN)

さて、最後の2バイトは4分音符あたりの分解能です。
01 E0
です。

4分音符の長さを、この曲データで扱う時間の最少単位(ティック)であらわしたものです。
ふつうはこのデータのように01E0H(480)であることが多いようです。
ちなみにMIDIの分解能はBPQN(bit per quarter note)というようです。
しかし、これだけでは音の長さは決まりません。
音の長さは、このBPQNと、次のトラックにあるテンポの数値によって決まります。

●最初のデータトラック

次は最初のデータトラックです。

9010  72 6B 00 00 00 1A 00 FF-51 03 0A 2C 2A 00 FF 58  rk......Q..,*..X
9020  04 04 02 18 08 00 FF 59-02 01 00 83 60 FF 2F 00  .......Y....`./.

フォーマット1の最初のデータトラックには、演奏のために必要な情報や、曲名などが入っています。
このトラックのデータはMIDI音源には送られません。

データトラックの先頭にはMTrkの4文字があります。
その次の4バイトはデータのバイト数です。
ここでは00 00 00 1A(26バイト)になっています。
その後ろから26バイトがデータです。

●データの表記ルール

MIDIのデータ(MIDIイベントとかステータスバイトとかと言うようです)は最初に時間を示す数値(1バイト以上の可変長)があって、その後ろに3バイトのコードがあります。
ただし、最初のトラックに多く書かれる特別のMIDIイベント(メタイベント)などは、それとはまた別のルールによります。

メタイベントは、第一バイトがFFで、次にイベントの種類を示すバイトがあり、その次にデータ数を示す数値(可変長)が置かれ、そのあとにそのイベントのデータが続きます。

●メタイベント FF 51 03(テンポを定義)

最初のデータは
00 FF 51 03 0A 2C 2A
です。

時間データは00ですから、この前に送られたデータと時間差を置かずに実行されます(されるべき)。
FFは音源には送らない特殊コード(メタイベント)であることを示します(その後ろにさらに1バイトのコードが続きます)。

FF 51 03は速度(テンポ)を示します。
最後の03はこの後ろに続く速度データが3バイトあることを示しますが、ここは常に3バイトのようです。

速度のデータは0A 2C 2Aになっています。
4分音符の長さをμsの単位で示したものです。

おお。
ここでやっと速度の情報が出てきました。
0A2C2AH=666666μsです。
えらく端数ですが、それにはわけがあります。
ふつう曲の速さは1分間当たりいくつの4分音符を演奏するかで示します。
上の数は4分音符1個あたりの時間ですから、秒の単位に直した上でその逆数を60倍すると1分間当たりの4分音符の数が求まります。
1/0.666666×60≒90です。

ところでさきほどBPQN=480という数が出てきました。
4分音符の長さを、この曲の最小の時間単位(ティック)で示したものでした。
そして、ここでその4分音符の演奏時間が666666μsである、という情報が得られました。
ということは、この曲の1ティックは、666666/480≒1389μsということになります。

MIDIの時間の長さは、このティックを単位にして示されます。
たとえばある音色のある高さの音を出して(ノート・オン)、それから240ティック後に音を止める(ノート・オフ)というように示します。
この場合にはその音は8分音符の長さで、それは1.389×240=333.36msという時間になります。

●メタイベント FF 58 04(拍子を定義)

次のデータは
00 FF 58 04 04 02 18 08
です。

先頭の時間データは00ですから、これも即座に実行されることを示しています。

FF 58 04
は、それに続く4バイトで拍子を示します。
ここは多分演奏には関係しないと思い、とりあえず無視しています(拍子による強弱等は音データに付加されていると勝手に期待していますが、確認はしていません)。

●メタイベント FF 59 02(調号を定義)

その次のデータは
00 FF 59 02 01 00
です。

これも先頭の時間データは00ですから、即座に実行されることを示しています。

FF 59 02
は、それに続く2バイトで調号を示します。
これも演奏には関係しないと判断して無視しています。

●メタイベント FF 2F 00(トラックの終わりを示す)

その次のデータは
83 60 FF 2F 00
です。

FF 2F 00
は、トラックの終わりを示します。

データトラックの終わりは必ずこのコードで終ります(ヘッダートラックにはありません)。
理屈の上では、トラックの先頭にデータバイト数がありますから、このコードは不要なはずなのですが、それでもこのコードは省略不可のようです。
このコードの前にある時間データには、可変長の表現が使われています。

●可変長の表記法

MIDIでは固定長であることが明確な場合以外は数値を可変長で示します。
たとえばコード(イベント)の前に置かれる時間データの最小値は00でこれは1バイトで済みます。

今回はそこが83 60になっています。
これは通常の16進数表記の8360Hではありません。
MIDIの可変長表記では1バイトの数の最上位ビットは、その次に下位バイトが続く(1)か、続かない(0)かの識別に使われています。

1バイトで表現できる最大の数は7F(0111111)です。
80H(10000000)は2バイトを使って、81 00(10000001 00000000)のようにあらわします。
実質的に各バイトは下位7ビットで数を示しますから、
1 0000001 0 0000000 →識別子を外すと00000000 10000000になる
↑識別子    ↑識別子
のように考えます。

ですから83 60は
1 0000011 0 1100000
なので識別子を外すと
00000001 11100000 つまり1E0Hになります。

1E0Hは、4分音符1個分の時間です。
なぜこれだけの時間を置いてからトラックの終わりにするのかは意味不明です。

以上で最初のトラックの解析は終了しました。
このトラックの情報はSC−88には送りません。
SC−88に送るデータは次のトラックからのデータになります。

●2番目と3番目のデータトラック

2番目と3番目のトラックをざっと眺めましたところ、MIDI音源に送る演奏データそのものの羅列のように見えました。
90 XX XX(ノートオン、音を出す)、90 XX 00(音を止める、実質的にノートオフ)だけが並んでいるようです。

むむむむむ。
これだけの情報を眺めまして、MIDI演奏プログラムをでっちあげてしまいました。

前回はシーケンサープログラムと書きましたが、MIDIシーケンサーといいますのはMIDIファイル作成ソフトのことを言うようです。
ですので私が作りたかったのは、MIDI演奏プログラムということになるようです。
あ。
1ティックが1.389msということになりますと、とてもBASICでは無理です。
オールマシン語で、Z8S180の内蔵タイマーを使った割り込みを利用いたしました。

最初は1日で仕上げるつもりでありましたが、さすがにそれは無理でありまして、デバッグを含めて3日を要しました。
とりあえず出来上がりましたのは、2月26日のことでありました。

E−80(仮称)ミニコンにロードした四季の歌のMIDIファイル(siki−b.mid)を、出来上がったばかりのMIDI演奏プログラムで実行しましたところ、SC−88に接続したアンプ−スピーカーから四季の歌の曲が流れ出しました。
おお、おお、おお。
感激でありました。

が。
そうなりますと、欲が出てまいります。

そう。
あのバッハ。
bwv140.midもぜひとも演奏してみたい。

ワンボードマイコンでCP/Mを![第338回]
2013.3.7upload

前へ
次へ
ホームページトップへ戻る