Cosmic Drone

droning chords using wavetable synthesis

Log in to post a comment.

// 2022/10/17
// by srtuss
ditty.bpm = 90;
input.radiation = .25; // min=0.1,max=.5,step=0.01
input.nebula = 0; // min=0,max=2,step=0.01

function synthharmonics(outp, harm, len, nh) {
    for(var j = 0; j < len; ++j) {
        outp[j] = 0;
    }
    var twopi = Math.PI * 2;
    for(var i = 0; i < nh; ++i) {
        var f = i + 1;
        var amp = harm[i];
        var phas = Math.random();
        for(var j = 0; j < len; ++j) {
            var x = j / len + phas;
            outp[j] += Math.sin(x * f * twopi) * amp;
        }
    }
}
function fract(x) {
    return x - Math.floor(x);
}

class MorphWave {
    constructor(len, nharm) {
        this.len = len;
        this.data = new Float32Array(len);
        this.nharm = Math.min(nharm || 512, len>>1);
        this.update();
    }
    update() {
        var nh = this.nharm;
        var harm = new Float32Array(nh);
        for(var i = 0; i < nh; ++i) {
            harm[i] = .3 * (1 + i)**-1 * Math.exp(-i * input.nebula);
        }
        for(var i = 0; i < nh; i += 1) {
            harm[i] *= Math.pow(Math.random(), 3);
        }
        synthharmonics(this.data, harm, this.len, nh);
    }
}

const syn = synth.def(
    class {
        constructor(opt) {
            this.nwtb = 1024;
            this.wtbs = [new MorphWave(this.nwtb, opt.nh), new MorphWave(this.nwtb, opt.nh)];
            for(var k = 0; k < 2; ++k) {
                this.wtbs[k].update();
            }
            this.ops = [];
            var nop = 8;
            for(var i = 0; i < nop; ++i) {
                this.ops.push({
                    t: Math.random(),
                    ff: 1 + Math.random() * .01,
                    p: i / (nop-1)});
            }
            this.morph = Math.random();
            this.shimmerc = 0;
            this.morphdt = .000005;
        }
        process(note, env, tick, options) {
            var suml = 0;
            var sumr = 0;
            var dt = midi_to_hz(note) * ditty.dt;
            this.morph += this.morphdt;
            if(this.morph >= 1) {
                this.morph -= 1;
                var t = this.wtbs[0]; this.wtbs[0] = this.wtbs[1]; this.wtbs[1] = t;
                this.wtbs[1].update();
            }
            var wtb0 = this.wtbs[0].data;
            var wtb1 = this.wtbs[1].data;
            var wtbm = this.morph;
            
            if(this.shimmerc >= 1) {
                this.shimmerc -= 1;
                for(var i = 0, ni = this.ops.length; i < ni; ++i) {
                    this.ops[i].ff = 2 ** ((Math.random() - .5) * input.radiation);
                }
            }
            this.shimmerc += 700 * ditty.dt;
            
            for(var i = 0, ni = this.ops.length; i < ni; ++i) {
                var op = this.ops[i];
                var idx = Math.floor(op.t * this.nwtb);
                var opv = wtb0[idx] * (1-wtbm) + wtb1[idx] * wtbm;
                suml += opv * op.p;
                sumr += opv * (1 - op.p);
                op.t += dt * op.ff;
                while(op.t >= 1) {
                    op.t -= 1;
                }
            }
            var m = (suml + sumr)*.5;
            var s = (suml - sumr)*.5;
            s *= 2;
            return [(m+s) * env.value, (m-s) * env.value];
        }
    });

loop(() => {
var k = [0, 2, -2, -4];
for(var i = 0; i < 3; ++i) {
    var o = k[i];
    var chord = [d4, c4, e4, b4];
    sine.play(c1+o, {duration: 16, attack: 5, release: 5, amp: .5});
    syn.play(c2+o, {duration: 16, attack: 5, release: 5, nh: 64});
    for(var j = 0; j < chord.length; ++j) {
        syn.play(chord[j]+o + [-12,0,0,0,7,12].choose(), {duration: 16, attack: 5+j, release: 5, nh: 32});
    }
    sleep(16);
}
}, {name: "pad"});