Philip Glass 'Opening'

A reproduction of Philip Glass's piece "Opening", from "Glassworks"

Log in to post a comment.

// A reproduction of Philip Glass's piece "Opening", from "Glassworks"
// by Alexis THIBAULT, 27/10/2023

const sect1a = [[f3,ab3,c4,f4,ab4,0.0], [-1,ab3,c4,c5,f4,0.2], [eb3,g3,bb3,g4,c5,0.5], [db3, g3, bb3, ab4, c5,0.4]];
const sect1end = [bb2,g3,bb3,g4,c5,0.5];
const sect2a = [[c3,f3,ab3,c4,f4,0.3],  [bb2,f3,ab3,d4,f4,0.9], [bb2,eb3,g3,bb3,eb4,0.8], [g2,eb3,g3,c4,eb4,0.6]];
const sect2end = [g2,eb3,g3,bb3,eb4,0.5];
const bb3m = bb3+0.01; // special marking for a special occurrence of this note
const sect3a = [[f3,bb3,d4,d5,f4,0.0], [-1,bb3,d4,ab4,d5,0.2], [ab3,c4,eb4,eb5,ab4,0.8], [bb3,c4,eb4,eb5,ab4,0.9]];

const sect1 = [...sect1a, ...sect1a, ...sect1a, ...sect1a.slice(0,3), sect1end];
const sect2 = [...sect2a, ...sect2a, ...sect2a, ...sect2a.slice(0,3), sect2end];
const sect3 = [...sect3a, ...sect3a, ...sect3a]; sect3[11] = [bb3m,c4,eb4,eb5,ab4,0.9];
const sect4 = [[g3,bb3,d4,d5,g4,1.0], [-1,bb3,d4,d5,g4,0.5], [f3,bb3,d4,d5,ab4,0.2], [f3,bb3,d4,d5,bb4,0.0]];

let mynotes = sect1.concat(sect2).concat(sect3).concat(sect4);

ditty.bpm = 92;

const recurrentSines = synth.def(class {
    constructor(options) {
        const N = options.notes.length;
        this.c1s = new Float32Array(N);
        this.c2s = new Float32Array(N);
        this.s_nm1s = new Float32Array(N);
        this.s_ns = new Float32Array(N);
        for(let i=0; i < N; i++) {
            const freq = midi_to_hz(options.notes[i]);
            const omega = 2 * Math.PI * freq;
            const gamma = 7/(options.duration * options.reldurs[i]); // --> 60 dB decay in "duration"
            const T = ditty.dt;
            this.c1s[i] = 2 * Math.exp(-gamma * T) * Math.cos(omega * T);
            this.c2s[i] = Math.exp(-2 * gamma * T);
            // Initial conditions:
            //      s[-1] = exp(γT) sin(-ωT) * a0,
            //      s[0] = 0,
            this.s_nm1s[i] = Math.exp(gamma*T) * Math.sin(-omega*T) * options.relamps[i];
            this.s_ns[i] = 0;
        }
        this.env = 0.0;
        this.a0 = ditty.dt / 0.002;
        this.pans = options.pans
    }
    process(note,env,tick,options) {
        const N = options.notes.length;
        let sig = [0,0];
        for(let i=0; i < N; i++) {
            const s_np1 = this.c1s[i] * this.s_ns[i] - this.c2s[i] * this.s_nm1s[i];
            this.s_nm1s[i] = this.s_ns[i];
            this.s_ns[i] = s_np1;
            sig[0] += (1 - this.pans[i]) * s_np1;
            sig[1] += (1 + this.pans[i]) * s_np1;
        }
        this.env += this.a0 * (1.0 - this.env); // Exponential envelope at the start (smooth note onset)
        return [sig[0]*this.env, sig[1]*this.env];
    }
}, {env:one, notes:[c4], duration:3, amp:0.1, relamps:[1], reldurs:[1], pans:[0]});

function playNote(n, vel) {
    vel = vel + Math.random()*0.1;
    let dur = 5 * (0.8)**((n-69)/12); // Slightly lengthen low notes
    // Shape the spectrum of the resonances
    let amp0 = (0.8)**((n-69)/12);
    let amp1 = amp0*(0.5+vel);
    let amp2 = amp1*(0.1+0.7*vel);
    let amp3 = amp2*(0.1+0.5*vel);
    let amp4 = amp3*0.5;
    let amp8 = 0.05*amp1*(0.7+vel);
    let amp12 = 0.02*amp1*(0.1+vel);
    let nns = [n-0.05, n+0.05, n+12+0.02, n+12-0.02, n+19, n+24, n+36, n+36+7];
    let relamps = [amp1, amp1, amp2, amp2, amp3, amp4, amp8, amp12];
    let reldurs = [1,1,0.7,0.7, 0.5, 0.4, 0.1, 0.02];
    let pans = [-1, 1, -1, 1, -0.5, 0.5, -0.2, 0.2];
    recurrentSines.play(0, {notes:nns, amp:1, relamps:relamps, reldurs:reldurs, pans:pans, duration: dur});
}

loop((i) => {
    let bar = ~~(i/24);
    let nn = mynotes.ring(bar);
    let nnp = mynotes.ring(bar+1);
    let j = i%24;
    let vel = nn[5] + (j/24) * (nnp[5]-nn[5]); // interpolate velocity into next bar
    if(j==0 && nn[0] > 0) {
        playNote(nn[0], vel);
    }
    // HACK to play the right note in the lower part of bar 15
    if(j==12 && nn[0] == bb3m) {
        playNote(ab3, vel);
    }
    if(j%6 == 0) {
        playNote(nn[1], vel);
    }
    if(j%6 == 3) {
        playNote(nn[2], vel);
    }
    if(j%4 == 0) {
        if(nn[0] == db3 && j == 20) {
            // HACK to play the right note at the end of bar 4
            playNote(g4, vel);
        } else {
            playNote(nn[3], vel);
        }
    }
    if(j%4 == 2) {
        playNote(nn[4], vel);
    }
    sleep(1/6);
}, {amp:0.1});