Bilinear Transform: RC Lowpass

The bilinear transform is a method for designing digital filters from analog models. More precisely, it brings filters from the Laplace domain to the z-domain. The desired outcome is a difference function, which can be used for the actual filtering process in the discrete time domain.

Passive RC Lowpass

A simple example for the bilinear transform is the RC lowpass:

Transfer Function

The resulting filter has a low pass characteristic with the following frequency response:

In the Laplace domain its transfer function is defined as:

$$ H(s) = \frac{\omega_c}{\omega_c + s} = \frac{1}{1 + \frac{s}{\omega_c}} $$

$s$ represents the Laplace operator

$$ s = \sigma + j \omega $$

and the cutoff frequency of the passive filter is:

$$ \omega_c = \frac{1}{RC} $$

Substitution

For a transform to the z-domain, the Laplace operator $s$ is subsstituted by the $z$ operator:

$$ s = \frac{2}{T}\frac{1-z^{-1}}{1+z^{-1}} $$$$ z = e^{sT} $$

The following steps rearrange the resulting transfer function to be comprised of summands with the factor $z^{-n}$:

$$ \begin{align} % % H(z) = & \frac{\omega_c}{\omega_c + \frac{2}{T} \frac{1-z^{-1}}{1+z^{-1}}} \\ % = & \frac{\frac{T}{2} \omega_c}{ \frac{T}{2} \omega_c + \frac{1-z^{-1}}{1+z^{-1}}} \\ % = & \frac{\frac{T}{2} \omega_c (1+ z^{-1})}{ \frac{T}{2} \omega_c (1+ z^{-1}) + 1-z^{-1}} \\ % = & \frac{\frac{T}{2} \omega_c + \frac{T}{2} \omega_c z^{-1}}{ \frac{T}{2} \omega_c (1+ z^{-1}) + 1-z^{-1}} \\ % = & \frac{\frac{T}{2} \omega_c + \frac{T}{2} \omega_c z^{-1}}{ \frac{T}{2} \omega_c + \frac{T}{2} \omega_c z^{-1} + 1-z^{-1}} \\ % = & \frac{\frac{T}{2} \omega_c + \frac{T}{2} \omega_c z^{-1}}{1 + \frac{T}{2} \omega_c + (\frac{T}{2} \omega_c -1 )z^{-1}} \\ % \end{align} $$

For getting the coefficients of our digital filter, the transfer function needs to meet the following strucutre where $a_0=1$:

$$ H(z) = \frac{b_0 + b_1 z^{-1}}{1+ a_1 z^{-1}} $$

This is achieved by expanding and simplifying:

$$ \begin{align} % % H(z) = & \frac{\frac{T}{2} \omega_c + \frac{T}{2} \omega_c z^{-1}}{ \underbrace{ \mathbf{\color{gray} 1 + \frac{T}{2} \omega_c }}_{\text{must be 1}} + \left(\frac{T}{2} \omega_c -1 \right)z^{-1}} \\ % = & \frac{\frac{\frac{T}{2} \omega_c}{ 1 + \frac{T}{2} \omega_c} + \frac{ \frac{T}{2} \omega_c}{ 1 + \frac{T}{2} \omega_c} z^{-1}}{1+ \left(\frac{\frac{T}{2} \omega_c}{1 + \frac{T}{2} \omega_c} - \frac{1}{1 + \frac{T}{2} \omega_c} \right)z^{-1}} \\ % = & \frac{\frac{\frac{T}{2} \omega_c}{ 1 + \frac{T}{2} \omega_c} + \frac{ \frac{T}{2} \omega_c}{ 1 + \frac{T}{2} \omega_c} z^{-1}}{1+ \left(\frac{\frac{T}{2} \omega_c -1}{ \frac{T}{2} \omega_c + 1} \right)z^{-1}} \\ \end{align} $$

Coefficients

Following the above rearrangements, the coefficients can be directly extracted from the transfer funcion:

