The 22nd IOCCC: Most tweetable 1-liner のエントリ

http://www.ioccc.org/2013/endoh3/endoh3.c

char a;float b,c;main(d){for(;d>2e3*c?c=1,scanf(" %c%f",&a,&c),d=55-a%32*9/5,b=d>9,d=d%13-a/32*12:1;a=2)++d<24?b*=89/84.:putchar(a=b*d);}

ツイート可能 (137B) なワンライナー

解説

ABC 記譜法 (のサブセット) のサウンドシンセサイザーです。ABC 記譜法とは、外国で使われる MML みたいなものです。

$ gcc -o endoh3 endoh3.c
$ echo "CDEFGABc" | ./endoh3 > /dev/dsp

とやるとドレミファソラシドが流れます。/dev/dsp が使えない人は padsp とか sox とか使って下さい。面倒な人のための動画。(音量注意)

137 バイトのコードの中に ABC 記譜法のパースと波形の生成が詰まってます。以下、実装について、概要だけご紹介。ドキュメントの Spoiler で結構詳細に説明しています。

パース

まず scanf で 1 音符分の音階と長さを持ってきます。

scanf(" %c%f", &a, &c)

これで得られた音階の ASCII コードに以下の謎の変換をかまします。*1

(a % 32 + 5) * 9 / 5 % 13 + a / 32 * 12 - 22

これによって音符を半音ずつ並べた数字 (MIDI ノート番号みたいなの) が得られます。

周波数への変換

この数字を周波数に直すには、pow(2, n/12) みたいな変換をします。が、pow は使えない *2 ので、pow(2, 1/12) を n 回掛け算します。この定数は 89/84. で近似できます。*3

波形の生成

あとは以下で波形を出力。

for(c = 0; c < len; c++) putchar(a = n * D);

n * D は単調増加ですが、char 型の変数に代入することで mod 256 の値になります *4 。これによってめでたく倍音豊かなのこぎり波が出力されます。


で、後はゴルフ。とりあえず 140B を切るまでは頑張りましたが、プロゴルファーならどのくらい削れるもんなんでしょうねえ。

狙いと戦略

これはわりと自信のある方でした。ワンライナーIOCCC の定番枠なので。

2 オクターブに収まり、(なるべく) 黒鍵を使わず、著作権フリーであるサンプル曲を探すのが大変でした。Happy Birthday to Youアメリカではまだ著作権存続中 (?) で去年も裁判起きたらしい、とか、長い休止符は例の曲著作権に抵触する可能性がある、とか。

*1:この式は試行錯誤+総当たりで見つけました。

*2:ワンライナーでは #include ができない。

*3:この近似値は Stern-Brocot tree を使って見つけました。

*4:厳密にはこれは未定義挙動ですが、まあ大概の環境でこうなるんじゃないですかと……。詳しくはドキュメントを参照。