トップC-Tips > ドレミ⇒周波数

ドレミ⇒周波数

音符をその音の周波数と長さの対で表すと、 楽譜は次のように表される。
784 16  698 16  660 32  587 8  660 8  698 8  587 8  523 16  0 16  -1 -1
しかし、数値の羅列は分かりにくし、ミスも混入しやすい。 例えば、次のように、ド, レ, ミをDo, Re, Mi で表す方が分かりやすい。 So1の1は基本音階のSoより1オクターブ高いことを表す。
So1 16  Fa1 16  Mi1 32  Re1 8  Mi1 8  Fa1 8  Re1 8  Do1 16  0 16  -1 -1
このような記号So1, Fa1 などを数値に変換するプログラムを以下に示す。
音階を表す記号(文字列)は4文字以下であることから、1文字以上の空白を加えて 5バイトの文字列として、全記号をつなげた文字列を定義している。 また、これらの音階に対応した周波数を配列で定義しておく。 strstr関数によって、何番目に登録された記号かを調べて、周波数に変換する。 周波数を記号ではなく、数値で表したものが混在してもよい。
#include <stdio.h>

char *str =  "La_1 Si_1 Do   Re   Mi   Fa   So   La   Si   "
             "Do1  Re1  Mi1  Fa1  So1  La1  Si1  Do2  Re2  ";
int val[] = { 220, 247, 262, 294, 330, 349, 392, 440, 494, 
              523, 587, 660, 698, 784, 880, 988,1047,1174 };

void getNote(int *pFreq, int *pLen) {
    char buf[10], *p;
    int ix;
    scanf(" %[^ ] %d ", buf, pLen);
    if (buf[0] == '-' || ('0' <= buf[0] && buf[0] <= '9')) {
        *pFreq = atoi(buf);
    } else {
	while (strlen(buf) < 4) strcat(buf, " ");
        p = (char*)strstr(str, buf);
        int ix = (p - str) / 5;
        *pFreq = val[ix];
    }
}

int main() {
    int n, freq;
    for (;;) {
        getNote(&freq, &n);
        printf("%d %d\n", freq, n);
        if (freq == -1) break;
    }
}
JavaではHashMapHashtable など連想メモリの典型的な例題に取り上げられそうな 問題であるが、項目数が数十で、文字列の長さが比較的短い場合は、 上のプログラムの方がシンプルで実行速度も速い。