Synthdef with controlling parameter

(

SynthDef(\saw, {|amp = 0.8,freq1 =500, freq2 = 800, pan= 0.5, amt=0.5 distortion= 0|
    var snd, amp2;
	amp2 = amt.linlin(0.0, 1.0, 1.0, 0.2); // if amt is 1.0 we reduce amp to 0.2
	amt = Lag.kr(amt, 0.01); // lag time

	snd = LFSaw.ar(freq1,0, 0.3);
    snd= (snd*distortion.lincurve(0, 1, 1, 1, 40)).tanh;
    snd = snd.tanh;
    snd = Integrator.ar(snd, amt);//distortion effect
    DelayN.ar(snd, amt, mul: amt);
    snd = Feedback.ar(snd, amt);
    snd = snd* (amp) * amp2;
    snd = Pan2.ar(snd, pan);
    Out.ar(0, snd);
}).add;

)


Synth(\saw).set(\amt, 0.1);
Synth(\saw).set(\amt, 0.9);

I have a question about controlling Synthdef with one or several parameters.
In this code, \amt, is the parameter to control Synthdef.

For instance, amt : 0 for dry signal amt: 1 fully wet, 100% long reverb/delay, and distorted sounds.
And I want to control the values of amt to control the different ways of behavior.

It has some changes within a synthdef, but It does not dramatically change depending on the parameter. Like parallel processing of source + fx bus track.

Is that a weird approach in SC, or was I missing something? Or Are there any tips for it??

The approach and the code looks valid to me - the question is what would like to improve on your approach?

If you looking into FX in general, it is a common approach to create a fx bus - this means not every synth needs to have its own reverb (which is computational heavy at some point) and makes re-using it easier.

(
// separate synth ...
SynthDef(\saw, {|out=0, freq1 =100|
	var snd = LFSaw.ar(freq1);
	snd = Pan2.ar(snd, pos: \pos.kr(0.0));
	Out.ar(out, snd * \amp.kr(0.2));
}).add;

// from fx
SynthDef(\fx, {|in, out=0|
	var numInput = 2;
	var sig = InFeedback.ar(bus: in, numChannels: numInput);
	
	sig = Integrator.ar(
		sig,
		coef: \integrator.kr(0.0)
	) * \preAmp.kr(1.0);
	sig = LeakDC.ar(sig.tanh) * \amp.kr(0.2);
	
	// add a delay
	sig = CombC.ar(
		in: sig * LFPulse.kr(10.0),
		maxdelaytime: 0.2,
		delaytime: 0.2,
		decaytime: 4.0,
	);
	
	Out.ar(out, sig);
}).add;
)

(
// create a fx bus which will be used as input for the fx synth
~fxBus = Bus.audio(server: s, numChannels: 2);

// spawn the fx synth
~fxSynth = Synth(\fx, [
	\in, ~fxBus,
	\out, 0,
]);
)

(
// create a group before the fxSynth to keep the order
~synthGroup = Group.before(~fxSynth);

// spawn a synth with output to fxBus
Synth(\saw, [
	\out, ~fxBus,
], target: ~synthGroup);
)

(
// spawn a 2nd synth with output to fxBus and before fxSynth
Synth(\saw, [
	\out, ~fxBus,
	\freq, 110,
], target: ~synthGroup);
)


// attention - this gets loud
~fxSynth.set(\integrator, 1.1);

// when freeing the group the delay rings out
~synthGroup.free;

There are also convenience UGens like XOut | SuperCollider 3.13.0 Help (crossfade on existing output) and InFeedback | SuperCollider 3.13.0 Help (synth order is not important anymore) which are worth looking into.

Also, maybe a ProxyChain | SuperCollider 3.13.0 Help could be worth taking a look at, which is part of GitHub - supercollider-quarks/JITLibExtensions: Some extensions to the common JITLib classes.

Thanks for the reply :slight_smile:
It is straightforward to use the bus in SC.

But what I have tried is a bit complicated with writing code.
It has a simulation. It is already set up as control multiple syntheses within a simulation.

