今私は小さな魚だけれど

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

【GB】差音を利用して2つの矩形波で50Hzの低音を鳴らす

音響学の本を読んでいて知ったのですが、人間の耳には「差音」と呼ばれる現象があるそうです。

差音(さおん)は、結合音の一種で、周波数の異なる2つの音を同時に鳴らした時に聞こえる、2つの音の周波数の差に等しい周波数の音である。 これは、うなりと同じだが聴覚器官の非直線性によって一つの音として認識されてしまう現象である。たとえば、440Hzと441Hzの音を鳴らすと、1Hzのうなりが生じる。とすれば、440Hzと490Hzの音を鳴らすと50Hzのうなりが生じる。50Hzのうなりは人間には聞き取れないため50Hzの音として聞こえる。これが差音の正体である。

abcviolin.com

この差音をうまく使えば、2つの矩形波を組み合わせて、ゲームボーイの音源そのものの仕様より低い音が出せる可能性があります。以下の本にバッハ時代のパイプオルガンの説明がありました。

教養としてのバッハ──生涯・時代・音楽を学ぶ14講 (叢書ビブリオムジカ)

教養としてのバッハ──生涯・時代・音楽を学ぶ14講 (叢書ビブリオムジカ)

オルガンのパイプの基本振動数は、基本的にはパイプの長さと音速によって決まる。基本振動数は音速に否定し、パイプの長さに反比例する。(中略)従ってオルガンで低い音を出すためには、本来なら巨大な楽器をつくるしかないはずであるが、適当な2音の差音を用いれば、長いパイプを用いずに低い音を出すことが可能なのである。オルガニストでもあったゾルゲはそれに気づいていたのだ。

GameBoy 音源仕様

NR13,NR14でセットされた周波数の値をxとすると 周波数 = 4194304/(32*(2048-x))Hz = 131072/(2048-x)Hz になる ∴再生できる周波数は64Hz~131.1KHzとなります

というわけで、実際にGBDKを使って「duty cycleが50%で、440Hzと490Hzの音を同時に鳴らす」ようなコードを実装で試してみました。…たしかに低音は聞こえるんですが、微妙にうなりがあってヘッドホンで聞くと気持ち悪くなるような音になってしまいました。どう工夫したものか。

#include <gb/gb.h>
#include <gb/drawing.h>
#define DELAY 2000

void sound1(UWORD f);
void sound2(UWORD f);


void main() {
    NR52_REG = 0x80;
    NR50_REG = 0x77;
    NR51_REG = 0xFF;

    gotogxy(1, 1);
    gprintf("====== Beep ======");
    
    gotogxy(2, 3);
    gprintf("Press any button");

    while (1) {
        UBYTE joypad_state = joypad();
        
        if (joypad_state) {   
            sound1(440);
            sound2(490);
            delay(DELAY);
            break;
        }
    }
}

void sound1(UWORD f) {
    NR10_REG = 0x00; 
    NR11_REG = 0x80;
    NR12_REG = 0xF0;
    NR13_REG = (UBYTE)(f & 0x00FF);
    NR14_REG = (UBYTE)((f >> 8) & 0x00FF) + 0x80;
}

void sound2(UWORD f) {
    NR21_REG = 0x80;
    NR22_REG = 0xF0;
    NR23_REG = (UBYTE)(f & 0x00FF);
    NR24_REG = (UBYTE)((f >> 8) & 0x00FF) + 0x80;
}