input.cutoff = 1.0; input.filterDecay = 0.5; input.attack = 0.0; input.decay = 0.5; input.sustain = 0.8; input.release = 0.5; function softclip(x) { return x < -1 ? -1 : x > 1 ? 1 : 1.5*(1 - x*x/3)*x; } function varsaw(p, formant) { let x = p%1; return (x - 0.5) * softclip(formant*x*(1-x)); } const sawPluck = synth.def( (phase, env, tick, options) => varsaw(phase, options.cutoff / midi_to_hz(options.note) * Math.exp(-options.decaySpeed*tick)) * env.value, { env:adsr, duration:0.5, cutoff:() => 200 * 100**input.cutoff, decaySpeed:() => 1 * 100**(1-input.filterDecay), attack:() => 0.01 * 1000**input.attack, decay:() => 0.01 * 1000**input.decay, sustain:() => input.sustain, release:() => 0.01 * 1000**input.release, } ); ditty.bpm = 120; const nn = [g2,c2,d2,bb1]; loop( (i) => { sawPluck.play(nn.ring(Math.floor(i/8))); sleep(0.5); });