quartz-research-note/content/Bytebeat.md
松浦 知也 Matsuura Tomoya 714b529438
All checks were successful
Build / build (push) Successful in 3m11s
[obsidian] vault backup: 2024-10-25 14:43:18[
2024-10-25 14:43:18 +09:00

3.0 KiB
Raw Blame History

date
2023-10-29T18:14:14+0900

#programming #music #sound

Algorithmic symphonies from one line of code -- how and why?(2011)

https://youtu.be/tCRPUv8V22o

Bytebeatは2011年にviznutがYoutube上の動画で公開し、自身のブログの解説などで広がっていった、短いプログラムでオーディオを生成する技法。

Algorithmic symphonies from one line of code -- how and why?(2011)

その後、Webブラウザ上でも同様のコードを実行できる環境がいくつか誕生

HTML5 Bytebeat

Bytebeat Composer


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年間を超えなければ連続再生しても大丈夫ということになるっぽい。