// A simple bell sound // Partial frequencies of the bell // Adapted from https://en.wikipedia.org/wiki/Bell#Bell_tuning // in semitones relative to middle C var partials = [-12,0,3,7,12,16.5,17,19,20.8,23,24.5,26,26.5,26.25,27.5,28.25,29,30,30.8]; const recurrentSine = synth.def(class { constructor(options) { const freq = midi_to_hz(options.note); const omega = 2 * Math.PI * freq; const gamma = 7/options.duration; // --> 60 dB decay in "duration" const T = ditty.dt; this.c1 = 2 * Math.exp(-gamma * T) * Math.cos(omega * T); this.c2 = Math.exp(-2 * gamma * T); // Initial conditions: // s[-1] = exp(γT) sin(-ωT), // s[0] = 0, this.s_nm1 = Math.exp(gamma*T) * Math.sin(-omega*T); this.s_n = 0; } process(phase,env,tick,options) { const s_np1 = this.c1 * this.s_n - this.c2 * this.s_nm1; this.s_nm1 = this.s_n; this.s_n = s_np1; return s_np1; } }, {env:one, duration:3, amp:0.1}); // Add a delayline for a slight Doppler/chorus effect due to the bell's side-to-side motion // Delayline adapted from srtuss class Delayline { constructor(n) { this.n = ~~n; this.p = 0; this.lastOut = 0; this.data = new Float32Array(n); } clock(input) { this.lastOut = this.data[this.p]; this.data[this.p] = input; if(++this.p >= this.n) { this.p = 0; } } tap(offset) { var x = this.p - offset - 1; x %= this.n; if(x < 0) { x += this.n; } // Add linear interpolation let ix = Math.floor(x), fx = x%1; return (1-fx) * this.data[ix] + fx * this.data[(ix+1)%this.n]; } } // A variable delay const oscDelay = filter.def(class { constructor(options) { this.n = Math.floor(options.maxDelay / ditty.dt); this.dline = new Delayline(this.n); this.phase = options.initPhase; } process(input, options) { this.phase += 6.28 * options.lfoHz * ditty.dt; let x = 0.5 + 0.4 * Math.cos(this.phase); // current delay x *= this.n; // current delay in samples this.dline.clock(input[0]); let sig = this.dline.tap(x); return [sig,sig]; } }, { maxDelay: 6e-3, initPhase: 0, lfoHz: 0.3}); loop((i) => { if(i==0) { sleep(1); } if(i < 10) { for(let j=0; j<partials.length; j++) { let r = Math.random(); let a0 = 0.1 * 2**(-0.6*partials[j]/12); recurrentSine.play(69+partials[j], {duration:a0*100, curve:-10, amp:(t)=>a0*Math.cos(t+6.3*r)}); } } sleep(5 + 0.3*Math.random()); }).connect(oscDelay.create());