$$ H(z) = \frac{\frac{\frac{T}{2} \omega_c}{ 1 + \frac{T}{2} \omega_c} + \frac{ \frac{T}{2} \omega_c}{ 1 + \frac{T}{2} \omega_c} z^{-1}}{1+ \left(\frac{\frac{T}{2} \omega_c -1}{ \frac{T}{2} \omega_c + 1} \right)z^{-1}} = \frac{b_0 + b_1 z^{-1}}{1+ a_1 z^{-1}} $$$$ \begin{align} \mathbf b_0 = & \frac{\frac{T}{2} \omega_c}{ 1 + \frac{T}{2} \omega_c} \\ \mathbf b_1 = & \frac{\frac{T}{2} \omega_c}{ 1 + \frac{T}{2} \omega_c} \\ \mathbf a_1 = & \frac{\frac{T}{2} \omega_c -1}{ \frac{T}{2} \omega_c + 1} \\ \end{align} $$

Filter Topology

The original dependence of the electrical components R and C is not important at this point. However, with $\omega_c$ = $2 \pi f_0$ we can get the exact coefficients for any cutoff frequency which can be used for the difference equation or applied in the following topology:

Plot Poles and Zeros in Z-Plane

Making a Kick in SuperCollider

The body of a basic electronic kick drum is a sine wave with an exponential decrease in frequency over time. Depending on the taste, this drop happens from about 200-300 Hz to 30-60 Hz. This can be achieves with temporal envelopes-

Define Envelopes

Before using an envelope, it needs to be defined, using the Env class. It is feature rich and well documented insice the SC help files. The following line creates an exponential envelope with a single segment, defining an exponential drop. It can be plotted using the envelope's plot method:

~env = Env([1, 0.0001], [0.15],\exp);
~env.plot;

/images/basics/sc-envelope.png

---

Using an Envelope Generator

Envelopes can be passed to envelope generators. The following example generates a control rate signal with the exponential characteristics. It will be sent to the control bus with the index 0 (Out.kr(0,)) and the created node will be freed once the envelope is done, defined by the done action. The bus can be monitored to see the result:

s.scope(1,0,rate:'control')

{Out.kr(0,EnvGen.kr(~env, doneAction: Done.freeSelf))}.play

A Sine Wave Node

The following node will be used for generating the kick itself. It has two arguments - the gain and the pitch:

(
~synth = {
    |gain=0,pitch=100|

    // send the signal to the output bus '0'
    Out.ar(0, gain*SinOsc.ar(pitch));

}.play;
)

The pitch argument needs to be mapped to the first control bus (0):

~synth.map(\pitch,0);

Triggering it

The envelope generator line can now be evaluated to retrigger the kick. It will keep on droning, but already has the characteristic punch:

{Out.kr(0,EnvGen.kr(~env, doneAction: Done.freeSelf))}.play

Exercise

Exercise

Add a second envelope for the gain to stop the kick from droning.

IIR Filters

Infinite Impulse Response (IIR) filters are such filters which include recursion, respectively feedback, in their implementation. Their impulse response does thus not drop to zero after a given time. They can become instable due to the feedback. However, IIR filters require less coefficients and thus less operations to get a filter with the desired effect than FIR filters. As FIR filters, IIR filters create an output sequence $y[n]$ from an input sequence $x[n]$:

The Difference Equation

For the difference equation the recursion is included with additional coefficients $a_n$, feeding back the output signal $y[n]$ with different delays. The following difference function represents a second order IIR filter. Also referred to as biquad filter, this is a basic component for many digital filter implementations:

$$ y[n] = b_0 x[n] + b_1 x[n-1]+ b_2 x[n-2] - a_1 y[n-1] - a_2 y[n-2] $$

Or short:

$$ y[n] = \sum\limits_{i=0}^{i=N} b_i x[n-i] - \sum\limits_{i=1}^{i=N} a_i y[n-i] $$

Implementation Structure

The above difference equation can be transferred into different implementation structures. These structures can be directly implemented in actual code. The following one is the direct form 1:

