parent
12d76b5363

commit
96afc58e84

`@ -0,0 +1,192 @@` |
||||

```
``` |
||||

`### Expr family objects by [Shahrokh Yadegari](http://yadegari.org/).` |
||||

```
``` |
||||

`Based on original sources from IRCAM's jMax Released under BSD License.` |
||||

```
``` |
||||

`The **expr** family is a set of C-like expression evaluation objects for the graphical music language Pure Data. It used to come as an 'extra' external, but it is now a built-in native object.` |
||||

```
``` |
||||

`**expr** runs in control rate and evaluates C-like expressions. See below for the list of operators. Multiple expressions separated by semicolons can be defined in a single expr object and this results in multiple outlets (up to 100, each for each expression). Expressions are evaluated from right to left (which means that the bottom expression will be the first executed.) The number of inlets in expr are defined by variables that take a few different forms: **$i#**, **$i#** and **$s#** for 'integers', 'floats' and 'symbols' ('#' is an inlet number from 1 up to 100, ordered from left to right). As an example, we can have 3 inlets defined as "$i1", "$f2" and "$s3", where:` |
||||

```
``` |
||||

`- **$i1** is the input from the first (left) inlet and is treated as an integer` |
||||

`- **$f2** is the input from the second (middle) inlet and is treated as a float` |
||||

`- **$s3** is the input from the third (middle) and expects a symbol (used to define array names)` |
||||

```
``` |
||||

`Arrays and variables (defined using the 'value' object) can be accessed the same way one dimensional arrays are accessed in C; for example, "valx + 10" will be evaluated to the value of variable 'valx' + 10 and "tabname[5]" will be evaluated to be the 5th element of an array named "tabname". As shown above, the name of the arrays can be given by an input; for example "$s2[5]" will be evaluated to be the 5 element of the array whose symbol has been passed in inlet 2.` |
||||

```
``` |
||||

`Type conversion from a float input to an integer is done automatically if the inlet is defined as an integer. Conversely, you can convert it explicitly by using functions (see below for the list of functions).` |
||||

```
``` |
||||

`**expr~** is designed to efficiently combine signal and control stream processing by vector operations on the basis of the audio block size. The operations, functions, and syntax for **expr~** is just like expr with the addition of the $v# variable for signal vector input (also numbered up to 100). The **'$v'** is needed at least for the first and main input, so:` |
||||

```
``` |
||||

`- **$v1** - means the first inlet is a signal input` |
||||

`- **$i2** - means the second inlet is an integer input` |
||||

`- **$f2** - means the third inlet is a float input` |
||||

`- **$s4** - means the fourth inlet is a symbol input` |
||||

```
``` |
||||

`The result of an expression from expr~ is also an audio signal and multiple expressions up to 1000 can also be defined via semicolons.` |
||||

```
``` |
||||

`Note for MSP users: Currently in the MSP version all signal inputs should come first followed by other types of inlet. (There seems to be no way of mixing signal and other types of inlets in their order in Max/MSP, if you know otherwise, please let me know.) This means that signal inlets cannot be mixed with other types of inlets. For example, "expr~ $v1$f2$v3 " is not legal. The second and third inlet should be switched and "expr~ $v1$v2$f3" should be used. In Pd you can mix them in any way you want.` |
||||

```
``` |
||||

`The **fexpr~** object provides a flexible mechanism for building FIR and IIR filters by evaluating expressions on a sample by sample basis and providing access to prior samples of the input and output audio streams. When fractional offset is used, **fexpr~** uses linear interpolation to determine the value of the indexed sample. The operations, functions, and syntax for expr~ is just like expr with the addition of **$x#** and **$y#** variables. **fexpr~** can access previous input and output samples up to the block size (64 by default).` |
||||

```
``` |
||||

