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 になります。
| |||||
DXMヘッダの全体構成 |
---|
それぞれの項目は3つの要素からなっており、それぞれ 1番目が項目ID、2番目がその項目のデータ開始アドレス、3番目がデータ長です。 データ開始アドレスは、DXMファイルの先頭から見たアドレスで、データ長とともに byte単位で記述します。
| |||
個々の項目の中身 |
---|
どのような項目があるかについては、項目一覧を見て下さい。
3. DXMデータの構成
DXMデータについては、DXMファイルの各項目毎にDXMヘッダで規定されたデータ開始アドレスからデータ長分を使って、各項目毎に必要な情報を記述します。 個別の内容については、項目一覧を見て下さい。 ただし、SMFデータを記述する項目については、別途次節で説明します。
4. SMF部の通常との違い
DXMファイル中には、基本的にはSMFファイルをそのまま埋め込みますが、一部分修正を行う必要があります。
- "MThd", "MTrk" をそれぞれ "CThd", "CTrk" に変更。
- 時間単位 (4分音符分のデルタタイム) は 24 に変更。
- 仕様で有効とされているもの以外のイベントを削除。 削除するイベントについては、DDI Pocket の feelsound converter 付属のマニュアルを参考にして下さい。
- ファイルサイズをできる限り小さくするために、feelsound converter ではランニングステータスを利用しています。 これは任意です。
例題
次のような、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 については、こちらを御覧下さい。
ただし、まだ解析した結果と照らし合わせて不満の残る点がいくつかあります。
- 低音・高音部を1オクターブずらす処理を行っていない
SMFにおいて、音程は0-127を指定することができますが、実際にこの範囲にあるすべての音を鳴らすことはできません。 DXMでは、発音できる範囲を越えたものについては、1オクターブずらすことになっていますが、本実装ではこの処理を行っていません。 - 項目 0280 の値が若干ずれる。
大筋で、仕様は掴めたと考えていますが、この数値だけ、ぴったりとは合いません。 それほど大きくは、ずれないので、誤差の範囲内であり、小数点以下の扱いの違いだけだと考えています。
また、DXMにはSMFの内容がほぼそのまま埋め込まれているだけですので、DXMをSMFに変換する機能もつけました。シンボリックリンクなどにより、``dxm2mid''という名前で呼び出すと、DXM形式のファイルをSMF Format 0に変換することができます。
参考資料
DXMファイルの解析を行うにあたって、以下のページを参考にしました。
また、DDI Pocket の feelsound converter に付属するマニュアルも、仕様を探る上で貴重な資料です。
DXMヘッダ項目一覧
説明の無い項目については、詳しい内容については不明です。 ただし、私が DDI Pocket の feelsound converter を使って変換した中では値が変化しなかった項目ですので、意味は分からなくても実用上問題は無いと思います。
項目ID | データ長 (bytes) | データ内容 | 説明 |
---|---|---|---|
00 00 | 4 | 30 31 2e 30 | |
00 01 | 0 | 0 | |
00 10 | 4 | ff ff | |
00 11 | 10 | 00 10 00 00 00 00 00 00 ff ff | |
00 20 | 0 | 0 | |
00 21 | 0 | 0 | |
00 30 | 0 | 0 | |
00 31 | 0 | 0 | |
02 00 | 0 | 0 | |
02 01 | 0 | 0 | |
02 02 | 2 | テンポ | テンポ (beat per minutes) ですが、実際にはSMF中のメタイベントであるセットテンポで指定される値 (4分音符一つ分の長さをµsec単位で表した値, 以下time per beatとする) から逆算した値になるため、MIDIデータ作成時に入力したものとは異なることがあります。 |
02 03 | 4 | パート指定 | 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 04 | 1 | 0 | |
02 05 | 0 or 4 | 演奏開始時の音色番号 (1 - 4 チャンネル) | 演奏開始時の音色番号(プログラムチェンジで指定する値)を、チャンネル毎に指定します。 1 byte 目が1チャンネルの指定です。 すべて0の場合は省略する事ができます。 省略する場合は、データ長は 0 byte になります。 |
02 40 | SMF データ | 必要な修正をしたSMFデータです。 | |
02 80 | 4 | (time per beat) × (delta time の演奏開始から終了までの合計) / (時間単位 × 1000) | |
02 81 | 4 | SMFデータのサイズ | SMFデータの大きさ (byte 数) です。 |
02 82 | 4 | 32 38 35 36 | |
02 83 | 8 | DXMファイル作成日時 | 1 byte 目は 00, 2 - 3 byte が西暦の年号, 以下1 byte ずつ月, 日, 時(24時間), 分, 秒です。 |
02 84 | 0 | 0 | |
02 85 | 1 | 0 | |
02 86 | 0 | 0 | |
02 C0 | Title | 曲のタイトルです。以下 Arranger まで省略可です。 | |
02 C1 | Artist | ||
02 C2 | Genre | ||
02 C3 | Copyright | ||
02 C4 | Creator | ||
02 C5 | Composer | ||
02 C6 | Lyrics | ||
02 C7 | Arranger | ||
FF FF | 0 | 0 |
用語解説
- ランニングステータス
- SMFファイルでは、同じチャンネルに同じイベントが続く場合、イベントの最初の1バイトであるステータスバイトを省略することができます。 これをランニングステータスと呼び、これによりデータサイズが小さくなるという利点があります。 詳しくはSMFの初歩を参照して下さい。