Skip to content

Mathematics

Mauro Moreno edited this page Aug 30, 2026 · 3 revisions

Mathematics and DSP

This chapter defines the principal equations used by Quesynth. It complements Synthesis theory, which focuses on audible results and patch design.

1. Notation and units

Symbol Meaning
n Discrete sample index
f_s Sample rate in samples per second
f_0 Fundamental or oscillator frequency in hertz
f_c Filter cutoff frequency in hertz
t Normalized oscillator phase in [0, 1)
Δt Phase increment per sample
x[n], y[n] Input and output samples
Q Filter quality factor
k Filter damping or feedback coefficient, as defined by context

Audio amplitudes are normalized linear values. Convert amplitude ratio A to decibels with

dB = 20 log10(A)
A  = 10^(dB/20)

For power ratios, use 10 log10(P_2/P_1).

2. Pitch and phase

Equal temperament maps MIDI note m to frequency:

f(m) = 440 · 2^((m - 69)/12)

An offset of s semitones or c cents multiplies frequency by

r_s = 2^(s/12)        r_c = 2^(c/1200)

The normalized phase increment is

Δt = f_0 / f_s
t[n+1] = (t[n] + Δt) mod 1

Quesynth limits oscillator frequency below Nyquist so that discontinuity corrections remain valid and numerical state remains finite.

Two oscillators at f_1 and f_2 produce a beating rate of

f_beat = |f_1 - f_2|

This relation explains why a fixed detune in cents produces faster absolute beating on higher notes.

3. Ideal waveform spectra

With angular frequency ω_0 = 2πf_0, the ideal series illustrate the harmonic content of common waveforms:

saw:      x(t) = (2/π) Σ[k=1..∞] (-1)^(k+1) sin(kω_0t)/k
square:   x(t) = (4/π) Σ[k=0..∞] sin((2k+1)ω_0t)/(2k+1)
triangle: x(t) = (8/π²) Σ[k=0..∞] (-1)^k sin((2k+1)ω_0t)/(2k+1)²

The exact sign and phase convention do not change the magnitude spectrum. A sampled system can represent only harmonics satisfying

k f_0 < f_s/2

so the largest directly representable harmonic index is approximately floor(f_s/(2f_0)). Higher components fold into the baseband. One useful alias mapping is

f_alias = |f - round(f/f_s) f_s|

with the result interpreted inside [0, f_s/2].

4. PolyBLEP oscillators

Quesynth corrects a discontinuous waveform locally with a polynomial band-limited step. Let dt = f_0/f_s and phase t ∈ [0,1):

                 2u - u² - 1,  u = t/dt,          t < dt
polyblep(t,dt) =  u² + 2u + 1,  u = (t-1)/dt,      t > 1-dt
                 0,                                otherwise

The engine uses a descending saw convention:

saw(t) = 1 - 2t + polyblep(t,dt)

Pulse width w is implemented as the difference between two corrected saws, not as a thresholded phase accumulator:

t_2      = (t - w) mod 1
pulse(t) = 0.5 [saw(t) - saw(t_2)]

The difference removes the width-dependent DC term and applies a band-limiting correction at both edges. Width is constrained to [0.01, 0.99].

5. Oscillator interactions

Ring modulation multiplies two signals. For sinusoidal inputs,

sin(ω_1t) sin(ω_2t) = 1/2 [cos((ω_1-ω_2)t) - cos((ω_1+ω_2)t)]

which produces components at the sum and difference frequencies.

Frequency modulation changes instantaneous phase advance. In general,

φ[n+1] = φ[n] + 2π f_c/f_s + I x_m[n]

where x_m is the modulator and I is a modulation index. Quesynth resolves the panel amount through a measured nonlinear parameter curve before applying the modulator. Hard sync instead resets the slave phase at a master-cycle boundary; sub-sample wrap position is retained to reduce timing jitter.

6. Mixing, unison, velocity, and panning

The oscillator crossfade is linear:

x_mix = (1-m)x_1 + m x_2,       0 ≤ m ≤ 1

Let m be oscillator mix, p be the normalized sub control, and a = 4p. The measured sub-oscillator law is

x = [(1-m)(x_1 + a x_sub) + m x_2]/[1 + a(1-m)]

The denominator compensates for the sub level actually present in oscillator 1's side of the crossfade. At a fully oscillator-2 mix, the sub is silent.

Velocity sensitivity is expressed as measured attenuation:

attenuation_dB = 29.77 · sensitivity · (1 - velocity)
gain           = 10^(-attenuation_dB/20)

Velocity and sensitivity are normalized to [0,1].

For N > 1 unison layers, a centered layer coordinate is

u_i = i/(N-1) - 1/2,       i = 0, …, N-1

Detune and pan spread are proportional to u_i. Quesynth follows the measured reference behavior and applies no 1/N or 1/√N normalization: layer outputs are summed at unity. Level therefore depends on phase correlation as well as layer count.

