Introduction

This page is supposed to be a short catalog of the fundamental techniques in digital sound synthesis. It doesn't go into great depth on any one topic but it is hoped it will provide some quick starting points for experimentation with SuperCollider.

I will keep adding to this as I discover more.

Whats is a UGen?

When designing synthesis instruments the task involves assembling simple building blocks which are know as Unit Generators (or UGens in SuperCollider).

The unit generator is a fundamental concept in digital synthesis. A UGen is either a signal generator or a signal modifier. A signal generator (such as an oscillator [i.e. SinOsc, VarSaw]) synthesizes signals such as a musical waveforms and envelopes. A signal modifier, such as a filter, take a signal as its input, and transforms that input signal in some way. [Roads (1996)]

So we have generator (oscillators) and modifiers (filters) and to construct instruments we put the output of one into the input(s) of another or combine the signals together in various ways.

For a comprehensive look at most (all?) the UGens in SuperCollider, and their groupings, checkout the mighty Tour of UGens in the help.

Tip: Seeing the sound

You might want to see what your doing with this Frequency Scope:

FreqScope.new

If you are working on poor quality speakers (i.e. laptop speakers) then you will not be able to hear all the sound you are generating, particularly low frequency sounds which might surprise you later when played on speakers that can reach those depth. Thus its a good idea to use the Frequency scope to see what you otherwise can not hear.

Tip: Levels

Clicking on the sound server box and pressing l will bring up the levels for that server so you can see if you are getting any clipping (i.e. the signal is above 1).

Summation

Additive Synthesis

With additive synthesis we create complex sounds by summing together simpler ones.

Discrete summation formula (DSF)

From Moorer (1976):

(
    Ndef(\dsf, {
        var freq = 112  // fundamental
            , a = 42    // decay
            , b = 300   // distance
        ;
        SinOsc.ar(freq) - (a * SinOsc.ar(freq - b)) / ((1 + a.pow(2)) - 2*a*SinOsc.ar(b, pi/2));
    }).play;
)

Here freq is the fundamental frequency, b is the harmonic spacing (distance), harmonic decay

Another approach from Dodge and Jerse (1985) which uses one less oscillator:

(
    Ndef(\dsf2, {
        var n = 12  // number of harmonics
            , amp = 0.8
            , freq = 100
        ;
        f = freq / 2;
        (SinOsc.ar((2*n+1)*f) / SinOsc.ar(f) - 1) * amp / (2 * n);
    }).play;
)

Waveshaping

From Waveshaping in SuperCollider by Chris Jeffs (also a good intro to the concepts there).

First to create the transfer function that the signal will be remapped with:

(
    // in order to be turned into a wavetable,
    // the signal must be half buffer size plus one

    x = Signal.sineFill(513, [0.5, 0.2, 0.3, 0.0, 0.2]);
    x.plot;
    b = Buffer.alloc(s, 1024, 1);
)
b.sendCollection(x.asWavetableNoWrap);

Then use the Shaper UGen to use it:

(
    Ndef(\waveshaper, {
        var input, output;
        input = SinOsc.ar(MouseX.kr(80, 800, \exponential), 0, 0.7);

        // Shaper is the waveshaping Ugen
        output = Shaper.ar(b.bufnum, input);
        output ! 2
    }).scope;
)

Chebyshev Polynomials

The first few Chebyshev polynomials of the first kind in the domain −1 < x < 1

Modulation

Modulation is the idea that you use one oscillator as the input to controlling a parameter of another.

Frequency Modulation (FM)

(
    Ndef(\fm, {

        var modulator = 200;
        var fm_index = 50;
        var carrier = 600;

        SinOsc.ar( SinOsc.ar(modulator) * fm_index + carrier )

    }).play
)

FM modulation gives frequency sidebands at integer multiples of the modulating frequency.

Move your mouse around to explore the sound space:

(
    Ndef(\fm_pm_fun, {
        SinOsc.ar(SinOsc.ar(MouseX.kr(0.5, 1000, 1), MouseY.kr(0.5, 1000, 1), 100, 200))
    }).play
)

Phase Modulation (PM)

Modulating the phase of an oscillator is the same as modulating its frequency. This gives us the chance to have both FM & PM at the same time.

Amplitude Modulation (AM)

AM is simply multiplying one signal by another which is like applying a dynamic envelope to a signal. The original signal is called the carrier and the multiplier signal is called the modulator.

Ring modulation

(
    Ndef(\ring, {

        a = SinOsc.ar(320); // carrier
        b = SinOsc.ar(440) + 1; // modulator

        (a * b) * 0.5
    }).play;
)

The +1 here means some carrier signal will make it to the output, the modulator frequency will not. Frequency sidebands appear at:

carrier, carrier ± modulator 

so here its 320Hz, 760Hz and 120Hz (this last one is reflected about zero).

All band modulator

We can increase the number of components with the following

(
    Ndef(\allband, {

        a = SinOsc.ar(320); // carrier
        b = SinOsc.ar(440); // modulator

        (a + b) + (a * b) * 0.5
    }).play;
)

Here we get Frequency sidebands of:

carrier, modulator, carrier ± modulator

so for the example 320Hz, 440Hz, 120Hz and 760Hz.

Cascading AM

AM signals can be cascaded to increase the complexity of the output signal like so, taking our original \ring example:

(
    Ndef(\ring, {

        a = SinOsc.ar(320); // carrier 1
        b = SinOsc.ar(440); // carrier 2
        c = SinOsc.ar(900); // modulator

        ((a + b) * 0.5) * c * 0.5
    }).play;
)

This gives 4 frequency sidebands:

carrier 1 ± modulator, carrier 1 ± modulator

So for the above example we get sidebands at 1300Hz, 500Hz, 1200Hz and 600Hz.

Since because SuperCollider is not some patching interface but instead a language we can easily create as many cascades as we like with a loop

(
    Ndef(\ring, {

        var freq = 320; // Base frequency
        var freq2 = 440;
        var n = 6;  // number of cascades
        var sound = SinOsc.ar(freq) + SinOsc.ar(freq2); // carriers

        // This repeats n-times and mixes the result together
        Mix.fill(n, { | i |
            sound * SinOsc.ar(freq + (i * 100)) * 0.5
        })

    }).play;
)

Feedback

FM Feedback

Sampling and Wavetables

Granular Synthesis

References

Farnell, Andy (2010) Designing Sound, MIT Press
Many of the code here originated from his PureData examples. There has also been some work to convert his more complex examples to SuperCollider code.
Roads, Curtis (1996) The Computer Music Tutorial, MIT Press
The seminal work of Curtis Roads that as comprehensive about computer music as it is heavy.