To get started boot the server.
s.boot
It will be useful to bring up a plot tree so we can monitor what synthdefs are still on the server.
s.plotTree
Env
and EnvGen
¶The classes Env and EnvGen are the two pieces of SuperCollider syntax we need to create amplitude envelopes. The class Env
works by taking in a series of amplitude points as an array in the 0th argument and specifying the time between those points in seconds as a second array.
The class Env
defines an envelope on the client-side (i.e., sclang). To send the envelope to the server to process, we use the UGen EnvGen
. Note here that we are using .kr
instead of .ar
. .kr
is generally used to modulate a sound. It is less expensive but also has less resolution. We could very well use .ar
but we will not hear any audible difference.
SynthDef(\sineEnv, {
arg freq = 440;
var env, sig;
env = Env.new([0, 1, 0.4, 0.4, 0], [1, 1, 2, 1], curve: 'lin');
env = EnvGen.kr(env);
sig = SinOsc.ar(freq) * env;
Out.ar(0, sig ! 2);
}).add;
x = Synth(\sineEnv)
x.free
CmdPeriod.run; // Clear all synths if need be
The class EnvGen
also has an option for what is called a Done action. This specifies what should happen to the Synth once the envelope has completed. Generally, when we are done with an envelope, we want the synth to free itself because no more sound should be produced. To do so, we use the integer 2
to specify this action.
SynthDef(\sineEnvDone, {
arg freq = 440;
var env, sig;
env = Env.new([0, 1, 0.4, 0.4, 0], [1, 1, 2, 1], curve: 'lin');
env = EnvGen.kr(env, doneAction: 2); // Free Synth when envelope is complete
sig = SinOsc.ar(freq) * env;
Out.ar(0, sig ! 2);
}).add;
Synth(\sineEnvDone) // automatically freed
SynthDef(\shortSine, {
arg freq = 440;
var env, sig;
env = Env.linen(
attackTime: 0.05, // Time in seconds to ramp up
sustainTime: 0.5, // Time in seconds to sustain
releaseTime: 0.1, // Time in seconds to release
level: 0.3, // What fraction of the overall level to reach
curve: 'sine' // curvature between points
);
env = EnvGen.kr(env, doneAction: 2);
sig = SinOsc.ar(freq);
Out.ar(0, sig * env ! 2);
}).add;
Synth(\shortSine) // Will free automatically when done
EnvGen has an optional parameter for a gate which can control when to trigger the start of the envelope. When the gate is set to 1, the envelope will be triggered. For fixed-time envelopes like .linen
or .perc
, the gate acts as a simple trigger. To retrigger the envelope, set the gate to zero and then back to 1.
SynthDef(\gatedSine, {
arg freq = 440, gate = 0; // Note how gate here is set to zero
var env, sig;
env = Env.linen(0.05, 1, 0.1, 0.3, 'sine');
env = EnvGen.kr(env, gate);
sig = SinOsc.ar(freq);
Out.ar(0, sig * env ! 2);
}).add;
~s1 = Synth(\gatedSine);
~s1.set(\gate, 1);
~s1.set(\gate, 0); // Gate needs to be reset to zero in order to be ready for triggering
~s1.free;
Here we are creating a randomized arpeggio pattern where we use Impulse
to change certain aspects of the code.
SynthDef(\shortSawPattern, {
arg out = 0, freq = 40;
var env, sig, gate;
// Triggers gate at a constant rate of six pulses per second
// The signal of impulse is 1 immediately follow by 0
gate = Impulse.kr(6);
// Sound source here is a single sawtooth wave
// The frequency of the sawtooth wave changes at the rate of Impulse.kr (i.e., 1/6 second)
// TIRand triggers a new random value using the gate
// The first TIRand controls whether the frequency is the original frequency or an octave above
// Select.kr randomly chooses from 4 interval options
sig = Saw.ar(
freq * TIRand.kr(1, 2, gate) * Select.kr(TIRand.kr(0, 3, gate), [
1, // root
(1.0595 ** 3), // three semitones up
(1.0595 ** 7), // seven semitones up
(1.0595 ** 8), // eight semitones up
])
);
// Envelope triggered by gate
env = Env.linen(
attackTime: 0.05,
sustainTime: 0.05,
releaseTime: 0.05,
level: 0.3,
curve: 'sine'
);
env = EnvGen.kr(env, gate);
sig = sig * env ! 2;
Out.ar(out, sig);
}).add;
SynthDef(\reverb, {
arg in, out = 0;
var sig = In.ar(in, 2);
sig = FreeVerb.ar(sig, 0.45, 0.5); // Remember sig is 2-channel array so no need to duplicate
Out.ar(out, sig);
}).add;
Create an audio bus to connect the synth to the reverb. Instantiate those two synths using the bus.
~reverbBus = Bus.audio(s, 2);
~pattern = Synth(\shortSawPattern, [\out, ~reverbBus, \freq, 100]);
~out = Synth(\reverb, [\in, ~reverbBus], ~pattern, 'addAfter');
~pattern.set(\freq, 50) // Change the frequency
Free resources and stop pattern.
~reverbBus.free;
~pattern.free;
~out.free;
The previous envelopes we have seen have been fixed-time envelopes. But many times, we want an envelope to sustain indefinitely until we are ready to cut it off. This is especially true with keyboard synthesizers where we press a key and then want to wait until we release the key to stop the note.
The most common pattern for this is an ADSR (attack, decay, sustain, release) envelope. Sustain-time envelopes like .adsr
are triggered and released using a gate. When the gate is 1, the envelope is triggered. When the gate is set to 0, the envelope is released.
SynthDef(\tri, {
arg freq = 440, aTime = 0.1, dTime = 2.5, rTime = 1.5, peakAmp = 0.4, sLvl = 0.3, gate = 1;
var sig, env;
// Envelope
env = Env.adsr(
attackTime: aTime, // attack time in seconds
decayTime: dTime, // decay time in seconds
sustainLevel: sLvl, // amplitude of sustain relative to peak
releaseTime: rTime, // release time in seconds
peakAmp: peakAmp // Corresponds to the point reached after the attack time before decay
);
env = EnvGen.kr(env, gate, doneAction: Done.freeSelf); // Done.freeself is equivalent to 2
// Signal with envelope applied
sig = LFTri.ar(freq) * env ! 2;
Out.ar(0, sig);
}).add;
~tri = Synth(\tri, [\freq, 150])
~tri.set(\gate, 0) // automatically freed because of done action