Envelope re-trigger , attack time >impulse rate

In the following piece of code you see an envelope that is triggered by an impulse
Both attack, decay of the envelope are set to 0.5s , the impulse has a rate of 1hz .
When I increase the envelope’s decay time so it’s longer then the impulse ( 1hz ) , the envelope retriggers , but this is not true for the attack stage

(
{
	var a,b,menv,multer,bw,triggie;
	bw=88;
	triggie=Impulse.ar(1);
	menv=EnvGen.ar(Env([0,1,0],[0.5,0.5],[0,-20]),gate:triggie,doneAction:0);
	multer=menv*10000;
	a=Formant.ar([110,108],multer,bw);
	a*0.3
}.
play)
////
(
{
	var a,b,menv,multer,bw,triggie;
	bw=88;
	triggie=Impulse.ar(1);
	menv=EnvGen.ar(Env([0,1,0],[0.5,3],[0,-20]),gate:triggie,doneAction:0);//decay increased to 3 second envelope still retriggeres 
	multer=menv*10000;
	a=Formant.ar([110,108],multer,bw);
	a*0.3
}.
play)
/////
(
{
	var a,b,menv,multer,bw,triggie;
	bw=88;
	triggie=Impulse.ar(1);
	menv=EnvGen.ar(Env([0,1,0],[3,1],[0,-20]),gate:triggie,doneAction:0);//attack increased  to 3 seconds, envelope does not re-trigger
	multer=menv*10000;
	a=Formant.ar([110,108],multer,bw);
	a*0.3
}.
play)
////

In the last example (attack time = 3), what is the behavior you expected?

hjh

Since the attack time is 3 seconds , and the impulse is is 1hz , I assumed it would retrigger after 1 second = the duration of the impulse

But… what do you mean by “retrigger”? Be specific. Clearly there is something in your mind for the concept of “retrigger,” and the synth isn’t doing that.

So what is the thing that it should be doing so that you would then say “ah, it’s retriggering”?

hjh

You can also isolate the envelope shape:

s.boot;

b = Buffer.alloc(s,  44100/64*5,  1);

(
{
	var a, /*b, */menv, multer, bw, triggie;
	bw = 88;
	triggie = Impulse.kr(1);
	menv = EnvGen.kr(Env([0, 1, 0], [3, 1], [0, -20]), gate: triggie, doneAction: 0);
	RecordBuf.kr(menv, b, loop: 0, doneAction: 2);
	Silent.ar(1)
}.play.onFree({ defer { b.plot } });
)

envelope-trig-during-attack

So the trigger is taking effect (otherwise it would just be a straight line).

hjh

All righty then.

I did have a point by asking that question.

Many programming problems are the result of not defining the problem in enough detail – of assuming that x or y or z will “just be done” in the way that I want.

“Retrigger my envelope” is not a precise enough specification. I’m guessing (guessing – because, nowhere in this thread did you say it specifically) that when you say “retrigger,” what you mean is that the envelope should go back to its initial level and restart from there.

But even this is not precise enough.

How long should it take to go back?

If you’re thinking it should be instantaneous – discontinuities in signal processing are bad. You don’t want an envelope to jump immediately from one level to another level (unless the user specifically asked for an envelope segment with duration 0).

So… if the jump time is not 0, how long? Should the envelope generator just make up a duration for you? 2 ms? 5 ms? 20 ms? 100 ms?

Which is meant to suggest that this is not the right approach.

Envelope generators avoid discontinuities by defining an envelope segment: “start from where you are now, and proceed toward the target in the requested amount of time.”

Consider an ADSR envelope with a long attack, sustain at 1, and a short release – and the gate closes in the middle of the attack segment. This graph shows:

  1. The gate signal.
  2. An ADSR where each segment starts from the current level (envelopes’ actual behavior).
  3. An ADSR where each segment starts from the previous level given in the Env – release phase would be “sustain level = 1 --> release level = 0” (the assumed “from-to” definition of a segment).

#2 is, of course, better, because you won’t have any pop when entering the release phase.

I’m pretty sure that the issue here is that you thought the attack segment is “start at 0, go toward 1.” But it isn’t – it’s “start from current level, go toward 1.” (So what about the initial 0? That’s used to initialize the envelope generator once and only once, at the start of the synth.)

So, in your example:

  1. The EG initializes to 0.
  2. Then it begins a ramp from 0 to 1, in 3 seconds.
  3. 1 second later, it receives a trigger. The level is now 1/3.
  4. In response to the trigger, it begins a ramp from 1/3 to 1, in 3 seconds.
  5. 1 second later, it receives a trigger. The level is now 1/3 + ((1 - 1/3) * 1/3) = 5/9.
  6. In response to the trigger, it begins a ramp from 5/9 to 1, in 3 seconds… and so on.

If you want a retrigger to reset to zero, you have to define the reset in the Env.

b = Buffer.alloc(s,  44100/64*5,  1);

(
{
	var a, /*b, */menv, multer, bw, triggie;
	bw = 88;
	triggie = Impulse.kr(1);
	menv = EnvGen.kr(
		// HERE: Short initial segment to 0
		Env([0, 0, 1, 0], [0.002, 3, 1], [0, 0, -20]),
		gate: triggie, doneAction: 0
	);
	RecordBuf.kr(menv, b, loop: 0, doneAction: 2);
	Silent.ar(1)
}.play.onFree({ defer { b.plot } });
)

envelope-trig-during-attack-with-reset

hjh