松浦 知也 Matsuura Tomoya
714b529438
All checks were successful
Build / build (push) Successful in 3m11s
3.0 KiB
3.0 KiB
date |
---|
2023-10-29T18:14:14+0900 |
#programming #music #sound
Algorithmic symphonies from one line of code -- how and why?(2011)
Bytebeatは2011年にviznutがYoutube上の動画で公開し、自身のブログの解説などで広がっていった、短いプログラムでオーディオを生成する技法。
Algorithmic symphonies from one line of code -- how and why?(2011)
その後、Webブラウザ上でも同様のコードを実行できる環境がいくつか誕生
BytebeatをNode.jsで
#tips
Bytebeatは元々次のようなC言語のプログラムで作られてた。
main(t){for(;;t++)putchar(((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7);}
このC言語のコードは極限まで圧縮されているのでもうちょっと丁寧に書くとこうなります(大昔のCコンパイラでない限りエラーで落とされる)。
#include <stdio.h>
int main(){
for(int t=0;;t++) putchar(((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7);
}
これをLinuxの、昔なら/dev/dsp
、今ならaplay
のようなパイプで直接音声波形を流し込めるものを使って音を鳴らしていた。
macOSでやろうとするなら、ffmpeg
に付属するffplay
で次のようなコードで書ける
program | ffplay -f u8 -i pipe:0 -ar 8k -ch_layout mono
どうせならC言語使わずにデータを生成したいが、シェルスクリプトで直接バイナリを扱うのは死ぬほどだるい(printf
コマンドやbc
であれこれすれば不可能でもないが、結局ファイルを一度経由しないと厳しい)
ので、Node.jsでやるとこういう感じでできる
const sample_rate = 8000;
const seconds = 1;
const length = sample_rate * seconds;
//メインの曲の生成部
const bytebeat = t =>
(((t >> 10 ^ t >> 11) % 5) * t >> 16) * ((t >> 14 & 3 ^ t >> 15 & 1) + 1) * t % 99 + ((3 + (t >> 14 & 3) - (t >> 16 & 1)) / 3 * t % 99 & 64);
let t = 0;
const mainProcess = () =>{
const data = Uint8Array.from({ length: length },
(v, _t) => bytebeat(t++)
);
process.stdout.write(data);
};
//場合によってはインターバルを少し短くしないとデータ不足で落ちることあり
setInterval(mainProcess,seconds / 1000.0);
これがうまくいくのはJavascriptの整数変換処理が32bitになったりするためなのだが、詳しいことは授業資料に書いた。割愛すると3238年間を超えなければ連続再生しても大丈夫ということになるっぽい。