A small remake of the classic riff by . I wanted to create a distortion effect and it turns out that FM synthesis 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;