`**$x#** is used to denote a signal input whose samples we would like to access. The syntax is $x followed by '#' (the inlet number up to 100) and the samples indexed by brackets, for example $x1[-1] specifies the previous sample of the first inlet. Therefore, if we are to build a simple filter which replaces every sample by the average of that sample and its previous one, we would use " **fexpr~ ($x1[0]+$x1[-1])/2** ". For ease of when the brackets are omitted, the current sample is implied, so we can write the previous filter expression as follows: " **fexpr~ ($x1+$x1[-1])/2** ". To build IIR filters **$y#** is used to access the previous output samples indexed from -1 inside brackets. Note now that '#' here is used to define the outlet number.` |
||||

```
``` |
||||

`- **$x1[n]** - means the first inlet is a signal input and 'n' is an index from 0 to -block size` |
||||

`- **$y1[n]** - is used to access output samples from the first expression and 'n' is an index from -1 to -block size` |
||||

`- **$i2** - means the second inlet is an integer input` |
||||

`- **$f2** - means the third inlet is a float input` |
||||

`- **$s4** - means the fourth inlet is a symbol input` |
||||

```
``` |
||||

```
``` |
||||

`------------------` |
||||

```
``` |
||||

`### The operators expr, expr~ and fexpr~ support (listed from highest precedence to lowest) are as follows:` |
||||

```
``` |
||||

`|Operator |Description|` |
||||

`|:----|----:|` |
||||

`|~ |One's complement|` |
||||

`|* |Multiply|` |
||||

`|/ |Divide|` |
||||

`|% |Modulo|` |
||||

`|+ |Add|` |
||||

`|- |Subtract|` |
||||

`|\<\< |Shift Left|` |
||||

`|\>\> |Shift Right|` |
||||

`|< |Less than (boolean)|` |
||||

`|<= |Less than or equal (boolean)|` |
||||

`|> |Greater than (boolean)|` |
||||

`|>= |Greater than or equal (boolean)|` |
||||

`|== |Equal (boolean)|` |
||||

`|!= |Not equal (boolean)|` |
||||

`|& |Bitwise And|` |
||||

`|^ |Exclusive Or|` |
||||

`|\| |Bitwise Or|` |
||||

`|&& |Logical And (boolean)|` |
||||

`|\|\| |Logical Or (boolean)|` |
||||

```
``` |
||||

`### The supported functions for expr, expr~ and fexpr~ are:` |
||||

```
``` |
||||

`|Functions |# of Args |Description|` |
||||

`|:---|:---|:--- |` |
||||

`|if () |3 |conditional - if (condition, IfTrue-expr, IfFalse-expr) - in expr~ if 'condition' is a signal, the result will be determined on sample by sample basis (added in version 0.4)v|` |
||||

`|int () |1 |convert to integer|` |
||||

`|rint () |1 |round a float to a nearby integer|` |
||||

`|float () |1 |convert to float|` |
||||

`|min () |2 |minimum|` |
||||

`|max () |2 |maximum|` |
||||

`|abs() |1 |absolute value (added in version 0.3)|` |
||||

`|if() |3 |conditional - if (condition, IfTrue-expr, IfFalse-expr) - in expr~ if 'condition' is a signal, the result will be determined on sample by sample basis (added in version 0.4)|` |
||||

`|isinf() |1 |is the value infinite (added in version 0.4)|` |
||||

`|finite() |1 |is the value finite (added in version 0.4)|` |
||||

`|isnan |1 |is the value non a number (added in version 0.4)|` |
||||

`|copysign() |1 |copy sign of a number(added in version 0.4)|` |
||||

`|imodf |1 |get signed integer value from floating point number(added in version 0.4)|` |
||||

`|modf |1 |get signed fractional value from floating-point number(added in version 0.4)|` |
||||

`|drem |2 |floating-point remainder function (added in version 0.4)|` |
||||

```
``` |
||||

```
``` |
||||

`### power functions` |
||||

```
``` |
||||

`|Functions |# of Args |Description|` |
||||

`|:---|:---|:--- |` |
||||

`|pow () |2 |raise to the power of {e.g., pow(x,y) is x to the power of y}|` |
||||

`|sqrt () |1 |square root|` |
||||