So It is a bit much easier to apply the effects like one Synthdef to control everything.
If I make some sound with synth and control synth manually, then It seems fine,
but for now, I am looking for a bit easier way to add more fx within a system (one synth def):slight_smile:

Basically, one synth type plays at once, so I am looking to add more effects with one synthdef and manipulate some of the parameters within Synthdef to drastically change the sounds.

I don’t really understand what you mean by simulation but it sounds very interesting?
Busses and Groups would also allow you to control multiple Synths at once.

Another way to quickly add/write effects by creating reusable building blocks in your SynthDefs is using functions. I am using Ndef here, but same works with vanilla SynthDefs.


(
~pitchShifter = {|sig, mix, density|
	"pitch".postln;
	SelectX.ar(mix, [sig, PitchShift.ar(
		in: sig,
		windowSize: exprand(0.01, 0.4),
		pitchRatio: exprand(0.5, 5.0) * density,
		pitchDispersion: 1.0.rand * density,
		timeDispersion: 1.0.rand * density,
	)]);
};

~dist = {|sig, mix, density|
	"dist".postln;
	LeakDC.ar(Integrator.ar(sig, 0.99999 * density));
};

Ndef(\mySig, {
	var density = \density.kr(0.0);
	var wet = \wet.kr(0.0);
	var sig = Blip.ar([2.0, 3.0], numharm: LFDNoise3.kr(0.2!2).exprange(100, 400)) * SinOscFB.ar(20, 0.5);
	// cascade 4 random pitch shifters / dist
	4.do({|i|
		if(0.5.coin, {
			sig = ~pitchShifter.(sig, wet, density);
		}, {
			sig = ~dist.(sig, wet, density);
		});
		
	});
	(sig * 4).tanh * \amp.kr(0.2);
}).play;
)

(
// activate fx
Ndef(\mySig).set(\wet, 0.4);
Ndef(\mySig).set(\density, 0.5);
)


(
// modulate wet
Ndef(\mySig).map(\wet, Ndef(\mySigWet, {SinOsc.kr(4.0).range(0.1, 0.9)}));
)

(
// modulate density
Ndef(\mySig).map(\density, Ndef(\mySigDensity, {SinOscFB.kr(1.3, 1.3).range(0.1, 0.9)}));
)

Ndef.clear(fadeTime: 10.0);

1 Like

Thanks!
This method is much easier to implement what I want :slight_smile:

I will test, and If I have some advance questions, I will leave a comment here!

Thank you!

and may I ask one more simple question?

in Ndef is quite easy to modulate Ndef with .map / .asMap method to modulate source,

Is it possible to modulate in SynthDef and Synth?

(
SynthDef(\test, {
	var snd; 
	snd = SinOsc.ar(\freq.kr(100));
	Out.ar(0, snd!2);
}).add

)


Synth(\test).set(\freq, {SinOsc.ar(100)});
Synth(\test).map(\freq, ) // other methods?

for example, in this code, If I modulate the synthdef with freq or amp, how do I apply modulation in Synthdef?

No. SynthDefs are fixed in structure. “Synth” is a low-level abstraction for a server Synth node and doesn’t have very flexible routing (except for the bus mapping mentioned earlier).

Also note here you’ve defined \freq as kr, so it can’t accept an ar input directly. (ar would at best be downsampled to control rate.)

In the last few weeks, I worked on an alternate Syn which can do some of what you want – GitHub - jamshark70/ddwPlug: SuperCollider dynamic per-note synth patching or Quarks.install("https://github.com/jamshark70/ddwPlug") if you have git installed (except Windows).

(
SynthDef(\test, {
	var snd; 
	snd = SinOsc.ar(\freq.kr(100));
	Out.ar(0, snd!2);
}).add;
)

x = Syn(\test);

x.set(\freq, Plug { SinOsc.kr(50).exprange(100, 700) });

x.free;

It uses bus mapping to implement these features, just using an easier programming interface.

hjh

1 Like