// Forked from "Backbeat experiment" by athibaul
// https://dittytoy.net/ditty/ee9a7420a7
const PI = 3.1415926;
const sin = Math.sin;
const cos = Math.cos;
const abs = Math.abs;
const rnd = () => Math.random() * 2 - 1;
function sinsaw(p,f) {
p = p%1;
return (1-p) * sin(2*PI*f*p);
}
const tri = (p) => 2*abs((p/*+.25*/)%1 - 0.5) - .5;
function softsat(x) {
x = clamp(x,-1,1);
return x*(3-x*x)/2;
}
const ssaw = synth.def(
(p,e,t,o) => sinsaw(p+o.pho,o.f)*e.value,
{attack:1e-3, release:0.2, duration:0.0, f:5, pho: 0}
);
const kick = synth.def(
(p,e,t,o) => softsat(o.a*tri(o.f0*t*(2*o.d-t))*clamp01(o.d-t)),
{f0:500,d:0.2,a:6}
);
const hp = filter.def(class {
constructor(opt) {
this.l = [0, 0];
}
process(inp, opt) {
this.fc = opt.fc || .3;
this.l[0] += (inp[0] - this.l[0]) * this.fc;
this.l[1] += (inp[1] - this.l[1]) * this.fc;
return [inp[0] - this.l[0], inp[1] - this.l[1]];
}
});
ditty.bpm = 150;
loop( (i) => {
if(!i) sleep(32);
//let f = 5**(1-cos(2*PI*i/64));
let f = 10**((i/64)%1);
let amp = 3 - abs((i%4)-2);
f *= amp/3;
//ssaw.play(a3, {f:2+Math.random(), amp:amp, pan:rnd()*.1, release: .1});
sine.play(a1, {f:2+Math.random(), amp:amp, release: .1});
//ssaw.play(a2, {f:f+rnd(), amp:amp, pan:rnd()});
//ssaw.play(a1, {f:2 + f/4, amp:amp});
sleep(0.25);
}, { name: 'bass', amp: .2 });
const wtf = filter.def(class {
constructor() {
}
process(inp) {
let am = 1;
inp[0] = tri(inp[0]*am)/am*.2;
inp[1] = tri(inp[1]*am)/am*.2;
return inp;
}
});
loop( (j) => {
if(!j) sleep(64);
let notes = ditty.tick < 160 ? [f1] : [a1, c2, e2, e2];
let x = notes[j % notes.length];
if(1) {
sine.play(x+12, {amp:-.6, duration:1.4, release:.001});
ssaw.play(x-.3, {f:1.5, amp:-1, duration:1.4, pan: -.5, release:.1});
ssaw.play(x+.3, {f:1.5, amp:1, duration:1.4, pan: .5, release:.1});
sleep(1.5);
sine.play(x+12, {amp:.6, duration:1.5, release:.001});
ssaw.play(x-.3, {f:1.5, amp:1, duration:1.5, pan: -.5, release:.1});
ssaw.play(x+.3, {f:1.5, amp:1, duration:1.5, pan: .5, release:.1});
sleep(2.5);
if(1) {
let fa = [t => Math.sin(clamp01(t-.5)*Math.PI)*12, t=>0].choose();
sine.play((t,o)=>x+12+fa(t), {amp:(t,o)=>.6+t*.2, duration:3.7, release: 0.001});
ssaw.play((t,o)=>x-.9+fa(t), {f:1.5, amp:1, duration:3.7, pan: -.5});
ssaw.play((t,o)=>x+.9+fa(t), {f:1.5, amp:1, duration:3.7, pan: .5});
}
sleep(4);
}
else {
sleep(1);
for(let i = 0; i < 4; ++i) {
sine.play(x+12, {amp:-.6, duration:.4, release:.001, decay: .1, sustain: .9});
ssaw.play(x-.3, {f:1.5, amp:-1, duration:.4, pan: -.5, release:.1, decay: .1, sustain: .9});
ssaw.play(x+.3, {f:1.5, amp:1, duration:.4, pan: .5, release:.1, decay: .1, sustain: .9});
sleep(.75);
}
sine.play(x+12, {amp:-.6, duration:2.8, release:.001});
sine.play(x+24+7, {amp:.2, duration:2.8, release:.001});
ssaw.play(x-.1, {f:1.5, amp:-1, duration:2.8, pan: -.5, release:.1});
ssaw.play(x+.1, {f:1.5, amp:1, duration:2.8, pan: .5, release:.1});
sleep(3);
}
}, { name: 'bass2' })
.connect(wtf.create())
.connect(hp.create({fc:() => 2**lerp(-1, -6.64, clamp01((ditty.tick-64)/64))}));
loop( (i) => {
if(!i) sleep(128);
let pattern = "k...k..k.kk...k.";
if(pattern.charAt(i%16) == "k") {
let r = 1.0 + 0.1*rnd();
let dm = 1;
kick.play(c4, {f0:20,d:1.0*dm,a:1.0}); // bass
//sine.play((t) => a1 + 3*(0.5-t), {attack:0.0, release:0.2, duration:0.5});
kick.play(c4, {f0:300*r,d:0.2*dm,a:6}); // thump
kick.play(c4, {f0:1e8*r,d:0.01*dm,a:20}); // click
kick.play(c4, {f0:1e7*r,d:0.05*dm,a:3}); // click, softer
}
sleep(0.25);
}, {name:'kick', amp:.5});
loop( (i) => {
if(!i) sleep(32);
let pattern = "..S.....S....";
if(pattern.charAt(i%16) == "S") {
let r = 1.0 + 0.1*rnd();
kick.play(c4, {f0:600*r,d:0.15,a:2}); // thump
kick.play(c4, {f0:1e7,d:0.15,a:6*r,amp:0.3}); // noise
kick.play(c4, {f0:6e7*r,d:0.02,a:30,amp:0.7}); // click
} else if (pattern.charAt(i%16) == "s") {
let r = 1.0 + 0.5*rnd();
kick.play(c4, {f0:300*r,d:0.2,a:1}); // thump
kick.play(c4, {f0:1e7,d:0.2,a:3*r,amp:0.1}); // noise
kick.play(c4, {f0:6e7*r,d:0.02,a:30,amp:0.2}); // click
}
sleep(0.25);
}, {name:"snare"});
loop( (i) => {
if(!i) sleep(32);
let pattern = 'k.k.s.kskskks.ks';
for(let j=0; j<pattern.length; j++) {
let r = 1.0 + 0.2*rnd();
switch(pattern.charAt(j)) {
case 'k':
kick.play(c4, {f0:200*r,d:0.3,a:20,amp:0.3,pan:rnd()}); // thump
kick.play(c4, {f0:1e6*r,d:0.2,a:3,amp:0.1,pan:rnd()}); // noise
break;
case 's':
kick.play(c4, {f0:300*r,d:0.3,a:20,amp:0.07,pan:rnd()}); // thump
kick.play(c4, {f0:1.5e6*r,d:0.3,a:3,amp:0.15,pan:rnd()}); // noise
break;
}
let d = 0.25;
sleep(d);
}
}, {name:"backbeat"}).connect(hp.create());
loop((i) => {
if(!i) sleep(32);
let pattern = 'ccc.cc..c..co.c.c.';
for(let j = 0; j < 16; ++j) {
switch(pattern.charAt(j)) {
case 'c':
kick.play(c4, {f0:5e6,d:0.05,a:30,amp:0.2,pan:rnd()*.5}); // click, softer
break;
case 'o':
kick.play(c4, {f0:1.5e6,d:.2,a:30,amp:0.2,pan:rnd()*.5}); // click, softer
break;
}
sleep(0.25);
}
}, {name:"hat"}).connect(hp.create());
class Swarm {
constructor(opt) {
this.op = [];
this.wf = sinsaw;
this.jtu = 0;
let nu = 10;
let pat = [0,7];
for(let i = 0; i < nu; ++i) {
let k = i/(nu-1);
let pa = k*2-1;
this.op.push({
p:Math.random(),
f:2**(pat[i%pat.length]/12),
ml:clamp01(pa+1),
mr:clamp01(1-pa),
jt:2**(rnd()*opt.det)
});
}
}
process(n, e, t, opt) {
this.jtu += ditty.dt*opt.jtf;
if(this.jtu >= 1) {
this.jtu -= 1;
for(let i = 0; i < this.op.length; ++i)
this.op[i].jt = 2**(rnd()*opt.det);
}
let dt = midi_to_hz(n)*ditty.dt;
let mix = [0,0];
for(let i = 0; i < this.op.length; ++i) {
let op = this.op[i];
mix[0] += this.wf(op.p, t*.1+i*.2) * op.ml;
mix[1] += this.wf(op.p, t*.1+i*.2) * op.mr;
op.p += dt * op.f * op.jt;
}
mix[0] *= e.value;
mix[1] *= e.value;
return mix;
}
}
const pad = synth.def(Swarm);
loop( (i) => {
let padChords = [
[a1,c5,es5]
];
let du = 64;
let ch = padChords.ring(i);
for(let j=0; j<ch.length; j++) {
pad.play(ch[j], {attack:.1,decay:.1,sustain:.5,release:8,duration:0, det:0.4,jtf:(x,t)=>800*Math.exp(-x*.5)});
}
sleep(du);
}, {name:"fx", amp: .1}).connect(hp.create({fc:.05}));
loop( (i) => {
let padChords = [
[a4,e4,c5,es4],
[c4,c3,e4,b4],
[e4,e3,e4,g4],
[e4,e3,g4,a4]
];
let du = 16;
let ch = padChords.ring(i);
for(let j=0; j<ch.length; j++) {
let rrr = Math.sin(j*646+i) * 32 + a5;
pad.play(ch[j], {attack:4,release:4,duration:du, det:0.03,jtf:60});
}
sleep(du);
}, {name:"pad", amp:()=>0.05*clamp01((90-ditty.tick)/16)}).connect(hp.create({fc:.0001}));