TutorialDistortion, 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 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. 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.

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.