Skip to content

Commit

Permalink
Add slop~ html documentation from vanilla.
Browse files Browse the repository at this point in the history
  • Loading branch information
agraef committed Sep 30, 2024
1 parent 96da5cf commit d2e863d
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 0 deletions.
Binary file added pd/doc/8.topics/slop-compander-patch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pd/doc/8.topics/slop-peak-meter-graph.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pd/doc/8.topics/slop-peak-meter-patch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pd/doc/8.topics/slop-slew-limit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pd/doc/8.topics/slop-slew-limiting-patch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pd/doc/8.topics/slop-tilde-1-curves.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 70 additions & 0 deletions pd/doc/8.topics/slop-tilde.htm
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>slop-tilde</title>
<style type="text/css">
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
</style>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<h3 id="nonlinear-filtering-using-the-slop-slew-limiting-low-pass-filter"><a href="#topics-slop">Nonlinear filtering using the [slop~] slew-limiting low-pass filter</a></h3>
<p>Tasks such as envelope following, dynamics processing, and soft saturation often rely on low-pass filtering in which the cutoff frequency of the filter (which you can alternatively think of as its reaction speed) varies according to whether the input is rising, stable, or falling. For example, a VU meter design might call for an envelope follower whose output can rise quickly but then drops off more slowly. To make this we could use a low-pass filter to make a moving average of the instantaneous signal level, but the moving average should react faster on rising inputs than on falling ones.</p>
<p>The simplest type of digital low-pass filter can be understood as a moving average:</p>
<p><br /><span class="math display"><em>y</em>[<em>n</em>] = <em>y</em>[<em>n</em> − 1] + <em>k</em> ⋅ (<em>x</em>[<em>n</em>] − <em>y</em>[<em>n</em> − 1])</span><br /></p>
<p>where <span class="math inline">0 ≤ <em>k</em> ≤ 1</span> is an averaging factor, usually much closer to zero than one. When the value of <span class="math inline"><em>k</em></span> is small enough (less than 1/2, say), it is approximately equal to the filter’s rolloff frequency in units of radians per sample. (The theory behind this is explained in <a href="http://msp.ucsd.edu/techniques.htm">Theory and Techniques of Electronic Music</a>, section 8.3, “designing filters”).</p>
<p>For our purposes we’ll rewrite this equation as:</p>
<p><br /><span class="math display"><em>y</em>[<em>n</em>] − <em>y</em>[<em>n</em> − 1] = <em>f</em>(<em>x</em>[<em>n</em>] − <em>y</em>[<em>n</em> − 1])</span><br /></p>
<p>where the function <span class="math inline"><em>f</em></span> is linear:</p>
<p><br /><span class="math display"><em>f</em>(<em>x</em>) = <em>k</em> ⋅ <em>x</em></span><br /></p>
<p>In words, this equation says, “increment your output by <span class="math inline"><em>k</em></span> times the distance you have to travel to reach the goal <span class="math inline"><em>x</em>[<em>n</em>]</span>”. (So far, we’ve described the action of the linear [lop~] object.) In the [slop~] object, this linear function is replaced by a nonlinear one with three segments, one for an interval <span class="math inline">( − <em>n</em>, <em>p</em>)</span> containing zero, and two others joining this one at the input values <span class="math inline"> − <em>n</em></span> and <span class="math inline"><em>p</em></span>. The three segments have slopes equal to <span class="math inline"><em>k</em><sub><em>n</em></sub></span>, <span class="math inline"><em>k</em></span>, and <span class="math inline"><em>k</em><sub><em>p</em></sub></span> for the negative, middle, and positive regions:</p>
<figure>
<img src="slop-tilde-1-curves.png" alt="response curve for [slop~]" /><figcaption>response curve for [slop~]</figcaption>
</figure>
<p><em>Rationale.</em> In general, <span class="math inline"><em>k</em></span> could depend on both the previous output <span class="math inline"><em>y</em>[<em>n</em> − 1]</span> and on the current input <span class="math inline"><em>x</em></span>. This would require that the invoking patch somehow specify a function of two variables, a feat for which Pd is ill suited. In [slop~] we make the simplifying assumption that adding an offset to both the filter’s state and its input should result in adding the same offset to the output; that is, the filter should be translation-invariant. (As will be seen below, through a bit of skulduggery we can still make translation-dependent effects such as soft saturation). One could also ask why we don’t allow the function <span class="math inline"><em>f</em></span> to refer to a stored array instead of restricting it to a 5-parameter family of piecewise linear functions. The reason for choosing the approach taken is that it is often desirable to modulate the parameters at audio rates, and that would be difficult if we used an array.</p>
<p>The following four examples are demonstrated in subpatches of the [slop~] help file. (If your browser is set up to open “.pd” files using Pure Data then you can open it with <a href="file:../5.reference/slop~-help.pd">this link</a>; alternatively you can create a [slop~] object in a patch and get help for it, or navigate to it using Pd’s help browser.)</p>
<h4 id="example-slew-limiter"><a href="#topics-slop-slew-limiter">example: slew limiter</a></h4>
<p>The output signal <span class="math inline"><em>y</em>[<em>n</em>]</span> has a time-varying slope equal to <span class="math inline">(<em>y</em>[<em>n</em>] − <em>y</em>[<em>n</em> − 1])/<em>τ</em></span>, where <span class="math inline"><em>τ</em></span> denotes the elapsed time between two samples, equal to one over the sample rate <span class="math inline"><em>R</em></span>. The slope can be rewritten as <span class="math inline"><em>R</em> ⋅ (<em>y</em>[<em>n</em>] − <em>y</em>[<em>n</em> − 1])</span>. Suppose we wish to create an output signal whose slope is limited between two values <span class="math inline"> − <em>s</em><sub><em>n</em></sub></span> and <span class="math inline"><em>s</em><sub><em>p</em></sub></span> (so <span class="math inline"><em>s</em><sub><em>n</em></sub></span> and <span class="math inline"><em>s</em><sub><em>p</em></sub></span>, both greater than zero, are the maximum downward and upward slope). This implies that we should limit the difference between successive outputs, <span class="math inline"><em>y</em>[<em>n</em>] − <em>y</em>[<em>n</em> − 1]</span> to lie between <span class="math inline"> − <em>s</em><sub><em>n</em></sub>/<em>R</em></span> and <span class="math inline"><em>s</em><sub><em>p</em></sub>/<em>R</em></span>. We therefore increment the output by a quantity <span class="math inline"><em>x</em>[<em>n</em>] − <em>y</em>[<em>n</em> − 1]</span> as long as that increment lies between those two limits. Beyond those limits the response speed should be zero so that the increment doesn’t vary past those limits. To do this we set the five filter coefficients to [slop~] to <span class="math inline"><em>k</em> = 1</span>, <span class="math inline"><em>n</em> = <em>s</em><sub><em>n</em></sub>/<em>R</em></span>, <span class="math inline"><em>p</em> = <em>s</em><sub><em>p</em></sub>/<em>R</em></span>, and <span class="math inline"><em>k</em><sub><em>n</em></sub> = <em>k</em><sub><em>p</em></sub> = 0</span>. Since the three speed inputs to [slop~] are in units of Hz, we can set <span class="math inline"><em>k</em> = 1</span> by giving a linear-response frequency higher than the sample rate. (In practice, “1e9”, meaning a billion, will do fine for any sample rate we expect to encounter.)</p>
<p>A patch to do this is shown here:</p>
<figure>
<img src="slop-slew-limiting-patch.png" alt="slew-limiting patch from [slop~] help file" /><figcaption>slew-limiting patch from [slop~] help file</figcaption>
</figure>
<p>A sample input and output are shown here:</p>
<figure>
<img src="slop-slew-limit.png" alt="slew-limiter input (at top) and its output (bottom)" /><figcaption>slew-limiter input (at top) and its output (bottom)</figcaption>
</figure>
<p>The input is a square pulse of unit height lasting 0.7 msec, at a sample rate of 48000. The upward maximum slope is set to 9000. For the first 5 samples of the pulse, the upward increment is limited to 9000/48000 units. At the sixth sample of the pulse the input is within that limit of the previous output, and so the increment becomes exactly what is needed to make the output reach the input in value.</p>
<p><em>Note</em>: slew limiting is useful for conditioning time-varting controls to avoid discontinuities. It’s not so useful as a way to generate triangle waves from rectangular pulse trains, because the rising and falling edges are quantized to an integer sample number, making audible (and ugly) non-periodicities.</p>
<h4 id="example-peak-meter"><a href="#topics-slop-peak-meter">example: peak meter</a></h4>
<p>To make a peak meter, we need an estimate of how strongly a signal has peaked in the recent past. This can be done using [slop~] as shown:</p>
<figure>
<img src="slop-peak-meter-patch.png" alt="instant-rise, controlled-decay envelope follower" /><figcaption>instant-rise, controlled-decay envelope follower</figcaption>
</figure>
<p>Here the [abs~] object takes the input’s absolute value (known in electronics as “rectification”) and the [slop~] object is set to have no linear region at all, but a rise region with an infinite (1e9) cutoff (so that it follows a rise in the input instantly), and a decay region with a controllable cutoff frequency that sets the speed of the decay. Here is the response to the same rectangular pulse input as the example above:</p>
<figure>
<img src="slop-peak-meter-graph.png" alt="response to a square pulse" /><figcaption>response to a square pulse</figcaption>
</figure>
<p>(In order to keep the same time scale, 100 samples, as above we have here set the decay speed to 1000 Hz, but for an envelope follower this will normally be between 0.1 and 5 Hz. Lower values will result in a less jittery output when an audio signal is input, but higher ones will cause the output to react faster to falling signal levels.) The result is in linear amplitude units, and can be converted to decibels for metering as shown in the help patch.</p>
<h4 id="using-slop-in-a-compander-limiter"><a href="#topics-slop-compander-limiter">using [slop~] in a compander-limiter</a></h4>
<p>Audio engineers make frequent use of dynamics processors such as companders (compressors/expanders) and limiters. Companders are most often used to compress the dynamic range of an audio signal to make it less likely that the level falls outside a useful range, but are also sometimes configured to expand dynamic range below a chosen threshold, so that they act as noise gates. Limiters are often used with instruments such as percussion and guitars whose attacks can have much higher amplitude than the body of the note. To hear the body one turns the gain up, but then one has to limit the attack amplitude in order to avoid distortion.</p>
<p>There is no one standard design for a dynamics processor, and few makers of modern ones have divulged their secrets, which might take the form of nonlinear transfer functions, carefully tuned filter parameters, and perhaps many other possible fudge factors. There is also a whole industry in which software designers try to emulate analog hardware dynamics processors. There are also stereo compressors (for mastering CDs and LPs) and multi-band ones. Engineers frequently allow one signal to control the level of a different one, in a technique popularly known as “side chaining”. If one is working from recorded tracks (as opposed to live sound), it’s possible to look ahead in the recorded sound to reduce the distortion that inevitably occurs when a limiter is hit too hard. And so on.</p>
<p>Here we’ll describe a fairly straightforward design based on the instant-attack envelope follower described in the previous example. (This is somewhat atypical; the implications of this approach are discussed a bit later.) Once the envelope is determined (and converted to decibels), a table lookup gives the desired dynamic, and the necessary gain is computed and applied. Thus:</p>
<figure>
<img src="slop-compander-patch.png" alt="compander using instant-rise envelope follower" /><figcaption>compander using instant-rise envelope follower</figcaption>
</figure>
<p>Since the envelope follower has an unlimited rise speed, it will report rises in the signal amplitude without delay. Its output is thus always at least equal to the absolute value of the input. A dynamic curve is then used to compute the desired gain - this gain (in decibels) is equal to the difference between the curve value and the envelope follower output itself. When this gain is applied the resulting signal level is at most what is shown on the curve (equal to it when the signal and the envelope follower agree exactly).</p>
<p>In effect, rising edges of the input signal, when they push outside the currently measured envelope, will be soft-clipped according to the dynamic curve. When the signal drops in amplitude the envelope follower relaxes at a speed decided by the user, and this is heard as a gradual change in gain. (Specifically, a decrease in gain if we are compressing and/or limiting.)</p>
<p>Because the dynamic curve acts as a saturation curve when the signal level is rising, in a situation when we are using it as a limiter (so that the curve is flat at the right-hand end), it is often desirable to make the dynamic curve level off smoothly. In this patch there are three parameters to configure limiting: the limit itself, a boost in DB to apply before limiting, and a “knee” which is the interval, in decibels, over which the dynamic curve bends from the 45-degree angle at low levels to the flat region where we reach the limit.</p>
<p>in addition there is a compander function controlled by two other parameters, “thresh” (a threshold, in decibels, below which companding is to be done) and the percentage, normally between 0 and 200, by which the dynamic range should be altered below that threshold. The “speed” parameter is the speed, in tenths of a Hz., at which the envelope follower output decays.</p>
<h4 id="using-slop-to-remove-signal-jitter"><a href="#topics-slop-jitter-remover">using [slop~] to remove signal jitter</a></h4>
<p>By setting the linear cutoff frequency to zero and the linear region to an interval of length <span class="math inline"><em>a</em></span> (either by setting <span class="math inline"><em>n</em> = 0, <em>p</em> = 1</span> or <span class="math inline"><em>n</em> = <em>p</em> = <em>a</em>/2</span>), and then setting <span class="math inline"><em>k</em><sub><em>n</em></sub> = <em>k</em><sub><em>p</em></sub> = inf </span>, we get a filter that allows its input to jitter over a range of <span class="math inline"><em>a</em></span> units before the filter responds to it. This is sometimes useful for quieting down noisy control sources (such as envelope followers or physical sensors). This is analogous to a loose physical linkage.</p>
</body>
</html>

0 comments on commit d2e863d

Please sign in to comment.