Ray of Hope

Ray of Hope, from corpse party

Log in to post a comment.

// === Instruments ===

function softsaw(p, formant) {
    const x = p-~~p;
    // polyblep
    const f = Math.abs(formant);
    const s = Math.max(0, Math.abs((f + 1) * (2*x - 1)) - f);
    // saw - polyblep
    return 2 * x - 1 - s*s * Math.sign(x - 0.5);
}

// from my Piano ditty, https://dittytoy.net/ditty/bc17a034bf
// modified slightly
const osc = class {
    constructor(options) {
        // The value of the note argument of the play call is retrievable via options.note.
        this.phases = new Float32Array(8).map(_ => Math.random());
        this.damping = new Float32Array(8).map(_ => Math.random());
        
        // frequency
        this.freq = midi_to_hz(options.note) * ditty.dt;
        
        // phasor
        this.time = Math.random();
    }
    process(note, env, tick, options) {
        // phasor times
        this.time += ditty.dt * 3;
        this.time -= this.time|0;
        let phase = Math.sin(this.time * 2 * Math.PI);
        
        // total
        let l = 0.0;
        let r = 0.0;
            
        // all saw waves
        for (let i = 0; i < this.phases.length; i++) {
            // sample wave
            const v = softsaw(
                this.phases[i],
                this.damping[i] * (options.soft_decay * env.value + options.soft_base)
            ) * env.value;
                
            // update phase
            this.phases[i] += this.freq * (options.detune||1) + phase * ditty.dt * (options.vibrato||0);
            this.phases[i] -= this.phases[i]|0;
                
            // stereo
            if (i < this.phases.length / 2) l += v;
            else r += v;
        }
            
        return [l * 0.707, r * 0.707];
    }
};

const piano = synth.def(
    osc, {
        soft_decay: 15,
        soft_base: 0,
        detune: 0.5,
        levels: [0, 1, 0, 0],
        times: [0.001, 6, 1],
        env: segenv,
    }
);

const cello = synth.def(
    osc, {
        soft_decay: 10,
        soft_base: 10,
        vibrato: 1.5,
        levels: [0, 1, 0.8, 0],
        times: [0.05, 0.1, 0.2],
        env: segenv,
    }
);

// === Song utils ===
// play several notes at the same time, for a given duration
function playMany(instr, duration, ...notes) {
    for (const note of notes) {
        // note can be multiple in parallel
        for (let i = 1; i < note.length; i++) instr.play(note[i], { duration: duration * note[0] });
        // duration is the first item in the sequence
        sleep(duration * note[0]);
    }
}

// play one note at a time
function playOne(instr, duration, ...notes) {
    for (const note of notes) {
        // just one
        if (note > 0) instr.play(note, { duration: duration });
        // no altered duration here
        sleep(duration);
    }
}

// === Song ===
ditty.bpm = 133;

// opening loop
function lead1(instr) {
    for (let i = 0; i < 4; i++) {
        playMany(instr, 1/4, [1, c5, g4], [1, c5, g4], [1, g4], [1, g4]);
        playMany(instr, 1/4, [1, d5, as4], [1, d5, as4], [1, g4], [1, g4]);
        playMany(instr, 1/4, [1, ds5, as4], [1, ds5, as4], [1, g4], [1, d5, as4, g4]);
        playMany(instr, 1/4, [1, d5, as4], [1, g4], [1, c5, g4], [1, c5, g4]);
    }
}

function bass1(instr) {
    for (let i = 0; i < 2; i++) {
        playMany(instr, 8, [1, g3, c3, g2]);
    }
}

