Skip to content

Instantly share code, notes, and snippets.

@ckmahoney
Last active June 2, 2022 06:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ckmahoney/8b889015bce2a79424a9f4c7982d5995 to your computer and use it in GitHub Desktop.
Save ckmahoney/8b889015bce2a79424a9f4c7982d5995 to your computer and use it in GitHub Desktop.
Demonstrating a method to route effects dynamically in SuperCollider.
/**
Routing dynamic effects for any Synth
The issue was that a list of arguments can not be passed in as args for SynthDef.
For examle say you want to apply both a reverb and delay to a hat sound.
One way is to write explicit args for each effect :
SynthDef(\hat, {|out=0, reverbChannel =4, delayChannel = 6|
var sig = WhiteNoise.ar() * Env.perc.ar(doneAction: 2);
Out.ar(out, sig);
Out.ar(reverbChannel, sig);
Out.ar(delayChannel, sig);
});
This works, but is not dynamic.
It is tempting to use an array to specify the effects channels:
SynthDef(\hat, {|out=0, allFx = [4, 6]|
var sig = WhiteNoise.ar() * Env.perc.ar(doneAction: 2);
Out.ar(out, sig);
allFx.do({|chan|
Out.ar(chan, sig);
});
});
But the languge does not apply an array passed as an argument to SynthDef. Instead it only reads the first element of the array and uses that as the value.
So instead, we can define our effects as data and apply them to the synth as additional synths.
See the example below for minimal steps to reproduce.
*/
(
// Instruments for playback
// Notice the default out bus is not 0. This means the synth will not be audible by default.
// The convention is to pass an instance of Bus to create a private bus; then route that into the effects.
SynthDef(\kick, {|freq, out = 12, scale|
var sig = RLPF.ar(SinOsc.ar(freq*scale) * Env.perc.ar(doneAction: 2), freq*2);
Out.ar(out, sig!2);
}).add;
SynthDef(\hat, {|freq, out|
var sig = HPF.ar(WhiteNoise.ar() * Env.perc.ar(doneAction: 2), freq * 4);
Out.ar(out, sig!2);
}).add;
// Effects
// These take a Bus for the in argument, and to a Bus which is output to the playback system.
SynthDef(\passthrough, {|in, out= 0|
// A clean signal to hear the original uneffected signal
Out.ar(out, In.ar(in)!2);
}).add;
SynthDef(\delay, {|in, dur, out = 0, repeats = 8|
var sig = 0;
repeats.do {|i|
sig = sig + CombC.ar(In.ar(in), dur, dur/(i + 1) * 8, dur, mul: 1/repeats);
};
Out.ar(out, sig!2);
}).add;
SynthDef(\reverb, {|in, out = 0, size = 8|
var bus = In.ar(in);
var sig = Limiter.ar(FreeVerb2.ar(bus, bus, 1, size, mul: 0.05), 1);
Out.ar(out, sig);
}).add;
~initFx = {|pair|
var name, args;
name = pair.at(0);
args = pair.at(1);
Synth.new(name, args);
};
)
(
// Private instances of Bus for routing.
var busKick = Bus.new, busHat = Bus.new;
// Lists of length 2.
// First element is the args for synth
// Second element is a list of args for effect synths
var kick = [[\kick, [freq: 150, out: busKick, scale: 1]],
[[\passthrough, [in: busKick, out: 0]],
[\delay, [in: busKick, out:0, repeats: 32]],
[\reverb, [in: busHat, out: 0, size: 20]]]];
var hat = [[\hat, [freq: 450, out: busHat, scale: 1]],
[[\passthrough, [in: busHat, out:0]],
[\reverb, [in: busHat, out: 0, size: 8]],
[\reverb, [in: busHat, out: 0, size: 32]],
[\delay, [in: busHat, out:0, repeats: 32]]]];
// Initialize the effects. Comment these out to hear the difference.
var fxs = kick.at(1).collect(~initFx.(_));
fxs = hat.at(1).collect(~initFx.(_));
// Play an infinite pattern
Pbind(\instrument, kick.at(0).at(0),
\dur, Pseq([1], inf),
*kick.at(0).at(1)
).play;
Pbind(\instrument, hat.at(0).at(0),
\dur, Pseq([Rest(1/2),1/2], inf),
*hat.at(0).at(1)
).play;
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment