今私は小さな魚だけれど

ちょっぴり非日常な音楽を紹介するブログです

【GB】割り込みを使って音を出してみる

いくつかの本を読んだところ、組み込み系のプログラミングでは「正確なタイミングで動作が必要なものは割り込みを使って、それ以外はポーリングでプログラミングするのがセオリー」だそうです。

というわけで、「 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;
    /* 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というライブラリを利用していました。

github.com

今回は「配列に音源の周波数を入れて0は休符扱いする」「音色は一種類に限定」と割り切って作ってしまったのですが、実用的なソフトのプログラミングを考えるとライブラリに頼ってしまうのが正解ですね。