quartz-research-note/content/Bytebeat.md
松浦 知也 Matsuura Tomoya 72368b208c
All checks were successful
Build / build (push) Successful in 1m54s
modified frontmatter depending on timestamp
2024-02-08 18:01:44 +09:00

3.0 KiB
Raw Blame History

date
2024-02-06T02:00:06+09:00

#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言語のコードは極限まで圧縮されているのでもうちょっと丁寧に書くとこうなります。

int main(int t){
    for(;;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 44k -ac 1

どうせなら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) => {
            const res = bytebeat(t);
            t += 1;
            return res
        }
    );
    process.stdout.write(data);
};
//場合によってはインターバルを少し短くしないとデータ不足で落ちることあり
setInterval(mainProcess,seconds / 1000.0);

これがうまくいくのはJavascriptの整数変換処理が32bitになったりするためなのだが、詳しいことは授業資料に書いた。割愛すると3238年間を超えなければ連続再生しても大丈夫ということになるっぽい。