`|exp() |1 |e raised to the power of the argument {e.g., exp(5.2) is e raised to the power of 5.2}|` |
||||

`|ln() and log() |1 |natural log|` |
||||

`|log10() |1 |log base 10|` |
||||

`|fact() |1 |factorial|` |
||||

`|erf() |1 |error function (added in version 0.4)|` |
||||

`|erfc() |1 |complementary error function (added in version 0.4)|` |
||||

`|cbrt() |1 |cube root (added in version 0.4)|` |
||||

`|expm1() |1 |exponential minus 1 (added in version 0.4)|` |
||||

`|log1p() |1 |logarithm of 1 plus (added in version 0.4)|` |
||||

`|ldexp() |1 |multiply floating-point number by integral power of 2 (added in version 0.4)|` |
||||

```
``` |
||||

`### Trigonometric` |
||||

```
``` |
||||

`|Functions |# of Args |Description|` |
||||

`|:---|:---|:--- |` |
||||

`|sin() |1 |sine|` |
||||

`|cos() |1 |cosine|` |
||||

`|tan() |1 |tangent|` |
||||

`|asin() |1 |arc sine|` |
||||

`|acos() |1 |arc cosine|` |
||||

`|atan() |1 |arc tangent|` |
||||

`|atan2() |2 |arc tangent of 2 variables|` |
||||

`|sinh() |1 |hyperbolic sine|` |
||||

`|cosh() |1 |hyperbolic cosine|` |
||||

`|tanh() |1 |hyperbolic tangent|` |
||||

`|asinh() |1 |inverse hyperbolic sine|` |
||||

`|acosh() |1 |inverse hyperbolic cosine|` |
||||

`|atan() |1 |inverse hyperbolic tangent|` |
||||

`|floor() |1 |largest integral value not greater than argument (added in version 0.4)|` |
||||

`|ceil() |1 |smallest integral value not less than argument (added in version 0.4)|` |
||||

`|fmod() |1 |floating-point remainder function (added in version 0.4)|` |
||||

```
``` |
||||

`### Table Functions` |
||||

```
``` |
||||

`|Functions |# of Args |Description|` |
||||

`|:---|:---|:--- |` |
||||

`|size() |1 |size of a table|` |
||||

`|sum() |1 |sum of all elements of a table|` |
||||

`|Sum() |3 |sum of elements of a specified boundary of a table|` |
||||

`|avg() |1 |averages all elements of a table|` |
||||

`|Avg() |3 |averages elements of a specified boundary of a table|` |
||||

```
``` |
||||

`### Acoustics` |
||||

```
``` |
||||

`|Functions |# of Args |Description|` |
||||

`|:---|:---|:--- |` |
||||

`|mtof() |1 |convert MIDI pitch to frequency in hertz|` |
||||

`|ftom() |1 |convert frequency in hertz to MIDI pitch|` |
||||

`|dbtorms() |1 |convert db to rms|` |
||||

`|rmstodb() |1 |convert rms to db|` |
||||

`|powtodb() |1 |convert power to db|` |
||||

`|dbtopow() |1 |convert db to power|` |
||||

```
``` |
||||

`--------------------------` |
||||

```
``` |
||||

`### CHANGELOG:` |
||||

```
``` |
||||

`#### New Additions in version 0.57` |
||||

```
``` |
||||

`- fixed a bug in fact().` |
||||

`- fact() (factorial) now calculates and returns its value in double` |
||||

`- fixed the bad lvalue bug - "4 + 5 = 3" was not caught before` |
||||

`Added mtof(), mtof(), dbtorms(), rmstodb(), powtodb(), dbtopow()` |
||||

```
``` |
||||

`#### New Additions in version 0.56` |
||||

```
``` |
||||

`- Fexpr~ now accepts a float in its first input.` |
||||

`- Added avg() and Avg() back to the list of functions` |
||||

```
``` |
||||

`#### New Additions in version 0.55` |
||||

```
``` |
||||

