Evaluation a function with parameters

Hi,

I wrote a function taking 2 parameters (2 envelops) and returning a UGen, and two envelops:

~stomper = {
   arg envFreq, envAmp;
   SinOsc.ar(EnvGen.kr(envFreq),0,EnvGen.kr(envAmp));
};

~envF1=Env.new(levels: [220, 440], times: [2], curve: [-15]);
~envA1=Env.new(levels: [0.8, 0.8, 0.2], times: [1,1], curve: [0,10]);

I wonder why do I need to add brackets before “playing” the function:

~stomper.value(~envF1,~envA1).play; // KO
{ ~stomper.value(~envF1,~envA1) }.play; // OK

If the function had no parameters, I would need to write it this way :

~stomper.play; // OK
~stomper.value.play; // KO
{~stomper.value}.play; // OK

Can someone explain we what’s the difference in those 3 lines differs ? Why the brackets are needed ? Doesn’t the function return in all case a UGen instance that could be “played” without any additional bracket ?

play is a method on Functions, and not on UGens. UGens (SinOsc, LFSaw, etc.) can be used to form a synthdef graph – the blueprint for a synth running on the server – but you cannot play a single UGen by itself.

when you add brackets around a bit of code, that whole block of code, the brackets and whatever’s inside, are a Function.

looking at your code examples in more detail:

~stomper.play; // a Function, can call `play`

// `value` evaluates the function stored in ~stomper, and then calls `play` on whatever the result of that
// function is. Since the last expression in the ~stomper Function is `SinOsc.ar(...)`, and the result of
// `SinOsc.ar(/* anything */)` is always a SinOsc, we end up calling `play` on a `SinOsc`, which won't work.
~stomper.value.play;

// a Function, can call `play`. generally, people don't write synthdef graphs this way because they can 
// just write ~stomper.value, but it does work.
{~stomper.value}.play; 

some relevant documentation:

hope this helps!

Hi, Thanks. It definitely helped.
To go a little bit further : when I try to post some debug info, I also get unexpected results.

~testPostSynth = { 
	arg freq = 440;
	"freq = % \n".postf(freq);
	SinOsc.ar(freq, 0, 0.3)
};

~testPostSynth.play // KO; ==> prints "freq = an OutputProxy" 
{~testPostSynth.value}.play //OK;   ==> freq = 440 
{~testPostSynth.value(220)}.play //OK;  ==> freq = 220 

Why do I have this “an OutputProxy” printed ? What are the consequences of this (my function is definitely using the right default value).

If you want to be able to do this…

a = ~testPostSynth.play;

// later
a.set(\freq, 880);

… then the OutputProxy is necessary and correct.

A value within a SynthDef may change only if it’s a UGen. If it’s not a UGen, then it will be a fixed, hardcoded value. In that case, there would be no point in using an argument at all.

So the SynthDef builder does this:

  1. Before evaluating the function, it scans the function’s declared arguments and collects information about default values and argument rates.

  2. Then it builds Control UGen instances reflecting all of the arguments.

  3. Control has multiple output channels (one channel per argument value), so it creates an OutputProxy for each distinct value.

  4. Then it evaluates the function, passing the OutputProxies as arguments.

Without this, you can’t set the argument value later – so I’d say the KO and OK assessments are backward in your example. The ones you labeled as “OK” are less useful and the “KO” one is the desired behavior.

hjh

If I understand you right, the 2 :

{~testPostSynth.value}.play //OK;   ==> freq = 440 
{~testPostSynth.value(220)}.play //OK;  ==> freq = 220 

erase the parameter \freq by some value (the default 440 in the first case, 220 in the second case), making me loose the possibility to control the SynthDef later. Correct ?

That’s right.

SynthDef.wrap can embed a function into a SynthDef and expose the inner function’s arguments as controls, but that may be overkill for this simple case.

hjh

Don’t forget that you can pass arguments to the play function that will be passed on to the generated synth in which case you will be able to change them later

{ ... }.play(..., args: [\freq, 220], ...)