Polynominals

A filter of the order $N$ can be expressed as a polynominal, using the Z-operator $z = e^{sT}$:

$$ h[n] = \frac{\sum\limits_{n=0}^{n=N} b_n z^{-n}}{\sum\limits_{n=1}^{n=N} a_n z^{-n}} $$

Pole-Zero Plot

IIR filters have roots in the demoninator, called zeros and in the nominator, called poles:

Additive Synhesis in SuperCollider

Multichannel Expansion

The following example implements simple additive synthesis in SuperCollider, suited as a starting point for more elaborate designs. Arrays can be helpful for using single UGens multiple times, which is the very idea of additive synthesis. The multichannel expansion allows the use of arrays as arguments for UGens, resulting in an array of objects with the initialization parameters given in the arrays. The following example will create five oscillators, sending their outputs to the buses 0...4:

(
{
        |pitch=100|
        SinOsc.ar(freq:pitch*[1,2,3,4,5],mul:1/[1,2,3,4,5]);
}.play;
)

s.scope(6,0);

Mixing

The single partials can be easily summed by wrapping them inside a Mix.ar() UGen. They are now all sent to the first output bus:

(
{
    |pitch=100|
    Mix.ar(SinOsc.ar(freq:pitch*[1,2,3,4,5],mul:1/[1,2,3,4,5]));
}.play;
)

s.scope(6,0);

Dynamic Allocation

For more partials and dynamic sizes, it makes sense to use the fill() method of the array class:

(

~n_part    = 30;
~add_synth = {

    |pitch=100|

    Mix.ar(
            SinOsc.ar(
                    freq:pitch*Array.fill(~n_part,{arg i; (i+1)}),
                    mul: Array.fill(~n_part,{arg i; 1/(((i)+1))})
            )
    );

}.play;
)

Exercises

Exercise

Combine the example with a mouse control, allowing to change the timbre and the pitch.

Exercise

Adjust the partial weight formula to generate basic waveforms (square, sawtooth, triangle).

FIR Filters

Finite Impulse Response (FIR) filters are such digital filters which have - as the name suggests - a finite impulse response. These filters do not have an internal feedback and can be applied simply by convolving an input sequence $x[n]$ with the filter's impulse response $h[n]$ to get the output sequence $y[n]$:

For FIR filters, the impulse response $h[n]$ is a one-dimensional array containing the $N$ filter coefficients. The following example shows the coefficients, respectively the impulse response for a filter of the order $N=5$:


The Difference Equation

From the impulse response we can get the coefficients $b_n$ to write the difference equation, which is the basis for applying a filter:

$$ y[n] = b_0 x[n] + b_1 x[n-1]+ b_2 x[n-2]+ b_3 x[n-3]+ b_4 x[n-4] + b_5 x[n-5] $$

Implementation Structure

Another important representation of a filter is the implementation structure. It can be directly derived from the difference equation. Each $z^{-1}$ block represents a delay by one sample. This case shows the direct form:


Applying a Filter by Convolution

Applying the filter with the given impulse response is done by simple convolution, which is the same as passing samples from the input of the implemtation structure above to its output. The above equation is then written as follows:

$$ \begin{eqnarray} y[m] &=& x[n] * h[m]\\ &=& \sum\limits_{m=0}^{M} h[m] x(n-m)\\ &=& \sum\limits_{m=0}^{M} b_m x(n-m)\\ \end{eqnarray} $$

The Transfer Function

The difference equation can also be expressed as a sum:

$$ h[n] = \sum\limits_{n=0}^{N} b_n x[n-1] $$

The transfer function of the filter can be obtained via the z-transform:

$$ H[z] = \sum\limits_{n=0}^{N} h[n] z^{-n} $$$$ H(z) = 0.5 z^{-2} + z^{-3} + 0.5 z^{-4} $$

In the next step, all exponents are shifted by expanding with $z^{4}$:

