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');

)