JSTechno

sadassdaa

Log in to post a comment.

// 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;

//effects
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;
}

// Simple allpass reverberator, based on this article:
// http://www.spinsemi.com/knowledge_base/effects.html
const reverb = filter.def(class {
    constructor(options) {
        this.lastReturn = 0;
        this.krt = .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] * .1 + input[0];
        ret[1] = ret[1] * .1 + 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;
    }
});
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]];
    }
});
const lowpass = filter.def(class {
    constructor(options) {
        this.hist0 = [0, 0];
        this.hist1 = [0, 0];
    }

    process(input, options) {
        const alpha = options.cutoff;

        if (input) {
            for (let i = 0; i < 2; i++) {
                this.hist0[i] += alpha * (input[i] - this.hist0[i]);
                this.hist1[i] += alpha * (this.hist0[i] - this.hist1[i]);
            }

            return this.hist1;
        }
    }
}, { cutoff: 1 });
ditty.bpm = 144;

function pumper(x, opt) {
    // Ensure the pumpRate is passed or defaults to 0.5 if not provided
    let pumpRate = tick_to_second(opt.pumpRate) || 0.5;
    
    // Use timeStep for updating time (simulating the passage of time)
    let time = opt.time || 0;
    
    // Calculate amplitude using a sine wave for the pumping effect
    let amplitude = Math.sin(2 * Math.PI * time * pumpRate) * 0.5 ;
    debug.log  ( "amp: ", amplitude )
    debug.log  ( "x: ", x )
    debug.log  ( "xamp: ", x + amplitude / amplitude )
    // Apply the amplitude modulation to the input signal
    return  x + amplitude / amplitude ;
}

loop( (i) => {
    if(!i) sleep(0);
    //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( (i) => {
    if(!i) sleep(0);
    let pattern = "k...k.s.k...k.ks";//"k.......k......."
    if (ditty.ticks > 32){
        pattern = "k...k.s.k...k.ks";//"k.....s.k.....ks"
    }
    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
    }
    else if (pattern.charAt(i%16) == "s") {
        let r = 1.0 + 0.5*rnd();
        kick.play(c3, {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:'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.05,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(0);
    let pattern = 's.......k.......';
    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()).connect(reverb.create());
loop((i) => {
    if(!i) sleep(32);//32
    let pattern = '..oc..o...o...o.';//'ccc.cc..c..co.c.c.'
    if (ditty.ticks > 96){
        pattern = '..oc..o...o...o.';
    }
    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());

input.droneSpeed1 = 0.4;
input.droneSpeed2 = 0.07;


let pianoData = [24,25,26,27,28,29,30,31,32,33,34,35];
const piano = synth.def(
    class {
        constructor(options) {
            // The value of the note argument of the play call is retrievable via options.note.
            this.phase = 0;
            this.c = 0
        }
        process(note, env, tick, options) {
            this.phase += ditty.dt * midi_to_hz(note) / (pianoData.length * input.speed1);
            let index = Math.round(clamp01(this.phase) * pianoData.length);
            let value = clamp01(pianoData[index] / 120);
            if (isNaN(value)) return 0;
            return [
                Math.cos(index * (60*input.speed2)) * value, 
                Math.sin(index * (60*input.speed2)) * value,
            ];
        }
    }
);
loop( (i) => {
        
    if(!i) sleep(32);
    
       piano.play(c3, { 
                    amp: 0.5, 
                    duration: 1, 
                    pan: 0.5 + rnd * (-1)
                }); 
    sleep(0.25);  
   
    
            
 
  
}, { name: `drone synth`,  amp: 0.25 })/* everything sounds nicer with fx */.connect(reverb.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}));
let pump = pumper(0.0001, {
            pumpRate: 1,
            time: ditty.dt
        });
loop( (i) => {
    let padChords = [
        [a4,e4,c5,es4],
        [c4,c3,e4,b4],
        [a4,e4,c5,es4],
        [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.1}).connect(hp.create({fc:.0001})).connect( lowpass.create( { cutoff: () => pump}));