いくつかの本を読んだところ、組み込み系のプログラミングでは「正確なタイミングで動作が必要なものは割り込みを使って、それ以外はポーリングでプログラミングするのがセオリー」だそうです。
というわけで、「 joypad
でキャラクターを動作させつつ」「タイマー割り込みで音を鳴らす」ような実装をしてみました。
詳しい実装はここでやっています。
github.com
メインの処理はこんな感じです。
本で読んだなんちゃってオブジェクト指向を使っています。
kiito.hatenablog.com
int main(void) {
Player player;
Player_init(&player, 0, 50, 75);
Sound_init(&sound);
Sound_intro(&sound);
init_interrupts();
for (;;) {
Player_move(&player, joypad());
}
}
void init_interrupts(void) {
disable_interrupts();
add_TIM(update_music);
enable_interrupts();
TMA_REG = 0x00U;
TAC_REG = 0x04U;
set_interrupts(VBL_IFLAG | TIM_IFLAG);
}
void update_music(void) {
count++;
if (count > 0x04U) {
Sound_play(&sound);
count = 0;
}
}
「今回はイントロのドラムだけ動作させて、その後のループするBGMは割り込みで実装する」ようにしてみたのですが、他の処理で時間がかかってしまって、イントロのドラムのタイミングが他の処理
イントロ部分も割り込みで正確なタイミングで実装し、その際にイントロからループ音源にシームレスに切り替えるようにするなど、もう少し工夫が必要そうです。
void Sound_intro(Sound* sound) {
NR41_REG = 0x1F;
NR42_REG = 0xF1;
NR43_REG = 0x35;
NR44_REG = 0xC0;
delay(500);
NR41_REG = 0x1F;
NR42_REG = 0xF1;
NR43_REG = 0x35;
NR44_REG = 0xC0;
delay(500);
NR41_REG = 0x1F;
NR42_REG = 0xF1;
NR43_REG = 0x35;
NR44_REG = 0xC0;
delay(500);
NR41_REG = 0x1F;
NR42_REG = 0xE1;
NR43_REG = 0x40;
NR44_REG = 0xC0;
delay(240);
}
void Sound_play(Sound* sound) {
if (score[sound->i]) {
beep(score[sound->i]);
}
sound->i++;
if (sound->i > 31) {
sound->i = 0;
}
}
void beep(UWORD f) {
NR10_REG = 0x06;
NR11_REG = 0x40;
NR12_REG = 0x73;
NR13_REG = (UBYTE)(f & 0x00FF);
NR14_REG = (UBYTE)((f >> 8) & 0x00FF) + 0x80;
}
また、Githubで公開されているTobu Tobu Girlのソースコードを確認したところ、mmlgbというライブラリを利用していました。
github.com
今回は「配列に音源の周波数を入れて0は休符扱いする」「音色は一種類に限定」と割り切って作ってしまったのですが、実用的なソフトのプログラミングを考えるとライブラリに頼ってしまうのが正解ですね。