Fork: Wizards & Warriors (main m

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);