A small remake of the classic riff by . I wanted to create a distortion effect and it turns out that FM modulation is quite usable for creating that effect when fiddling with the modulation frequency - here, it has to be really close to but not exactly a quarter of the carrier frequency to yield a somewhat convincing result. Other values just sound meh.
Log in to post a comment.
const sin = Math.sin; const TAU = 2*Math.PI; ditty.bpm = 120; // synths const kick = synth.def( (phase, env, tick, options) => sin(phase*TAU*(tick<0.5?0.5-tick:0)) * env.value); const strat = synth.def( class { constructor (options) { this.t = 0 this.iom = options.iom || 13.37; this.mul = options.multiplier || 0.251; } add_fm(note, env) { // carrier frequency const fc = midi_to_hz(note); // modulator frequency const fm = fc * this.mul; return sin( TAU*this.t*fc + this.iom*sin(TAU*this.t*fm) ) * env.value; } process(note, env, tick, options) { // forward time this.t += ditty.dt; // overdrive the oscillators const gain = 5; // create power-chord, i.e. perfect fifth let osc = this.add_fm(note, env) * gain; osc += this.add_fm(note+7, env) * gain; // clip osc = osc < -1 ? -1 : osc > 1 ? 1 : osc; return [osc, osc]; // left, right } }); // filters // from https://dittytoy.net/syntax#filters const lowpass = filter.def(class { constructor(options) { this.hist0 = [0, 0]; this.hist1 = [0, 0]; this.t = 0; } process(input, options) { const alpha = clamp(options.cutoff, 0.01, 1); 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; } } }); // notes const riff = [ [d4, 1], [f4, 1], [g4, 1.5], [d4, 1], [f4, 1], [gs4, 0.5], [g4, 2], [d4, 1], [f4, 1], [g4, 1.5], [f4, 1], [d4, 0.5], [d4, 3], ]; let i = 0; // loops loop( () => { kick.play(g4, { attack: 0.01, release: 0.05, duration: 0.2, amp: 1.2 }); sleep(1); }, { name: 'Kick' }); loop( () => { let [note, duration] = riff[i++]; if (i>=riff.length) i = 0; const ds = 0.25 * duration; const dt = second_to_tick(ds); strat.play(note, { attack: 0.01, decay: ds, duration: dt, amp: 0.7 }); sleep(duration); // sleep in ticks }, { name: 'Riff' }).connect( lowpass.create( { cutoff: () => input.cutoff } )); // inputs input.cutoff = 0.464;