`- Expr, expr~, and fexpr~ are now built-in native objects.` |
||||

`- The arrays now redraw after a store into one of their members` |
||||

`- ex_if() (the "if()" function is reworked to only evaluate either the left or the right args depending on the truth value of the condition. However, if the condition is an audio vector, both the left and the right are evaluated regardless.` |
||||

`- priority of ',' and '=' was switched to fix the bug of using store "=" in functions with multiple arguments, which caused an error during execution.` |
||||

`- The number of inlet and outlets (MAX_VARS) is now set at 100` |
||||

```
``` |
||||

`#### New Additions in version 0.5` |
||||

```
``` |
||||

`- Expr, expr~, and fexpr~ are now built-in native objects.` |
||||

`- minor fixes/improvements.` |
||||

```
``` |
||||

`#### New Additions in version 0.4` |
||||

```
``` |
||||

`- Expr, expr~, and fexpr~ now support multiple expressions separated by semicolons which results in multiple outlets.` |
||||

`- Variables are supported now in the same way they are supported in C. - Variables have to be defined with the "value" object prior to execution.` |
||||

`- A new if function if (condition-expression, IfTrue-expression, IfFalse-expression) has been added.` |
||||

`- New math functions added.` |
||||

`- New shorthand notations for fexpr~ have been added.` |
||||

` - $x ->$x1[0] $x# -> $x#[0]` |
||||

` - $y = $y1[-1] and $y# = $y#[-1]` |
||||

`- New 'set' and 'clear' methods were added for fexpr~` |
||||

` - clear - clears all the past input and output buffers` |
||||

` - clear x# - clears all the past values of the #th input` |
||||

` - clear y# - clears all the past values of the #th output` |
||||

` - set x# val-1 val-2 ... - sets as many supplied value of the #th input; e.g., "set x2 3.4 0.4" - sets x2[-1]=3.4 and x2[-2]=0.4` |
||||

` - set y# val-1 val-2 ... - sets as many supplied values of the #th output; e.g, "set y3 1.1 3.3 4.5" - sets y3[-1]=1.1 y3[-2]=3.3 and y3[-3]=4.5;` |
||||

` - set val val ... - sets the first past values of each output; e.g., e.g., "set 0.1 2.2 0.4" - sets y1[-1]=0.1, y2[-1]=2.2, y3[-1]=0.4` |
||||

```
``` |
||||

`---------------` |

`@ -0,0 +1,73 @@` |
||||

`---` |
||||

`title: FUDI` |
||||

`---` |
||||

```
``` |
||||

`# FUDI` |
||||

```
``` |
||||

`FUDI (**F**ast **U**niversal **D**igital **I**nterface)is a networking ` |
||||

`protocol invented by Miller Puckette and used in Pure Data.` |
||||

`It is a string based protocol in which messages are separated by ` |
||||

`semicolons. Messages are made up of tokens separated by whitespaces, ` |
||||

`and numerical tokens are represented as strings.` |
||||

```
``` |
||||

`### Format` |
||||

```
``` |
||||

`**FUDI** is a packet oriented protocol.` |
||||

```
``` |
||||

`Each message consists of one or more **atom**s, separated by one or more ` |
||||

`**whitespace** characters, and it's terminated by a **semicolon** ` |
||||

`character.` |
||||

```
``` |
||||

`An **atom** is a sequence of one or more characters; whitespaces inside` |
||||

` atoms can be escaped by the **backslash** (ascii 92) character (see ` |
||||

`Examples below).` |
||||

```
``` |
||||

`A **whitespace** is either a space (ascii 32), a tab (ascii 9) or a newline` |
||||

`(ascii 10).` |
||||

```
``` |
||||

`A **semicolon** (ascii 59) is mandatory to terminate (and send) a ` |
||||

`message. **newline** is just treated as whitespace and not needed for ` |
||||

`message termination.` |
||||

```
``` |
||||

`### Implementations` |
||||

```
``` |
||||

`**pdsend / pdreceive**` |
||||

