Filtered sawtooth

Defining a synth which generates the "filtered sawtooth" sound, characteristic of analog synths

Log in to post a comment.

input.cutoff=0.5; // min=0, max=1, step=0.001
ditty.bpm = 60; // 1 tick = 1 second

const smoothstep = (a,b,x) => { x = clamp01((x-a)/(b-a)); return x*x*(3-2*x); };
const linexp = (x,x0,x1,y0,y1) => y0 * (y1/y0) ** ((x-x0)/(x1-x0));

const osc = synth.def(
    class {
        constructor(options) {
            this.phase = Math.random();
            //this.ampHz = linexp(Math.random(), 0, 1, 0.1, 2.0);
            //this.ampPhase = Math.random();
        }
        process(note, env, tick, options) {
            let freq = midi_to_hz(note);
            // Periodic amplitude variation (tremolo)
            let amp = 1 + 0.5*Math.sin(2*Math.PI * (tick*options.ampHz + options.ampPhase));
            // Also affect the cutoff
            let cfreq = linexp(input.cutoff * amp**0.5, 0,1, 300, 20000);
            let w = clamp01(freq/cfreq) * 0.5;
            this.phase = (this.phase + freq * ditty.dt) % 1;
            let x = this.phase;
            let sig = lerp(2*x, 2*(x-1), smoothstep(0.5-w,0.5+w,x));
            return sig * env.value * amp;
        }
    }, { xenv: 0.5, amp: 1, attack: 0.01, release: 0.3 }
);


// Cmin9 chord
chord = [36,48,55, 58,62,63,67];
for(let j=0; j<7; j++) {
    let freq = midi_to_hz(chord[j]);
    for (let i=0; i<1; i++) { // Create a "supersaw" sound using several synths if your computer can handle it
        osc.play(chord[j]+(Math.random()-0.5)*0.05, // slight detune
        { 
            attack: 5,
            duration: 60,
            release: 10,
            amp: 0.2 * (440/freq)**0.5, // higher amplitude for low notes
            ampHz: 0.2 * (1.2)**j, // higher notes have faster tremolo
            ampPhase: Math.random(),
        });
    }
}