The Math behind DSF Synthesis
Created on 2024-10-08T10:32:54-05:00
DSF Synthesis
DSF synths come from the paper "The Synthesis of Complex Audio Spectra by Means of Discrete Summation Formulae" by James A. Moorer.
Samples are created by sum[k=0..N](a^k * sin(theta + k * beta)) which places sine waves at equidistant harmonics with exponential decays.
s(t) = ∑_k=0..N w^k sin(2p(f_c + k f_m) t / sampletime),
where
s(t) is the sample to be output at sample step t
fc is the fundamental frequency
fm is the "distance frequency" between the sine waves (hence called harmonics)
wk is the magnitude of the k-th harmonic (ie. for w<1, the magnitudes of the higher harmonics are getting smaller and smaller)
t is the number of the sample step (with." t / sampletime" being the time in seconds at which sample t is output)
N is the number of harmonics plus 1 (for the fundamental)
N must be chosen such that f_c + N * f_m is less than the nyquist frequency.
Phasors
Cosine and sine arcs can be stored and rotated uing a mathematical device called "phasors." This stores the position in a polar coordinate system.
Phasors can write their position as a complex number: A * cos(wt+delta) + i * sin(wt+delta), or in programmer notation
You can create an angle as a phasor with
Multiplying a phasor by another phasor adapts the size and rotation; since you can create an angle with an area of 1, this will only rotate the phasor without changing anything else.
Phasors may then be rotated using only a few multiply operations against the complex numbers--skipping trigonometry table lookups.
Multiplying phasors applies the rotations because: (A*[cos(w),sin(w)]) * (B* [cos(g),sin(g)]) = A*B * [cos(w+g),sin(w+g)]
# complex multiply result.real = a.real * b.real - a.imaginary * b.imaginary result.imaginary = a.real * b.imaginary + a.imaginary * b.real
# renormalize the phasor let t = 1.0 / sqrt(self.real * self.real + self.imaginary * self.imaginary) self.real *= t self.imaginary *= t
Geometric series shenanigans
Geometric series convergence
Given a geometric series:
sum[k=0..N](a * b^k)
there exist two "closed form" convergence formulas:
when r=1: a(n+1)
otherwise: a(1-r^(n+1) / 1-r)
Which the original article describes as "wooshing" away the sum.
Complex numbers
The Verklage reformulates the synthesizer as sum[k=0..N](a * b^k), the geometric series with a known shortcut solution.
This is backed by replacing 'a' with the phasor (complex number) and 'b' with the phasor multiplied by w (the magnitude of harmonics.)
The exponent 'k' is distributed from the phase to the real and imaginary component of its complex number, thus:
When the two complex numbers are multiplied, the w^k is distributed to both components of the remaining complex number.
Result
All of these shenanigans justify why you can generate a sample with a * (1-b^(+1)/1-b) where 'a' and 'b' are phasors (complex numbers) and the result is a phasor with the cosine (real component) and sine (imaginary component) together. Which is more computationally efficient than having to generate each sine and decay it per-sample.
Normalization
Since signals are being summed it could get louder based on N. Burkhard uses a rule that sum[k=0..N](w^k) is the highest signal possible, which has a convergence of (1-w^(N+1))/(1-w), so the resultant signal can be divided by this amount to 'normalize' the output.