// melody 1 loop
function lead2(instr) {
    playOne(instr, 1/4, d5, ds5, f5, ds5);
    playOne(instr, 3/2, d5);
    playOne(instr, 1/2, g4, c5, ds5);
    
    playOne(instr, 3/2, d5);
    playOne(instr, 1/2, d5);
    playOne(instr, 1/1, g5);
    playOne(instr, 1/1, d5);
    
    playOne(instr, 2/1, d5);
    playOne(instr, 1/2, d5, ds5, f5, g5);
    
    playOne(instr, 3/2, f5);
    playOne(instr, 1/4, g5, f5);
    playOne(instr, 2/1, c5);
    
    // part 2
    playOne(instr, 5/2, ds5);
    playOne(instr, 1/4, c5, d5, ds5, g5, c6, ds6);
    
    playOne(instr, 2/1, d6);
    playOne(instr, 1/1, a5, d6);
    playOne(instr, 3/1, c6);
    
    playOne(instr, 1/4, c6, d6, c6, a5);
    playOne(instr, 4/1, b5);
}

function bass2(instr) {
    playMany(instr, 4, [1, c4, g3, c3]);
    playMany(instr, 4, [1, b3, g3, d3, b2]);
    playMany(instr, 4, [1, as3, f3, d3, as2]);
    playMany(instr, 4, [1, a3, f3, c3, a2]);
    
    // part 2
    playMany(instr, 4, [1, gs3, ds3, c3, gs2]);
    playMany(instr, 4, [1, d4, a3, fs3, d3]);
    playMany(instr, 4, [1, c4, g3, d3, g2]);
    playMany(instr, 4, [1, b3, g3, d3, b2]);
}

// melody 2 loop
function lead3(instr) {
    playOne(instr, 5/2, g5);
    playOne(instr, 1/2, ds5, f5, g5);
    playOne(instr, 2/1, f5, d5);
    
    playOne(instr, 3/1, f5);
    playOne(instr, 1/4, f5, g5, f5, d5);
    
    playOne(instr, 4/1, e5);
    
    // part 2
    playOne(instr, 5/2, g5);
    playOne(instr, 1/2, ds5, f5, g5);
    playOne(instr, 2/1, f5);
    playOne(instr, 1/1, d5, as4);
    
    playOne(instr, 4/1, as4, a4);
}

// melody 3 loop
function lead4(instr) {
    playOne(instr, 5/2, g5);
    playOne(instr, 1/2, ds5, f5, g5);
    playOne(instr, 2/1, f5, d5);
    
    playOne(instr, 3/1, f5);
    playOne(instr, 1/4, f5, g5, f5, d5);
    
    playOne(instr, 5/2, e5);
    playOne(instr, 1/2, e5);
    playOne(instr, 1/2, g5, c6);
    
    // part 2
    playOne(instr, 5/2, ds6);
    playOne(instr, 1/2, ds6, f6, g6);
    playOne(instr, 2/1, f6);
    
    playOne(instr, 1/1, d6);
    playOne(instr, 1/1, as5);
    
    playOne(instr, 3/1, as5);
    sleep(1/4);
    playOne(instr, 1/4, c6, as5, g5);
    
    playOne(instr, 4/1, a5);
}

function bass4(instr) {
    // same as melody 2
    bass3(instr);
}

function bass3(instr) {
    playMany(instr, 4, [1, c4, gs3, ds3]);
    playMany(instr, 4, [1, d4, c4, as3, f3]);
    playMany(instr, 4, [1, f4, c4, g3, c3]);
    playMany(instr, 4, [1, e4, c4, g3, e3]);
    
    // part 2
    playMany(instr, 4, [1, c4, gs3, ds3]);
    playMany(instr, 4, [1, d4, c4, as3, f3]);
    playMany(instr, 4, [1, c4, as3, f3, as2]);
    playMany(instr, 4, [1, c4, a3, f3, c3]);
}
    
loop(() => {
    lead1(piano);
    lead2(piano);
    lead2(piano);
    lead3(piano);
    lead4(piano);
}, { name: 'lead', amp: 0.5 });

loop(() => {
    bass1(cello);
    bass2(cello);
    bass2(cello);
    bass3(cello);
    bass4(cello);
}, { name: 'bass', amp: 0.5 });