DXM 形式についての解説


feelsound では、DXMという、SMF (Standard MIDI File) に似た形式のファイルを再生することができます。 ファイル形式は非公開ですが、独自に解析しました。 内容については一切無保証です。

用語についても創作したものがほとんどです。 (あるのかどうかは知りませんが)他の解説ではおそらく違う言葉を用いていると思われます。 ほとんどの説明は、ある程度SMFファイルの構成を理解していることが前提になっています。 SMFに関することがらについては、参考資料をみて下さい。

この説明だけで理解するのは、困難だと思いますが、実際の DXMファイルを見てみるとそれほど複雑な形式ではありませんので、自分で解析を行う際の参考程度に御覧下さい。

DXMファイルを解析しよう!というページを見つけました。両方で補完するとより完全に近付きそうです。順次こちらにも反映させて行こうと思います。

目次

ファイル形式の概説

1. 全体構成

DXMファイルは、2つのブロックに分割されています。 1番目のブロックはDXMヘッダ、2番目のブロックはDXMデータです。 DXMデータブロックに記述されるデータの項目、記述場所、及びデータ長は、すべてDXMヘッダブロック中で規定されます。

DXMヘッダDXMデータ

2. DXMヘッダの構成

まず、始めの4 byte は、ファイル形式を示すアスキー文字列4文字で、常に "MCDF" です。 それ以降のDXMヘッダは、各項目毎に10bytesずつ使用され、それが項目の数だけ並ぶという構成になっています。 全部で31項目ありますので、DXMヘッダの長さは全部で 4 + 10 × 31 = 314 bytes になります。

"MCDF"
(4 bytes)
項目1
(10 bytes)
項目2
(10 bytes)
・・・項目31
(10 bytes)
DXMヘッダの全体構成

それぞれの項目は3つの要素からなっており、それぞれ 1番目が項目ID、2番目がその項目のデータ開始アドレス、3番目がデータ長です。 データ開始アドレスは、DXMファイルの先頭から見たアドレスで、データ長とともに byte単位で記述します。

項目ID
(2 bytes)
データ開始アドレス
(4 bytes)
データ長
(4 bytes)
個々の項目の中身

どのような項目があるかについては、項目一覧を見て下さい。

3. DXMデータの構成

DXMデータについては、DXMファイルの各項目毎にDXMヘッダで規定されたデータ開始アドレスからデータ長分を使って、各項目毎に必要な情報を記述します。 個別の内容については、項目一覧を見て下さい。 ただし、SMFデータを記述する項目については、別途次節で説明します。

4. SMF部の通常との違い

DXMファイル中には、基本的にはSMFファイルをそのまま埋め込みますが、一部分修正を行う必要があります。

例題

次のような、SMF Format 0 のファイルを、DXMに変換してみます。 見方は、1項目がアドレスで2項目以降がデータで、すべて16進数です。 # や、データの後ろの : 以降はコメントです。

----beegin sample.mid----
# ヘッダチャンク
0000:  4D 54 68 64			: MThd
0004:  00 00 00 06
0008:  00 00
000A:  00 01
000C:  00 30				: 時間単位 (48)

# データチャンク
000E:  4D 54 72 6B			: MTrk
0012:  00 00 00 28			: データ長 (40 bytes)
0016:  00 FF 03 0A  73 61 6D 70 6C 65 20 73 6D 66
0024:  00 FF 02 00
0028:  00 FF 51 03  07 A1 20
002F:  00 C0 01
0032:  00 90 3C 64
0036:  2F 90 3C 00
003A:  00 FF 2F 00
----end sample.mid----

時間単位が48の、SMFファイルです。 以下は、これをDXM形式に変換したものです。

