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