Fork with added Synth by me and a Reverb by @athibaul
That is all passed through a filter that downscales to 8-bit. Also added panning.
Original sound by romaindurand settings:
Fork: Wizards & Warriors (main m (variation)
Closer to original settings: Fork: Wizards & Warriors (main m (variation)
Log in to post a comment.
ditty.bpm = 210; //START--------- REVERB BY athibaul FROM https://dittytoy.net/ditty/900d59c1f0 -------------------------------------// const ISQRT2 = Math.sqrt(0.5); const SQRT8 = Math.sqrt(8); const ISQRT8 = 1/SQRT8; // const fibodelays = [1410,1662,1872,1993,2049,2114,2280,2610]; // X^8 = X+1 const fibodelays = [1467,1691,1932,2138,2286,2567,3141,3897]; // X^8 = X+2 let meandelay = fibodelays[3] * ditty.dt; class Delayline { constructor(n) { this.n = n; this.p = 0; this.data = new Float32Array(n); } current() { return this.data[this.p]; // using lastOut results in 1 sample excess delay } clock(input) { this.data[this.p] = input; if(++this.p >= this.n) { this.p = 0; } } } const reverb = filter.def(class { constructor(options) { this.outgain = 0.3; this.dls = []; this.s0 = new Float32Array(8); // Lowpass filter memory for(let i=0; i<8; i++) { this.dls.push(new Delayline(fibodelays[i])); this.s0[i] = 0; } } process(input, options) { let rt60 = options.rt60; let loopgain = 10 ** (-3*meandelay / rt60) * ISQRT8; let higain = 10 ** (-3*meandelay / options.rtHi) * ISQRT8; let v = this.dls.map( (dl) => dl.current()); // Fast Walsh-Hadamard transform // https://formulasearchengine.com/wiki/Fast_Walsh%E2%80%93Hadamard_transform let w = [v[0]+v[4], v[1]+v[5], v[2]+v[6], v[3]+v[7], v[0]-v[4], v[1]-v[5], v[2]-v[6], v[3]-v[7]]; let x = [w[0]+w[2], w[1]+w[3], w[0]-w[2], w[1]-w[3], w[4]+w[6], w[5]+w[7], w[4]-w[6], w[5]-w[7]]; let y = [x[0]+x[1], x[0]-x[1], x[2]+x[3], x[2]-x[3], x[4]+x[5], x[4]-x[5], x[6]+x[7], x[6]-x[7]]; y[0] += input[0]*SQRT8; y[2] += input[1]*SQRT8; let a0 = clamp01(2*Math.PI*options.cutoff*ditty.dt); for(let i=0; i<8; i++) { let hipass = y[i] - this.s0[i]; this.dls[i].clock(this.s0[i] * loopgain + hipass * higain); this.s0[i] += a0 * hipass; } return [lerp(input[0], v[0], options.mix), lerp(input[1], v[1], options.mix)]; } }, {mix:0.2, rt60:1, cutoff:5000, rtHi:0.8}); input.reverbMix = 0.44; // min=0, max=1, step=0.01 input.reverbTime = 2.5; // min=0.1, max=10, step=0.1 input.reverbTimeHi = 0.6; // min=0.1, max=10, step=0.1 input.reverbCutoff = 5000; // min=200, max=10000, step=10 const hallverb = reverb.createShared({ mix:() => input.reverbMix, rt60:() => input.reverbTime, cutoff:() => input.reverbCutoff, rtHi:() => input.reverbTimeHi }); //END----------- REVERB BY athibaul FROM https://dittytoy.net/ditty/900d59c1f0 -------------------------------------// //START--------- SYNTH BY jurgen FROM https://dittytoy.net/ditty/2a0acbb94b ----------------------------------------// input.waveformMelody = 4;// min=1 max=7 step=1 (Sine, Square, Triangle, Saw, Semi Circle, Shark fin, Surf) input.waveformBass = 4;// min=1 max=7 step=1 (Sine, Square, Triangle, Saw, Semi Circle, Shark fin, Surf) 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 }); //END----------- SYNTH BY jurgen FROM https://dittytoy.net/ditty/2a0acbb94b ----------------------------------------// //START--------- 8bit filter BY jurgen FROM https://dittytoy.net/ditty/2a0acbb94b ----------------------------------// const toBits = filter.def((input, options) => { const values = (2**options.bits) / 2; return input.map(v => Math.round(clamp(v, -1, 1) * values) / values); }, {bits: 8}) //END----------- 8bit filter BY jurgen FROM https://dittytoy.net/ditty/2a0acbb94b ----------------------------------// //const fltr = hallverb.connect(clampNbit.create( { bits: 8 } )); //START FORK FROM WORK BY romaindurand // I added if(note > 0) at .play locations to prevent ticking from low notes (value 0) function melodyPattern(notes, baseNote) { return () => { for(i = 0; i < notes.length; i++) { if(notes[i] > 0) basicSynth.play(notes[i], {attack: 0, waveForm: () => input.waveformMelody }); sleep(0.5); if(baseNote > 0) basicSynth.play(baseNote, {attack: 0, waveForm: () => input.waveformMelody, amp: .7 }); sleep(0.5); } }; } function simpleMelodyPattern(notes) { return () => { for(i = 0; i < notes.length; i++) { if(notes[i] > 0) basicSynth.play(notes[i], {attack: 0, waveForm: () => input.waveformBass }); sleep(0.5); } }; } const melodySeq0 = () => { melodyPattern([d5, e5, f5, g5], a4)(); melodyPattern([bb4, d5, g5, f5], g4)(); melodyPattern([e5, f5], c5)(); melodyPattern([g5, c5], g4)(); melodyPattern([bb4, a4, f5, e5], f4)(); const notes0 = [d5, e5, f5, d5]; melodyPattern(notes0, bb4)(); //same as previous pattern with another base note melodyPattern(notes0, g4)(); melodyPattern([e5, cs5, e5, cs5], a4)(); melodyPattern([a5, cs5, a5, a5], a4)(); }; const melodySeq1 = () => { melodyPattern([f5, d5], a4)(); simpleMelodyPattern([f5, g5, a5, a4])(); simpleMelodyPattern([ a5, bb4, d5, bb5, a5, bb4, d5, bb5, g5, g4, c5, g4, e5, f5, g5, c5, g5, a4, c5, a5, f5, a4, e5, c5, f5, bb4, d5, bb4, d5, e5, f5, bb4, f5, g4, bb4, g4, d5, e5, f5, g4 ])(); melodyPattern([f5, cs5, e5, cs5, a5, cs5], a4)(); melodyPattern([a5, a5], cs5)(); }; const melodySeqEnd = simpleMelodyPattern([d5, a4, e5, f5]); const bassPattern = simpleMelodyPattern([ d3, 0, 0, 0, 0, d3, e3, f3, g3, 0, 0, 0, 0, g3, a3, bb3, c4, 0, 0, 0, 0, bb3, a3, g3, f3, 0, g3, 0, a3, 0, f3, 0, bb3, 0, 0, 0, 0, c4, bb3, a3, g3, 0, 0, 0, 0, a3, bb3, g3, a3, 0, 0, 0, a3, 0, 0, 0, a3, 0, g3, 0, f3, 0, e3, 0 ]); loop(() => { melodySeq0(); melodySeq0(); melodySeq1(); melodySeq1(); melodySeqEnd(); melodySeqEnd(); melodySeqEnd(); simpleMelodyPattern([d5, 0, 0, 0])(); }, { name: 'melody', pan: 1 }).connect(toBits.create()).connect(hallverb); loop(() => { sleep(32); bassPattern(); bassPattern(); bassPattern(); simpleMelodyPattern([d3, 0, 0, 0, 0, 0, 0, 0])(); sleep(4); }, { name: 'bass', pan: -1 }).connect(toBits.create()).connect(hallverb);