cuspy memo


ドレミファソラシドの定義(どう書く?org - Tiny MML)

2007/08/11 Saturday 03:06:03

ちょっと前に書いたどう書く?org の問題でカエルの歌を鳴らせっていうお題があって、意外にも評価が高かったのと、自分でもあまり書いたことがない部類の問題だったのでコメントを残しておく。

お題: http://ja.doukaku.org/31/
投稿: http://ja.doukaku.org/comment/1480/

このお題は、各言語におけるサウンドデバイスの制御方法と音階に関する知識が問われる問題で、まずは linux での音の鳴らせ方から。

linux のサウンドドライバは昔だと OSS, 最近だと ALSA が使われている場合が多いのだけど、両方ともユーザープログラムからは /dev/dsp というキャラクタデバイスで制御を行う。
適切にドライバが組み込まれていると

% cat /dev/urandom > /dev/dsp

を実行すると砂嵐のテレビのような雑音が鳴る。
プログラムから鳴らす場合も、ただ open() して write() すればよいのだけれどその前に流し込む音データの種類を設定する必要がある。/dev/dsp を open() したディスクリプタに対して ioctl() で以下のような設定を行った。

  • PCMフォーマットの設定

  • int fmt = AFMT_U8;
    ioctl(fd, SOUND_PCM_SETFMT, &fmt);

    PCMフォーマットとは音の大きさを量子化するときの bit数の事。
    AFMT_U8 は 符号なし 8bit を意味するので、この場合 256 種類の音量を表現できる。音は波なのでこの bit 数が少ないと音質は荒くなり多いと音質は良くなる。カエルの歌ごときでは 8bit で十分。というか 16 bit になるとエンディアンを考慮しなければならなくなるので面倒になる。

  • チャンネル数の設定

  • int channels = 1;
    ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &channels);

    もちろん 1 がモノラルで、2 がステレオ。カエルの歌ごときではモノラルで十分。

  • サンプリングレートの設定

  • #define RATE 8000
    int rate = RATE;
    ioctl(fd, SOUND_PCM_WRITE_RATE, &rate);

    サンプリングレートとは音の波形を時間軸で分割するサンプリング(標本化)を一秒間に何回分割するかという回数の事。
    ここでは 8000(8kHz)を設定したので\frac{1}{8000}=0.000125秒の間隔で波形をサンプリングできる。(latex が役に立った!)

上記のコードで、
PCMフォーマット: 8bit
チャンネル数: モノラル
サンプリングレート: 8000Hz
という設定が行えた。

次にどうやってドレミファソラシドを鳴らせば良いのか解らなかったので音階について調べてみた。
ドレミファソラシドの定義方法には「純正律」と「平均律」という2種類の方法があるそうだ、最近の子は「平均律」を使うそうなのでこっちでやってみた。
平均律ではラの音(440Hz)を基準にして周波数は以下のように算出できる

440 = 440Hz
440 \times {(\sqrt[12]{2})}^{2} = 493.88Hz
440 \times {(\sqrt[12]{2})}^{3} = 523.25Hz
440 \times {(\sqrt[12]{2})}^{5} = 587.32Hz
440 \times {(\sqrt[12]{2})}^{7} = 659.25Hz
ファ 440 \times {(\sqrt[12]{2})}^{8} = 698.45Hz
440 \times {(\sqrt[12]{2})}^{10} = 783.99Hz
440 \times {(\sqrt[12]{2})}^{12} = 880Hz (1オクターブ下のラの2倍の周波数)
440 \times {(\sqrt[12]{2})}^{14} = 987.76Hz (1オクターブ下のシの2倍の周波数)
440 \times {(\sqrt[12]{2})}^{15} = 1046.50Hz (1オクターブ下のドの2倍の周波数)

\sqrt[12]{2}の乗数が 1つ飛びだったり2 つ飛びだったりしているのは間に #の音が入っているから。

ドレミファソラシドの周波数が解ったところでこれらの周波数で描くサインカーブを最初に設定したサンプリングレートでサンプリングを行う。
周波数fの音の波形は\sin(2 \pi ft)で表せられるので 8kHz の PCMフォーマットでサンプリングするには以下のようなコードになる。

for(j = 0;j < RATE; j++)
buf[j] = 255 * sin(2.0 * M_PI * freq * j / RATE);

buf は 8000 byte の配列で 8000Hz でサンプリングするのでちょうど1秒間の音データとなる。

参考:
http://homepage3.nifty.com/iromono/kougi/ningen/node16.html

  1. Robert wrote related post…

    Silk posts and stories…

    Trackback by Robert wrote related post — 2008/06/10 Tuesday @ 00:18:16

Leave a comment

You must be logged in to post a comment.

hoge