I began experimenting with Dittytoy and especially FM synthesis and ended up with this rendition of my Amstrad CPC cover of Zetrix/Arcane's Amiga cover (youtube.com/watch?v=5vy6g42ezby) of The Last Hero by Future Freak (csdb.dk/sid/?id=12652)
Log in to post a comment.
// Constants
const TAU = Math.PI * 2;
const sin = Math.sin;
const exp = Math.exp;
// Envelopes
my_env = adsr.create( { attack: 0.1, decay: 0.2, release: 0.2, duration: 10 });
// Synths
const square = synth.def( (phase, env, tick, options) => Math.sign(sin(phase*TAU)) * env.value);
const saw = synth.def( (phase, env, tick, options) => ((phase % 1)*2-1) * env.value);
const sinus = synth.def( (phase, env, tick, options) => sin(phase*TAU) * env.value);
const fm = synth.def( class {
constructor (options) {
this.phase = 0;
this.t = 0
// index of modulation
this.iom = options.iom || 13.7;
this.mul = options.multiplier || 0.5;
}
process(note, env, tick, options) {
// carrier frequency
const fc = midi_to_hz(note);
// modulator frequency
const fm = fc * this.mul;
// forward time
this.t += ditty.dt;
const osc = sin(
TAU*this.t*fc + this.iom*sin(TAU*this.t*fm)
) * env.value;
// swing panning
const swing = tick * 1.6128;
const x = TAU*swing;
const m = 0.5;
const L = m - sin(x) * m;
const R = m - sin(x+Math.PI) * m;
return [osc * L, osc * R]; // left, right
}
});
class ST128 {
constructor(options) {
this.synth = options.synth;
this.notes = options.notes;
this.max = this.notes.length;
this.adsr = options.adsr || {
attack: 0.01, release: 0.25, duration: 0.125, amp: 1.0 };
this.speed = options.speed || 0.05;
this.transpose = options.transpose || 0;
this.arps = options.arps || [];
this.pos = 0;
}
process(loopCount) {
let tpl = this.notes[this.pos++];
if (this.pos >= this.max) this.pos = 0;
this.pause = tpl[0];
this.note = tpl[1];
this.adsr.duration = this.pause * (this.speed * 0.95);
if (this.note > 0)
this.synth.play(this.note + this.transpose, this.adsr);
sleep(this.pause * this.speed);
}
}
// Song data
const tracks = [
[[640, 0], [320, 0], [320, 0], [1280,0]] ,
[[640, 0], [30, 48], [20, 48], [30, 48], [30, 48], [20, 48], [20, 48], [10, 48], [30, 48], [20, 48], [30, 48], [30, 48], [20, 48], [20, 48], [10, 48], [30, 56], [20, 56], [30, 56], [30, 56], [20, 56], [30, 56], [30, 51], [20, 51], [20, 51], [10, 51], [30, 46], [20, 46], [30, 46], [30, 48], [20, 48], [30, 48], [30, 48], [20, 48], [20, 48], [10, 48], [30, 48], [20, 48], [30, 48], [30, 48], [20, 48], [20, 48], [10, 48], [30, 56], [20, 56], [30, 56], [30, 56], [20, 56], [30, 56], [30, 51], [20, 51], [20, 51], [10, 51], [30, 46], [20, 46], [30, 46], [30, 48], [20, 48], [30, 48], [30, 48], [20, 48], [20, 48], [10, 48], [30, 48], [20, 48], [30, 48], [30, 48], [20, 48], [20, 48], [10, 48], [30, 56], [20, 56], [30, 56], [30, 56], [20, 56], [30, 56], [30, 51], [20, 51], [20, 51], [10, 51], [30, 46], [20, 46], [20, 46], [10,0]] ,
[[660, 0], [40, 72], [10, 72], [30, 72], [40, 72], [40, 72], [40, 72], [10, 72], [30, 72], [40, 72], [40, 72], [40, 80], [10, 80], [30, 80], [40, 80], [10, 80], [30, 80], [40, 75], [40, 75], [40, 70], [10, 70], [30, 70], [40, 72], [10, 72], [30, 72], [40, 72], [40, 72], [40, 72], [10, 72], [30, 72], [40, 72], [40, 72], [40, 80], [10, 80], [30, 80], [40, 80], [10, 80], [30, 80], [40, 75], [40, 75], [40, 70], [10, 70], [30, 70], [40, 72], [10, 72], [30, 72], [40, 72], [40, 72], [40, 72], [10, 72], [30, 72], [40, 72], [40, 72], [40, 80], [10, 80], [30, 80], [40, 80], [10, 80], [30, 80], [40, 75], [40, 75], [40, 70], [10, 70], [10, 70]] ,
[[320, 60], [160, 68], [80, 67], [80, 65], [1920,0]] ,
[[10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 91], [10, 82], [10, 87], [10, 82], [10, 91], [10, 82], [10, 87], [10, 82], [10, 89], [10, 82], [10, 86], [10, 82], [10, 89], [10, 82], [10, 86], [10, 82], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 91], [10, 82], [10, 87], [10, 82], [10, 91], [10, 82], [10, 87], [10, 82], [10, 89], [10, 82], [10, 86], [10, 82], [10, 89], [10, 82], [10, 86], [10, 82], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 91], [10, 82], [10, 87], [10, 82], [10, 91], [10, 82], [10, 87], [10, 82], [10, 89], [10, 82], [10, 86], [10, 82], [10, 89], [10, 82], [10, 86], [10, 82], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 91], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 92], [10, 84], [10, 87], [10, 84], [10, 91], [10, 82], [10, 87], [10, 82], [10, 91], [10, 82], [10, 87], [10, 82], [10, 89], [10, 82], [10, 86], [10, 82], [10, 89], [10, 82], [10, 86], [10, 82]] ,
[[1280, 0], [40, 79], [20, 77], [10, 79], [40, 72], [10, 72], [20, 75], [20, 77], [40, 79], [20, 77], [10, 82], [50, 79], [20, 75], [20, 77], [40, 79], [20, 77], [10, 79], [40, 70], [10, 70], [20, 75], [20, 77], [30, 79], [30, 80], [20, 82], [30, 79], [30, 77], [10, 75], [10, 77], [40, 79], [20, 77], [10, 79], [40, 72], [10, 72], [20, 75], [20, 77], [40, 79], [20, 77], [10, 82], [50, 79], [20, 75], [20, 77], [40, 79], [20, 77], [10, 79], [40, 70], [10, 70], [20, 75], [20, 77], [30, 79], [30, 80], [20, 82], [30, 79], [30, 77], [10, 75], [10, 77]] ,
[[1425, 0], [160, 77], [160, 77], [320, 77], [160, 77], [160, 77], [160, 77], [15,0]] ,
];
const song = [
{
name: 'Bass 1', synth: saw, notes: tracks[1], transpose: -12, adsr: {
attack: 0.01, decay: 0.7, sustain: 0.25, amp: 0.6128
}
},{
name: 'Arps 1', synth: sinus, notes: tracks[2], adsr: {
attack: 0.01, decay: 0.5, sustain: 0.0, amp: 0.464
}
},{
name: 'Bass 2', synth: fm, notes: tracks[3], transpose: -12, adsr: {
attack: 0.01, decay: 4, sustain: 0.0, amp: 0.368
},
},{
name: 'Arps 2', synth: fm, iom: 4.23, notes: tracks[4], adsr: {
attack: 0.01, decay: 0.5, sustain: 0.3, amp: 0.464
}
},{
name: 'Lead', synth: saw, notes: tracks[5], adsr: {
attack: 0.01, decay: 0.5, sustain: 0.2, amp: 0.6128
}
}
];
// Play it again, Arnold!
ditty.bpm = 120;
for (let track of song) {
loop(ST128, track);
}