SuperCollider is a powerful, open-source platform for audio synthesis, composition, and algorithmic sound design. It consists of two main components:

  1. sclang: The SuperCollider programming language, a dynamically-typed, high-level language for controlling the synthesis server and structuring musical ideas.
  2. scsynth: The synthesis engine (server), responsible for real-time audio processing, implementing unit generators (UGens) for synthesis, effects, and routing.

Core Architecture

Language (sclang)

SuperCollider’s language (sclang) is a functional, object-oriented language inspired by Smalltalk and Lisp. It includes constructs for defining instruments, scheduling events, and live coding.

Example: Defining a Simple Synth

// Define a SynthDef (synthesis definition)
SynthDef(\sine, {
    |freq = 440, amp = 0.5|
    var signal = SinOsc.ar(freq, 0, amp); // Sine wave oscillator
    Out.ar(0, signal); // Send to output bus 0
}).add; // Add to the server's SynthDef library
 
// Instantiate the SynthDef on the server
Synth(\sine, [\freq, 880, \amp, 0.2]);

Server (scsynth)

The server (scsynth) operates as a separate real-time process, handling low-latency audio computation. Communication with sclang or other clients occurs through Open Sound Control (OSC) messages.

Server Features

  • Graph-based Synthesis: All operations are defined as a directed acyclic graph (DAG), connecting UGens for precise audio routing and processing.
  • Unit Generators (UGens): Modular building blocks for synthesis, including oscillators, filters, and audio effects.

Example: Understanding Node Graphs

// Visualize the node graph
s.plotTree;
 
// Create nodes with varying routing
Synth(\sine, [\freq, 440]); // Node 1000
Synth(\sine, [\freq, 660]); // Node 1001

The plotTree method visually represents nodes and their connections, invaluable for debugging complex audio graphs.

Best Practices

Using Buses for Modular Routing

SuperCollider uses buses (audio and control) to route signals efficiently. This modular approach enables advanced setups like parallel processing.

// Create an audio bus
~bus = Bus.audio(s, 2); // Stereo bus
 
// Synth writing audio to the bus
SynthDef(\modulator, {
    |outBus, freq = 440|
    var signal = SinOsc.ar(freq, 0, 0.5);
    Out.ar(outBus, signal);
}).add;
 
Synth(\modulator, [\outBus, ~bus]); // Write to bus
 
// Synth reading from the bus
SynthDef(\output, {
    |inBus|
    var input = In.ar(inBus, 2);
    Out.ar(0, input); // Output to speakers
}).add;
 
Synth(\output, [\inBus, ~bus]);

Scheduling with Patterns

Patterns provide a declarative way to sequence events, encapsulating rhythm, pitch, and dynamics.

// Create a pattern
Pbind(
    \instrument, \sine,
    \freq, Pseq([440, 660, 880], inf), // Sequence of frequencies
    \dur, 0.25 // Duration of each event
).play;

Integration with External Tools

Using OSC for Interoperability

SuperCollider supports OSC for communicating with other software (e.g., Sonic Pi, Max/MSP). This enables distributed systems and hybrid setups.

// Receive OSC messages
OSCdef(\example, {
    |msg, time, addr, port|
    "Received OSC message: %".format(msg).postln;
}, '/example');
 
// Send OSC messages
NetAddr("127.0.0.1", 57120).sendMsg("/example", 1, 2, 3);

PipeWire and SuperCollider

While SuperCollider traditionally interacts with JACK or ALSA, PipeWire provides a flexible environment for integrating SuperCollider with modern Linux audio systems. Use tools like qpwgraph to visualize and manage connections.

pw-cli create-link <scsynth-node-id> <sink-id>

Advanced Techniques

Custom Unit Generators (UGens)

Develop custom UGens in C++ for specialized audio processing. UGens are defined by extending the UGen class, providing real-time DSP.

#include "SC_PlugIn.h"
 
struct MySaw : public SCUnit {
    float mPhase;
    MySaw() { mPhase = 0.0f; }
    void next(int nSamples) {
        // Generate sawtooth waveform
    }
};
 
PluginLoad(MyUGen) {
    ft = inTable;
    DefineSimpleUnit(MySaw);
}

Buffer Manipulation

Use buffers for sampling and granular synthesis.

// Load a sound file
b = Buffer.read(s, "path/to/sound.wav");
 
// Play the buffer
SynthDef(\playBuf, {
    |bufnum|
    var signal = PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum), loop: 1);
    Out.ar(0, signal);
}).add;
 
Synth(\playBuf, [\bufnum, b]);

Resources