引用元:https://www.ioccc.org/2013/endoh3/endoh3.c
審査員・作者による説明:https://www.ioccc.org/2013/endoh3/hint.html
動作
次のように実行する。
$ gcc -o endoh3 endoh3.c
$ echo 'CDEFGABc' | ./endoh3 | ruby wavify.rb > cde.wav
cde.wavを再生すると、ドレミファソラシドが流れる。
音量に注意。
音:cde.wav
きらきら星。
$ cat twinkle.abc | ./endoh3 | ruby wavify.rb > twinkle.wav
音:twinkle.wav
メヌエット。
$ cat menuet.abc | ./endoh3 | ruby wavify.rb > menuet.wav
音:menuet.wav
解説
ABC記譜法のサブセットから音楽データを合成する。
対応している機能は次の通り。
- 音符は
CからB(低いオクターブ)、cからb(高いオクターブ)
- 休符は
z
- 音符の長さは
Aで8分音符、A2で4分音符、A3で付点4分音符、A4で2分音符。
楽譜データのパースはscanf(" %c%f")で行う。
音符を音程に変えるのは、ブルートフォースで見つけた次の式。
(a % 32 + 5) * 9 / 5 % 13 + a / 32 * 12 - 22
音符のASCIIコードをaに入れてこの式を計算すると、次のように音程の数字が得られる。
'C' (ASCII 67) → 3
'D' (ASCII 68) → 5
'E' (ASCII 69) → 7
'F' (ASCII 70) → 9
'G' (ASCII 71) → 10
'A' (ASCII 65) → 12
'B' (ASCII 66) → 14
'c' (ASCII 99) → 15
'd' (ASCII 100) → 17
'e' (ASCII 101) → 19
'f' (ASCII 102) → 20
'g' (ASCII 103) → 22
'a' (ASCII 97) → 24
'b' (ASCII 98) → 26
シャープやフラットの記法(^Cや_C)には対応していないが、次の文字で音を出すことは可能。
'K' → 4 (= C#)
'L' → 6 (= D#)
'U' → 8 (= F#)
'V' → 11 (= G#)
'P' → 13 (= A#)
この音程の数字に対してpow(2, n / 12.0)を計算すれば周波数が得られる。
しかしpowは使えないので、pow(2, 1 / 12.0) ≒ 89/84.0をn回掛けることで算出する。
そして、次の計算で出力を行う。
for(c = 0; c < 音符の長さに相当する回数; c++) putchar(a = c * D);
aはchar型の変数、Dが周波数、cはカウンタ。
aに代入することで、D * cを256で割った余りになるので、のこぎり波が出力されることになる。
以上の処理を1つのfor文で行うことで実現されている。
細かいことだが、scanf(" %c%f")でパースする都合上、C2E2とは書けない。
2e2が浮動小数点数リテラルとみなされるため。
C2 E2のようにスペースをあけば回避できる。
賞名の”tweetable”は、「さえずる」という意味と、Twitterにツイート可能という意味を重ねている。
当時のTwitterの文字数制限(140文字)に収まるコードなので。