[戻る]
OpenMPT 互換 MOD/XM プレイヤの実装注)2000/07/11 時点の情報に基づいた内容に、若干の加筆修正をしたものです。 当時は OpenMPT はソース非公開だったため、解析ベースの内容となっています。MOD プレイヤの内部動作に関する完全な仕様書は存在しません。 歴代のトラッカーが抱えていたバグは、後の仕様となり、 それが代々蓄積して現在に至っています。 MOD プレイヤを実装するには、 これらのアンドキュメンテッド仕様を再現する必要があります。 この文書では、アンドキュメンテッドな仕様も含めて、 可能な限り高い再現度のプレイヤを作成することを試みます。 関連ソースコードや資料は以下の URL から入手可能です。
SourceForge 上でオープンな開発が行われており、 ソースを入手することが出来ます。 こちら Open Cubic Player です。 マイナーなフォーマットをサポートし、しかもソース付きです。 Windows 環境でも問題無しと有りますが、私の環境では動作しませんでした。 やはり DOS 環境前提なのでしょうか。 こちら かつての MEGA DEMO の総本山 Hornet。 MOD の資料やソースが多数置いて有ります。 GUS(グラビス ウルトラ サウンド と言う名の DOS 時代の音源ボード) を前提とするソースばかりなのは難点ですが・・・。 サンプリング再生レートの算出方法再生レートとは、秒間あたりに再生される波形要素数を表します。 例えば、波形を 8bit で持っていて、再生レートが 1000 だったら、 1秒間で 1000 バイト分の波形を再生すれば良いという事になります。各フォーマットごとに、 サンプリングレート導出の手法は異なっているので注意が必要です。 テーブル誤差の差異等から、同じ音階でも違った音程が得られる事があります (OpenMPT 上で、フォーマットタイプを変更する時、 音程が若干ずれる様子を確認することが可能です。 耳で聴いてわかるレベルの差異が生じています)。
(この項目の情報元は、Hornet に掲載されていた MODFORM.TXT / MOD_FORM.TXT / MOD-INFO.TXT です。 OpenMPT でも合致するかは未確認です。) 音階は、波長を示す Period と呼ばれる値で指定されます。 再生レートの算出は、PAL の Amiga と、 NTSC の Amiga のどちらの環境で作成されたデータなのかによって異なっているようです。 PAL の Amiga(CPU Clock = 7.0937892 MHz)で作成されたデータでは、 再生レート(Hz) = 7093789.2 / (Period * 2) NTSC の Amiga(CPU Clock = 7.1590905 MHz)で作成されたデータでは、 再生レート(Hz) = 7159090.5 / (Period * 2) だそうです。 なお、PAL の Amiga で作成されたデータと、 NTSC の Amiga で作成されたデータの区別の仕方は不明です (大半のデータは、PAL とみなして良い様ですが)。 そして、上記の式で得られた再生レートを Finetune 値(-8 〜 +7)で 補正したものを実際の再生レートとします。 Finetune値 とは、+8 で半音階上がるような値です。 実際の再生レート = 再生レート * (2 ^ (Finetune値/(12 * 8))) 再生レートは、十分な精度で計算しておかないと、 音痴になってしまうので注意です。 固定小数で扱う場合、小数部 12 bit ぐらいは必要です。 音程は Period でなく、キー番号で持っています。 また、再生レートの制御の方法には、以下の 2 通りが有ります。 ビブラートやポルタメント等の音程のスライドが、周波数に対して行われます。 人間の耳には、等間隔で音程の変化が行われているように聞こえます。 MOD 形式と同じく、ビブラートやポルタメント等の音程のスライドが、波長に対して行われます。 その結果、人間の耳には、等間隔で音程の変化が行われているように聞こえません。 低音部では音程変化が緩やかになり、高音部では音程変化が急になります。 あえてこのモードを選択するメリットはありません。 ここで困った事が一つあります。 XM 形式の Amiga-Period モードは、周波数の算出方法が MOD と完全互換ではなく、 Period の単位が 4 倍精度になっており、想定している Amiga CPU Clock は 7158728.0 Hz です。 よって、 再生レート(Hz) = 7158728.0 / (Period / 2)となります。 MOD 形式の Period テーブル(この項目の情報元は、Hornet に掲載されていた MODFORM.TXT / MOD_FORM.TXT / MOD-INFO.TXT です。 OpenMPT でも合致するかは未確認です。)確音階の Period は以下のようになります。が、Octave 1〜3 のみが 公式な値です。後は非公式な値であり、トラッカー毎にまちまちの値 を用いています。非公式な値を指定すると、クラッシュする プレイヤーもあるかも知れないので、トラッカーを作る場合は注意だそうです。 0x6B0,0x650,0x5F5,0x5A0,0x54F,0x503,0x4BB,0x477,0x436,0x3FA,0x3C1,0x38B, /* Octave 0 */ 0x358,0x328,0x2FB,0x2D0,0x2A7,0x281,0x25D,0x23B,0x21B,0x1FD,0x1E0,0x1C5, /* Octave 1 */ 0x1AC,0x194,0x17D,0x168,0x154,0x141,0x12F,0x11E,0x10E,0x0FE,0x0F0,0x0E3, /* Octave 2 */ 0x0D6,0x0CA,0x0BF,0x0B4,0x0AA,0x0A0,0x097,0x08F,0x087,0x07F,0x078,0x071, /* Octave 3 */ 0x06B,0x065,0x05F,0x05A,0x055,0x050,0x04C,0x047,0x043,0x040,0x03C,0x039, /* Octave 4 */ 0x035,0x032,0x030,0x02D,0x02A,0x028,0x026,0x024,0x022,0x020,0x01E,0x01C, /* Octave 5 */ 0x01B,0x019,0x018,0x016,0x015,0x014,0x013,0x012,0x011,0x010,0x00F,0x00E /* Octave 6 */ 発声の条件(この項目は、私が OpenMPT 上で動作確認した結果を元に書いています。)note 上に音階指定があると、発声が行われます。但し例外的に Tone-Portamento 中は、 音階指定があっても、その音階が目標値に設定されるだけで、発声は行われません。 さらに例外があって、Tone-Portamento 中であっても、音階指定と音色指定が同時に 行われると、発声が行われます。実にややこしいです。 この振る舞いは、MOD 形式、XM 形式で共通です。 MOD 形式のデフォルト音量の扱い(この項目は、私が OpenMPT 上で動作確認した結果を元に書いています。)note 上に音色の指定があると、 その音色のデフォルト音量がチャンネルに反映されます。 これは、音階指定が無くても反映されます。 したがって、例えば音色1の音を再生中に、音階指定無しで音色2を指定すると、 音色1の再生は継続されたまま、音量のみが音色2のデフォルト音量になる、 という直感的でない振る舞いをします。 この妙な仕様は、恐らく当時のバグだと思うのですが、 MOD 形式の不器用さを補う重要テクニックに転用されることになったため、 アンドキュメンテッドな重要仕様となっています。 ビブラート、トレモロの波形指定(この項目は、私が OpenMPT 上で動作確認した結果と、 その他のプレイヤーのソースを解析した結果を元に書いています。)波形は No.0 〜 No.3 の4種から選ぶ事が出来ますが、No.3 の ランダム波 は 古い MOD の資料では未使用となっていたり、 トラッカーによっては別の波形だったりします。 No.3 を指定すると、No.0 〜 No.2 のうちからランダムで選択される、 としている資料もありました。 OpenMPT では、MOD 形式、XM 形式ともに、No.3 は ランダム波です。 ビブラート、トレモロの振幅(この項目は、私が OpenMPT 上で動作確認した結果と、 その他のプレイヤーのソースを解析した結果を元に書いています。)実は、これもトラッカー毎に範囲が異なっているようです。 OpenMPT では、以下のような振幅になっています。
コマンドの発効のタイミングについて(この項目は、私が OpenMPT 上で動作確認した結果と勝手な推測を元に書いています。)古いトラッカーでは、 持続的な変化を起こすコマンドでは、各 row 内の tick 0(初回 tick)ではコマンド解釈のみが行われ、 発効はしない仕様だったようです。具体的には、以下のコマンドです。
そこで OpenMPT では、上記のコマンド群に対しては、 実際の変化量 = 指定の変化量 * (1 row あたりの tick 数 - 1) / (1 row あたりの tick 数)のようにパラメーターを補正して、これを tick 0 から発効させる事により、 コマンドの効果の互換性を保ちつつ、効果のムラを除去しているようです。 なお、この仕様は、MOD 形式、XM 形式で共通でした。 パラメーターに 0 を指定した時に、前回の値を用いるコマンド(この項目は、私が OpenMPT 上で動作確認した結果を元に書いています。)パラメーターに 0 を指定した時に、前回の値を用いる事を、 continue と表記します。 OpenMPT のパラメーター設定欄にて、スライドバーを 0 に持っていくと、 continue と表記される場合がそれです。 しかし、この表記の内容にはウソが含まれているので注意です。 MOD 形式データのローカルバージョン(+1080 バイト目の識別文字列などから判別可能) によって、continue と解釈されるか否かが変わってくるかも知れませんが、 私の方で確認した限りでは、以下のような対応になっているようです。 コマンド 0xy MOD 形式、XM 形式ともに、パラメーター 0 はコマンド自体の無効化を意味する。コマンド 1xy コマンド 2xy コマンド 3xy コマンド 5xy コマンド 6xy コマンド Axy コマンド E1x コマンド E2x コマンド EAx コマンド EBx MOD 形式:パラメーター 0 をそのまま適用。コマンド 4xy コマンド 7xy MOD 形式、XM 形式ともに、x が 0 なら前回の x を、y が 0 なら前回の y を適用する。 言わば、x y 個別に continue 指定が可能。コマンド 9xy MOD 形式、XM 形式ともに、パラメーター 0 は continue。 コマンドの効果を保持する変数は、独立か共有か?(この項目は、私が OpenMPT 上で動作確認した結果を元に書いています。)パラメーターに省略値を指定すると、前回の値を用いるコマンドがありますが、 ここの部分はプレイヤー毎の仕様の差異の温床となっている様です。 OpenMPT では、以下の様になっている事がわかりました。
コマンド 108(8 の Portamento Up)の順序で実行するとき、コマンド 200 は、8 の Portamento Down となります。 これは、コマンド 1xy と 2xy の xy を保持する変数が同一のために起こる現象です。 なお、これらの解析結果は、MOD 形式、XM 形式で共通でした。 Grissando 処理は、Vibrato 量を反映する前に行う(この項目は、私が OpenMPT 上で動作確認した結果を元に書いています。)Grissando 有効の時に Vibrato を指定する時、 周波数加工は、Vibrato 処理後に Grissando 処理するのではなく、 Grissando 処理後に Vibrato 処理を行います。 MOD 形式、XM 形式で共通の仕様でした。 Tone-Portamento の目標値設定の条件(この項目は、私が OpenMPT 上で動作確認した結果を元に書いています。)現在 Tone-Portamento の最中かどうかに関わらず、 音階指定があった場合は無条件にそれを Tone-Portamento の目標値に設定します。 |C-5 01...101 |.........101 |.........101 |.........101 |.........301 |.........301 |.........301 |.........301 |.........301 |.........301この例では、C-5 の音階から始まって、Portamento UP して行きますが、 Tone-Portamento の部分に差し掛かると C-5 を目標として音程が下がっていきます。 最初に C-5 を指定した時点で、C-5 が Tone-Portamento の目標に設定されていることを示しています。 値が省略された場合の処理(この項目は、私が OpenMPT 上で動作確認した結果を元に書いています。)値が省略されている場合、トラッカーの内部フローに依存した結果が露呈してしまいます。 例えば以下のような4つのケースが代表的です。
すなわち、トラッカーの内部変数の構成や処理フローまで真似ないと 完全互換 MOD プレイヤが作れないという非常に困った状況が発生しています。 データ作成者は、プレイヤの差異を避けるには、 下手に値の省略を行わず、 特に音程と音色は両者セットで指定するように注意する必要が有ります。 音量指定欄(Volume Column)で設定できるコマンド(この項目は、私が OpenMPT 上で動作確認した結果を元に書いています。)音量指定欄でも、ポルタメントやボリュームスライド等のコマンドが指定できますが、 ここでの省略値の適用ルールは、通常のコマンド欄とは異なっています。 音量指定欄のコマンドでパラメーター値を省略(0 を指定した状態)する時、 前回の値が用いられるのですが、 この「前回の値」を保持する変数は、内部で1個しか用意されて居ないという、 理解に苦しむ仕様となっています。 例えば、 OpenMPT XM |........F0F |C-501...C40 |.....b08... <- Fine Vol Down 08 |.....a08... <- Fine Vol Up 08 |........... |.....u01... <- Vibrato Speed 01 |........... |.....b00... <- Fine Vol Down 01 |.....b00... <- Fine Vol Down 01 |.....b00... <- Fine Vol Down 01 |.....b00... <- Fine Vol Down 01 |.....b00... <- Fine Vol Down 01 |.....a00... <- Fine Vol Up 01 |.....a00... <- Fine Vol Up 01 |.....a00... <- Fine Vol Up 01 |.....a00... <- Fine Vol Up 01と言うような結果が得られます。これは、コマンド a b の省略値が、 コマンド u によって上書きされた事を示しています。 OpenMPT 互換 MOD/XM プレイヤのソースコード以上の解析結果に基づき作成した、 OpenMPT 互換 MOD/XM プレイヤのソースコードを公開します。コア部分のソースのみ公開しています。 このソースを利用したプレイヤーの実行サンプルはありませんので、 各自で作成してください。 ソース自体は、流用が効きやすいように配慮して記述しました。 利用方法などは、梱包されたドキュメントを参照して下さい。 2001/03/08 バージョン [戻る] |