2.1 Subtractive and Additive Synthesis
2.1 Subtractive and Additive Synthesis
Note that sounds will at first be in mono, in the left ear. Later on we will sort
out stereo position.
We are going to use the scope with many of these tutorials, to see the sound
waveform. We'll get oscilloscope views of the sounds we synthesise, which assists
with explaining some concepts.
On SC3.6, use of the scope requires no special treatment. But for SC3.5 or earlier,
you need the internal Server for this tutorial:
s.boot; //or you may turn on the internal server via the graphical window; make
sure the default button is pressed and highlighted; this tells the system which
synthesizer to send instructions to
For our convenience we will be using a certain shortcut construction for practising
sound synthesis. Later we will see another way of doing this that is a more
recommended method, but we shall begin with the notation below because it avoids
some issues for the moment, and allows us to get going straight away.
The construct looks like the following, but don't try and run this code:
(
{
//some synthesis code
}.scope
)
//Run the following line though: this will create a frequency analysizer which we
will continue to run in the background for spectral plotting of the sounds we
explore.
FreqScope.new
Unit Generators
SuperCollider follows the Unit Generator paradigm also used in other synthesis
languages like Csound, Max/MSP, Pd, Reaktor and others.
There are many primitive building blocks, like types of tone generator, filter or
spatialiser, that are the unit generators. These are connected together in a
processing graph to make more complicated synthesisers and sound processors. These
primitives are referred to as UGens.
Each UGen has some set of inputs and outputs. Most UGens have just one output, an
audio stream or some sort of control signal. The inputs vary a lot depending on the
function of the UGen.
You will get used to the typical parameter values expected as inputs or outputs as
you learn about the different UGens.
There are certain ways to program connections which are part of the syntax of the
SuperCollider language, and particular names for units that you will encounter as
you learn this system.
Subtractive Synthesis
{WhiteNoise.ar(0.1)}.scope //this line will make a pure white noise source, equal
energy at all spectral frequencies; it can be unpleasant to listen to- the 0.1
makes sure its not too loud, but be careful playing this
That was just the source alone. Now I have to filter it to make a less raw sound.
{LPF.ar(WhiteNoise.ar(0.1),1000)}.scope
The LPF is a Low Pass Filter which tails off energy above its cutoff frequency,
which is 1000Hz in this example
In SuperCollider, to plug the white noise generator WhiteNoise into the filter LPF
I nest one within the other. You can think of a UGen's inputs being the list of
slots within the parentheses
LPF.ar(input signal, cutoff frequency, ... )
and in the example above, the thing to plug into the input signal slot is a white
noise source, so that's where the WhiteNoise generator goes. The cutoff frequency
is a fixed number, 1000, the second argument.
Say that we now want a varying filter cutoff over time. One UGen we could use here
is the line generator, Line:
So instead of the fixed value 1000, the Line UGen goes in that second slot
There are lots of possible sources and lots of possible filters (try these help
files)
Oscillators
[Saw]
[Blip]
Noise Sources
[PinkNoise]
[LFNoise0]
{Resonz.ar(LFNoise0.ar(400),1000,0.1)}.scope
Again using the Line generator to change the filter centre frequency over time
{Resonz.ar(LFNoise0.ar(400),Line.kr(10000,1000,10),0.1)}.scope
An explicit and neater way to write this (we'll come back to this formulation)
(
{
var source, line, filter; //local variables to hold objects as we build the
patch up
source=LFNoise0.ar(400);
line=Line.kr(10000,1000,10);
filter=Resonz.ar(source,line,0.1); //the filtered output is the input source
filtered by Resonz with a line control for the resonant frequency
filter // last thing is returned from function in curly brackets, i.e. this is the
final sound we hear
}.scope;
)
Additive Synthesis
Rather than starting with something complex and taking energy away to sculpt a
sound, we can start with simple building blocks and add many of them together to
create more involved sounds
{SinOsc.ar(400,0,0.1) + SinOsc.ar(660,0,0.1)}.scope
{SinOsc.ar([400,660],0,0.1)}.scope
Something special just happened to the stereo field, and I'll explain this in a
moment.
Let me first introduce a panning UGen
{Pan2.ar(WhiteNoise.ar(0.1), MouseX.kr(-1,1))}.scope
So the panner takes a mono signal, and places it in the stereo field.
We'll look at arrays more closely in a later week, but for now just think of them
as lists of data
We need a way to take multiple channels of sound and turn them into a mono or
stereo signal
And then, of course, Pan2 allows me to place this in the stereo field:
Recipes for common waveforms are known from the Fourier theory of sound (sinusoids
at which frequencies and amplitudes to add up to create certain waveform shapes).
SinOsc.ar(440*(i+1))*mult
});
}.scope;
)
(
{
var n = 10;
}.scope;
)
Triangle wave: also odd harmonics only, falls off as 1 over harmonicnumber squared
with alternating sign
(
{
var n = 10;
SinOsc.ar(440*index)*mult })/n;
}.scope;
)
{Mix(SinOsc.ar(500*[0.5,1,1.19,1.56,2,2.51,2.66,3.01,4.1],0,0.1))}.scope //bell
spectra, all partials the same volume
I can also give each partial its own amplitude in the mix, rather than defaulting
them all to 0.1
{Mix(SinOsc.ar(500*[0.5,1,1.19,1.56,2,2.51,2.66,3.01,4.1],0,0.1*[0.25,1,0.8,0.5,0.9
,0.4,0.3,0.6,0.1]))}.scope //bell spectra, different volumes for partials
Here is a generalisable patch that uses the variable n to hold the number of sine
tones desired for each run of the code:
(
var n = 10;
{Mix(SinOsc.ar(250*(1..n),0,1/n))}.scope;
(1..10) //run this line and see what comes up in the post window
There are lots of ways of dealing with arrays of data in SuperCollider, that we'll
investigate as we go.