ditty.bpm = 142; const osc = synth.def( (phase, env, tick, options) => ((phase%2. - 1.) * Math.sin(phase * 2 * Math.PI)) * env.value ); // Reverb by srtuss class Delayline { constructor(n) { this.n = ~~n; this.p = 0; this.lastOut = 0; this.data = new Float32Array(n); } clock(input) { this.lastOut = this.data[this.p]; this.data[this.p] = input; if(++this.p >= this.n) { this.p = 0; } } tap(offset) { var x = this.p - offset - 1; x %= this.n; if(x < 0) { x += this.n; } return this.data[x]; } } function allpass(delayline, x, k) { var delayin = x - delayline.lastOut * k; var y = delayline.lastOut + k * delayin; delayline.clock(delayin); return y; } const reverb = filter.def(class { constructor(options) { this.lastReturn = 0; this.krt = 0.7; this.delaylines = []; // Create several delay lines with random lengths for(var i = 0; i < 12; ++i) { this.delaylines.push(new Delayline(10 + Math.floor(Math.random() * 5000))); } } process(input, options) { var inv = input[0] + input[1]; var v = this.lastReturn; // Let the signal pass through the loop of delay lines. Inject input signal at multiple locations. v = allpass(this.delaylines[0], v + inv, .5); v = allpass(this.delaylines[1], v, .5); this.delaylines[2].clock(v); v = this.delaylines[2].lastOut * this.krt; v = allpass(this.delaylines[3], v + inv, .5); v = allpass(this.delaylines[4], v, .5); this.delaylines[5].clock(v); v = this.delaylines[5].lastOut * this.krt; v = allpass(this.delaylines[6], v + inv, .5); v = allpass(this.delaylines[7], v, .5); this.delaylines[8].clock(v); v = this.delaylines[8].lastOut * this.krt; v = allpass(this.delaylines[9], v + inv, .5); v = allpass(this.delaylines[10], v, .5); this.delaylines[11].clock(v); v = this.delaylines[11].lastOut * this.krt; this.lastReturn = v; // Tap the delay lines at randomized locations and accumulate the output signal. var ret = [0, 0]; ret[0] += this.delaylines[2].tap(111); ret[1] += this.delaylines[2].tap(2250); ret[0] += this.delaylines[5].tap(311); ret[1] += this.delaylines[5].tap(1150); ret[0] += this.delaylines[8].tap(511); ret[1] += this.delaylines[8].tap(50); ret[0] += this.delaylines[11].tap(4411); ret[1] += this.delaylines[11].tap(540); // Mix wet + dry signal. ret[0] = ret[0] * .2 + input[0]; ret[1] = ret[1] * .2 + input[1]; // // Slight stereo widening: var m = (ret[0] + ret[1]) * .5; var s = (ret[1] - ret[0]) * .5; ret[0] = m + s * 1.5; ret[1] = m - s * 1.5; return ret; } }); var x = 0.5; var y = 0.5; loop( (loopCount) => { var r = Math.random(); if(r<0.33){ x = x*0.5; y = y*0.5; } else if(r<0.66){ x = (x+1)*0.5; y = y*0.5; } else{ x = y*0.5; y = (x+1)*0.5; } debug.log ("x", x); debug.log ("y", y); osc.play(48 + x*48, { attack: 0.01, release: 0.25, duration: 0.1, pan: x*2. - 1., amp: 0.88 }); sleep(0.25 + y); }, { name: 'Sierpinski' }).connect(reverb.create());