240 bits per mile on synths
240 Bits per Mile but with a lot more supersaws
Log in to post a comment.
// 240 Bits per Mile (Leon Riskin), from the Five Nights at Freddy's 6 soundtrack
// see https://dittytoy.net/ditty/a5f81ae6ff for the original
function softsaw(p, formant) {
const x = p-~~p;
// polyblep
const f = Math.abs(formant);
const s = Math.max(0, Math.abs((f + 1) * (2*x - 1)) - f);
// saw - polyblep
return 2 * x - 1 - s*s * Math.sign(x - 0.5);
}
// from my Piano ditty, https://dittytoy.net/ditty/bc17a034bf
// modified slightly
const osc = class {
constructor(options) {
// The value of the note argument of the play call is retrievable via options.note.
this.phases = new Float32Array(16).map(_ => Math.random());
this.damping = new Float32Array(16).map(_ => 1);
// frequency
this.freq = midi_to_hz(options.note) * ditty.dt;
// phasor
this.time = Math.random();
}
process(note, env, tick, options) {
// phasor times
this.time += ditty.dt * 3;
this.time -= this.time|0;
let phase = Math.sin(this.time * 2 * Math.PI);
// total
let l = 0.0;
let r = 0.0;
// all saw waves
for (let i = 0; i < this.phases.length; i++) {
// sample wave
const v = softsaw(
this.phases[i],
this.damping[i] * (options.soft_decay * env.value + options.soft_base)
) * env.value;
// update phase
this.phases[i] += this.freq * (options.detune||1) * (i%2 + 1) + i * 4 * ditty.dt / this.phases.length;
this.phases[i] -= this.phases[i]|0;
// stereo
if (i < this.phases.length / 2) l += v;
else r += v;
}
return [l / Math.sqrt(this.phases.length), r / Math.sqrt(this.phases.length)];
}
};
// Sounds fine:tm:, but could be better
// Piano is very hard to recreate properly
const piano = synth.def(
osc, {
soft_decay: 24,
soft_base: 0,
detune: 0.5,
levels: [0, 1, 0, 0],
times: [0.001, 4, 0.5],
env: segenv,
}
);
// closest I could get this to a saxaphone
const sax = synth.def(
osc, {
soft_decay: 20,
soft_base: 10,
vibrato: 1.2,
detune: 1,
levels: [0, 1, 0.7, 0],
times: [0.05, 2, 0.2],
env: segenv,
}
);
// more of a violin considering how high it plays, but reasonably close
const cello = synth.def(
osc, {
soft_decay: 4,
soft_base: 4,
vibrato: 3,
detune: 1,
levels: [0, 1, 0.7, 0],
times: [0.05, 0.2, 0.2],
env: segenv,
}
);
// === Song ===
// 240 bits per mile is indeed 240 bpm
ditty.bpm = 240;
// play the drums
function playDrums(kick, snare, duration, notes) {
for (const note of notes) {
// string encoding of the note
// k = kick
// s = snare
// b = both
// rest is ignored
if (note == "k" || note == "b") kick.play(20);
if (note == "s" || note == "b") snare.play(20);
sleep(duration);
}
}
// Alternate 3 notes
function alt(instr, count, a, b, low) {
for (let i = 0; i < 4 * count; i++) {
instr.play([a, low, b, low].ring(i), { duration: 0.5 });
sleep(0.5);
}
}
// Alternate with a pattern
function altpat(instr, low, pat) {
for (const note of pat) {
instr.play(note, { duration: 0.5 });
sleep(0.5);
instr.play(low, { duration: 0.5 });
sleep(0.5);
}
}
// play a pattern
function pat(instr, time, pat) {
for (const note of pat) {
if (note > 0) instr.play(note, { duration: time });
sleep(time);
}
}
// play a pattern, merge notes
function patmerge(instr, time, pat) {
const notes = [];
for (const note of pat) {
if (notes.length && notes[notes.length - 2] == note) notes[notes.length - 1] += time;
else notes.push(note, time);
}
for (let i = 0; i < notes.length; i += 2) {
if (notes[i] > 0) instr.play(notes[i], { duration: notes[i + 1] });
sleep(notes[i + 1]);
}
}
// intro base pattern
// 64 beats
function bass1(instr) {
alt(instr, 4, ds4, c4, g3);
alt(instr, 4, ds4, c4, gs3);
alt(instr, 4, ds4, c4, f3);
alt(instr, 2, ds4, c4, gs3);
alt(instr, 2, f4, d4, as3);
alt(instr, 4, g4, ds4, c4);
alt(instr, 4, gs4, ds4, c4);
alt(instr, 4, g4, ds4, as3);
alt(instr, 4, g4, d4, b3);
}
// 64 beats
function bass2(instr) {
alt(instr, 4, gs4, ds4, c4);
alt(instr, 4, gs4, f4, c4);
alt(instr, 4, g4, ds4, c4);
alt(instr, 4, g4, ds4, as3);
alt(instr, 4, gs4, ds4, c4);
alt(instr, 4, gs4, f4, c4);
// join the crazy bit
patmerge(instr, 1/3, [a4, fs4, d4, a4, fs4, d4, a4, fs4, d4, a4, fs4, d4]);
patmerge(instr, 1/3, [a4, fs4, d4, a4, fs4, d4, a4, fs4, d4, a4, fs4, d4]);
// even crazier
patmerge(instr, 0.25, [b4, g4, d4, b3, g3, d3, c3, d3, g3, b3, d4, g4, b4, d5]);
patmerge(instr, 0.25, [b4, g4, d4, b3, g3, d3, c3, d3, g3, b3, d4, g4, b4, d5]);
patmerge(instr, 0.25, [b4, g4, d4, b3]);
}
// 64 beats
function bass3(instr) {
alt(instr, 4, ds4, c4, g3);
alt(instr, 4, ds4, c4, gs3);
alt(instr, 4, ds4, c4, a3);
alt(instr, 2, ds4, c4, gs3);
sleep(0.5);
pat(instr, 0.5, [c4, ds4, f4, fs4, f4, ds4, f4]);
alt(instr, 4, ds4, c4, g3);
alt(instr, 4, ds4, c4, gs3);
alt(instr, 4, ds4, c4, f3);
alt(instr, 2, ds4, c4, gs3);
alt(instr, 2, f4, d4, as3);
}
// 64 beats
function bass4(instr) {
alt(instr, 4, ds4, c4, g3);
alt(instr, 2, ds4, c4, gs3);
alt(instr, 2, d4, b3, g3);
alt(instr, 4, ds4, c4, g3);
alt(instr, 2, ds4, c4, gs3);
alt(instr, 2, d4, b3, g3);
alt(instr, 4, ds4, c4, g3);
alt(instr, 2, ds4, c4, gs3);
alt(instr, 2, d4, b3, g3);
alt(instr, 4, ds4, c4, g3);
alt(instr, 2, ds4, c4, gs3);
alt(instr, 2, d4, b3, g3);
}
// 64 beats
function lead1(instr) {
altpat(instr, g4, [c5, c5, d5, ds5, c5, c5, d5, ds5]);
altpat(instr, gs4, [c5, c5, d5, ds5, c5, c5, d5, ds5]);
altpat(instr, a4, [c5, c5, d5, ds5, c5, c5, d5, ds5]);
altpat(instr, gs4, [c5, c5, d5, ds5]);
altpat(instr, as4, [d5, d5, ds5, f5]);
patmerge(instr, 0.5, [c5, 0, c6, 0, as5, 0, g5, 0, f5, f5, ds5, 0, d5, ds5, 0, d5, d5, d5]);
patmerge(instr, 0.5, [c5, 0, d5, 0, ds5, gs5, gs5, gs5, c5, c5, d5, 0, ds5, 0]);
patmerge(instr, 1, [g5, as4, ds5, g5, gs5]);
patmerge(instr, 0.5, [g5, f5, f5, ds5, ds5, d5, d5, d5]);
patmerge(instr, 0.5, [b4, b4, d5, b4, f5, b4, g5, b4, f5, b4, ds5, b4, d5, b4]);
}
// 64 beats
function lead2(instr) {
patmerge(instr, 0.5, [c5, 0, c5, c5, d5, ds5, ds5, c5, 0, c5, 0, c5, d5, d5, ds5, ds5]);
patmerge(instr, 0.5, [c5, 0, c5, c5, d5, ds5, ds5, c5, 0, c5, 0, c5, ds5, ds5, d5, d5]);
patmerge(instr, 0.5, [c5, 0, c5, c5, d5, ds5, ds5, c5, 0, c5, 0, c5, d5, d5, ds5, ds5]);
patmerge(instr, 0.5, [g5, g5, as4, as4, ds5, g5, g5, gs5, gs5, g5, g5, f5, f5, ds5, ds5, 0]);
patmerge(instr, 0.5, [gs5, gs5, c5, 0, gs5, gs5, c5, 0, as5, as5, gs5, g5, 0, f5, f5, gs5]);
patmerge(instr, 0.5, [0, gs5, c5, 0, gs5, gs5, c5, 0, as5, as5, gs5, g5, 0, f5, f5, f5]);
// the wild one
// 24 * 1/3 = 8 beats
patmerge(instr, 1/3, [fs5, fs5, d5, a5, fs5, d5, as5, fs5, d5, c6, fs5, d5, as5, fs5, d5, a5, fs5, d5, g5, fs5, d5, fs5, fs5, d5]);
// the even wilder bit
patmerge(instr, 0.25, [g5, d5, b4, g4, d4, b3, g3, b3, d4, g4, b4, d5, g5, b5]);
patmerge(instr, 0.25, [g5, d5, b4, g4, d4, b3, g3, b3, d4, g4, b4, d5, g5, b5]);
patmerge(instr, 0.25, [g5, d5, b4, g4]);
}
// 64 beats
function lead3(instr) {
pat(instr, 0.5, [ds5, g4, c5, g4, ds5, g4, c5, g4, ds5, g4, c5, ds5, g4, f5, g4, ds5]);
pat(instr, 0.5, [gs4, ds5, c5, gs4, ds5, gs4, c5, gs4, ds5, gs4, c5, ds5, gs4, f5, gs4, ds5]);
pat(instr, 0.5, [a4, ds5, c5, a4, ds5, a4, c5, a4, ds5, a4, c5, ds5, a4, f5, a4, ds5]);
pat(instr, 0.5, [gs4, ds5, c5, gs4, ds5, gs4, c5, ds5, 0, c5, ds5, f5, fs5, f5, ds5, f5]);
pat(instr, 0.5, [c5, g4, c5, g4, d5, ds5, g4, g5, g4, c5, g4, c5, d5, g4, ds5, g4]);
pat(instr, 0.5, [gs5, gs4, c5, gs4, d5, ds5, gs4, gs5, gs4, c5, gs4, c5, d5, gs4, ds5, gs4]);
pat(instr, 0.5, [a5, a4, c5, a4, d5, ds5, a4, a5, a4, c5, a4, c5, d5, a4, ds5, a4]);
pat(instr, 0.5, [gs5, gs4, ds5, gs4, c5, gs5, gs4, g5, as4, f5, as4, f5, ds5, as4, d5, as4]);
}
// 64 beats
function lead4(instr) {
pat(instr, 0.5, [0, g4]); // 1
pat(instr, 1/3, [d5, ds5, d5, c5, g4, ds4, g4, c5, ds5]); // 3 -- 4
pat(instr, 0.25, [g5, f5, ds5, d5, c5, d5]); // 1.5 -- 5.5
pat(instr, 0.5, [c5]); // 0.5 -- 6
pat(instr, 0.25, [g5, ds5, c5, g4]); // 1 -- 7
pat(instr, 0.5, [0, g4]); // 1 -- 8
pat(instr, 1/3, [gs5, ds5, c5, gs5, ds5, c5, gs5, ds5, c5, as5, ds5, c5]); // 12
pat(instr, 1/3, [g5, d5, b4, f5, d5, b4, ds5, d5, b4, bs4, b4, g4]); // + 12 * 1/3 = 8
pat(instr, 1/3, [c5, g5, ds5, g5, ds5, c5, ds5, c5, g4, c5, g4, ds4]); // 12
pat(instr, 1/3, [c4, ds4, g4, c5, ds5, g5, c6, ds6, g6, ds6, c6, ds6]); // + 12 * 1/3 = 8
pat(instr, 0.25, [g5, 0]); // 0.5
patmerge(instr, 0.5, [g5, f5, 0, ds5, f5, 0, ds5, ds5, ds5]); // 4.5 -- 5
patmerge(instr, 0.25, [d5, 0, d5, d5, c5, 0, c5, c5, b4, 0, b4, b4]); // 3 -- 8
patmerge(instr, 0.25, [c5, c5, c5, c5, g5, 0, g5, g5, f5, 0, f5, f5, ds5, ds5]); // 3.5 -- 3.5
patmerge(instr, 0.25, [c5, c5, c5, 0, c5, c5, g5, 0, g5, g5, f5, 0, f5, f5, ds5, 0, ds5, ds5]); // 4.5 -- 8
pat(instr, 1, [c5]); // 1
patmerge(instr, 0.25, [g5, 0, g5, g5, f5, 0, f5, f5, ds5, ds5, c5, c5, c5, 0, c5, c5]); // 4 -- 5
patmerge(instr, 0.25, [g5, 0, g5, g5, f5, 0, f5, f5, ds5, 0, ds5, ds5]); // 3 -- 8
pat(instr, 1, [c5]); // 1
patmerge(instr, 0.25, [g5, 0, g5, g5, f5, 0, f5, f5, ds5, ds5, c5, c5, c5, 0, c5, c5]); // 4 -- 5
patmerge(instr, 0.25, [g5, 0, g5, g5, f5, 0, f5, f5, ds5, 0, ds5, ds5]); // 3 -- 8
pat(instr, 1, [c5]); // 1
patmerge(instr, 0.25, [g5, 0, g5, g5, f5, 0, f5, f5, ds5, ds5]); // 2.5 -- 3.5
pat(instr, 4.5, [c5]); // 4.5 -- 8
}
// 64 beats
function pad1(instr) {
pat(instr, 8, [c3, gs2, f2]);
pat(instr, 4, [gs2, as2]);
pat(instr, 8, [c3, gs3, ds3, g2]);
}
// 64 beats
function pad2(instr) {
pat(instr, 2, [gs2, gs3, ds3, c3]);
pat(instr, 2, [f3, c3, gs3, f3]);
pat(instr, 2, [c3, c4, g3, ds3]);
pat(instr, 2, [ds3, ds4, as3, g3]);
pat(instr, 2, [gs2, gs3, gs2, gs3]);
pat(instr, 2, [f2, f3, f2, f3]);
pat(instr, 2, [fs2, fs3]);
pat(instr, 1, [fs2, a2, d3, fs3]);
pat(instr, 8, [g3]);
}
// 64 beats
function pad3(instr) {
pat(instr, 8, [c3, gs2, f2]);
pat(instr, 4, [gs2]);
pat(instr, 0.5, [as2, c3, ds3, f3, fs3, f3, ds3, f3]);
pat(instr, 8, [c3, gs2, f2]);
pat(instr, 4, [gs2, as2]);
}
// 64 beats
function pad4(instr) {
pat(instr, 8, [c3]);
pat(instr, 4, [gs2, g2]);
pat(instr, 8, [c3]);
pat(instr, 4, [gs2, g2]);
pat(instr, 8, [c3]);
pat(instr, 4, [gs2, g2]);
pat(instr, 8, [c3]);
pat(instr, 4, [gs2, g2]);
}
// === Loops ===
loop(() => {
sleep(64);
lead1(piano);
lead2(piano);
lead3(piano);
lead4(piano);
}, { name: "Lead" });
loop(() => {
bass1(cello);
bass1(cello);
bass2(cello);
bass3(cello);
bass4(cello);
}, { name: "Bass" });
loop(() => {
pad1(sax);
pad1(sax);
pad2(sax);
pad3(sax);
pad4(sax);
}, { name: "Pad" });
// Animate the car
const road = "⬛⬛⬛⬛⬛⬛⬛⬛⬛";
const top = "⬛⬛⬛🟪⬛🟪⬛⬛⬛";
const bottom = "⬛⬛⬛🟪⬛🟪⬛⬛⬛";
const frames = [
"⬜⬜⬛🟪🟪🟪🟪⬛⬛",
"⬜⬛⬛🟪🟪🟪🟪⬛⬜",
"⬛⬛⬛🟪🟪🟪🟪⬜⬜",
"⬛⬛⬛🟪🟪🟪🟪⬜⬛",
"⬛⬛⬛🟪🟪🟪🟪⬛⬛",
"⬛⬛⬛🟪🟪🟪🟪⬛⬛",
"⬛⬛⬛🟪🟪🟪🟪⬛⬛",
"⬛⬛⬜🟪🟪🟪🟪⬛⬛",
"⬛⬜⬜🟪🟪🟪🟪⬛⬛",
];
loop(() => {
// print the car
const rate = 4;
debug.log(1, road);
debug.log(2, top);
debug.log(3, frames.ring(ditty.tick * rate));
debug.log(4, bottom);
debug.log(5, road);
sleep(1 / rate);
}, { name: "Car" });