The familiar 8-bit sounding Ditty
Sources used:
musescore.com/xiaomi…ario-bros-main-theme
youtube.com/watch?v=nta6xbzfq1u songsterr.com/a/wsa/…heme-drum-tab-s14064
youtube.com/watch?v=ofreoeqppri
Update Oct 31: updated the synthesiser
Log in to post a comment.
input.overdriveLevel = 1 //min=1, max=100 step=.1
//
// RTFM You can find the DittyToy API Reference here: https://dittytoy.net/syntax
//
const WAVEFORM = {SINE: 1, SQUARE: 2, TRIANGLE: 3, SAW: 4, SEMICIRCLE: 5, SHARK_FIN: 6, SURF: 7 };
const basicSynth = synth.def(class {
process(note, env, tick, options) {
const value = (hz) => {
const inversionMultiplier = options.inverted? -1: 1;
const phase = ditty.time * hz;
const f = phase * Math.PI * 2;
switch(options.waveForm) {
case WAVEFORM.SQUARE:
return (Math.sin(f) > 0? 1: -1);
case WAVEFORM.TRIANGLE:
return (Math.abs((phase % 1) * 4 - 2) - 1);
case WAVEFORM.SAW:
return ((phase % 1) * (inversionMultiplier*2) - inversionMultiplier);
case WAVEFORM.SEMICIRCLE:
return (options.inverted?(Math.cos(f) < 0? 1: -1):0) + Math.sqrt(1 - ((2 * Math.asin(Math.sin(f))) / Math.PI)**2) * (Math.cos(f) < 0? -1: 1);
case WAVEFORM.SHARK_FIN:
return inversionMultiplier * Math.sin(f) > 0? Math.sqrt(1 - ((2 * Math.asin(Math.sin(f/2))) / Math.PI)**2) : -Math.sqrt(1 - ((2 * Math.asin(Math.sin((f+Math.PI)/2))) / Math.PI)**2);
case WAVEFORM.SURF:
return inversionMultiplier * Math.sin(f) > 0? 2 * Math.sqrt(1 - ((2 * Math.asin(Math.sin(f/2))) / Math.PI)**2) - 1 : -2 * Math.sqrt(1 - ((2 * Math.asin(Math.sin((f+Math.PI)/2))) / Math.PI)**2) + 1;
case WAVEFORM.SINE:
default:
return Math.sin(f);
}
}
let divisor = 0;
return Array.from({length: options.overtones}).map((v, i) => value(midi_to_hz(note) * (i + 1))).reduce((p, c, i) => { const div = 1 / (1 + i*options.overtoneDecay); divisor += div; return p + (c * div); }, 0) / divisor * env.value;
}
}, { name: 'basicSynth++', waveForm: WAVEFORM.SQUARE, inverted: false, overtones: 1, overtoneDecay: 1, env: adsr2 });
const noise = synth.def(class {
constructor(options) {
this.hold = 0;
this.holdReset = (options.hold / ditty.dt) | 0;
}
process(note, env, tick, options) {
if(this.hold <= 0) {
this.value = Math.random() * 2 - 1;
this.hold = this.holdReset;
}
this.hold--;
return this.value * env.value;
}
}, { name: 'noise', hold: 0, env: adsr2 });
const overdrive = filter.def( (input, options) => [...input.map(i => clamp(i * options.level, -1, 1))], { level: 1 });
const hands = {
leftHand: {
one: {
config: { synth: basicSynth, amp: 1.4, playConfig: {attack: 0, waveForm: () => WAVEFORM.TRIANGLE } },
score: [ //lows
[1, [[d3, .5], [d3, .5], [false, .5], [d3, .5], [false, .5], [d3, .5], [d3, 1],
[g4, 1], [false, 1], [g2, 1], [false, 1]]],
//8
[2, [[g3, 1], [false, .5], [d3, .5], [false, 1], [c3, 1],
[false, .5], [f3, .5], [false, .5], [g3, .5], [false, .5], [gb3, .5], [f3, 1],
[e3, 2/3], [c4, 2/3], [e4, 2/3], [f4, .5], [false, .5], [d4, .5], [e4, .5],
[false, .5],[c4, .5], [false, .5], [a3, .5], [b3, .5], [g3, .5], [false, 1]]], //6
//40
[2, [[c3, 1], [false, .5], [g3, .5], [false, 1], [c4, 1],
[f3, 1], [false, .5], [c4, .5], [c4, 1], [f3, 1],
[c3, 1], [false, .5], [g3, .5], [false, 1], [g3, .5], [c4, .5],
[false, .5], [f5, .5], [false, .5], [f5, .5], [f5, 1], [g3, 1],
[c3, 1], [false, .5], [g3, .5], [false, 1], [c4, 1], //11
[f3, 1], [false, .5], [c4, .5], [c4, 1], [f3, 1],
[c3, 1], [ab3, 1], [false, .5], [bb3, .5], [false, 1],
[c4, 1], [false, .5], [g3, .5], [g3, 1], [c3, 1]]],
//104
[3, [[ab2, 1], [false, .5], [eb3, .5], [false, 1], [ab3, 1], //15
[g3, 1], [false, .5], [c3, .5], [false, 1], [g2, 1]]],
[1, [[d3, .5], [d3, .5], [false, .5], [d3, .5], [false, .5], [d3, .5], [d3, 1], //21
[g4, 1], [false, 1], [g2, 1], [false, 1]]],
//136
[2, [[g3, 1], [false, .5], [d3, .5], [false, 1], [c3, 1], //22
[false, .5], [f3, .5], [false, .5], [g3, .5], [false, .5], [gb3, .5], [f3, 1],
[e3, 2/3], [c4, 2/3], [e4, 2/3], [f4, .5], [false, .5], [d4, .5], [e4, .5],
[false, .5], [c4, .5], [false, .5], [a3, .5], [b3, .5], [g3, .5], [false, 1]]],
//168
[2, [[c3, 1], [false, .5], [fs3, .5], [g3, 1], [c4, 1], //27
[f3, 1], [f3, 1], [c4, .5], [c4, .5], [f3, 1],
[d3, 1], [false, .5], [f3, .5], [g3, 1], [b3, 1],
[g3, 1], [g3, 1], [c4, .5], [c4, .5], [g3, 1],
[c3, 1], [false, .5], [fs3, .5], [g3, 1], [c4, 1], //31
[f3, 1], [f3, 1], [c4, .5], [c4, .5], [f3, 1],
[g3, 1], [false, .5], [g3, .5], [g3, 2/3], [a3, 2/3], [b3, 2/3],
[c4, 1], [g3, 1], [c3, 1], [false, 1]]],
//232
[3, [[ab2, 1], [false, .5], [eb3, .5], [false, 1], [ab3, 1], //35
[g3, 1], [false, .5], [c3, .5], [false, 1], [g2, 1]]],
[1, [[d3, .5], [d3, .5], [false, .5], [d3, .5], [false, .5], [d3, .5], [d3, 1], //39
[g4, 1], [false, 1], [g2, 1], [false, 1]]],
[1, [[c3, 1], [false, .5], [fs3, .5], [g3, 1], [c4, 1], //43
[f3, 1], [f3, 1], [c4, .5], [c4, .5], [f3, 1],
[d3, 1], [false, .5], [f3, .5], [g3, 1], [b3, 1],
[g3, 1], [g3, 1], [c4, .5], [c4, .5], [g3, 1],
[c3, 1], [false, .5], [fs3, .5], [g3, 1], [c4, 1], //47
[f3, 1], [f3, 1], [c4, .5], [c4, .5], [f3, 1],
[g3, 1], [false, .5], [g3, .5], [g3, 2/3], [a3, 2/3], [b3, 2/3],
[c4, 1], [g3, 1], [c3, 1], [false, 1]]],
]
}
},
rightHand: {
one: {
config: { synth: basicSynth, amp: .5, playConfig: {attack: 0, waveForm: () => WAVEFORM.SQUARE } },
score: [ //lows
[1, [[fs4, .5], [fs4, .5], [false, .5], [fs4, .5], [false, .5], [fs4, .5], [fs4, 1],
[b4, 1], [false, 1], [g4, 1], [false, 1]]],
[2, [[e4, 1], [false, .5], [c4, .5], [false, 1], [g3, 1],
[false, .5], [c4, .5], [false, .5], [d4, .5], [false, .5], [db4, .5], [c4, 1],
[c4, 2/3], [g4, 2/3], [b4, 2/3], [c5, .5], [false, .5], [a4, .5], [b4, .5],
[false, .5], [a4, .5], [false, .5], [e4, .5], [f4, .5], [d4, .5], [false, 1]]], //6
[2, [[false, 1], [e5, .5], [eb5, .5], [d5, .5], [b4, .5], [false, .5], [c5, .5],
[false, .5], [e4, .5], [f4, .5], [g4, .5], [false, .5], [c4, .5], [e4, .5], [f4, .5],
[false, 1], [e5, .5], [eb5, .5], [d5, .5], [b4, .5], [false, .5], [c5, .5],
[false, .5], [g5, .5], [false, .5], [g5, .5], [g5, 1], [false, 1], //11
[false, 1], [e5, .5], [eb5, .5], [d5, .5], [b4, .5], [false, .5], [c5, .5],
[false, .5], [e4, .5], [f4, .5], [g4, .5], [false, .5], [c4, .5], [e4, .5], [f4, .5],
[false, 1], [ab4, 1], [false, .5], [f4, .5], [false, 1],
[e4, 1], [false, 3]]],
[1, [[ab4, .5], [ab4, .5], [false, .5], [ab4, .5], [false, .5], [ab4, .5], [bb4, 1], //15
[g4, .5], [e4, .5], [false, .5], [e4, .5], [c4, 1], [false, 1],
[ab4, .5], [ab4, .5], [false, .5], [ab4, .5], [false, .5], [ab4, .5], [bb4, .5], [g4, .5],
[false, 4],
[ab4, .5], [ab4, .5], [false, .5], [ab4, .5], [false, .5], [ab4, .5], [bb4, 1], //19
[g4, .5], [e4, .5], [false, .5], [e4, .5], [c4, 1], [false, 1],
[fs4, .5], [fs4, .5], [false, .5], [fs4, .5], [false, .5], [fs4, .5], [fs4, 1],
[b4, 1], [false, 1], [g4, 1], [false, 1]]],
[2, [[e4, 1], [false, .5], [c4, .5], [false, 1], [g3, 1], //22
[false, .5], [c4, .5], [false, .5], [d4, .5], [false, .5], [db4, .5], [c4, 1],
[c4, 2/3], [g4, 2/3], [b4, 2/3], [c5, .5], [false, .5], [a4, .5], [b4, .5],
[false, .5], [a4, .5], [false, .5], [e4, .5], [f4, .5], [d4, .5], [false, 1]]],
[2, [[c5, .5], [a4, .5], [false, .5], [e4, .5], [false, 1], [e4, 1], //27
[f4, .5], [c5, .5], [false, .5], [c5, .5], [f4, 1], [false, 1],
[g4, 2/3], [f5, 2/3], [f5, 2/3], [f5, 2/3], [e5, 2/3], [d5, 2/3],
[c5, .5], [a4, .5], [false, .5], [f4, .5], [e4, 1], [false, 1],
[c5, .5], [a4, .5], [false, .5], [e4, .5], [false, 1], [e4, 1], //31
[f4, .5], [a4, .5], [false, .5], [a4, .5], [f4, 1], [false, 1],
[g4, .5], [d5, .5], [false, .5], [d5, .5], [d5, 2/3], [c5, 2/3], [b4, 2/3],
[g4, .5], [e4, .5], [false, .5], [e4, .5], [c4, 1], [false, 1]]],
[1, [[ab4, .5], [ab4, .5], [false, .5], [ab4, .5], [false, .5], [ab4, .5], [bb4, 1], //35
[g4, .5], [e4, .5], [false, .5], [e4, .5], [c4, 1], [false, 1],
[ab4, .5], [ab4, .5], [false, .5], [ab4, .5], [false, .5], [ab4, .5], [bb4, .5], [g4, .5],
[false, 4],
[ab4, .5], [ab4, .5], [false, .5], [ab4, .5], [false, .5], [ab4, .5], [bb4, 1], //39
[g4, .5], [e4, .5], [false, .5], [e4, .5], [c4, 1], [false, 1],
[fs4, .5], [fs4, .5], [false, .5], [fs4, .5], [false, .5], [fs4, .5], [fs4, 1],
[b4, 1], [false, 1], [g4, 1], [false, 1]]],
[1, [[c5, .5], [a4, .5], [false, .5], [e4, .5], [false, 1], [e4, 1], //43
[f4, .5], [c5, .5], [false, .5], [c5, .5], [f4, 1], [false, 1],
[g4, 2/3], [f5, 2/3], [f5, 2/3], [f5, 2/3], [e5, 2/3], [d5, 2/3],
[c5, .5], [a4, .5], [false, .5], [f4, .5], [e4, 1], [false, 1],
[c5, .5], [a4, .5], [false, .5], [e4, .5], [false, 1], [e4, 1], //47
[f4, .5], [a4, .5], [false, .5], [a4, .5], [f4, 1], [false, 1],
[g4, .5], [d5, .5], [false, .5], [d5, .5], [d5, 2/3], [c5, 2/3], [b4, 2/3],
[g4, .5], [e4, .5], [false, .5], [e4, .5], [c4, 1], [false, 1]]],
]
},
two: {
config: { synth: basicSynth, amp: .5, playConfig: {attack: 0, waveForm: () => WAVEFORM.SQUARE } },
score: [ //highs
[1, [[e5, .5], [e5, .5], [false, .5], [e5, .5], [false, .5], [c5, .5], [e5, 1],
[g5, 1], [false, 3]]],
[2, [[c5, 1], [false, .5], [g4, .5], [false, 1], [e4, 1],
[false, .5], [a4, .5], [false, .5], [b4, .5], [false, .5], [bb4, .5], [a4, 1],
[g4, 2/3], [e5, 2/3], [g5, 2/3], [a5, .5], [false, .5], [f5, .5], [g5, .5],
[false, .5], [e5, .5], [false, .5], [c5, .5], [d5, .5], [b4, .5], [false, 1]]], //6
[2, [[false, 1], [g5, .5], [gb5, .5], [f5, .5], [ds5, .5], [false, .5], [e5, .5],
[false, .5], [gs4, .5], [a4, .5], [c5, .5], [false, .5], [a4, .5], [c5, .5], [d5, .5],
[false, 1], [g5, .5], [gb5, .5], [f5, .5], [ds5, .5], [false, .5], [e5, .5],
[false, .5], [c6, .5], [false, .5], [c6, .5], [c6, 1], [false, 1], //11
[false, 1], [g5, .5], [gb5, .5], [f5, .5], [ds5, .5], [false, .5], [e5, .5],
[false, .5], [gs4, .5], [a4, .5], [c5, .5], [false, .5], [a4, .5], [c5, .5], [d5, .5],
[false, 1], [eb5, 1], [false, .5], [d5, .5], [false, 1],
[c5, 1], [false, 3]]],
[1, [[c5, .5], [c5, .5], [false, .5], [c5, .5], [false, .5], [c5, .5], [d5 ,1], //15
[e5, .5], [c5, .5], [false, .5], [a4, .5], [g4, 1], [false, 1],
[c5, .5], [c5, .5], [false, .5], [c5, .5], [false, .5], [c5, .5], [d5 , .5], [e5, .5],
[false, 4],
[c5, .5], [c5, .5], [false, .5], [c5, .5], [false, .5], [c5, .5], [d5 ,1], //19
[e5, .5], [c5, .5], [false, .5], [a4, .5], [g4, 1], [false, 1],
[e5, .5], [e5, .5], [false, .5], [e5, .5], [false, .5], [c5, .5], [e5, 1],
[g5, 1], [false, 3]]],
[2, [[c5, 1], [false, .5], [g4, .5], [false, 1], [e4, 1],
[false, .5], [a4, .5], [false, .5], [b4, .5], [false, .5], [bb4, .5], [a4, 1],
[g4, 2/3], [e5, 2/3], [g5, 2/3], [a5, .5], [false, .5], [f5, .5], [g5, .5],
[false, .5], [e5, .5], [false, .5], [c5, .5], [d5, .5], [b4, .5], [false, 1]]],
[2, [[e5, .5], [c5, .5], [false, .5], [g4, .5], [false, 1], [gs4, 1], //27
[a4, .5], [f5, .5], [false, .5], [f5, .5], [a4, 1], [false, 1],
[b4, 2/3], [a5, 2/3], [a5, 2/3], [a5, 2/3], [g5, 2/3], [f5, 2/3],
[e5, .5], [c5, .5], [false, .5], [a4, .5], [g4, 1], [false, 1],
[e5, .5], [c5, .5], [false, .5], [g4, .5], [false, 1], [gs4, 1], //31
[a4, .5], [f5, .5], [false, .5], [f5, .5], [a4, 1], [false, 1],
[b4, .5], [f5, .5], [false, .5], [f5, .5], [f5, 2/3], [e5, 2/3], [d5, 2/3],
[c5, .5], [false, 3.5]]],
[1, [[c5, .5], [c5, .5], [false, .5], [c5, .5], [false, .5], [c5, .5], [d5 ,1], //35
[e5, .5], [c5, .5], [false, .5], [a4, .5], [g4, 1], [false, 1],
[c5, .5], [c5, .5], [false, .5], [c5, .5], [false, .5], [c5, .5], [d5 , .5], [e5, .5],
[false, 4],
[c5, .5], [c5, .5], [false, .5], [c5, .5], [false, .5], [c5, .5], [d5 ,1], //39
[e5, .5], [c5, .5], [false, .5], [a4, .5], [g4, 1], [false, 1],
[e5, .5], [e5, .5], [false, .5], [e5, .5], [false, .5], [c5, .5], [e5, 1],
[g5, 1], [false, 3]]],
[1, [[e5, .5], [c5, .5], [false, .5], [g4, .5], [false, 1], [gs4, 1], //43
[a4, .5], [f5, .5], [false, .5], [f5, .5], [a4, 1], [false, 1],
[b4, 2/3], [a5, 2/3], [a5, 2/3], [a5, 2/3], [g5, 2/3], [f5, 2/3],
[e5, .5], [c5, .5], [false, .5], [a4, .5], [g4, 1], [false, 1],
[e5, .5], [c5, .5], [false, .5], [g4, .5], [false, 1], [gs4, 1], //47
[a4, .5], [f5, .5], [false, .5], [f5, .5], [a4, 1], [false, 1],
[b4, .5], [f5, .5], [false, .5], [f5, .5], [f5, 2/3], [e5, 2/3], [d5, 2/3],
[c5, .5], [false, 3.5]]],
]
}
},
drum: {
hihat: {
config: { synth: noise, amp: .7, playConfig: {attack: 0 } },
score: [ //lows
[1, [[c7, .125], [false, .875], [c7, .125], [false, .875], [c7, .125], [false, .375], [c7, .125], [false, 1.375],
[c7, .125], [false, 1.875], [false, .5], [c7, .125], [false, .375], [c7, .125], [false, .375], [c7, .125], [false, .375]]],
[48, [[false, 1], [c7, .125], [false, .5], [c7, .125], [false, .25]]],
//40
[4, [[c7, .125], [false, .875], [c7, .125], [false, .875], [c7, .125], [false, .375], [c7, .125], [false, 1.375],
[c7, .125], [false, 1.875], [false, .5], [c7, .125], [false, .375], [c7, .125], [false, .375], [c7, .125], [false, .375]]],
[48, [[false, 1], [c7, .125], [false, .5], [c7, .125], [false, .25]]],
[4, [[c7, .125], [false, .875], [c7, .125], [false, .875], [c7, .125], [false, .375], [c7, .125], [false, 1.375],
[c7, .125], [false, 1.875], [false, .5], [c7, .125], [false, .375], [c7, .125], [false, .375], [c7, .125], [false, .375]]],
[16, [[false, 1], [c7, .125], [false, .5], [c7, .125], [false, .25]]],
]},
snare: {
config: { synth: noise, amp: .5, playConfig: {attack: 0 } },
score: [
[1, [[d3, .5], [false, 1], [d3, .5], [false, 1], [d3, .5], [false, .5],
[g4, .5], [false, 1.5], [g2, .5], [false, 1.5]]],
[24, [[false, 2], [e6, .5], [false, 1.5]]],
//40
[4, [[d3, .5], [false, 1], [d3, .5], [false, 1], [d3, .5], [false, .5],
[g4, .5], [false, 1.5], [g2, .5], [false, 1.5]]],
[24, [[false, 2], [e6, .5], [false, 1.5]]],
[4, [[d3, .5], [false, 1], [d3, .5], [false, 1], [d3, .5], [false, .5],
[g4, .5], [false, 1.5], [g2, .5], [false, 1.5]]],
[8, [[false, 2], [e6, .5], [false, 1.5]]],
]},
kick: {
config: { synth: noise, amp: .8, playConfig: { attack: 0, hold: .0005 } },
score: [
[1, [[false, 4],
[false, 4]]],
[24, [[c2, .14], [false, 3.86]]],
//40
[4, [[false, 4],
[false, 4]]],
[24, [[c2, .14], [false, 3.86]]],
[4, [[false, 4],
[false, 4]]],
[8, [[c2, .14], [false, 3.86]]],
]}
}
}
ditty.bpm=180;
let skip = 0;
Object.keys(hands).forEach(hand =>
Object.keys(hands[hand]).forEach(finger => {
loop( () => {
hands[hand][finger].score.forEach(noteSet => {
for(let i = 0; i < noteSet[0]; i++) {
noteSet[1].forEach(note => play(hands[hand][finger].config.synth, hands[hand][finger].config.playConfig, ...note));
}
});
}, { amp: hands[hand][finger].config.amp, name: hand+'_'+finger }).connect(overdrive.create({level: () => input.overdriveLevel}));
})
);
const play = (synth, config, note, length) => {
if(skip > .0001) {
skip -= length;
return;
}
if(note !== false) synth.play(note, { ...config, release: length - config.attack });
sleep(length);
}