Using Python for Control

Python offers many useful tools for preparing data and controlling synthesis processes. Although it can also be used for actual digital signal processing, its versatility makes it a great tool for auxuliary tasks. Most notably, it can be used for flexible processing and routing of OSC messages, especially in the field of data sonification.


Python & OSC

A large variety of Python packages offers the possibility of using OSC. They can be installed using pip:

$ pip install python-osc
$ pip install pythonosc

An example project for controlling a Faust-built synthesizer with Python is featured in this software repository: https://github.com/anwaldt/py2faust_synth


Python & JACK

The JACK Audio Connection Kit Client for Python by Matthias Geier connects Python processes to the JACK server. This integration of Python in a JACK ecosystem can be helpful not only for audio processing, but also for synchronization of processes. Since the Python package also implements the JACK transport functions, it can be used to couple Python threads to the timeline of audio projects.

Using Buses in SuperCollider

Control Rate vs Audio Rate

SC works with two internal signal types or rates. When something is used with the extension .ar, this refers to audio signals (audio rate), whereas .kr uses the control rate. For both rates, buses can be created.


Creating Buses

An audio bus with a single channel is created on the default server s with the following command:

~aBus = Bus.audio(s,1);

A control bus with a single channel is created on the default server s with the following command:

~cBus = Bus.control(s,1);

Bus Indices

The variable ~aBus is the client-side representation of the Bus. The server only knows it by its bus index. Bus indices are counted upwards and can be queried with the following command:

~aBus.index
~cBus.index

The indices of user-defined audio buses start counting after all output an input buses. The number of input and output buses can be defined before booting a server. The default setting uses 2 input and 2 output buses.

Audio buses

Indices

Audio Buses

0...1

Outputs

2...3

Inputs

4

First user-defined bus

The number of input and output buses can be queried after boot:

s.options.numOutputBusChannels;
s.options.numInputBusChannels;

Audio Input

The SoundIn UGen makes it convenient to access the audio input buses without keeping track of the outputs. This node simply passes the first input to the firs output:

{ Out.ar(0,SoundIn.ar(0))}.play

Note that this is equivalent to using the proper offset with a regular audio input:

{ Out.ar(0,In.ar(s.options.numOutputBusChannels))}.play

Monitoring Buses

Any bus can be monitored with the builtin scope with the following command. The first argument defines the number of buses to be shown, the second the index of the first buses:

s.scope(1,~aBus.index,rate:'audio')

There is a short version, which has limitations and does not specify the bus type:

~aBus.scope()

Frequency Scope

Any bus can also be monitored with a frequency scope. The first arguments define the size. The third argument defines the bus to analyze, in this case the first output bus:

FreqScope.new(400, 200, 0, server: s);

Control Buses

This simple sawtooth node will be used for showing how to use control buses. It has one argument freq, which affects the fundamental frequency and uses the first hardware output:

~osc  = {arg freq=100; Out.ar(0,Saw.ar(freq))}.play;

Mapping a Control Bus

The map() function of a node can connect a control bus, identified by its index, with a node parameter:

~osc.map(\freq,~cBus.index);

Setting a Control Bus

After mapping the bus, the synth stops its sound., since the control bus is still set to the default value 0. This can be visualized with the scope command. A simple and quick way for changing the control bus to a different value is the set() function of a node. It can be used for all arguments of the node which are internally used for control rates:

~cBus.set(50);

Multichannel Buses

Both control and audio rate buses can be created as multi channel buses. A scope will automatically show all channels. Individual channels can be mapped with an offset in relation to the index of the first channel. The setAt() function can be used for changing individual channel values:

~mBus = Bus.control(s,8);

~mBus.scope;

~osc.map(\freq,~mBus.index+3);

~mBus.setAt(3,150);

Faust: Conditional Logic

The select2() directive can be used as a switch condition with two cases, as shown in switch_example.dsp

// switch_example.dsp
//
//
// Henrik von Coler
// 2020-05-28

import("all.lib");

// outputs 0 if x is greater 1
// and 1 if x is below 0
// 'l' is used as an implicit argument
sel(l,x) = select2((x>=0), 0, 1);

process = -0.1 : sel(2);

Filter Characteristics and Parameters

Filters have many applications in sound synthesis and signal processing. Their basic job is to shape the spectrum of a signal by emphasizing or supressing frequencies. They are the essential component in subtractive synthesis and their individual qualities are responsible for an instrument's distincive sound. Famous filter designs, like the Moog Ladder Filter, are thus standards in the design of analog and digital musical instruments.

Filter Characteristics

Regardless of the implementation details, both analog and digital filters can be categorized by their filter characteristics. These describe, which frequency components of the signal are passed through and which frequencies are rejected. This section describes the three most frequently used filer types.

The central parameter for most filter types is the cutoff frequency $f_c$. Depending on the characteristic, the cutoff frequency is that frequency which separates passed from rejected frequencies.

Lowpass

The lowpass filter (LP) is the most frequently used characteristic in sound synthesis. It is used for the typical bass sounds known from analog and digital subtractive synthesis. With the right envelope settings, it creates the plucked sounds. An LP filter lets all frequencies below the cutoff frequency pass. $f_c$ is defined as that frequency where the gain of the filter is $-3\ \mathrm{dB}$, which is equivalent to $50\ \%$. The following plot shows the frequency-dependent gain of a lowpass with a cutoff at $100\ \mathrm{Hz}$.

Highpass Filter

The highpass (HP) filter is the opposite of the lowpass filter. It rejects low frequencies and lets high frequencies pass. The following plot shows the frequency-dependent gain of a highpass with a cutoff at $100\ \mathrm{Hz}$.

Bandpass Filter

The bandbass (BP) filter is a combination of lowpass and highpass. It lets frequencies between a lower cutoff frequency $f_{low}$ and an upper cutoff frequency $f_{up}$ pass. The BP filter can thus also be defined by its center frequency

$f_{cent} = \frac{f_{up}+f_{low}}{2}$

and the bandwith of the so called passband

$b = f_{up}-f_{low}$.

The following plot shows a bandpass with a center frequency of $f_{cent} = 100\ \mathrm{Hz}$ and a bandwidht of $50\ \mathrm{Hz}$.

38

Fourier Series: Triangular

Formula

The triangular wave is a symmetric waveform with a stronger decrease towards higher partials than square wave or sawtooth. Its Fourier series has the following characteristics:

  • only odd harmonics

  • altering sign

  • (squared)

\begin{equation*} \displaystyle X(t) = \frac{8}{\pi^2} \sum\limits_{i=0}^{N} (-1)^{(i)} \frac{\sin(2 \pi (2i +1) f\ t)}{(2i +1)^2} \end{equation*}

Interactive Example

Pitch (Hz):

Number of Harmonics:

Output Gain:

Time Domain:

Frequency Domain:

Sampling & Aliasing: Sine Example

In the following example, a sine wave's frequency can be changed with an upper limit of $10\ \mathrm{kHz}$. Depending on the sample frequency of the system running the browser, this will lead to aliasing, once the frequency passes the Nyquist frequency:

Pitch (Hz):

Output Gain:

Time Domain:

Frequency Domain:

Sampling & Aliasing: Theory and Math