Shared filter: compressor

Testing the new "shared filters" feature - a compressor!

Log in to post a comment.

const compressor = filter.def(class {
    constructor(options) {
        this.gain = 1;
    }
    process(input, options) {
        let sigdb = 10 * Math.log10(input[0]**2 + input[1]**2);
        let overdb = sigdb - options.threshold;
        if(overdb < 0) {
            overdb = 0;
        }
        debug.probe("overdb", overdb, 10, 2);
        let target_gaindb = overdb * (1 - options.ratio)/options.ratio;
        let target_gain = 10 ** (target_gaindb / 20);
        let time = this.gain > target_gain ? options.attack : options.release;
        let a0 = clamp01(2.3 * ditty.dt / time); // 'time' to go to within 10% of the final value
        this.gain = lerp(this.gain, target_gain, a0);
        let gr = 20 * Math.log10(this.gain);
        debug.probe("gain reduction", gr, 10, 2);
        
        return [input[0] * this.gain, input[1] * this.gain];
    }
}, {threshold:-18, ratio:2, attack:0.005, release:0.2});

let comp1 = compressor.createShared({ratio:4, attack:0.008, release:0.7, threshold:-12});

ditty.bpm = 120;

/*
loop( (i) => {
    sine.play(c4, {env:adsr, attack:0.001, release:0.01, duration:1});
    sleep(1);
    sine.play(c4, {env:one, attack:0.001, release:0.01, duration:1, amp:0.1}); 
    sleep(1);
}).connect(compressor.create({attack:0.01, release:0.2}));
*/

const linexp = (x,x0,x1,y0,y1) => y0 * (y1/y0) ** ((x-x0)/(x1-x0));
const linlin = (x,x0,x1,y0,y1) => y0 + (y1 - y0) * clamp01((x-x0)/(x1-x0));

const tri = synth.def((phase, env) => 1.22*(4.*Math.abs(phase % 1 - 0.5) - 1.) * env.value);
const dist = synth.def((phase, env, tick, options) => Math.atan(25*(Math.abs(phase % 1 - 0.5) - 0.25) * env.value));

let rhythm = [16,
              1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.5,0.5,
              1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.5,0.5,
              ];
loop( (i) => {
    // click
    //sine.play((tick) => linlin(tick, 0, 0.02, 127, 0), {attack:0.0005, release:0.05, amp:0.1});
    // boom
    sine.play(
        (tick) => linlin(tick, 0, 0.2, a1, f1),
        //{env: adsr2, attack:0.01, decay:0.1, decay_level:0.1, sustain:0.15, sustain_level:0.5, release:0.2, amp:2}
        {env: adsr, attack:0.01, release:0.2, amp:0.8}
    );
    // low thump
    tri.play(
        (tick) => linlin(tick, 0, 0.1, e3, f1),
        {env: adsr, attack:0.005, release:0.1, amp:0.6}
    );
    // high tump
    
    tri.play(
        (tick) => linlin(tick, 0, 0.03, a5, a3),
        {env: adsr, attack:0.005, release:0.03, amp:0.1}
    );
    
    sleep(rhythm.ring(i));
    
}, {name:"kick"})
.connect(comp1);

const sin2p = (x) => Math.sin(2*Math.PI*x);
const fmsynth = synth.def( (phase, env, tick) => sin2p(phase + 0.5*sin2p(phase)* env.value) * env.value);

const basenotes = [c3,d3,e3,f3,g3,a3];
const M7 = chords.major7, M6 = chords['6'], m7 = chords.m7;
const myChords = [[M7, M6], [m7], [m7], [M7, M6], [M6], [m7]];
let basenote = basenotes[0];
let currentChord = myChords[0].choose();
let offset = 1;
loop( (i) => {
    if(i%16 == 0) {
        //basenote = c3 + Math.floor(12 * Math.random());
        //basenote = basenotes.ring(Math.floor(i/16));
        let idx = 0;
        do {
            idx = Math.floor(basenotes.length * Math.random());
        } while (basenote == basenotes[idx]);
        basenote = basenotes[idx];
        //currentChord = myChords.ring(Math.floor(i/16));
        currentChord = myChords[idx].choose();
        offset = [1,3].choose();
    }
    fmsynth.play(basenote - 12, {amp:0.1, release:0.5, pan:2*Math.random()-1}); // spacy bass
    fmsynth.play(basenote + currentChord.ring(i+offset), {amp:0.1, attack:0.001, release:1, pan:Math.random()-0.5}); // arpeggios
    sleep(0.25);
}, {name:"synth+bass"})
.connect(comp1);

const leadsynth = synth.def(
    (phase, env, tick, options) => sin2p(clamp01((phase%1) * (options.formant + options.dformant*sin2p(options.formhz*tick_to_second(tick))))) * env.value,
    {attack:0.1, release:0.5, duration:1, amp:0.2, formant:4, dformant:0.6, formhz:5}
);

const penta = [-100, -100, c4, d4, e4, g4, a4];
loop( (i) => {
    let note = penta.choose();
    leadsynth.play(note, {formant: 3 + 3*Math.random()});
    leadsynth.play(note-12.03, {formant: 6, amp:0.1, pan:Math.random()-0.5});
    sleep(1);
    leadsynth.play(note-0.05, {amp:0.05, release:2, formant:3, dformant:0.3, formhz:3, pan:-0.5});
    leadsynth.play(note+0.05, {amp:0.05, release:2, formant:2.8, dformant:0.3, formhz:2.62, pan:0.5});
    sleep(3);
}, {name:"lead"})
.connect(comp1);