Morphing Between LFO Shapes with 3 Intuitive Parameters
Table Of Contents
Introduction
Low Frequency Oscillator (LFO) is a popular modulator every sound designer have in their arsernal. A standard LFO generally propose as a serie of shapes (Triangle, Sinus, Square, Saw, Inverted Saw, Pulse, …) that you can access threw a menu widget.
Instead of having a finite number of shape, we propose to offer to the user three parameters :
- Shape
- Symmetry
- Pulse Width
Those three parameters will allow you to morph between all of those standard shapes, so you will have an unlimited number of shapes at disposal.
The benefit of doing this is double, you can first fine tune the shape, and as sound designer pov, it makes also 3 new parameters that you can modulate to morph the shape of your LFO over the time.
Input
The starting point of our parametric lfo is a triangle wave. Defined as follow :
|
|
Parameter #1: Shape
The shape parameter allows us to morph between triangular, sinusoidal, and sawtooth waveforms using an y-axis mapping function defined as follows:
$$ f_{0}(x, \alpha) = \begin{cases} (1 + x)^{\alpha} - 1, & \qquad x < 0 \newline 1 - (1 - x)^{\alpha}, & \qquad \text{else} \end{cases} $$
This function is particularly interesting because it approaches a sinusoidal shape for \(\alpha = 1.75028\).
The \(\alpha\) parameter can be remapped to a new parameter \(v\), allowing a natural user range between 0% and 100%, where 0% corresponds to a pure triangle and 100% corresponds to a pure square. We use the following mapping:
$$ \alpha = f_{1}(v) = e^{2.82 \operatorname{arctanh}(v)} = \left(\frac{1 + v}{1 - v}\right)^{1.41} $$
We may extend the shape parameter range to \([-100\%, 100\%]\) to allow for more complex, spiky shapes, as illustrated in the code below. In this case, it would be advantageous to include an epsilon multiplier in the shape function to mitigate spike intensities as the curve approaches -100%, thereby preventing aliasing.
Combining all this information, our final shape function is expressed as:
$$ \operatorname{shape}(x, v) = f_{0}(0.995x, f_{1}(v)) $$
|
|
Parameter #2: Symmetry
The symmetry parameter provide a way to morph between a sawtooth, a triangle and an inverted sawtooth by remapping the phase holded by the input variable \(x\), using the following function:
$$ g(x, v) = \begin{cases} \frac{x - v}{1 - v}, & \qquad x > v \newline \frac{x - v}{1 + v}, & \qquad \text{else} \end{cases} $$
The \(g\) function is valid for input \(x\in]-1, +1[\), in order to make it working on the whole phase range \(x\in]-\infty, +\infty[\), we need to wrap \(x\) by some modulus function as we did for the triangle formula.
We also might bias the waveform phase in function of the input parameter \(v\) such that the points on the middle of the y-axis remains invariant, as it was found to be more natural to use.
The complete transformation function \(\operatorname{sym}(x, v)\), incorporating the wrapped and biased phase, is provided in the code below.
|
|
Parameter #3: Pulse Width
The final parameter we introduce is the pulse width parameter, which enables us to shift the waveform to the left or right by adjusting the phase axis, similar to the symmetry parameter. Additionally, we want this parameter to support the creation of exponential decay curves, commonly used in envelope modulation. This requirement constrains the pulse width function as follows:
$$ \operatorname{pw}(x, v) = \begin{cases} x, & \qquad v = 0 \newline \frac{e^{v(1 - x)} - e^{v}}{1 - e^{v}}, & \qquad v \neq 0, \ x < 0 \newline -\frac{e^{v(1 + x)} - e^{v}}{1 - e^{v}}, & \qquad \text{else} \end{cases} $$
To handle potential overflow in floating-point arithmetic when \(v\) becomes large, an alternative form is:
$$ \operatorname{pw}(x, v) = \begin{cases} x, & \qquad v = 0 \newline \frac{e^{-v x} - e^{v}}{1 - e^{v}} - 1, & \qquad v \neq 0, \ x < 0 \newline 1 - \frac{e^{v x} - e^{v}}{1 - e^{v}}, & \qquad \text{else} \end{cases} $$
With this approach, the pulse width parameter effectively enables both waveform shifting and exponential decay, allowing greater flexibility in waveform shaping.
We might then invert the \(\operatorname{pw}\) function to compute \(v\) in function of the position of the center, that will provide a more intuitive control for the user.
|
|
Assembling Everything
The final result consists of calling the created functions in the correct sequence to obtain:
$$ \operatorname{LFO}(v_{\text{shape}}, v_{\text{pw}}, v_{\text{sym}}) = \operatorname{shape}\left(\operatorname{triangle}\left(\operatorname{pw}\left(\operatorname{sym}(x, v_{\text{sym}}), v_{\text{pw}}\right)\right), v_{\text{shape}}\right) $$
With this setup, we can experiment with different values to create various LFO presets using these three control parameters.
|
|