Distortion, Saturation, and Wave Shaping
Saturation is a type of distortion that we can emulate in the digital
domain through the application of a stateless transfer function (or shaper
function, wave shaper) to an incoming audio signal. The notion of
saturation typically comes from analog hardware, where for example, the
hardware components (i.e. tubes, valves) of various amplifiers would subtly
and gradually deform at large voltages. Therefore the types of transfer
functions we're interested in for simulating saturation are those which
approximate the identity function, y(x) = x
, near the origin, and which
gradually and nonlinearly taper as they approach the limits of the signal
domain, i.e. [-1.0, 1.0]
for floating point signals.
The classic choice of such a transfer function for applying saturation
in DSP is y(x) = tanh(g * x)
, which we parameterize through choice of g
to
amplify or drive the signal x
into the nonlinear regions of the tanh curve.
Notice in the example above how near the upper limit of g
the output
sine tone starts to resemble a square wave. A square wave is composed of a fundamental
tone plus a series of odd harmonics on top of that fundamental. A nonlinear transfer function
which is perfectly symmetric about the origin, such as y(x) = tanh(g * x)
will
impart odd harmonics on the output signal, hence the square wave resemblance. Introducing asymmetry here,
either in the transfer function or in the input signal itself before it
visits the saturator, will start to yield even harmonics as well.
The character of a saturator is in large part due to the balance of harmonics produced by the transfer function, so it's worth spending time exploring various functions and means of introducing asymmetry to your input signal. Filtering, whether before or after the saturation step, is also integral to the process of building a nice saturation process. It's important to understand that, because these transfer functions are stateless, their output depends exactly on the input given, so pre-filtering the input signal can have just as significant a role in shaping the overall character as can post-filtering. Let's put these ideas together into a larger, cohesive example.
While saturation refers to a type or a subset of distortion, wave shaping refers to a process or a technique for applying distortion, through the use of a stateless transformation. All of the prior discussion on saturation transfer functions (or shaper functions) can therefore be referred to as wave shaping.
To elaborate further on technique then, we turn our discussion to lookup tables, which open the door to all kinds of highly complex transfer functions. Notice in the last example that we compute a polynomial for each sample of the input signal. For complicated polynomials or expensive trigonometric functions, this can become an expensive processing task. Historically, this detail gave way to the use of lookup tables to precompute the expensive functions for various input values so that, while processing our audio, all we need to do is lookup the correct output value given an input value. In modern DSP, this still holds for improving computational efficiency, but it also allows us to push incredibly complex functions into our distortion algorithms.
We'll wrap up our tutorial here with one final demonstration, using
Elementary's el.table
to implement a complicated, lookup-table based wave
shaping distortion. Because el.table
is read with a position value on the
range [0, 1]
, we'll see that we can simply map our input signal from its
typical [-1, 1]
range onto [0, 1]
and fill our lookup table such that the
origin is represented in the middle of our data table. Finally, we'll pull
in our prior discussion and use some bias and filtering to define the
character of our distortion.
At this point, hopefully you've learned enough to jumpstart your own exploration into the broad world of saturation, wave shaping, and distortion. The last detail to leave with, then, is a small caution that wave shaping can have drastic and sometimes undesirable results. If the result of applying the transformation is an output signal with discontinuities or drastically sharp edges, you will likely encounter aliasing, which we often aim to avoid. This is an entire topic on its own, but if you're interested you can read on in Introduction to Oversampling for Alias Reduction (opens in a new tab).
If you have any questions, comments, or want to share your own take on saturation and distortion, join our Discord community! (opens in a new tab)