----begin sample.dxm----
# DXM ヘッダ
0000:  4D 43 44 46			: "MCDF"
0004:  00 00  00 00 01 3A  00 00 00 04
000E:  00 01  00 00 00 00  00 00 00 00
0018:  00 10  00 00 01 3E  00 00 00 04
0022:  00 11  00 00 01 42  00 00 00 0A
002C:  00 20  00 00 00 00  00 00 00 00
0036:  00 21  00 00 00 00  00 00 00 00
0040:  00 30  00 00 00 00  00 00 00 00
004A:  00 31  00 00 00 00  00 00 00 00
0054:  02 00  00 00 00 00  00 00 00 00
005E:  02 01  00 00 00 00  00 00 00 00
0068:  02 02  00 00 01 4C  00 00 00 02
0072:  02 03  00 00 01 4E  00 00 00 04
007C:  02 04  00 00 01 52  00 00 00 01
0086:  02 05  00 00 01 53  00 00 00 04
0090:  02 40  00 00 01 76  00 00 00 2B
009A:  02 80  00 00 01 57  00 00 00 04
00A4:  02 81  00 00 01 5B  00 00 00 04
00AE:  02 82  00 00 01 5F  00 00 00 04
00B8:  02 83  00 00 01 63  00 00 00 08
00C2:  02 84  00 00 00 00  00 00 00 00
00CC:  02 85  00 00 01 6B  00 00 00 01
00D6:  02 86  00 00 00 00  00 00 00 00
00E0:  02 C0  00 00 01 6C  00 00 00 0A	: タイトル
00EA:  02 C1  00 00 00 00  00 00 00 00	: アーティスト (ここでは省略)
00F4:  02 C2  00 00 00 00  00 00 00 00
00FE:  02 C3  00 00 00 00  00 00 00 00
0108:  02 C4  00 00 00 00  00 00 00 00
0112:  02 C5  00 00 00 00  00 00 00 00
011C:  02 C6  00 00 00 00  00 00 00 00
0126:  02 C7  00 00 00 00  00 00 00 00
0130:  FF FF  00 00 00 00  00 00 00 00

# DXM データ
013A:  30 31 2E 30
013E:  00 00 FF FF
0142:  00 10 00 00 00 00 00 00 FF FF
014C:  00 78				: テンポ (120)
014E:  01 02 03 04
0152:  00
0153:  01 00 00 00
0157:  00 00 01 E0
015B:  00 00 00 2B			: SMFデータサイズ (43 bytes)
015F:  32 38 35 36
0163:  00 07 D2 01 11 15 19 21		: 2002年1月17日21時25分33秒
016B:  00
016C:  73 61 6D 70 6C 65 20 73 6D 66	: "sample smf"
# DXM データ中に埋め込まれた SMF データ
0176:  43 54 68 64			: "CThd"
017A:  00 00 00 06
017E:  00 00
0180:  00 01
0182:  00 18				: 時間単位 (24)
0184:  43 54 72 6B			: "CTrk"
0188:  00 00 00 15			: データ長 (21 bytes)
018C:  00 FF 51 03  07 A1 20
0193:  00 C0 01
0196:  00 90 3C 64
019A:  17    3C 00			: ランニングステータス利用
019D:  00 FF 2F 00
----end sample.dxm----

まず、2行目 (4 byte 目から) を見て下さい。

0004:  00 00  00 00 01 3A  00 00 00 04

となっています。 これは、項目 0000 のデータは、 アドレス013A から 4 bytes 分だということを示しています。 ということで、アドレス013A を見て下さい。

013A:  30 31 2E 30

これが、項目 0000 のデータの内容だということが分かります。 DXMヘッダとDXMデータの関係は、以下これと全く同じことが繰り返されています。 項目のデータを省略する場合は、データ開始アドレスとして、0000を指定します。 SMFデータも、これと全く同じ形で、埋め込まれています。 具体的には、項目 0240 がSMFデータですので、ヘッダを見ると、

0090:  02 40  00 00 01 76  00 00 00 2B

となっており、アドレス0176 から 43 bytes が SMF データだと分かります。

DXMファイル全体の構成が分かったところで、 SMF データの変更された部分を見てみる事にします。 まず一見して分かるのが、MThd, MTrk がそれぞれ CThd, CTrk になっている部分です。 これは単純に置き換えるだけです。 また、いくつかのメタイベントが削除されていたり、ランニングステータスを利用していたりするため、SMF部の長さが短くなっています。

