It is possible to use a recurrence formula to compute sine waves efficiently. One multiplication per sample is required for a normal sine wave, two for a decaying one.
This used to be a significant time-saving technique for early computer music ; nowadays I think it has mainly fallen out of use.
Log in to post a comment.
// This synth calculates the signal // // s[n] = exp(-nγT) sin(nωT), // // where // n is the sample number, // T = ditty.dt is the sampling period, // ω = 2πf is the angular frequency, // γ is the decay speed, // using the following recurrence formula: // // s[n+1] = 2 exp(-γT) cos(ωT) s[n] - exp(-2γT) s[n-1]. // // Assuming ω and γ are constant, the coefficients can be precomputed, // thus only two multiplications are needed for each sample. // Moreover if there is no decay, meaning that // // γ = 0 // // then the recurrence formula can be simplified to // // s[n+1] = 2 cos(ωT) s[n] - s[n-1]. // // In that case, only one multiplication is needed for each sample. // The recurrence formula can be derived by using the trigonometric formula: // // sin(a+b) = sin(a) cos(b) + cos(a) sin(b). // 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}); //const nn = [c4,d4,e4,f4,g4,a4,b4,c5]; const nn = [ab2,eb3,ab3,bb3,c4,eb4,f4]; loop( (i) => { let octave = Math.floor(3*Math.random()) * 12; recurrentSine.play(nn.choose() + octave, {duration:5, pan:2*Math.random()-1}); sleep(Math.random() + 0.5); });