Last active
June 2, 2022 06:50
-
-
Save ckmahoney/8b889015bce2a79424a9f4c7982d5995 to your computer and use it in GitHub Desktop.
Demonstrating a method to route effects dynamically in SuperCollider.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
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