SCNotes_1: Modulation_1
Some code for doing subtractive synthesis with modulation in Supercollider. SC seems like it is an ideal program for working with modulation, plugging one thing into another.
//the synth def we will use. Modified from an example file.
SynthDef(\bass_mod2, { | out = 0, gate = 1, freq = 200, freqMod = 200, modMul = 100, modAdd = 200, amp = 0.2, pulseFreqW = 100, fr = 300 |
var aEnv, fEnv, osc, flt,flt2, pulseW, fmod, pulseD;
fmod = SinOsc.kr(freqMod, mul: modMul, add: modAdd );
pulseW = SinOsc.ar(0.2).exprange(0.1, 0.9);
pulseD = SinOsc.ar(pulseFreqW).exprange(0.9,1);
//pulseD= SinOsc.kr(1, mul: modMul, add: modAdd );
aEnv = EnvGen.kr(Env.asr(0, 1, 30), gate, doneAction: 2);
fEnv = EnvGen.kr(Env.perc(0.2, 30), levelScale: 6000);
osc = Mix([Saw.ar(freq * [1, LFTri.ar(0.2).exprange(1, 1.03)]), Pulse.ar(freq*pulseD , pulseW)]);
//osc = Mix([Saw.ar(freq * [1, LFTri.ar(0.2).exprange(1, 1.03)]), Pulse.ar(freq , 0.2)]);
flt = LPF.ar(osc, fmod , aEnv);
flt2 = Resonz.ar(flt, fr , 0.6);
Out.ar(out, flt2*amp);
}).add;
//the rough GUI for manipulating some of the mod params.
(
var w, fmod, modMul, modAdd, pulseFreqW,freq,freqReson ;
w=Window("Modulation Test", Rect(100, 400, 800, 600));
w.view.decorator = FlowLayout(w.view.bounds);
x = Synth(\bass_mod2);
fmod= EZSlider(w, 300@50, "freqMod", ControlSpec(21, 300, 'exponential', 0.2, 200), {|ez| x.set(\freqMod, ez.value)});
w.view.decorator.nextLine;
modMul= EZSlider(w, 300@50, "modMul", ControlSpec(3, 100, 'exponential', 0.1, 100), {|ez| x.set(\modMul, ez.value)});
w.view.decorator.nextLine;
modAdd= EZSlider(w, 300@50, "modAdd", ControlSpec(110, 2000, 'linear', 0.01, 200), {|ez| x.set(\modAdd, ez.value)});
w.view.decorator.nextLine;
pulseFreqW = EZSlider(w, 300@50, "pulseFreqW", ControlSpec(0.1, 100, 'linear', 0.01, 20), {|ez| x.set(\pulseFreqW, ez.value)});
w.view.decorator.nextLine;
freq= EZSlider(w, 300@50, "freq", ControlSpec(40, 1000, 'linear', 0.01, 200), {|ez| x.set(\freq, ez.value)});
w.view.decorator.nextLine;
freqReson= EZSlider(w, 300@50, "freqReson", ControlSpec(40, 2000, 'linear', 0.001, 300), {|ez| x.set(\fr, ez.value)});
w.front;
)
To create a more musical solution and escape from the drone, add a new envelope and a Pbind to trigger a pattern. Also a couple of changes to the synthdef. This solution is not ideal but the sound is moving towards a useable zone.
//adding a shorter envelope for notes.
(
SynthDef(\bass_mod_pattern, { | out=0, gate=1, freq = 65, freqMod = 200, modMul = 100, modAdd = 200, amp =0.5, pulseFreqW = 100,fr =300, mainEnv |
var aEnv, fEnv, osc, flt,flt2, pulseW,fmod, pulseD;
fmod = SinOsc.kr(freqMod, mul: modMul, add: modAdd );
pulseW = SinOsc.ar(0.2).exprange(0.1, 0.9);
pulseD = SinOsc.ar(pulseFreqW).exprange(0.9,1);
aEnv = EnvGen.kr(Env.asr(0, 1, 30), gate, doneAction: 2);
fEnv = EnvGen.kr(Env.perc(0.2, 30), levelScale: 6000);
mainEnv = EnvGen.kr(Env.perc(0.01, 3), doneAction: 2);
osc = Mix([Saw.ar(freq.midicps * [1, LFTri.ar(0.2).exprange(1, 1.03)]), Pulse.ar(freq.midicps * pulseD , pulseW)]);
flt = LPF.ar(osc, fmod , aEnv);
flt2 = Resonz.ar(flt, fr , 0.6);
Out.ar(out, flt2*amp*mainEnv);
}).add;
)
//add a pbinddef to trigger a pattern of notes.
(
var w, fmod, modMul, modAdd, pulseFreqW,freq,freqReson ;
w=Window("Modulation Test", Rect(100, 400, 800, 600));
w.view.decorator = FlowLayout(w.view.bounds);
//x =Synth(\bass_mod_midi);
x = Pbindef(\b, \instrument, \bass_mod_pattern).play;
x = Pbindef(\b, \freq, Pseq([44,55,66,45,43], inf));
fmod= EZSlider(w, 300@50, "freqMod", ControlSpec(21, 300, 'exponential', 0.2, 200), {|ez| x.set(\freqMod, ez.value)});
w.view.decorator.nextLine;
modMul= EZSlider(w, 300@50, "modMul", ControlSpec(3, 100, 'exponential', 0.1, 100), {|ez| x.set(\modMul, ez.value)});
w.view.decorator.nextLine;
modAdd= EZSlider(w, 300@50, "modAdd", ControlSpec(110, 2000, 'linear', 0.01, 200), {|ez| x.set(\modAdd, ez.value)});
w.view.decorator.nextLine;
pulseFreqW = EZSlider(w, 300@50, "pulseFreqW", ControlSpec(0.1, 100, 'linear', 0.01, 20), {|ez| x.set(\pulseFreqW, ez.value)});
w.view.decorator.nextLine;
/*freq= EZSlider(w, 300@50, "freq", ControlSpec(40, 65, 'linear', 1, 55), {|ez| x.set(\freq, ez.value)});
w.view.decorator.nextLine;*/
freqReson= EZSlider(w, 300@50, "freqReson", ControlSpec(40, 2000, 'linear', 0.001, 300), {|ez| x.set(\fr, ez.value)});
w.front;
)
One more addition: a debugger for our synthdef arguments. I often need a way to check that the right values are appearing in my Synths - using "sendreply.kr" is useful in this context.
(
SynthDef(\bass_mod_pattern, { | out=0, gate=1, freq = 65, freqMod = 200, modMul = 100, modAdd = 200, amp =0.5, pulseFreqW = 100, fr =300, mainEnv |
var aEnv, fEnv, osc, flt, flt2, pulseW, pulseD, fmod;
fmod = SinOsc.kr(freqMod, mul: modMul, add: modAdd );
pulseW = SinOsc.ar(0.2).exprange(0.1, 0.9);
pulseD = SinOsc.ar(pulseFreqW).exprange(0.9,1);
aEnv = EnvGen.kr(Env.asr(0, 1, 30), gate, doneAction: 2);
fEnv = EnvGen.kr(Env.perc(0.2, 30), levelScale: 6000);
mainEnv = EnvGen.kr(Env.perc(0.01, 3), doneAction: 2);
osc = Mix([Saw.ar(freq.midicps * [1, LFTri.ar(0.2).exprange(1, 1.03)]), Pulse.ar(freq.midicps * pulseD , pulseW)]);
SendReply.kr(Impulse.kr(20), '/pulseD', pulseD, 1);
SendReply.kr(Impulse.kr(20), '/freqMod', freqMod, 1);
flt = LPF.ar(osc, fmod , aEnv);
flt2 = Resonz.ar(flt, fr , 0.6);
Out.ar(out, flt2*amp*mainEnv);
}).add;
)
//receive the information back from the server via OSC.
(OSCdef.new(\tracker,{
arg msg;
"pulseD ".post;
msg[3].postln;
}, '/pulseD');
)
(OSCdef.new(\tracker2,{
arg msg;
"freqMod ".post;
msg[3].postln;
}, '/freqMod');
)