いくつかの本を読んだところ、組み込み系のプログラミングでは「正確なタイミングで動作が必要なものは割り込みを使って、それ以外はポーリングでプログラミングするのがセオリー」だそうです。
というわけで、「 joypad
でキャラクターを動作させつつ」「タイマー割り込みで音を鳴らす」ような実装をしてみました。
詳しい実装はここでやっています。
メインの処理はこんな感じです。
本で読んだなんちゃってオブジェクト指向を使っています。
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; /* Set clock to 4096 Hertz */ 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というライブラリを利用していました。
今回は「配列に音源の周波数を入れて0は休符扱いする」「音色は一種類に限定」と割り切って作ってしまったのですが、実用的なソフトのプログラミングを考えるとライブラリに頼ってしまうのが正解ですね。