$$ H(z) = \frac{z^2 + 2z + 1}{z^4} $$

Factor the denominator to find its roots:

$$ H(z) = \frac{(z+1)(z+1)}{z^4} $$

There is a double zero at $\mathbf{z=-1}$. As all causal FIR filters it has $N=4$ poles at the origin $z=0$.

With $z=e^{j\omega}$ the complex roots can be obtained:

$$ Z = (e^{-i\omega} +1)(e^{-i\omega} +1) $$

In the Unit Cirlce

An LTI system can be visualized in the z-plane by plotting the zeros, respectively the roots of its impulse response.

Frequency Response

The frequency response of the filter

SuperCollider: Synchronous vs Asynchronous

The Problem

Most examples in this class can not be run as a complete script. They need to be evaluated line by line or block wise. One reason for this is the difference between synchronous and asynchronous execution. This problem is in detail explained in the SC guides: https://doc.sccode.org/Guides/Sync-Async.html This site just gives a shorter answer.

Issues usually arise, when a command has been sent to the server and following command depends on the completion of that action. Examples can be the creation of nodes or the loading and filling of buffers. Running this simple block at once will result in an error. It creates a node and does not wait for completion before it uses the .set() method. The result is a FAILURE IN SERVER /n_set Node 1000 not found:

(

// create white noise node with gain control
~test = {arg gain=1; WhiteNoise.ar(gain)}.play;

// try to set the gain
~test.set(\gain, 0.1);

)

A Solution

There are several ways of dealing with the above introduced problem. One solution is to wrap the code block in a Routine, which allows to control the order of execution. In the case of asynchronous tasks, the command s.sync can be used inside a routine. It waits for the sever to finish all asynchronous tasks and the below example works without errors:

(
Routine({

    // create white noise node with gain control
    ~test = {arg gain=1; WhiteNoise.ar(gain)}.play;

    // wait for the server to finish all asynchronous tasks
    s.sync;

    // try to set the gain
    ~test.set(\gain, 0.1);

}).play
)

Plans and Directions

The original SPRAWL System was designed as a means for enhanced interaction in Local Area Networks (LAN) (read more). Due to the needs for online teaching and musical performance in 2020, the concept was extended to a Wide Area Network (WAN) solution. In the coming months, the project with be extended in a cooperation with LTU, Sweden and Ritmo, Oslo. The following areas can be tackled in this period.


Multichannel Clients

In this semester we will extend the system to incorporate clients and endpoints with difeerent configurations, including larger loudspeaker setups:

/images/nsmi/sprawl_flow.png

Extended SPRAWL System, allowing Access Points with different configurations.


Gestural Control

All three sites in the project will be equipped with a motion tracking system and multichannel loudspeaker setups, using different configurations. Experiments will aim at sharing spatial sound between the sites and manipulating them with gestural input.

/images/nsmi/bol_setup_1.jpg

21 channel Ambisonics setup with motion capturing system at BOL.


Clocking and Syncing

Another focus will be experiments with shared clocks and sequences, in cooperation with the RITMO group in Oslo: https://www.uio.no/ritmo/english/projects/dr-squiggles/index.html

We have the possibility to join the Rhythm Production and Perception Workshop for our closing concert on June 22-25.


Back to NSMI Contents

Digital Filters

Digital filters are delay-based processing units. In short: they affect a signal by overlapping it with delayed versions of the same signal. There are two basic categories of digital filters:

FIR filters

Finite Impulse Response (FIR) filters can be considered simple convolution processors. They are implemented without recursion, respectively feedback. IIR filters are robust and easy to design, yet they are more CPU expensive.

IIR Filters

Infinite Impulse Response (IIR) filters are recursive computational structures. They are used for many time-critical operations, since they are less CPU hungry. In contrast to FIR filters, they can become unstable and may affect the signal in unwanted ways.


Both categories will be introduced in the following sections. In a detailed comparison, they show a couple of differences, both having advantages and disadvantages.



Contents © Henrik von Coler 2021 - Contact