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