はじめに
以前書いたシステムROMの関数の呼び出され方の記事でも触れたように、ゲームボーイアドバンスにはシステムROM(「BIOS」とも呼ばれる)が搭載され、システムROMには様々な機能(以下「システム関数」)が存在します。
その中にはサウンド関連のシステム関数もありますが、サウンド関連についてはGBATEKですらそこまで情報がありません。
今回取り上げるSoundGetJumpListについてもGBATEKの説明は少なく、引数の説明を除くと
Receives pointers to 36 additional sound-related BIOS functions.
とのみ記載されています。
この説明は正しく、引数1(レジスタr0)に指定されたアドレスを先頭にサウンド関連のシステムROMの関数ポインタが36個書き込まれます。
やや蛇足ではありますが、GBATEKのこの関数の引数の説明は一部間違っており、正しくは「144(90h)バイトのバッファ」となります。
ですが、その関数ポインタが指す処理や、この関数の意図などはGBATEKには記載されていません。
今回は、その辺りについて説明していきます。
受け取った関数ポインタの意味するところ
36個の関数ポインタを利用目的で分けると、最初の30個と最後の6個に分けられます。
最初の30個
最初の30個はシーケンスプレイヤーのイベント処理用関数となります。
イベント番号0xb1~0xceまでの各イベントひとつにつき1個、イベント番号から0xb1を減じた値の要素番号の関数ポインタが対応しています。
これらの30個については、その処理のポインタを知ることとは別の利用方法があります(後述)。
関数ポインタとシーケンスのイベントの対応表(長いので折り畳み)
| 要素番号 | イベント番号 | 説明 | 備考 | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
[0] |
0xb1 |
トラック終了イベント | |||||||||
[1] |
0xb2 |
ジャンプイベント | |||||||||
[2] |
0xb3 |
関数イベント |
次のイベントのアドレスをトラックのコールスタックに積んで指定されたアドレスを読み取りアドレスに設定する。 関数は1トラックにつき最大3回までネストできる。 |
||||||||
[3] |
0xb4 |
関数復帰イベント |
トラックのコールスタックからアドレスを取り出し、そのアドレスを読み取りアドレスに設定する。 コールスタックが空の場合は無視される。 |
||||||||
[4] |
0xb5 |
ループ終了イベント |
到達時にトラックのループカウンターとループ回数を比較し、
|
||||||||
[5] |
0xb6 |
未定義イベント | 初期状態ではトラック終了イベント(0xb1)と同じポインタが設定されている。 |
||||||||
[6] |
0xb7 |
||||||||||
[7] |
0xb8 |
||||||||||
[8] |
0xb9 |
変数操作/条件分岐イベント (システムROMでは未定義イベントと同等) |
初期状態ではトラック終了イベント(0xb1)と同じポインタが設定されている。また、市販ソフトで広く使用されているサウンドシステムであるMP2kでは、この番号のイベントと関数ポインタに変数操作と変数による条件分岐用のイベントが実装されている。 |
||||||||
[9] |
0xba |
トラック優先度イベント | |||||||||
[10] |
0xbb |
テンポイベント | 設定したいテンポの半分の値を指定する | ||||||||
[11] |
0xbc |
ノートシフトイベント | |||||||||
[12] |
0xbd |
プログラムチェンジイベント | |||||||||
[13] |
0xbe |
トラック音量イベント | |||||||||
[14] |
0xbf |
パン位置イベント | |||||||||
[15] |
0xc0 |
ピッチベンドイベント | |||||||||
[16] |
0xc1 |
ピッチベンド幅イベント | |||||||||
[17] |
0xc2 |
LFO速度イベント | LFOの実際の速さはテンポに依存する | ||||||||
[18] |
0xc3 |
LFO遅延Tickイベント | |||||||||
[19] |
0xc4 |
LFO深度イベント | |||||||||
[20] |
0xc5 |
LFO適用先イベント |
|
||||||||
[21] |
0xc6 |
未定義イベント | 初期状態ではトラック終了イベント(0xb1)と同じポインタが設定されている。 |
||||||||
[22] |
0xc7 |
||||||||||
[23] |
0xc8 |
半音未満のチューニングイベント | |||||||||
[24] |
0xc9 |
未定義イベント | 初期状態ではトラック終了イベント(0xb1)と同じポインタが設定されている。 |
||||||||
[25] |
0xca |
||||||||||
[26] |
0xcb |
||||||||||
[27] |
0xcc |
サウンドI/O設定イベント |
*(volatile unsigned char*)(0x04000060 + [パラメーター1]) = [パラメーター2];と同等の処理を行う。
|
||||||||
[28] |
0xcd |
チャンネル設定イベント (システムROMでは未定義イベントと同等) |
初期状態ではトラック終了イベント(0xb1)と同じポインタが設定されている。また、市販ソフトで広く使用されているサウンドシステムであるMP2kでは、この番号のイベントと関数ポインタにチャンネルに関する設定を行う処理が実装されている。 |
||||||||
[29] |
0xce |
ノートオフイベント | ノートオンイベント(0xcf)と一組で使用し、ノートオンイベントで発音した音を消音する。 |
最後の6個
最後の6個はシステムサウンドやシーケンスプレイヤーの処理の内部処理の関数ポインタとなります。
シーケンスの処理を自前で実装する際に利用できる便利な関数のポインタが提供されます。
これらの関数はシステム関数やシステムROMのサウンド機能から直接的に呼び出されているものなので、使用方法さえ合っていれば同等の処理が見込めます。
| 要素番号 | シグネチャ | 説明 |
|---|---|---|
[30] |
void Func30(u32 mode); |
システムサウンドの再生周波数を設定する(SoundDriverModeの再生周波数設定部分のみ) |
[31] |
void Func31(SystemSoundPlayerArea* player, SystemSoundPlayerTrack* track); |
指定されたトラックに紐づいたチャンネルの発音を停止させる |
[32] |
void Func32(SystemSoundPlayerArea* player); |
指定されたシーケンスプレイヤーのフェードアウトを処理する(1回の呼び出しにつき1フレームぶん) |
[33] |
void Func33(SystemSoundPlayerArea* player, SystemSoundPlayerTrack* track); |
指定されたトラックの音量、パン、ピッチをトラックの設定や状態に応じて更新する(1回の呼び出しにつき1フレームぶん) |
[34] |
void Func34(SystemSoundChannel* channel); |
指定されたチャンネルをチャンネルの連結リストから取り除く(トラックによって再生されたチャンネルの使用解除) |
[35] |
void Func35(SystemSoundChannel* channel); |
指定されたチャンネルをゼロクリアする |
システム関数SoundGetJumpListの使いどころ
この関数はシステムROM領域の特定の場所にある関数ポインタ配列を指定されたアドレスに転写する処理を行います。
実は、システムサウンドの制御データ(詳細は別記事参照)には、SoundDriverInitで初期化した直後にこのシステムROM領域の特定の場所のポインタが52(0x34)バイト目に書き込まれています。
シーケンスプレイヤーの再生処理は、イベントを処理する際にこの領域の最初の30個を使用します。言い換えるとコールバックとして利用されています(休符、ノートオン系イベントは例外)。
つまり、制御データの52バイト目にSoundGetJumpListの引数に設定した領域のポインタを設定し、その領域の希望する位置の関数ポインタを置き換えることによって、シーケンスのイベントの処理を差し替えることができます。
未定義イベントを差し替えて独自の挙動を追加することも可能です。