ここまでは、単純に置き換えたり一部削除したりするだけでしたが、時間単位の変更については、適切に処理を行う必要があります。 この例の場合、時間単位が48から24に変更されていますので、デルタタイムをすべて半分にする必要があります。 元のファイルでは、

0036:  2F 90 3C 00

このように47だったものが、DXMファイルでは、

019A:  17    3C 00

23 と、半分になっているのがわかります。 このように、すべてのデルタタイムを処理を行い、DXMファイルに埋め込みます。

実装例 : feelsound converter - mid2dxm.rb

以上の解析結果を元に、feelsound converter を Ruby で実装しました。 ここに書いた以上の know-how については、こちらを御覧下さい。

ただし、まだ解析した結果と照らし合わせて不満の残る点がいくつかあります。

また、DXMにはSMFの内容がほぼそのまま埋め込まれているだけですので、DXMをSMFに変換する機能もつけました。シンボリックリンクなどにより、``dxm2mid''という名前で呼び出すと、DXM形式のファイルをSMF Format 0に変換することができます。

参考資料

DXMファイルの解析を行うにあたって、以下のページを参考にしました。

また、DDI Pocket の feelsound converter に付属するマニュアルも、仕様を探る上で貴重な資料です。

DXMヘッダ項目一覧

説明の無い項目については、詳しい内容については不明です。 ただし、私が DDI Pocket の feelsound converter を使って変換した中では値が変化しなかった項目ですので、意味は分からなくても実用上問題は無いと思います。

項目IDデータ長 (bytes)データ内容説明
00 00430 31 2e 30
00 0100
00 104ff ff
00 111000 10 00 00 00 00 00 00 ff ff
00 2000
00 2100
00 3000
00 3100
02 0000
02 0100
02 022テンポ テンポ (beat per minutes) ですが、実際にはSMF中のメタイベントであるセットテンポで指定される値 (4分音符一つ分の長さをµsec単位で表した値, 以下time per beatとする) から逆算した値になるため、MIDIデータ作成時に入力したものとは異なることがあります。
02 034パート指定 1チャンネルから4チャンネルまでを、それぞれ Melody, Bass, 伴奏1, 伴奏2 に割り当てます。 このMelody, Bass, 伴奏1, 伴奏2 をそれぞれ 1, 2, 3, 4 という数字で表し、1 byte目で1チャンネルの指定, 2 byte 目で2チャンネルの指定というように、4つのチャンネルに対してパートを指定します。 Melody, Bass, などは、それぞれ一度しか指定できません。 実際には 01 02 03 04 を指定していれば問題無いと思われます。
02 0410
02 050 or 4演奏開始時の音色番号 (1 - 4 チャンネル) 演奏開始時の音色番号(プログラムチェンジで指定する値)を、チャンネル毎に指定します。 1 byte 目が1チャンネルの指定です。 すべて0の場合は省略する事ができます。 省略する場合は、データ長は 0 byte になります。
02 40SMF データ必要な修正をしたSMFデータです。
02 804(time per beat) × (delta time の演奏開始から終了までの合計) / (時間単位 × 1000)
02 814SMFデータのサイズSMFデータの大きさ (byte 数) です。
02 82432 38 35 36
02 838DXMファイル作成日時1 byte 目は 00, 2 - 3 byte が西暦の年号, 以下1 byte ずつ月, 日, 時(24時間), 分, 秒です。
02 8400
02 8510
02 8600
02 C0Title曲のタイトルです。以下 Arranger まで省略可です。
02 C1Artist
02 C2Genre
02 C3Copyright
02 C4Creator
02 C5Composer
02 C6Lyrics
02 C7Arranger
FF FF00

用語解説

ランニングステータス
SMFファイルでは、同じチャンネルに同じイベントが続く場合、イベントの最初の1バイトであるステータスバイトを省略することができます。 これをランニングステータスと呼び、これによりデータサイズが小さくなるという利点があります。 詳しくはSMFの初歩を参照して下さい。