Sending an Envelope as a NamedControl

Hi -

I am currently trying to send an envelope as a NamedControl. My understanding is that the array size needs to be established within the SynthDef or there will be an error. In this case, the error is “envelope went past end of inputs.”

Is if there is a way to specify an maximum number of envelope points and then have the Env object interpret the incoming number, as long as it is less than the maximum?

Also, is there a simple way to invert the envelope below without retyping it?

Thank you for the help.

SynthDef("sine",
	{
		var sig, env;
		sig = SinOsc.ar(440);
		env = EnvGen.kr(\env.kr(Env.linen(0, 1, 1)), doneAction:2);
		Out.ar(0, sig*env);
}).add;

g = Env.new([0, 1, 0, 1,0], [1, 1, 3, 1]);
z = Synth("sine", [\env, g] );

What you intend is possible, it just works a bit different from how you described it. Env is a language thing, the server can only deal with array args. So all boils down to a topic that comes up in a variety of ways: building a synthdef that can deal with arrays up to a predefined max size.

This is Ex. 4 of miSCellaneous_lib’s tutorial “Event patterns and array args”, sequencing Envs of different length.


(
// NamedControl is recommended in that case as a literal Array of special format would be impractical.
// Define a SynthDef with maximum envelope size you expect to pass

SynthDef(\envArray_1, { |out = 0, freq = 440, amp = 0.1, timeScale = 1, gate = 1|
    var sig, env, envArray, envSig;
    envArray = Env([0, 1, 0, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0]).asArray; // works also without '.asArray'
    env = \env.kr(envArray);  // shortcut for NamedControl.kr(\env, envArray)
    envSig = EnvGen.kr(env, gate, timeScale: timeScale, doneAction: 2); 
    sig = Saw.ar([freq, freq * 2.01], amp); 
    Out.ar(out, sig * envSig)
}).add
)

// Pbind uses event type \on to avoid setting gate to 0 and receiving messages "node not found",
// synths are ended by envelopes anyway.


( 
p = Pbind( 
    \type, Pshuf([\on, \on, \rest], inf), 
    \instrument, \envArray_1, 
    \dur, Pn(Pshuf([1, 1/2, 1/2]), 30), 
    \midinote, Pn(Pshuf((40..80))) + Pn(Pshuf([[0.5, 11.5], [0, 4], [0, -7], [-0.5, -9.5]])),
    
    // envData contains env types, determined by levels and times.
    // Times are only relations, within the synthdef they are scaled by the timeScale arg.
    \envData, Pn(Pshuf([ 
        [[0, 1, 0], [1, 1]], 
        [[0, 1, 1/4, 1, 0], [1, 1, 1, 1]], 
        [[0, 1, 1/4, 1, 1/4, 1, 0], [1, 1, 1, 1, 1, 1]]
    ])),
    
    // *x splits the envData array into levels and times, Env is converted in to an Array automatically
    \env, Pkey(\envData).collect { |x| Env(*x) }, 
    
    // when wanting to pass the Array explicitely it would require
    // wrapping into an additional Array which is necessary for passing:
    // \env, Pkey(\envData).collect { |x| [Env(*x).asArray] }, 
    
    \timeScale, Pfunc { |e| e.dur / e.envData[1].sum } 
).trace.play; 
) 

p.stop;

For inverting an envelope, I’m not aware of an existing tool, you could write a little helper function using the reverse method for arrays.

1 Like

Another formulation is:

\env.kr(
    Env.newClear(maxSegments)
    .overWrite(Env(
         ... define a default envelope here ...
    ).asArray)
)

The default envelope’s size should be <= maxSegments (and maxSegments must be hardwired into the SynthDef – it can’t be an argument).

hjh

1 Like