```
``` |
||||

`Those command-line tools are distributed with the software Pure Data. ` |
||||

`They are meant to be used with their counterparts, the classes [netsend] / [netreceive] of Pd.` |
||||

```
``` |
||||

`**[netsend] / [netreceive]**` |
||||

```
``` |
||||

`Those classes can be used to transport Pd-messages over a TCP or UDP ` |
||||

`socket. Both are part of Pd-vanilla.` |
||||

```
``` |
||||

`**[netserver] / [netclient]**` |
||||

```
``` |
||||

`Those are part of maxlib and allow bidirectional connections of multiple ` |
||||

`clients with one server.` |
||||

```
``` |
||||

`### Example messages` |
||||

````` |
||||

`test/blah 123.45314;` |
||||

````` |
||||

````` |
||||

`my-slider 12;` |
||||

````` |
||||

````` |
||||

`hello this is a message;` |
||||

````` |
||||

````` |
||||

`this message continues` |
||||

`in the following` |
||||

`line;` |
||||

````` |
||||

````` |
||||

`you; can; send; multiple messages; in a line;` |
||||

````` |
||||

````` |
||||

`this\ is\ one\ whole\ atom;` |
||||

````` |
||||

````` |
||||

`this_atom_contains_a\` |
||||

`newline_character_in_it;` |
||||

````` |

`@ -0,0 +1,198 @@` |
||||

`---` |
||||

`comment: This document is in 'markdown' format for use with pandoc` |
||||

`---` |
||||

```
``` |
||||

`### Nonlinear filtering using the slop~ slew-limiting low-pass filter` |
||||

```
``` |
||||

`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.` |
||||

```
``` |
||||

`The simplest type of digital low-pass filter can be understood as a moving` |
||||

`average:` |
||||

```
``` |
||||

`$$y[n] = y[n-1] + k \cdot (x[n] - y[n-1])$$` |
||||

```
``` |
||||

`where $0 \le k \le 1$ is an averaging factor, usually much closer to zero than` |
||||

`one. When the value of $k$ 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` |
||||

`[Theory and Techniques of Electronic Music](http://msp.ucsd.edu/techniques.htm), section 8.3, "designing filters").` |
||||

```
``` |
||||

`For our purposes we'll rewrite this equation as:` |
||||

```
``` |
||||

`$$y[n] - y[n-1] = f (x[n] - y[n-1])$$` |
||||

```
``` |
||||

`where the function $f$ is linear:` |
||||

```
``` |
||||

`$$f(x) = k \cdot x$$` |
||||

```
``` |
||||

`In words, this equation says, "increment your output by $k$ times the distance` |
||||

`you have to travel to reach the goal $x[n]$". (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 $(-n, p)$` |
||||

`containing zero, and two others joining this one at the input values $-n$ and` |
||||

`$p$. The three segments have slopes equal to $k_n$, $k$, and $k_p$ for the` |
||||

`negative, middle, and positive regions:` |
||||

```
``` |
||||

```
``` |
||||

`![response curve for slop~](x/slop-tilde/slop-tilde-1-curves.png)` |
||||

```
``` |
||||

```
``` |
||||

`_Rationale._ In general, $k$ could depend on both the previous output $y[n-1]$` |
||||

`and on the current input $x$. 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 $f$ 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.` |
||||

```
``` |
||||

`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 [this link](file:../5.reference/slop~-help.pd); alternatively` |
||||

`you can create a slop~ object in a patch and get help for it, or navigate to it` |
||||

`using Pd's help browser.)` |
||||

```
``` |
||||

`#### example: slew limiter` |
||||

```
``` |
||||

`The output signal $y[n]$ has a time-varying slope equal to $(y[n]-y[n-1])/\tau$,` |
||||

`where $\tau$ denotes the elapsed time between two samples, equal to one over the` |
||||

`sample rate $R$. The slope can be rewritten as $R \cdot (y[n]-y[n-1])$.` |
||||

