Skip retriggering with incomplete release?

Is there an elegant or recommended way to ignore a trigger/gate signal when a monophonic envelope’s release phase has not finished? I want to wait to reach the final before it is “ready” to receive more triggers/gates.

I managed to wire something up with LocalIn/LocalOut and a DetectSilence on the envelope itself, but it felt hacky and the delay introduced by the local bus caused timing problems.

Just confirming, is it a gated envelope?

If you know the duration that the envelope should be closed to new triggers, you can measure the time using Sweep.kr(trig, 1) (or .ar) – if it’s a gated envelope, then the timer should start when releasing, so Sweep.kr(gate <= 0, 1).

So the “gate mask” would be:

  • Triggered envelope: Sweep.kr(trig, 1) > totalEnvDuration;
  • Gated envelope: Sweep.kr(gate <= 0, 1) > releaseDuration.

But you’ll need another condition for the first one, so I guess it’ll be like this (untested), for a trigger:

// in the SynthDef
arg ......., t_trig, ......;
var trigMask = ((PulseCount.kr(trig) < 0) + (Sweep.kr(trig, 1) > totalDur)) > 0,
eg = EnvGen.kr(env..., t_trig * trigMask, ...);

+ is a logical-or here, and * is logical-and: the trigger will be passed (“and true”) if it’s never been triggered before or enough time has passed, and suppressed (“and false”) otherwise.

That’s a good way to do it. LocalIn/Out delay should be on the order of 1.5 ms only. I’d expect DetectSilence to be too slow for this purpose: why not just eg <= 0?

hjh

1 Like

In case the env is not gated and its duration is known (james’ first case), there is also a simple implementation with Trig1:

Ndef(\noSus){|envDur=1|
    // Trig1 ignores subsequent trigs for envDur secs after each trig
    var trig = Trig1.kr(\trig.tr(0),envDur);
    EnvGen.kr(Env.perc, trig, timeScale:envDur);
}.scope
1 Like

I tried the simpler one first and it worked beautifully. Thank you both!

Ah, I didn’t realize DetectSilence might be what was slowing things down. I tried something along the lines of the eg <= 0 you mentioned, but I got an error about using a Boolean in a Synth.

FYI, for my purposes, it didn’t matter whether it was gated. I could have gone either way.

eg <= 0 gives you a signal that is 1 when the envelope is off, and 0 when it’s active.

You want triggers to be suppressed when the envelope is active.

So, trig * (LocalIn.kr(1) <= 0) means… envelope not active, triggers OK, trig * 1… envelope active, triggers not OK, trig * 0. if is not needed (and shouldn’t be used) here.

hjh

1 Like
(
SynthDef(\trig_eg, { |out, t_trig, time = 4, freq = 440, amp = 0.1|
	var sig = SinOsc.ar(freq) * amp,
	feedbackEg = LocalIn.kr(1),
	maskedTrig = t_trig * (feedbackEg <= 0),
	eg = EnvGen.kr(Env.perc(0.01, 0.99), maskedTrig, timeScale: time);
	LocalOut.kr(eg);
	t_trig.poll(t_trig * (maskedTrig <= 0), "trigger rejected");
	Out.ar(out, (sig * eg).dup);
}).add;
)

a = Synth(\trig_eg);
a.set(\t_trig, 1);  // first should play

a.set(\t_trig, 1);  // second, while note is sounding
trigger rejected: 1

a.set(\t_trig, 1);  // third, when silent, no problem

hjh