EDIT .add is now added to the synthdef, sorry I have been looking for a way to create a relatively simple universal modulator synth which is able to produce many different shapes over a user specified amount of time. In some cases the synthdef below is overkill, e.g. when producing a simple lfo modulation, but I figured that the advantages of this unified approach outweighs the added cpu load (which is low).
I chose to specify the lfo modulation as a ratio (lfo.midiratio). There are case where one might prefer an added lfo rather than a multiplied ratio. In these case a separate but similar modulation synth could be used or the current one expanded with a Select.kr choosing between env * lfo.midiratio and env + lfo.
I should also the mention that the total duration of the shape is specified by the dur argument unless (atk + rel) > dur in which case the total duration is (atk + rel).
I am curious if other people are using a similar approach?
I came up with this:
(
SynthDef(\mod, {|dur=3, ampLo=0, ampHi=0, ampAtk=0, ampRel=0, rateLo=5, rateHi=5, rateAtk=0, rateRel=0, lo=0, hi=0, atk=0.5, rel=0.5, out, phase=0, mul=1, add=0, curve=0, round=0|
var ampEnv = Env([ampLo, ampHi, ampHi, ampLo], [ampAtk, (dur-ampAtk-ampRel).max(0), ampRel], curve).kr(0);
var rateEnv = Env([rateLo, rateHi, rateHi, rateLo], [rateAtk, (dur-rateAtk-rateRel).max(0), rateRel], curve).kr(0);
var lfo = SinOsc.kr(rateEnv, phase, ampEnv);
var env = Env([lo, hi, hi, lo], [atk, (dur-atk-rel).max(0), rel], curve).kr(2) * lfo.midiratio.round(round);
Out.kr(out, env * mul + add)
}).add;
)
This can create a lot of different shapes. Here are a few different ones. Each plot takes 3 seconds (sorry!).
b = Bus.control(s);
// trapez
(
Synth(\mod, [\out, b, \hi, 1]);
b.plot(3)
)
// triangle
(
Synth(\mod, [\out, b, \hi, 1, \atk, 1.5, \rel, 1.5]);
b.plot(3)
)
// ramp
(
Synth(\mod, [\out, b, \hi, 1, \atk, 3]);
b.plot(3)
)
// lfo modulation of basevalue 100
(
Synth(\mod, [\out, b, \lo, 100, \hi, 100, \atk, 0, \rel, 0, \ampHi, 1, \rateHi, 5]);
b.plot(3)
)
// envelope with amp modulation
(
Synth(\mod, [\out, b, \hi, 10, \ampHi, 2, \ampAtk, 0.5, \ampRel, 0.5, \rateLo, 10, \rateHi, 10]);
b.plot(3)
)
// amp modulation
(
Synth(\mod, [\out, b, \lo, 2, \hi, 2, \ampLo, 0, \ampHi, 10, \ampAtk, 3, \ampRel, 0, \rateHi, 10]);
b.plot(3)
)
// rate modulation
(
Synth(\mod, [\out, b, \lo, 2, \hi, 2, \ampHi, 1, \rateLo, 2, \rateHi, 20, \rateAtk, 3]);
b.plot(3)
)
// envelope with amp and rate modulation
(
Synth(\mod, [\out, b, \hi, 10, \atk, 1, \rel, 0.5, \ampLo, 1, \ampHi, 10, \ampAtk, 3, \rateLo, 1, \rateHi, 20, \rateAtk, 3]);
b.plot(3)
)
// using round
(
Synth(\mod, [\out, b, \hi, 100, \ampHi, 3, \rateHi, 5, \round, 0.1]);
b.plot(3)
)
Here are some sound examples using a non-gated synth. If used one a gated synth the user would have to keep track of old mappings or use several control busses to keep modulation of multiple arguments separate.
(
SynthDef(\synth, {
var sig = 2.collect{Saw.ar(\freq.kr(110) * LFNoise1.kr(1).bipolar(\det.kr(0.2)).midiratio * \vib.kr(0).midiratio)};
var env = Env.perc(\atk.kr(0.1), \rel.kr(3), 1, \curve.kr(2)).kr();
sig = RLPF.ar(sig, \lp.kr(1000), \rq.kr(1).max(0.1));
sig = RHPF.ar(sig, \hp.kr(60), \rq.kr(1).max(0.1));
sig = Balance2.ar(sig[0], sig[1], \pan.kr(0), \amp.kr(1) * 0.3);
Out.ar(\out.kr(0), sig * env)
}).add
)
// The un-modulated synth //
Synth(\synth);
// rate and amp modulation of lopass filter
(
s.makeBundle(nil, {
x = Synth(\synth);
Synth(\mod, [\out, b, \lo, 150, \hi, 700, \atk, 1, \rel, 1, \ampLo, 1, \ampHi, 10, \ampAtk, 2, \ampRel, 2, \rateLo, 0.1, \rateHi, 20, \rateAtk, 2, \rateRel, 2, \curve, 4]);
x.set(\lp, b.asMap)
})
)
// rate and amp modulation of hipass filter
(
s.makeBundle(nil, {
x = Synth(\synth);
Synth(\mod, [\out, b, \lo, 60, \hi, 800, \ampLo, 20, \ampHi, 2, \ampRel, 3, \rateLo, 2, \rateHi, 20, \rateAtk, 1, \rateRel, 2]);
x.set(\hp, b.asMap)
})
)
// pan left to right over duration of note, progressively more vibrato
(
c = Bus.control(s); // this example uses 2 control busses so we define an extra one
s.makeBundle(nil, {
x = Synth(\synth);
Synth(\mod, [\out, c, \hi, 1, \add, -1, \atk, 0, \ampHi, 24, \ampAtk, 3, \curve, 4]);
Synth(\mod, [\out, b, \lo, -1, \hi, 1, \atk, 3]);
x.set(\pan, b.asMap, \vib, c.asMap)
})
)
// tremolo
(
s.makeBundle(nil, {
x = Synth(\synth);
Synth(\mod, [\out, b, \hi, 1, \atk, 0, \rel, 0, \ampHi, 24, \mul, 0.25]);
x.set(\amp, b.asMap)
})
)
// detune, the sound is quite different each time due to the use of LFNoise in the synthdef
(
s.makeBundle(nil, {
x = Synth(\synth);
Synth(\mod, [\out, b, \lo, 0.2, \hi, 12, \atk, 3, \curve, 2]);
x.set(\det, b.asMap)
})
)
// modulation of frequency
(
s.makeBundle(nil, {
x = Synth(\synth, [\rel, 2]);
Synth(\mod, [\out, b, \lo, 110, \hi, 220, \dur, 2, \ampHi, 3, \round, 2]);
x.set(\freq, b.asMap)
})
)
b.free;
c.free;