Skip to content

Commit

Permalink
more tweaks to slop~ documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
millerpuckette committed Jul 18, 2019
1 parent c47bc5b commit 49ca1ab
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 184 deletions.
205 changes: 41 additions & 164 deletions doc/5.reference/slop~-help.pd
Original file line number Diff line number Diff line change
Expand Up @@ -155,69 +155,6 @@
#X obj 12 12 slop~;
#X text 73 13 - slew-limiting low-pass filter;
#X text 142 139 cutoff frequency in linear region;
#N canvas 627 309 650 583 soft-one-sided-clipping 0;
#X obj 73 52 osc~;
#X floatatom 73 26 5 0 0 0 - - -;
#X obj 95 317 metro 500;
#X obj 95 291 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
1;
#X floatatom 344 59 5 0 10000 0 - - -;
#X floatatom 435 62 5 0 0 0 - - -;
#X msg 29 216 set 0;
#X obj 108 91 *~ 1e+10;
#X obj 108 116 clip~ 0 1;
#X text 311 31 free lowpass;
#X text 422 40 clip lowpass;
#X obj 190 91 -~;
#X floatatom 208 44 5 0 0 0 - - -;
#X obj 208 67 / 100;
#X text 196 19 lower limit;
#X obj 432 96 t b f;
#X obj 275 160 *~ 0;
#X obj 266 202 -~;
#X obj 234 204 *~;
#X obj 198 205 +~;
#X obj 283 129 expr $f2/($f1-$f2);
#X obj 198 230 *~ -1;
#X obj 108 156 *~ 0;
#X obj 325 309 out1~;
#X obj 69 348 tabwrite~ \$0-clip1-graph;
#N canvas 0 50 450 250 (subpatch) 0;
#X array \$0-clip1-graph 1000 float 2;
#X coords 0 1 1000 -1 500 140 1 0 0;
#X restore 48 418 graph;
#X obj 67 256 slop~ 1000 1e+09 0 1e+09 0;
#X connect 0 0 11 0;
#X connect 0 0 26 0;
#X connect 1 0 0 0;
#X connect 2 0 24 0;
#X connect 3 0 2 0;
#X connect 4 0 20 0;
#X connect 4 0 22 1;
#X connect 5 0 15 0;
#X connect 6 0 26 0;
#X connect 7 0 8 0;
#X connect 8 0 18 0;
#X connect 8 0 22 0;
#X connect 11 0 7 0;
#X connect 11 0 16 0;
#X connect 11 0 19 0;
#X connect 11 0 17 1;
#X connect 12 0 13 0;
#X connect 13 0 11 1;
#X connect 15 0 20 0;
#X connect 15 1 20 1;
#X connect 15 1 26 3;
#X connect 16 0 17 0;
#X connect 17 0 18 1;
#X connect 18 0 19 1;
#X connect 19 0 21 0;
#X connect 20 0 16 1;
#X connect 21 0 26 2;
#X connect 22 0 26 1;
#X connect 26 0 23 0;
#X connect 26 0 24 0;
#X restore 665 273 pd soft-one-sided-clipping;
#X floatatom 170 215 5 0 10000 0 - - -;
#X text 180 165 maximum downward slew of linear region;
#X text 213 205 asymptotic downward cutoff frequency (for downward
Expand All @@ -227,105 +164,6 @@ slews greater than maximum), f 41;
greater than maximum), f 41;
#X floatatom 242 299 5 0 10000 0 - - -;
#X text 105 65 frequency of test oscillator;
#N canvas 318 167 870 830 soft-two-sided-clipping 0;
#X obj 160 103 osc~;
#X floatatom 160 77 5 0 0 0 - - -;
#X obj 167 513 metro 500;
#X obj 167 487 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
1;
#X floatatom 69 207 5 0 10000 0 - - -;
#X floatatom 411 95 5 0 0 0 - - -;
#X msg 85 411 set 0;
#X obj 229 174 *~ 1e+10;
#X obj 228 199 clip~ 0 1;
#X text 54 180 free lowpass;
#X obj 306 150 -~;
#X floatatom 324 87 5 0 0 0 - - -;
#X obj 324 110 / 100;
#X text 312 62 lower limit;
#X obj 404 157 t b f;
#X obj 353 300 *~ 0;
#X obj 335 341 -~;
#X obj 303 343 *~;
#X obj 267 344 +~;
#X obj 139 452 slop~ 1000 1e+09 0 1e+09 0;
#X obj 267 369 *~ -1;
#X text 404 61 clip-bottom lowpass, f 11;
#X obj 175 294 *~ 0;
#X floatatom 718 101 5 0 0 0 - - -;
#X obj 535 212 clip~ 0 1;
#X obj 613 161 -~;
#X floatatom 631 93 5 0 0 0 - - -;
#X obj 631 116 / 100;
#X obj 713 165 t b f;
#X obj 665 298 *~ 0;
#X obj 647 339 -~;
#X obj 615 341 *~;
#X obj 579 342 +~;
#X text 619 68 upper limit;
#X text 711 67 clip-upper lowpass, f 11;
#X obj 536 187 *~ -1e+10;
#X obj 176 262 *~;
#N canvas 0 50 450 250 (subpatch) 0;
#X array \$0-clip2-graph 1000 float 2;
#X coords 0 1 1000 -1 500 140 1 0 0;
#X restore 47 653 graph;
#X obj 141 544 tabwrite~ \$0-clip2-graph;
#X obj 51 488 out1~;
#X obj 376 254 expr $f2/ ($f1-$f2), f 9;
#X obj 693 255 expr -$f2/ ($f1-$f2), f 10;
#X connect 0 0 10 0;
#X connect 0 0 19 0;
#X connect 0 0 25 0;
#X connect 1 0 0 0;
#X connect 2 0 38 0;
#X connect 3 0 2 0;
#X connect 4 0 22 1;
#X connect 4 0 40 0;
#X connect 4 0 41 0;
#X connect 5 0 14 0;
#X connect 6 0 19 0;
#X connect 7 0 8 0;
#X connect 8 0 17 0;
#X connect 8 0 36 0;
#X connect 10 0 7 0;
#X connect 10 0 15 0;
#X connect 10 0 18 0;
#X connect 10 0 16 1;
#X connect 11 0 12 0;
#X connect 12 0 10 1;
#X connect 14 0 40 0;
#X connect 14 1 19 3;
#X connect 14 1 40 1;
#X connect 15 0 16 0;
#X connect 16 0 17 1;
#X connect 17 0 18 1;
#X connect 18 0 20 0;
#X connect 19 0 38 0;
#X connect 19 0 39 0;
#X connect 20 0 19 2;
#X connect 22 0 19 1;
#X connect 23 0 28 0;
#X connect 24 0 31 0;
#X connect 24 0 36 1;
#X connect 25 0 29 0;
#X connect 25 0 32 0;
#X connect 25 0 30 1;
#X connect 25 0 35 0;
#X connect 26 0 27 0;
#X connect 27 0 25 1;
#X connect 28 0 41 0;
#X connect 28 1 19 5;
#X connect 28 1 41 1;
#X connect 29 0 30 0;
#X connect 30 0 31 1;
#X connect 31 0 32 1;
#X connect 32 0 19 4;
#X connect 35 0 24 0;
#X connect 36 0 22 0;
#X connect 40 0 15 1;
#X connect 41 0 29 1;
#X restore 666 303 pd soft-two-sided-clipping;
#X obj 493 9 bng 15 250 50 0 \$0-mainlink empty empty 17 7 0 10 -262144
-1 -1;
#N canvas 503 120 524 258 works 0;
Expand Down Expand Up @@ -778,6 +616,45 @@ at all so the linear cutoff frequency has no effect.;
#X connect 26 0 20 0;
#X restore 665 217 pd peak-meter;
#X text 118 114 set state (previously stored output);
#N canvas 467 212 848 759 jitter-remover 0;
#X floatatom 114 148 5 0 100 0 - - -;
#X obj 89 406 metro 500;
#X obj 89 380 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1
1;
#X text 114 378 <- start metronome to graph output;
#N canvas 0 50 450 250 (subpatch) 0;
#X array \$0-jitter-graph 1000 float 2;
#X coords 0 1 1000 -1 500 140 1 0 0;
#X restore 81 530 graph;
#X obj 64 52 osc~ 100;
#X obj 63 437 tabwrite~ \$0-jitter-graph;
#X obj 64 83 clip~ -0.3 0.3;
#X obj 182 57 osc~ 1000;
#X obj 182 82 *~ 0.03;
#X obj 65 118 +~;
#X obj 62 327 slop~ 0 0 1e+09 0 1e+09;
#X obj 114 172 / 1000;
#X text 163 147 jitter range \, 0 to 0.1;
#X text 42 7 jitter remover;
#X text 387 56 If you have a noisy sensor \, one approach to smoothing
it is to set slop~ u pto only react to motion past a certain window.
The linear frequeny cutoff is set to zero so that the output only changes
when the input strays by more than the maximum upward or downward slew.
;
#X text 386 147 Set the jitter range to zero to see the original signal
and raise it to see smoothing.;
#X connect 0 0 12 0;
#X connect 1 0 6 0;
#X connect 2 0 1 0;
#X connect 5 0 7 0;
#X connect 7 0 10 0;
#X connect 8 0 9 0;
#X connect 9 0 10 1;
#X connect 10 0 11 0;
#X connect 11 0 6 0;
#X connect 12 0 11 2;
#X connect 12 0 11 4;
#X restore 665 272 pd jitter-remover;
#X connect 0 0 12 0;
#X connect 2 0 0 0;
#X connect 4 0 3 0;
Expand All @@ -789,5 +666,5 @@ at all so the linear cutoff frequency has no effect.;
#X connect 10 0 12 4;
#X connect 11 0 12 2;
#X connect 12 0 3 0;
#X connect 17 0 12 3;
#X connect 22 0 12 5;
#X connect 16 0 12 3;
#X connect 21 0 12 5;
12 changes: 9 additions & 3 deletions doc/8.topics/slop-tilde.htm
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ <h3 id="nonlinear-filtering-using-the-slop-slew-limiting-low-pass-filter"><a hre
<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-varting 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>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>
Expand All @@ -41,14 +41,14 @@ <h4 id="example-slew-limiter"><a href="#topics-slop-slew-limiter">example: slew
<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 teh 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>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 teh 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>
<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>
Expand All @@ -60,5 +60,11 @@ <h4 id="using-slop-in-a-compander-limiter"><a href="#topics-slop-compander-limit
<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>
Loading

0 comments on commit 49ca1ab

Please sign in to comment.