about

supercollider

visual

This page documents my use of the environment and programming language SuperCollider. It will focus on practical examples that can help you integrate SuperCollider in your setup as well as reflections on sound synthesis and the basics of programming. Most of this will be written for MacOS which I currently use. If you want to get started with SuperCollider I recommend that you watch the videos of Eli Fieldsteel or have a look at the book A gentle introduction to SuperCollider by Bruno Ruviaro.



Posts:

14-12-2024 | Control Structures
14-12-2024 | SynthDef Basics
13-03-2024 | functions in SC
04-12-2023 | setting up SuperCollider (2)
05-10-2023 | Setting up SuperCollider (1)

24-07-2024


Control Structures
---------------------------

Control structures are the building blocks of programming languages and allow us to "control" when our code is exectuded based on certain conditions. In SuperCollider a number of these statements are available. In this post four basic control structures will be discussed. Lastly I will demonstrate how you can use them to control a SynthDef that we created in the previous post.

  1. IF

    The if-statement allows us to take an action based on a condition being either true or false (also known as a boolean). For example if a value is greater than 5 print “it’s greater than 5!” and if it is less than 5 print “it’s less than 5”.

    In the SuperCollider we can write this as follows:

    if (condition) {True} {False}

    For Example

    if(x > 5) {“it’s greater than 5”.postln} {“it’s less than 5”.postln}

  2. WHILE

    The while-statement allows us to take a certain action while a condition is True. For instance while a value is less than 5: add 1 to that value.

    In the SuperCollider we can write this as follows:

    while {condition} {do this}

    for example:

    i=0 while {I < 5} { I=I + 1};

  3. FOR

    another way we can control the execution of our program in SuperCollider is a forBy loop. This statement allows us to iterate over an integer series (for) with a variable step size (by). Each iteration evaluation the numeric value is passed to a function as an argument

    WE use the following syntax:

    forBy {startValue, endValue, stepSize } { do something Value }

    For example:

    forBy {0, 10, 2} { arg I; I.postln }

    (will print values 0,2,4,6,8,10)

  4. DO

    We can use do to iterate over a collection. A collection can also be an integer, calling the function on each iteration with the numeric value as argument.

    The syntax is as follows:

    collection.do { do this }

    For Example

    5.do {arg I; I.postln);

    If we want to execute a function indefinetely we can do this as follows

    inf.do { I.postln)

Now lets try to use these control structures to make sound. Lets first create our synthesizer using synthDef

