Proving Env or UGen as Synth parameter

Is it possible to pass a UGen as SynthDef parameter for, e.g., controlling its freq ?

(
SynthDef(\test, { 
	arg freq=440;
    Out.ar(0,
		SinOsc.ar(freq) * EnvGen.kr(Env.new(levels: [0.5, 0.8, 0], times: [0.2,3.8], curve: [1]), doneAction: Done.freeSelf)
    )
}).add;
)

u=EnvGen.kr(Env.new(levels: [220, 440], times: [4], curve: [-15]));
a = Synth(\test,[\freq, u]); // KO
a = Synth(\test,[\freq, 156]); // OK

No, this will never be supported.

A SynthDef is a fixed, unchanging network of UGens. The only way to add/insert/replace UGens is to make a new SynthDef (and this won’t affect existing synths).

What you can do instead is play the control signal onto a bus and map the synth control to the bus. http://doc.sccode.org/Classes/Bus.html#-asMap is an easy way.

hjh

You can however, pass in an envelope! (Your title mentioned this, but the question didn’t). Look under Env.newClear on the help file. Ask back if you get stuck!

On a side note, you could make a function that defines a synthdef, that way you can pass in what ever you like, but only at compile, before you add it to the server (sclang, not scsynth). I often do this when I don’t know how many input buses or what size they will be untill I’ve finished writing the piece.
J

Great !!

Anyway to have the doneAction having some effect?

(
c= Bus.control(s,1);
a = Synth(\test);
h= {Out.kr(
	c.index, 
	EnvGen.kr(
		Env.new(levels: [220, 440], times: [10], curve: [0])
		, doneAction: 2)
	)}.play;
a.map(\freq, c.index);
)

Thanks. I did that already. But I wanted my SynthDef to be even more agnostic.

The doneAction will take effect on the envelope synth.

If you use doneAction: 2, this is defined as “free this synth” but not any other synth. So naturally it would not affect the synth making the sound.

If you want it to stop the other synth too, you’d need to create them to be adjacent (see addAfter or addBefore addActions), and use a different doneAction such as “free this synth and the one after it.” I forget the number – see help for Done.

Also, I’d recommend not to hardcode the bus index. Use an argument for the output bus number (out is the convention for this).

hjh

Thanks.

Just posting my code, in case it inspires some other newcomer:

(
// -- One group for the synth and its envelops
~g1=Group.new;
// -- The Synth, placed in ~g1
~s1=Synth.new(\mySynth, target: ~g1);
// -- The frequency envelop, placed in ~g1. No doneAction
~cf1= Bus.control(s,1);
{Out.kr(
	~cf1.index,
	EnvGen.kr(~envF1)
	)}.play(target: ~g1);
~s1.map(\freq, ~cf1.index);

// -- The amplitude envelop, placed in ~g1, with doneAction set to Done.freeGroup
~ca1= Bus.control(s,1);
{Out.kr(
	~ca1.index,
	EnvGen.kr(~envA1, doneAction: Done.freeGroup)
	)}.play(target: ~g1);
~s1.map(\amp, ~ca1.index);
)

How to do the release of such an Envelop (and therefore the Synth) ?

// -- The amplitude envelop, placed in ~g1, with doneAction set to Done.freeGroup
~ca1= Bus.control(s,1);
{Out.kr(
	~ca1.index,
	EnvGen.kr(Env.adsr(5,0.1,0.5,5,0.8), ~gate1, doneAction: Done.freeGroup, doneAction: Done.freeGroup)
	)}.play(target: ~g1);
~s1.map(\amp, ~ca1.index);

How to access the gate property of the EnvGen ? Or how to command for release ?

In all the examples I watched, the release is at the level of Synth, which contains the Envelop. Here with the envelop being outside of the Synth, how to release ?

~gate1 = 0; // release the envelop -- KO
~s1.release; // release the Synth directly, not taking into account the envelop -- KO, do nothing
~g1.release; // release the Group directly, not taking into account the envelop -- KO, do nothing

Synth arguments are the solution.

So far, you are rigorously avoiding the use of synth arguments – but they are really essential.

http://doc.sccode.org/Tutorials/Getting-Started/10-SynthDefs-and-Synths.html#Creating%20Variety%20with%20SynthDefs

hjh

I guess I’m indeed deviating from SC essence.
Actually my goal is to work in an Object-Oriented way at the level of the SynthDef in order to build SynthDef and Synth upon a core versions. And it seems SC is not meant that way. So, I will see two other options:

  1. Define my “core” SynthDef as a Function and incorporated it in smaller SynthDef through “wrap” function.
  2. Define my own UGen type implementing my core mechanism and use it in smaller SynthDef.(I don’t if this is possible. I need to investigate it).

Do you see other options ?

If you have a value that you want to use in the server, and you will need the value to change in the server, then the value must be represented by a unit generator. No exceptions to that rule.

Also, UGen objects must be created within the SynthDef being built. You can’t create a UGen in some arbitrary code location and reference it in a SynthDef. No exceptions to that rule either.

~gate1 = 1 is not a UGen. So if you use ~gate1 in a SynthDef, then it’s a hardcoded 1. It will never change. So you can’t use it to close the gate.

If your SynthDef has a gate argument, this is converted internally to a channel of a Control UGen. So then it is a signal that can change after the fact. Control channels are changed by set messages.

You can inject new values into a synth by Control arguments, or control buses. There aren’t any other ways (except maybe Buffer, but that’s a lot less convenient, not worth considering). You’ll have to adapt your planned design to this methodology.

hjh

I worked with a new UGen class and I found little issue with that approach:

StomperOsc : UGen {
	*ar {
		arg freq = 440, amp = 0.5, form = 0, distort = 0, noise = 0, gate = 1;

		var sig, snoise; // UGen
		var rq, freqd; // variables
		var env; //Envelop with gate

		// rq: 0.0001: presque pur, 1 bruit complet
		rq=noise.linlin(0,1,0.0001,1,\minmax);
		freqd=freq*(1+LFNoise1.kr(500,distort));

		sig = SinOsc.ar(freqd).pow(form.linexp(0,1,1,20,nil));
		// amplitude compensation: 1/rq trop peu dans noise = 1, 1/rq.sqrt trop peu dans noise = 0
		snoise=Resonz.ar(PinkNoise.ar(1),freqd,rq,2/rq.pow(1,5).sqrt);

		sig=XFade2.ar(sig,snoise,noise.linlin(0,1,-1,1));

		sig = sig * amp * EnvGen.kr(Env.asr(),gate);
		^sig;
	}
}

And now I can use this new UGen as basis for my Synth/SynthDef:

// -exemple 2: Noise+Distort
// ** define the SynthDef **
(
SynthDef(\s2, {
	arg out = 0, // specific
	freq= 440, amp= 0.5, form = 0, gate = 1;
	var sig = StomperOsc.ar(
		freq: freq, form: form, gate: 1, // I force gate = 1 to bypass the default gate and use only the specific one
		amp: EnvGen.kr(Env.asr(20,1,10), gate, doneAction: Done.freeSelf) * amp,
		noise: LFNoise0.kr(LFNoise0.kr(10).range(0.25,2)).range(0,0.8),
	    distort: LFNoise0.kr(LFNoise0.kr(10).range(0.25,2)).range(0,0.8),
	);
	Out.ar(out, sig);
}).add;
)

// ** play **
( r = Routine(
    { ~s2=Synth.new(\s2,args: [\out,1]);})
.play(SystemClock);
)
~s2.set(\freq,320);
~s2.release;