7. Envelope generators

Attack is linear. For attack time T_a, the per-sample increment is

step = 1/(T_a f_s)

with a minimum duration of one sample. Decay approaches sustain S exponentially, while release approaches zero:

decay:  e[n] = S + (e[n-1] - S)a_d
release:e[n] = e[n-1]a_r

a = exp(-L/(T f_s))

Quesynth uses L = ln(1000) ≈ 6.9078, so the residual reaches 10^-3, or -60 dB, in the tabled segment time. A release is declared complete below 10^-4 (-80 dB); otherwise an exponential tail would never reach an exact zero and the voice could not be reclaimed.

8. Parameter smoothing

Continuous controls that would click when stepped use a one-pole smoother:

y[n] = x_target + (y[n-1] - x_target)a
a    = exp(-L/(T f_s))

The configured time is the interval required to cover 99.9% of the step. Patch loads reset smoothers directly so the first note begins at the loaded setting rather than gliding from the previous patch.

9. State-variable filter

The multimode filter is a topology-preserving-transform state-variable filter. For cutoff f_c, damping k, and sample rate f_s:

g  = tan(π f_c/f_s)
a1 = 1/[1 + g(g+k)]
a2 = g a1
a3 = g a2

With integrator-equivalent states s_1 and s_2, one section evaluates:

v3 = x - s_2
v1 = a1 s_1 + a2 v3
v2 = s_2 + a2 s_1 + a3 v3

s_1 ← 2v1 - s_1
s_2 ← 2v2 - s_2

LP = v2
BP = v1
HP = x - k v1 - v2
Notch = LP + HP

The tangent pre-warps the cutoff for the bilinear mapping. Cutoff is clamped to a safe interval below Nyquist and damping is bounded to preserve useful f32 precision. The 24 dB multimode path cascades two sections; each receives √k because their peak gains multiply.

The post-filter saturation curve is algebraic and peak-normalized:

y = x(1+d)/(1+|xd|)

where d ≥ 0 is the measured drive value. At small amplitudes the gain approaches 1+d, while x = ±1 remains at the corresponding full-scale rail.

10. Four-pole ladder filter

The LP24 and LPDL filter states currently use a four-pole zero-delay-feedback ladder. Each one-pole stage has

g = tan(πf_c/f_s)       G = g/(1+g)
y = Gx + (1-G)s

For four cascaded stages, write the output as y_4 = A u + B, where A = G^4 and B is the contribution of the four stored states. Closing the feedback loop with u = x - k y_4 gives the non-iterative solution

y_4 = (A x + B)/(1 + A k)

This structure changes resonance through output feedback without moving the individual pole frequency. No DC-gain compensation is applied; the measured reference response loses low-frequency gain as feedback rises.

11. LFO and tempo synchronization

If one modulation cycle occupies B beats at tempo b beats per minute, then

T = 60B/b              f_LFO = b/(60B)

Arpeggiator steps use the same beat-domain principle. Quesynth preserves the reference's nineteen divisions, including its literal /3 entries:

Step Beats Step Beats
(1) 4 (4) 1
(2)+(4)+(8) 3.5 (8)+(16)+(32) 0.875
(2)+(4) 3 (8)+(16) 0.75
(2) 2 (2)/3 2/3
(4)+(8)+(16) 1.75 (8) 0.5
(4)+(8) 1.5 (16)+(32) 0.375
(1)/3 4/3 (4)/3 1/3
(16) 0.25 (8)/3 1/6
(32) 0.125 (16)/3 1/12
(32)/3 1/24

These labels are evaluated arithmetically; /3 is not the conventional musical triplet multiplier of 2/3.

12. Delay and chorus

A delay time T_d seconds corresponds to

D = T_d f_s

samples. Fractional positions use interpolation between adjacent buffer samples. With feedback magnitude |g| < 1, successive repeats have amplitude

A_k = A_0 g^k

and alternate polarity when g < 0. Stereo, cross, and ping-pong modes differ in how each channel feeds the opposite delay line.

Chorus modulates a short fractional delay:

D[n] = D_c + D_m sin(2π f_m n/f_s + φ)

where D_c is the center delay, D_m is depth, and f_m is modulation rate. Multiple stages use phase offsets to create a denser stereo field.

13. Measurement and null depth

For sample-aligned reference signal r[n] and test signal q[n], the residual is

e[n] = r[n] - q[n]

and relative null depth can be reported as

null_dB = 20 log10(RMS(e)/RMS(r))

More negative values indicate a closer match. A null is meaningful only after latency, polarity, level, and event timing have been controlled. Spectrum and envelope metrics are retained because a single residual value does not identify the source of a mismatch.


← MIDI control · Parameter storage →

Clone this wiki locally