( SynthDef(\mySynth, { arg freq; var amp = 0.5; // set the amplitude var asdr = EnvGen.kr(Env.adsr(0.5, 0, 0, 0.5, 1), doneAction: 2); // var osc = SinOsc.ar(freq, 0, amp * asdr); //apply asdr by multiplying it with the amplitude Out.ar([0,1], osc); }).add; )

I’ve added a simple envelope so that we can clear when the sound start and ends. Now we can use the do statement to trigger our Synth 5 times...but there is a problem, if we just write the following statement our loop is executed in such rapid succesion that we are unable to distinguish the different iterations.

5.do { Synth(\mySynth, [\freq, 440]); }

We can use the wait method to wait 1 second between iterations. To do this we have to embed our do statement inside a routine as follows:

Routine({ 5.do { Synth(\mySynth, [\freq, 440]); 1.wait; } }).play;

24-07-2024


SynthDef: Basics
---------------------------

In the previous post we created a function that we stored in a variable b.

( b = {arg freq;
var control;
var osc;
control = SinOsc.kr(2);
osc = SinOsc.ar(freq, 0, control);
Out.ar([0,1],osc)}
)

The function had one argument freq that allowed us to set the frequency of the oscillator. We also declared two variables : control, a control-rate (kr) oscillator to modulate the amplitude of our signal, and osc, an audio oscillator that generates a using SinOsc operating at audioratio.

We can now rewrite this function as a synth definition or SynthDef. As the name suggest this is a reusable client side representation of a synthesizer that can be created and controlled in a more efficient way.

( SynthDef(\mySynth, { //name our Synth so we can call it later
arg freq;
var control, osc;
control = SinOsc.kr(2);
osc = SinOsc.ar(freq, 0, control);
Out.ar([0,1], osc);
}).add; // we “add” the synth to memory
)

We can now store the synthesizer as a synth def with the name mySynth so that it can be instatiated. We create an instance of our synthesizer as follows:

Synth(\mySynth, [\freq, 440]);

We can make multiple instances of the synth with different parameteres.

Synth(\mySynth, [\freq, 440]);
Synth(\mySynth, [\freq, 220]);

13-03-2024


Functions in SuperCollider
---------------------------

In this post I want to have a brief look at functions and how you can use them in SC. Functions are a central concept in computer programming. A function can be defined as a callable unit that has a well-defined behavior and can be invoked by other software units to exhibit that behavior. Usually this means that a value goes into the function, a behavior or series of behaviors is executed inside the function and a new value is returned. For instance if we want to write a function that subtracts half of a given number. We would have to make the following calculation:

y = x - x/2

In SuperCollider we can write this as a reusable function as follows:

a = {arg x; x = x - (x/2)}

We now have a function stored in a variable (a) that takes as an argument x and performs the necessary calculation. This means that we can easily perform our calculation for different numbers. We can use the .value method to evaluate our function, this method can also be written as value(a,4). For example:

a.value(4) a.value(8)


Some things to note:

Now how can we use this to make sounds? In the previous post we wrote the following code to send our SinOsc to bus 7.

( b = {var osc; osc = SinOsc.ar(220); Out.ar(7,osc)})

Now we can see that it is a function. Similarly to our previous example lets add a variable that we can use to modify our sound and send it to bus 0 an 1.

(b = {arg freq;
var osc;
osc = SinOsc.ar(freq);
Out.ar([0,1],osc)})

Now we can use our function to play this sine wave at different frequencies:

b.play(args: ["freq", 110])
b.play(args: ["freq", 220])
b.play(args: ["freq", 240.43])

To make it a bit more interesting lets add another variable called control which outputs a control rate signal. This signal can then be used to modify the amplitude of the signal.

( b = {arg freq;
var control;
var osc;
control = SinOsc.kr(4);
osc = SinOsc.ar(freq, 0, control);
Out.ar([0,1],osc)}
)

And run it with a few different frequencies:

b.play(args: ["freq", 140.23])
b.play(args: ["freq", 110])
b.play(args: ["freq", 220])
b.play(args: ["freq", 240.43])

04-12-2023


Setting up SuperCollider (2)
---------------------------

If you want to use SuperCollider it might be interesting to incorporate external instruments or use send/return effects. To do this you will need an external audio interface. I’m using the Native Instruments Komplete Audio 6 MK2. To see which output devices are available for SuperCollider run the following code:

ServerOptions.outDevices; // output devices

Based on this information you can set one of these as the default device. The previous post discussed routing your audio internally into your DAW using software such as Soundflower. Now we are going to combine this with our external audio interface to make an make aggregate device. For MacOs users this is pretty straightforward (Windows user can try using ASIO4ALL)

Once you’ve set up the aggregate device you want to run the above command again, if all is well it should show up in the list of possible output devices. Next use the following code to set it as a the default device.

Server.default.options.outDevice_("[insert the name of your aggregate device here]");

My aggregate device now consists of 6 channels on my external audio interface and 2 additional channels, these are the channels that will contain the SuperCollider audio. However, we do have to make sure that the audio from SuperCollider is being sent to channel 7/8. In order to do this we first need to expand our number of output channels to 8.

s.options.numOutputBusChannels(8);

You can check the levels of the different busses by running:

s.meter

Let’s run the simple sine oscillator with two busses that we made in the previous post. Notice that the sound will not be sent through to your DAW as it is sent to busses 1 & 2. in order to write a signal to another bus we have to use the Out() class which has the following methods:

Out.ar(bus, channelsArray)
Out.kr(bus, channelsArray)

You can see that you can either send audio rate (ar) or control rate (kr). In this case we want to send out our sine wave audio rate. We can do this by creating a function (more on functions later) for our oscillator and setting our bus to 6 and 7 with an array.

(
( b = {var osc;
osc = SinOsc.ar(220);
Out.ar([6,7],osc)})

Notice that we are setting it to 6 and 7 but it enters your DAW in input 7/8 because in SC we start counting from 0 instead of 1.

05-10-2023


Setting up SuperCollider (1)
---------------------------

SuperCollider is based on a client/server architecture. In short we have an audio server (which makes the sound) that we are able to communicate with using a client. The server is also called scsynth and the client as sclang, which functions as both the client and the interpreter. To execute commands we type our code in the high-level SuperColliding language which is then translated and sent to the server. That’s why, when you start the SuperCollider environment you will see this at the bottom:

Meaning that your Interpreter (or client) is Active but you still need to start (or boot) the server. You can do this using the following command.

s.boot

To run the code: put your text cursor at the end of the line of code you want to run and pres shift + enter. There is one default server stored in the interpreter variable s. The server object has a number of methods which can be accessed by writing a period (.) followed by the name of the method, e.g. server.boot. For more information about the server class have a look at the documentation. If you want to stop the server you can use the quit method by writing s.quit. Once you’ve booted the server, the next thing you want to do is set a hard limit for the output. This is not built into SuperCollider but can be installed as an additional package or a Quark. You can find more information about how to use Quarks over here: Using Quarks. The Quark you need is called Safety. I usually set the limiter at 0.5 but you can find your own preference.

Safety.setLimit(0.5)

Instead of just running SuperCollider on its own it can be interesting to run it into your DAW of choice. You can route your audio into another device internally using open source software such as Soundflower for Mac OS. You can check if it is working by running a simple sine oscillator at 220 hertz as follows: (before playing the sound turn your volume way down and then gradually turn it back up)

{SinOsc.ar(220)}.play

To stop the sound press shift +command + . (period). You've probably noticed that the sound is just coming from one speaker. Let’s make it a bit more interesting by expanding it to two channels by adding an array:

{SinOsc.ar([220, 220]}.play

So what we've got so far is

s.boot //booting the server
Safety.setLimit(0.5) //set a limit of the output
{SinOsc.ar([220, 220]}.play //play a sine oscillator 220 Hz on channel 0 and 1