-
Notifications
You must be signed in to change notification settings - Fork 2
Basic Concepts
Supercollider can refer to three distinct things, often used interchangeably or without regard to the specific use case:
- the Supercollider Language
- the Supercollider IDE
- the Supercollider Server
The Supercollider Server is a process that runs locally and is accessible as an object through the global variable s
. Note that
the Supercollider Server is not quite the same as the 'Servers' referred to colloquially in common JackTrip usage. When a JackTrip user
spins up an audio server, that audio server generates a process internal to that server which can be referred to as the 'Supercollider Server'.
Any time Supercollider code is executed, it is not executed directly on the Supercollider server. Rather, the executation happens in a separate
client process that communicates with the Supercollider Server. Both the client process and the Supercollider run on the audio servers.
Suggested Readings:
Getting Started with SC - First Steps
A Unit Generator ("UGen") represent the basic building blocks from which more complex audio processing pipelines can be built. A UGen can calculate at the audio rate (44,100 samples per second), the control rate (44,100 / 64 = 689 samples per second), or at initialization only. A UGen should output at the audio rate if the output is expected to directly go to the user's headset. A UGen should output at the control rate if the UGen is configured to output a signal used to control other UGens.
For example, you can use a UGen such as a sine oscillation outputting at control rate to multiply the signal from a UGen outputting at audio rate. In this example, each output from the sine oscillation affects 64 samples from the main audio signal. In this way, the sine oscillation is used to 'control' the amplitude of the main audio signal. Note that the sine oscillation doesn't need to output at audio rate, because that would require it to recompute its output 44,100 times per second as well (which probably won't make a noticeable difference and is also computationally wasteful).
Some useful UGens include the In
and Out
Ugens, which respectively read from and write to audio buses. These UGens are used throughout the jacktrip-sc
repository to read the audio inputs from all of the JackTrip users and to send the processed results back. They can also be used for internal processing
of audio if intermediate steps are required. The In
and Out
UGens are documented here and
here
Suggested Readings:
Getting Started with SC - A Tour of UGens
A Synthdef is used to define a template for how UGens are connected. Internally, a Snythdef contains a UGen graph function, which defines the relationship between UGens. When a Synthdef is created, it is converted into byte code, which the Supercollider server can understand.
While a Synthdef represents the definition/template for execution, a Synth is the representation of the actual execution of the Synthdef.
var func, synth;
func = { SinOsc.ar }; // func is of type Function
synth = func.play; // Function.play returns a Synth and starts the audio output
A simple SynthDef equivalent to the preceding example.
SynthDef("SYNTHDEFNAME", {
var signal;
signal = SinOsc.ar;
Out.ar(0, signal); // Synthdefs require an output UGen such as Out
}).send(s); // When .send is called, the SynthDef is compiled, sent to the
// server and immediately executed.
A more complicated SynthDef, which combines all input tracks and sends the result to all output channels, plus the same track shifted up a perfect fifth.
SynthDef("SYNTHDEFNAME", {
var in, sum;
var fifthUp;
in = Mix.fill(~maxClients * 2, { arg channelNum; // Mix.fill is a UGen that sums channels together, in this case
var input; // from channels 0 to ~maxClients * 2 - 1
input = SoundIn.ar(channelNum); // Use either the In or SoundIn UGens for audio input
input = LPF.ar(input, 20000);
HPF.ar(input, 20);
});
fifthUp = PitchShift.ar(in, pitchRatio: 1.5); // PitchShift takes `in` and shifts it up a perfect fifth
sum = in + 0.75 * fifthUp;
Out.ar(~allChannels, [sum, sum]); // After audio is processed, use the Out UGen for audio output
}).send(s);
Function.play
is a convenient way to test code quickly, but use Synth
and SynthDef
where reusability and flexibility
are important (i.e. such as on JackTrip VS servers).
Last thing: you can pass an external argument to a synth def, either using an arg
statement at the beginning, or using
symbol notation (with a \
). Each argument passed is of type Control, even if there are default parameters of another type.
That's why we have .ir
and .kr
methods associated with the arguments.
SynthDef("jacktrip_autopan_in", {
var client = \client.ir(0); // \client is an argument, and its type is the class Control
var mono = Mix.fill(inputChannelsPerClient, { arg channelNum;
var in = SoundIn.ar((client*inputChannelsPerClient)+channelNum);
in = LPF.ar(in, \lpf.kr(20000)); // \lpf is an argument, and its type is the class Control
HPF.ar(in, \hpf.kr(20)); // \hpf is an argument, and its type is the class Control
});
var panned = Pan2.ar(mono, \pan.kr(0)); // \pan is an argument, and its type is the class Control
Out.ar(\out.ir(0), panned * masterVolume * \mul.kr(1)); // \mul, \out are arguments, and their type is the class Control
}).send(server);
The Supercollider language is an object-oriented language. You can call a function by sending a message (method) to an object, known as the receiver:
receiver.message
A Node is an abstraction of executions on a server. We do not use Node directly, just its subclasses: Group and Synth. A Group is a collection of Nodes (Hence the tree structure of executions).
s.options.numAudioBusChannels // to see the fixed number of audio busses available (defaults to 128)
s.options.numOutputBusChannels // to see the number of output audio busses (defaults to 8, #0 - #7)
s.options.numInputBusChannels // to see the number of input audio busses (defaults to 8, #8 - #15)
#16 - #127 are reserved for private busses. These values can be configured upon server startup / reboot. Note that the output busses are numbered lower than input busses.
You can allocate busses using the Bus
class, which will always take the lowest-numbered available
private bus. When SuperCollider receives a Bus object as an argument to a function call, it automatically gets
converted to the correct bus number
Note that each bus represents one channel. There are no multichannel busses. Using the Bus
object to create
a multichannel (e.g. Stereo) bus actually allocates two separate busses with different consecutive indices.
Suggested Readings: