Sending an envelope array, via Pattern - wondering about gates

Hi there -

My question is directly related to this thread - but I am not sure if the preference is to bump old threads or simply post anew.

The following code does not rely on the gate trigger - so adding a key for
/sendGate, false
solves for the “node not found” errors.

However, if I’d like to add a releaseNode argument to this code and allow the gate to determine the sustain - I was hoping to get a little advice in figuring out how to refactor the code.

Thank you!

SynthDef(\simpleSine, 
	{ |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], 
			[1, 1.neg, 1, 1.neg]); 
    env = \env.kr(envArray); 
    envSig = EnvGen.kr(env, gate, doneAction: 2); 
		sig = SinOsc.ar(freq, 0, 0.1); 
    Out.ar(out, sig * envSig)
}).add;

Pbindef(\i,
    \instrument, \simplerSine,
	\dur, Pseq([0.2], inf),
    \envData, Pn(Pshuf([ 
		[[0, 1, 1/4, 1, 1/4, 1, 0], [0.02!6], [1, -1, 1, -1]]
    ])),
	\env, Pkey(\envData).collect { |x| Env(*x)}, 
        \gate, 1,
	\sendGate, false,
	\freq, Pseq([200, 300, 400, 500, 600, 700], inf)
).play;

I am not really sure I am undestanding. The above code (when I fix the typo simplerSine vs. simpleSine) works the same for \sendGate, false and \sendGate, true. Since you are spawning a new synth on every event, sending a gate = 1 from a pattern or not sending gate = 1 and relying on default val in synthdef amounts to same behavior, so why is it that you don’t want to rely on sendGate? In either case I dont see any ‘node not found’ messages.

hello, here is what i-ve got.
i managed to send different sustained env-shapes via Pbindef and recreated the Env-Shape you provided with Env.step. Also setting some releaseNodes and loopNodes with Env.step seems to work. Changing back from Env.step to Env.adsr results in ERROR: Message 'at' not understood. though. So not a real solution but maybe it sparks some ideas…

s.boot;

(
SynthDef(\simpleSine,
	{ |out = 0, freq = 440, amp = 0.1, timeScale = 1, gate = 1|
    var sig, env, envArray, envSig;
    envArray = Env(
			// 8 levels values because we have 7 time-values
			[0, 1, 0, 0, 0, 0, 0, 0],
			// 7 time-values, because use of Env.step later.
			[1, 1, 0, 0, 0, 0, 0],
			[1, 1.neg, 1, 1.neg]);
    env = \env.kr(envArray);
    envSig = EnvGen.kr(env, gate, doneAction: 2);
	sig = SinOsc.ar(freq, 0, 0.1);
    Out.ar(out, sig * envSig)
}).add;
)

( // send a sustained envelope .adsr.
Pbindef(\i,
    \instrument, \simpleSine,
	\dur, Pseq([1], inf),
    \envData, [0.02, 0.2, 0.25, 1, 1.0, -4, 0],
	\env, Pkey(\envData).collect { |envdata| Env.adsr(*envdata) },
	\freq, Pseq([200, 300, 400, 500, 600, 700], inf),
).play;
)

// change env-shape
Pbindef(\i, \envData, [0.3, 0.3, 0.5, 2, 1.0, -4, 0])

( // change to Env.asr
Pbindef(\i,
	\envData, [0.5, 1, 0.5, 0],
	\env, Pkey(\envData).collect { |envdata| Env.asr(*envdata) },
)
)

( // change to Env.cutoff
Pbindef(\i,
	\envData, [0.5, 0.5, 0],
	\env, Pkey(\envData).collect { |envdata| Env.cutoff(*envdata) },
)
)

( // Recreation of the original env with Env.step
// Node not found messages
Pbindef(\i,
    \instrument, \simpleSine,
	\dur, Pseq([0.2], inf),
	\envData, [[0, 1, 1/4, 1, 1/4, 1, 0], 0.02!7],
	\env, Pkey(\envData).collect { |envdata| Env.step(*envdata) },
	\freq, Pseq([200, 300, 400, 500, 600, 700], inf),
).play;
)

( // Node not found messages gone, when we change time-values in the Env to 0.05
Pbindef(\i,
	\envData, [[0, 1, 1/4, 1, 1/4, 1, 0], 0.05!7, nil, nil, 0],
	\env, Pkey(\envData).collect { |envdata| Env.step(*envdata) },
)
)


( // Releasenode: 4
Pbindef(\i,
	\envData, [[0, 1, 1/4, 1, 1/3, 1, 0], 0.05!7, 4, nil, 0],
	\env, Pkey(\envData).collect { |envdata| Env.step(*envdata) },
)
)

( // Releasenode: 5
Pbindef(\i,
	\envData, [[0, 1, 1/4, 1, 1/3, 1, 0], 0.02!7, 5, nil, 0],
	\env, Pkey(\envData).collect { |envdata| Env.step(*envdata) },
)
)

( // Releasenode: 6
Pbindef(\i,
	\envData, [[0, 1, 1/4, 1, 1/3, 1, 0], 0.02!7, 6, nil, 0],
	\env, Pkey(\envData).collect { |envdata| Env.step(*envdata) },
)
)

( // Releasenode: 6, loopnode: 1
Pbindef(\i,
	\envData, [[0, 1, 1/4, 1, 1/3, 1, 0], 0.05!7, 6, 1, 0],
	\env, Pkey(\envData).collect { |envdata| Env.step(*envdata) },
)
)

( // Releasenode: 4, loopnode: 1
Pbindef(\i,
	\envData, [[0, 1, 1/4, 1, 1/3, 1, 0], 0.05!7, 4, 1, 0],
	\env, Pkey(\envData).collect { |envdata| Env.step(*envdata) },
)
)

( // Releasenode: 5, loopnode: 2
Pbindef(\i,
	\envData, [[0, 1, 1/4, 1, 1/3, 1, 0], 0.05!7, 5, 2, 0],
	\env, Pkey(\envData).collect { |envdata| Env.step(*envdata) },
)
)

( // Going back to Env.adsr results in Error :(
Pbindef(\i,
    \envData, [0.02, 0.2, 0.25, 1, 1.0, -4, 0],
	\env, Pkey(\envData).collect { |envdata| Env.adsr(*envdata) },
);
)

ERROR: Message 'at' not understood.
Perhaps you misspelled 'as', or meant to call 'at' on another receiver?
RECEIVER:
   Float 0.020000   47AE147B 3F947AE1
ARGS:
   Integer 0
KEYWORD ARGUMENTS:
PATH: /Users/mstep/2025-12-27 Sending an env array via Pbindef.scd

PROTECTED CALL STACK:
	Meta_MethodError:new	0x1382e7e40
		arg this = DoesNotUnderstandError
		arg what = nil
		arg receiver = 0.02
	Meta_DoesNotUnderstandError:new	0x1382ea580
		arg this = DoesNotUnderstandError
		arg receiver = 0.02
		arg selector = at
		arg args = [0]
		arg keywordArgumentPairs = []
	Object:doesNotUnderstand	0x13814bb40
		arg this = 0.02
		arg selector = at
		arg args = nil
		arg kwargs = nil
	Meta_Env:step	0x138d138c0
		arg this = Env
		arg levels = 0.02
		arg times = 0.2
		arg releaseNode = 0.25
		arg loopNode = 1
		arg offset = 1.0
	a FunctionDef	0x138b38340
		sourceCode = "<an open Function>"
		arg inval = ('instrument': simpleSine, 'dur': 1, 'envData': [0.02, 0.2, 0.25, 1, 1.0, -4, 0])
		var nextval = [0.02, 0.2, 0.25, 1, 1.0, -4, 0]
	a FunctionDef	0x138b56d80
		sourceCode = "<an open Function>"
	a FunctionDef	0x140b02340
		sourceCode = "<an open Function>"
	Function:prTry	0x138520d40
		arg this = a Function
		var result = nil
		var thread = a Routine
		var next = nil
		var wasInProtectedFunc = true

CALL STACK:
	DoesNotUnderstandError:reportError
		arg this = <instance of DoesNotUnderstandError>
	Nil:handleError
		arg this = nil
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError
		arg this = <instance of Thread>
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError
		arg this = <instance of Routine>
		arg error = <instance of DoesNotUnderstandError>
	Object:throw
		arg this = <instance of DoesNotUnderstandError>
	Function:protect
		arg this = <instance of Function>
		arg handler = <instance of Function>
		var result = <instance of DoesNotUnderstandError>
	Routine:prStart
		arg this = <instance of Routine>
		arg inval = 14.0
^^ ERROR: Message 'at' not understood.
Perhaps you misspelled 'as', or meant to call 'at' on another receiver?
RECEIVER: 0.02



1 Like
Meta_Env:step	0x140b338c0
		arg this = Env
		arg levels = 0.02
		arg times = 0.2
		arg releaseNode = 0.25
		arg loopNode = 1
		arg offset = 1.0
	a FunctionDef	0x160a30340
		sourceCode = "<an open Function>"
		arg inval = ('instrument': simpleSine, 'dur': 0.2, 'envData': [0.02, 0.2, 0.25, 1, 1.0, -4, 0])
		var nextval = [0.02, 0.2, 0.25, 1, 1.0, -4, 0]

hm. somehow the arguments meant for Env.adsr get interpreted as arguments to Env.step
seems like here you can change from Env-shapes where you use a flat Array for \envData to Env-shapes where you use a two-dimensional Array but not back again. The values of a flat Array of arguments get fed into Env.step, which expects a two-dimensional Array. Right?

\sendGate is not about sending attack gates, but rather about sending a release gate = 0 message.

The default behavior is: If the SynthDef defines a gate input, then it assumes that the envelope is waiting for a [/n_set, nodeID, \gate, 0] message to release – so it sends that message. If the SynthDef doesn’t define a gate input, then it assumes that the envelope is timed, and will end on its own – so it doesn’t send a release message.

This is a little tricky with variable envelope definitions. You can create a SynthDef with a gate input, then send it a timed envelope that ignores gate. If this timed envelope is shorter than the event’s sustain time, then the node will be gone by the time the release message arrives → “node not found.”

The event doesn’t know how you are representing the envelope, so there’s no way for the event to determine automatically whether it needs to send a release or not.

So the design is – you defined the envelope, so you know whether the release message is needed or not. And you can set the \sendGate event property appropriately.

So when you have “// Recreation of the original env with Env.step // Node not found messages” – this is because of changing from a sustaining envelope to a timed envelope, without setting \sendGate.

That is quite weird – I would have assumed that setting, in Pbindef, both envData and env in the same instruction would change them in the pattern at the same time. But, in at least one cycle, it has changed envData without changing env.

It’s probably related to the Pbindef’s quant (but even there, I would have assumed that quant would apply identically to all parameters).

Even as “the pattern guy,” I have to admit that I don’t understand the EventPatternProxy family well enough to understand why its behavior is counterintuitive here (and, probably, buggy).

hjh

1 Like

actually \envData plus \env, \Pkey(\envdata).collect... is not needed. You can send the Envelope directly in the \env-Key wrapped in an Array. Then changing from Env.step to Env.adsr in the Pbindef works.
I didn-t manage to work out the right combination of setting the doneAction of the EnvGen (seems you could do this in a Synth-Def argument) and setting \sendGate accordingly. Things started to get weird when setting the releasenodes, but i guess thats because of my limited understanding at this point.
EDIT: Workedt it out but Env.step behaves counterintuitive here to me. Allthough it-s a sustained env according to the help file you get rid of the “node not found” messages when setting \sendGate, false. But when i start to set the releaseNode arguments to non-nil values, i need to set \sendGate, true again otherwise the synths don-t get freed (guess that-s because of my naive use of the releaseNode and loopNode values).

Updated code below

s.boot;

(
SynthDef(\simpleSine,
	{ |out = 0, freq = 440, amp = 0.1, timeScale = 1, gate = 1|
    var sig, env, envSig;
	var envArray = Env.newClear(8);
    env = \env.kr(envArray);
    envSig = EnvGen.kr(env, gate, doneAction: 2);
	sig = SinOsc.ar(freq, 0, 0.1);
    Out.ar(out, sig * envSig)
}).add;
)

( // send a sustained envelope .adsr.
Pbindef( \i,
    \instrument, \simpleSine,
	\dur, Pseq([1], inf),
	\env, [ Env.adsr(0.02, 0.2, 0.25, 1, 1.0, -4, 0) ],
	\freq, Pseq([200, 300, 400, 500, 600, 700], inf),
).play;
)

// change env-shape
Pbindef(\i, \env, [ Env.adsr(0.3, 0.3, 0.5, 2, 1.0, -4, 0) ])

// change to Env.asr
Pbindef(\i, \env, [ Env.asr(0.5, 1, 0.5, 0)] )

// change to Env.cutoff
Pbindef(\i, \env, [ Env.cutoff(0.5, 0.5, 0)])

// change to Env.perc
Pbindef(\i, \sendGate, false, \env, [ Env.perc(0.01, 0.5) ])

// change to Env.sine
Pbindef(\i, \env, [ Env.sine(0.3, 0.5) ])

( // Recreation of the original env with Env.step
Pbindef(\i,
    \instrument, \simpleSine,
	\dur, Pseq([0.2], inf),
	\env, [ Env.step([0, 1, 1/4, 1, 1/4, 1, 0], 0.02!7) ],
	\freq, Pseq([200, 300, 400, 500, 600, 700], inf),
	\sendGate, false, 
).play;
)

// change time-values in the Env to 0.05
Pbindef(\i, \env, [ Env.step([0, 1, 1/4, 1, 1/4, 1, 0], 0.05!7, nil, nil, 0) ])

// Releasenode: 4
Pbindef(\i, \sendGate, true, \env, [ Env.step([0, 1, 1/4, 1, 1/3, 1, 0], 0.05!7, 4, nil, 0) ])

// Releasenode: 5
Pbindef(\i, \env, [ Env.step([0, 1, 1/4, 1, 1/3, 1, 0], 0.02!7, 5, nil, 0) ])

// Releasenode: 6
Pbindef(\i, \env, [ Env.step([0, 1, 1/4, 1, 1/3, 1, 0], 0.02!7, 6, nil, 0) ])

// Releasenode: 6, loopnode: 1
Pbindef(\i, \env, [ Env.step([0, 1, 1/4, 1, 1/3, 1, 0], 0.05!7, 6, 1, 0) ])

// Releasenode: 4, loopnode: 1
Pbindef(\i, \env, [ Env.step([0, 1, 1/4, 1, 1/3, 1, 0], 0.05!7, 4, 1, 0) ])

// Releasenode: 5, loopnode: 2
Pbindef(\i, \env, [ Env.step([0, 1, 1/4, 1, 1/3, 1, 0], 0.05!7, 5, 2, 0) ])

// change to Env.circle
Pbindef(\i, \env, [ Env.circle([0, 1, 1/4, 1, 1/4, 1, 0], 0.05!7, [1, -1]) ])

// Going back to Env.adsr works :)
Pbindef(\i, \env, [ Env.adsr(0.02, 0.2, 0.25, 1, 1.0, -4, 0) ]);

Pbindef(\i).stop
1 Like

The help for Env.step describes the releaseNode argument as “an Integer or nil.”

The Env is sustaining only if you supply a releaseNode. If releaseNode is nil, then by definition it can’t be sustaining anymore. (Where should it sustain, if releaseNode is nil?)

In your usage, you haven’t specified a releaseNode – so there is none – so release messages are irrelevant and sendGate should be set to false.

Referring to my earlier explanation, I think this behavior should have been expected. If it has a releaseNode then the gate-off message is not optional.

If, in the Pbindef, you had previously set sendGate to false, then you can’t set it to nil in the Pbindef (I believe it would stop in that case, could be wrong though). So \sendGate, true is the right thing in that case.

hjh

Ah, ok! Makes sense. Thank you for the explanation, @jamshark70.

Also we should probably update the Env help file to make it more clear that step may be either timed or sustaining, depending on the releaseNode value.

step and new are the only constructors that can vary in this way:

Meta_Env.methods
.select { |m| if(m.argNames.notNil) { m.argNames.includes(\releaseNode) } { false } }
.collect(_.name)
-> [new, step]

hjh

1 Like