<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[[BLOG] How does DSP work? [Part 3]]]></title><description><![CDATA[<p dir="auto"><a href="https://forum.hise.audio/topic/14957/blog-how-does-dsp-work-part-1">Part 1</a><br />
<a href="https://forum.hise.audio/topic/14965/blog-how-does-dsp-work-part-2">Part 2</a></p>
<h2>How does DSP work? [Part 3]</h2>
<p dir="auto">This time, we're going to look at distortion.</p>
<p dir="auto">In Part 1, I briefly described distortion as "bending the waveform" by changing the sample values.<br />
Now let's unpack what that actually means.</p>
<h2>Distorting a waveform</h2>
<p dir="auto">To "distort" something means to change its shape.</p>
<p dir="auto">A distortion effect does exactly that to a waveform:</p>
<p dir="auto"><img src="/assets/uploads/files/1785700259143-88b1a706-e7d2-401e-8b68-854c0c78a4df-distortion-shape.png" alt="88b1a706-e7d2-401e-8b68-854c0c78a4df-distortion-shape.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">We talked previously about how in a gain/volume effect, every sample is multiplied by the same amount.<br />
The waveform becomes taller or shorter, but it keeps the same shape.</p>
<p dir="auto">A distortion effect changes sample heights by <em>different</em> amounts.<br />
That is what allows the waveform to bend shape.</p>
<h2>Clipping</h2>
<p dir="auto">The simplest example of distortion is clipping.</p>
<p dir="auto">For example, a clipper that only allows sample values between <code>-1.0</code> and <code>+1.0</code>.</p>
<p dir="auto">(fact: Most audio file formats will clip at this value)</p>
<p dir="auto">Values inside that range are left alone.<br />
Anything above <code>+1.0</code> or below <code>-1.0</code> is clamped.</p>
<p dir="auto">Watch what happens as the waveform gets louder:</p>
<p dir="auto"><img src="/assets/uploads/files/1785700275694-62a3a3c9-0cde-49d6-b4ff-55b81cf7b7a9-clipping-drive.gif" alt="62a3a3c9-0cde-49d6-b4ff-55b81cf7b7a9-clipping-drive.gif" class=" img-fluid img-markdown" /></p>
<p dir="auto">At first, the waveform simply gets taller.</p>
<p dir="auto">Once the peaks reach the limits, they cannot get any taller.<br />
That's how a clipper works. This is a distortion.</p>
<p dir="auto">Let's zoom in and look at a few of the sample values:</p>
<p dir="auto"><img src="/assets/uploads/files/1785700288019-585f2c9d-f85f-433e-bd11-bcb730cc0210-clipping-samples.png" alt="585f2c9d-f85f-433e-bd11-bcb730cc0210-clipping-samples.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">Only the sample values that exceed the limits are changed.<br />
The extra height is cut off, leaving the waveform with flat edges.</p>
<p dir="auto">In pseudocode, the whole effect could be written like this:</p>
<pre><code class="language-text">for each sample in the buffer:
    sample = clamp(sample, -1.0, 1.0)
</code></pre>
<p dir="auto">In other words:<br />
if the sample value is less than <code>-1.0</code>, it gets set to <code>-1.0</code>.<br />
if the sample value is greater than <code>1.0</code>, it gets set to <code>1.0</code>.</p>
<h2>Waveshaping</h2>
<p dir="auto">This kind of distortion is called a <strong>Waveshaper</strong>.<br />
<code>Hardclipping</code> is just the straightforward clamp.</p>
<p dir="auto"><img src="/assets/uploads/files/1785700305852-9bcd1f2d-c637-45bc-850e-ff1a41ad3482-function-one-value.png" alt="9bcd1f2d-c637-45bc-850e-ff1a41ad3482-function-one-value.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">But we can use other rules/functions to create different waveshapers.</p>
<p dir="auto">You can create waveshapers by defining a function that maps <strong>input values</strong> to <strong>output values</strong>.</p>
<p dir="auto"><img src="/assets/uploads/files/1785700322967-174ff98f-2991-4840-8d7f-c14e30f8cde7-mapping-pairs.png" alt="174ff98f-2991-4840-8d7f-c14e30f8cde7-mapping-pairs.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">In other words:<br />
If the original sample value is equal to <code>-0.75</code> we set it to <code>-0.88</code>.<br />
If the original sample value is equal to <code>-0.50</code> we set it to <code>-0.67</code>,<br />
and so on.</p>
<p dir="auto">This is called <strong>mapping</strong>.<br />
One value is mapped to another value.</p>
<p dir="auto">And the table is called a <strong>lookup table</strong>.</p>
<h2>Drawing the table</h2>
<p dir="auto">The input and output mapping can also be drawn as points on a graph.<br />
You've probably seen this kind of thing before:</p>
<p dir="auto"><img src="/assets/uploads/files/1785700344723-aab8ad25-cb34-43a8-bfd2-3e89c5bef259-mapping-curve.png" alt="aab8ad25-cb34-43a8-bfd2-3e89c5bef259-mapping-curve.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">It's an easy way to visualize the waveshaper.</p>
<p dir="auto">Input values run along the bottom.<br />
Output values run up the right side.</p>
<p dir="auto">To look up an input of <code>0.50</code>, start at <code>0.50</code> along the bottom, move up until the line is reached, then move right to read the output:</p>
<p dir="auto"><img src="/assets/uploads/files/1785700369395-db05a93e-37b5-4e34-bf1c-026ffa55ce31-curve-lookup.gif" alt="db05a93e-37b5-4e34-bf1c-026ffa55ce31-curve-lookup.gif" class=" img-fluid img-markdown" /></p>
<p dir="auto">The result is <code>0.67</code>.</p>
<p dir="auto">This plot is called a <strong>transfer curve</strong> or <strong>waveshaper curve</strong>.</p>
<p dir="auto"><img src="/assets/uploads/files/1785700388991-bc282e62-4fae-4ad5-b594-ef945a0d6966-identity-and-shaper.png" alt="bc282e62-4fae-4ad5-b594-ef945a0d6966-identity-and-shaper.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">Bending the line means we are mapping the samples to different output values.</p>
<h2>A waveshaper</h2>
<p dir="auto">Here is a waveshaper processing a waveform:</p>
<p dir="auto"><img src="/assets/uploads/files/1785700403773-4fa77841-29c4-448a-bd75-340360a71ff0-waveshaper-samples.gif" alt="4fa77841-29c4-448a-bd75-340360a71ff0-waveshaper-samples.gif" class=" img-fluid img-markdown" /></p>
<p dir="auto">For each sample of the original wave, we look up the input value on the transfer curve,<br />
and replace it with the corresponding output value.</p>
<p dir="auto">The same process on a more dense smooth waveform looks like this:</p>
<p dir="auto"><img src="/assets/uploads/files/1785700413924-89c81430-ee51-4fca-94af-020254731854-waveshaper-waveform.png" alt="89c81430-ee51-4fca-94af-020254731854-waveshaper-waveform.png" class=" img-fluid img-markdown" /></p>
<h2>The Math</h2>
<p dir="auto">The input and output mappings do not have to be stored in a lookup table.<br />
Some waveshaper distortions use a table, but some use a formula instead.</p>
<p dir="auto">Here is the most common "soft-clipping" formula:</p>
<pre><code class="language-text">output = tanh(input * drive)
</code></pre>
<p dir="auto"><code>input</code> is the current sample value.<br />
<code>tanh</code> is a math function that returns the new sample value.</p>
<p dir="auto">The function is simply another way of providing an input-to-output map.</p>
<p dir="auto">(You could also use <code>sin</code>, <code>cos</code>, or any other function that takes an input value and gives you an output value!)</p>
<p dir="auto">The <code>drive</code> multiplication in my code makes the original sample value larger (increases gain) before <code>tanh</code> waveshapes the output.<br />
<code>Tanh</code> has more of a shape at the extreme edges, so increasing the volume of our waveform means we get mapped with all those strong parts of the curve.<br />
And get a more warped (distorted) waveform.</p>
<p dir="auto"><img src="/assets/uploads/files/1785700576758-bcdc3b56-7673-49dc-aba6-bc153a4f23b7-waveshaper-drive.gif" alt="bcdc3b56-7673-49dc-aba6-bc153a4f23b7-waveshaper-drive.gif" class=" img-fluid img-markdown" /></p>
<p dir="auto">Basically we get more distortion when we go into the waveshaper with a louder signal.</p>
<p dir="auto">The algorithm is still very simple:</p>
<pre><code class="language-text">for each sample in the buffer:
    sample = tanh(sample * drive)
</code></pre>
<p dir="auto">In HISE-style C++:</p>
<p dir="auto"><img src="/assets/uploads/files/1785700598489-b1aa719a-7b52-4613-abb3-cb385b65a8d1-hise-waveshaper-cpp.png" alt="b1aa719a-7b52-4613-abb3-cb385b65a8d1-hise-waveshaper-cpp.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">HISE's <a href="https://docs.hise.dev/scriptnode/list/math/expr.html" rel="nofollow ugc"><code>math.expr</code></a> node does exactly this kind of thing, and it lets you type in your own waveshaping formula!</p>
<p dir="auto"><img src="/assets/uploads/files/1785700661336-85d91bf7-130f-483c-b0d7-a486664dd23f-image.png" alt="85d91bf7-130f-483c-b0d7-a486664dd23f-image.png" class=" img-fluid img-markdown" /></p>
<h2>What distortion sounds like</h2>
<p dir="auto">Different shaped waveforms sound different (well duh).</p>
<p dir="auto">A sine wave sounds smooth and pure.<br />
A square wave sounds harsh and loud.</p>
<p dir="auto">When we apply waveshaping, we are bending the waveform to make new waveforms.</p>
<p dir="auto"><img src="/assets/uploads/files/1785700613369-37ec97d5-de58-43d0-af8c-a7215c506e4c-distortion-harmonics.png" alt="37ec97d5-de58-43d0-af8c-a7215c506e4c-distortion-harmonics.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">More bending changes the shape more (and creates all kinds of harmonics)!</p>
<p dir="auto">You can use a lot of different kinds of formulas to bend waveforms in weird ways.<br />
Different transfer curves create different effects!</p>
<p dir="auto"><img src="/assets/uploads/files/1785700623142-ea20e5d9-6987-4cfe-b677-6fb11a2d830f-distortion-family.png" alt="ea20e5d9-6987-4cfe-b677-6fb11a2d830f-distortion-family.png" class=" img-fluid img-markdown" /></p>
<h2>To Recap</h2>
<p dir="auto">A distortion effect changes the shape of a waveform by altering the sample values.</p>
<p dir="auto">A clipper does this by limiting/clamping sample values.</p>
<p dir="auto">Waveshapers use a function to map each input sample value to a new output value.<br />
You can visualise this mapping nicely as a transfer curve.</p>
<p dir="auto">Apply the mapping to every sample in the buffer, and the new sample heights you get, form a distorted waveform.</p>
<p dir="auto">That is how a waveshaper works.</p>
]]></description><link>https://forum.hise.audio/topic/14972/blog-how-does-dsp-work-part-3</link><generator>RSS for Node</generator><lastBuildDate>Sun, 02 Aug 2026 23:03:19 GMT</lastBuildDate><atom:link href="https://forum.hise.audio/topic/14972.rss" rel="self" type="application/rss+xml"/><pubDate>Sun, 02 Aug 2026 19:58:01 GMT</pubDate><ttl>60</ttl></channel></rss>