Christmas Planet

Log in to post a comment.

// srtuss, 2023

ditty.bpm = 100;
// athibaul's reverb filter with added modulation
class Delayline { constructor(n) { this.n = n; this.p = 0; this.data = new Float32Array(n); } get end() { return this.data[this.p]; } tap(offset) { var x = this.p + offset; x = ~~x; x %= this.n; return this.data[x]; } sample(offset) { if(offset < 0) offset = 0; var p = offset; var pi = ~~p; return lerp(this.tap(pi), this.tap(pi+1), p-pi); } clock(input) { var end = this.data[this.p]; this.data[this.p] = input; if(++this.p >= this.n) { this.p = 0; } return end; } }  const ISQRT2 = Math.sqrt(0.5); const SQRT2 = Math.sqrt(2); const SQRT8 = Math.sqrt(8); const ISQRT8 = 1/SQRT8;  const fibodelays = [1467,1691,1932,2138,2286,2567,3141,3897]; const fiboshort = [465, 537, 617, 698, 742, 802, 963, 1215]; let meandelay = fibodelays[3] * ditty.dt;
const reverb = filter.def(class { constructor(options) { this.outgain = 0.3; this.predl = [new Delayline(2), new Delayline(2)]; this.dls = []; this.s0 = new Float32Array(8); for(let i=0; i<8; i++) { this.dls.push(new Delayline(fibodelays[i])); this.dls[i].i = i; this.s0[i] = 0; } this.a0 = clamp01(2*Math.PI*options.cutoff*ditty.dt);  this.dls_nr = []; for(let i=0; i<4; i++) { this.dls_nr.push(new Delayline(fiboshort[i*2])); } } process(inpt, options) {  let s0 = this.predl[0].clock(inpt[0]), s1 = this.predl[1].clock(inpt[1]); for(let i=0; i<4; i++) { let u0 = 0.6*s0 + 0.8*s1, u1 = 0.8*s0 - 0.6*s1; let u1p = this.dls_nr[i].end; this.dls_nr[i].clock(u1); s0 = u0; s1 = u1p; } let v0 = (s0 + s1); let v1 = (s0 - s1);     let rt60 = options.rt60; let loopgain = 10 ** (-3*meandelay / rt60) * ISQRT8; let higain = 10 ** (-3*meandelay / options.rtHi) * ISQRT8; let mod = 10; let v = this.dls.map( (dl) => dl.sample((1 + Math.sin(ditty.tick * (3 + dl.i * 2))) * mod));   let w = [v[0]+v[4], v[1]+v[5], v[2]+v[6], v[3]+v[7], v[0]-v[4], v[1]-v[5], v[2]-v[6], v[3]-v[7]]; let x = [w[0]+w[2], w[1]+w[3], w[0]-w[2], w[1]-w[3], w[4]+w[6], w[5]+w[7], w[4]-w[6], w[5]-w[7]]; let y = [x[0]+x[1], x[0]-x[1], x[2]+x[3], x[2]-x[3], x[4]+x[5], x[4]-x[5], x[6]+x[7], x[6]-x[7]]; y[0] += v0; y[2] += v1; for(let i=0; i<8; i++) { let hipass = y[i] - this.s0[i]; let lopass = this.s0[i]; this.dls[i].clock(lopass * loopgain + hipass * higain); this.s0[i] += this.a0 * hipass; }  return [lerp(inpt[0], v[0], options.mix), lerp(inpt[1], v[2], options.mix)]; } }, {mix:1, rt60:10, cutoff:20000, rtHi:0.9});

const TAU = Math.PI * 2;
const wave0 = p => Math.sin(p * TAU);
const wave1 = p => Math.cos(clamp((p+10) % 1, 0, .5) * TAU * 2) - .5;
const sy = synth.def((p,e,t,o) => wave1(p) * e.value);

loop( (lc) => {
    var chords = [
        [a5, a6, c7, e7, c8],
        [a5, a6, b6, e7, a7],
        [e5, g6, b6, e7, b7],
        [e5, g6, b6, d7, b7],
        [f5, f6, a6, c7, a7],
        [f5, d6, f6, a6, f7],
        [d5, d6, g6, a6, c8],
        [d5, d6, fs6, a6, d8]
    ];
    var tp = -18 + (lc&1)*12 - 12;
    chords.forEach(chord => {
        chord.forEach(note => {
            sy.play(note + tp, {duration: 4, attack: .1, release: 1, amp: .1});
        });
        sy.play(chord[0] + tp - 24, {duration: 4, attack: .1, release: 1, amp: .1});
        for(let i = 0; i < 16; ++i) {
            sy.play(chord[4 - i % 5] + tp, {duration: 0, attack: 0.01, release: .1, amp: .5, pan: Math.random() - .5});
            sleep(.25);
        }
    });

}, { name: 'synth' })
    .connect(reverb.create());