`Suppose we wish to create an output signal whose slope is limited between two` |
||||

`values $-s_n$ and $s_p$ (so $s_n$ and $s_p$, both greater than zero, are the` |
||||

`maximum downward and upward slope). This implies that we should limit the` |
||||

`difference between successive outputs, $y[n] - y[n-1]$ to lie between $-s_n/R$` |
||||

`and $s_p/R$. We therefore increment the output by a quantity $x[n]-y[n-1]$ 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 $k=1$, $n =` |
||||

`s_n/R$, $p = s_p/R$, and $k_n = k_p = 0$. Since the three speed inputs to slop~` |
||||

`are in units of Hz, we can set $k=1$ 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.)` |
||||

```
``` |
||||

`A patch to do this is shown here:` |
||||

```
``` |
||||

`![slew-limiting patch from slop~ help file](x/slop-tilde/slop-slew-limiting-patch.png)` |
||||

```
``` |
||||

`A sample input and output are shown here:` |
||||

```
``` |
||||

`![slew-limiter input (at top) and its output (bottom)](x/slop-tilde/slop-slew-limit.png)` |
||||

```
``` |
||||

`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.` |
||||

```
``` |
||||

`_Note_: 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.` |
||||

```
``` |
||||

`#### example: peak meter` |
||||

```
``` |
||||

`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:` |
||||

```
``` |
||||

`![instant-rise, controlled-decay envelope follower](x/slop-tilde/slop-peak-meter-patch.png)` |
||||

```
``` |
||||

`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:` |
||||

```
``` |
||||

`![response to a square pulse](x/slop-tilde/slop-peak-meter-graph.png)` |
||||

```
``` |
||||

`(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.` |
||||

```
``` |
||||

`#### using slop~ in a compander-limiter` |
||||

```
``` |
||||

`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.` |
||||

```
``` |
||||

`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.` |
||||

```
``` |
||||

`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:` |
||||

```
``` |
||||

`![compander using instant-rise envelope follower](x/slop-tilde/slop-compander-patch.png)` |
||||

```
``` |
||||

`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).` |
||||

```
``` |
||||

`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.)` |
||||

```
``` |
||||

`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.` |
||||

```
``` |
||||

`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.` |
||||

```
``` |
||||

```
``` |
||||

`#### using slop~ to remove signal jitter` |
||||

```
``` |
||||

`By setting the linear cutoff frequency to zero and the linear region to an` |
||||

`interval of length $a$ (either by setting $n=0, p=1$ or $n=p=a/2$), and then` |
||||

`setting $k_n = k_p = \inf$, we get a filter that allows its input to jitter over a` |
||||

`range of $a$ 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.` |
||||

```
``` |
||||

```
``` |
||||

`{{< has-math >}}` |

`@ -0,0 +1,22 @@` |
||||

`<script>` |
||||

` MathJax = {` |
||||

` tex: {` |
||||

` inlineMath: [['$', '$'], ['\\(', '\\)']],` |
||||

` displayMath: [['$$','$$'], ['\\[', '\\]']],` |
||||

` processEscapes: true,` |
||||

` processEnvironments: true` |
||||

` },` |
||||

` options: {` |
||||

` skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre']` |
||||

` }` |
||||

` };` |
||||

```
``` |
||||

` window.addEventListener('load', (event) => {` |
||||

` document.querySelectorAll("mjx-container").forEach(function(x){` |
||||

` x.parentElement.classList += 'has-jax'})` |
||||

` });` |
||||

```
``` |
||||

`</script>` |
||||

`<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>` |
||||

`<script type="text/javascript" id="MathJax-script" async` |
||||

` src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>` |

After Width: | Height: | Size: 18 KiB |

After Width: | Height: | Size: 5.3 KiB |

After Width: | Height: | Size: 5.7 KiB |

After Width: | Height: | Size: 8.7 KiB |

After Width: | Height: | Size: 7.8 KiB |

After Width: | Height: | Size: 6.8 KiB |

Loading…

Reference in new issue