ADSR - How to Trigger Envelope?

bcgreen24
Posts: 13
Joined: Mon Oct 04, 2021 9:25 pm

ADSR - How to Trigger Envelope?

Post by bcgreen24 »

In my ProcessSample() function I have this:

if(inputJackGate.GetValue() > 0.0){
envelope.SetGate(true);
}

txtDebug.SetText(String.valueOf(envelope.GetValue()));
envelope.AdvanceSample();

...which I would expect to display the envelope's value via the label 'txtDebug' when a signal is applied to 'inputJackGate'. However--- when a gate signal is applied, I see '5.0', and when the gate is 'released', I see '0.0' (rather than changing values). Can someone share a code snippet that shows how to use the ADSREnvelope class? The documentation/example modules are sorely lacking this information.

Thanks!
Bryan
bcgreen24
Posts: 13
Joined: Mon Oct 04, 2021 9:25 pm

Re: ADSR - How to Trigger Envelope?

Post by bcgreen24 »

Also, this is one of several unanwered/partially answered questions on the forum regarding the ADSR envelope...it would be nice if the devs chimed in. :)

Bryan
ColinP
Posts: 939
Joined: Mon Aug 03, 2020 7:46 pm

Re: ADSR - How to Trigger Envelope?

Post by ColinP »

Hi,

I think it's perhaps best to code envelopes from scratch rather than use the VM class because it's far easier than trying to understand code that you can't see and I'm not a fan of the way ADSR is handled in VM anyway as the attack phase aborts as soon as the gate drops so they are useless when driven by a trigger.

My advice is to start from first principles and simply use ProcessSample() as your source of timing. Forget about fancy curves and simply build a linear version. You can add fancy curves later.

So, you could use an int as a state variable - say 0 = off, 1 = attack, 2 = decay, 3 = sustain, 4 = release or whatever - I'm bored with ADSR so would like people to be a bit more imaginative.

In ProcessSample() look for a trigger/gate rising edge and set the state variable to attack. Then just use a counter to track the number of calls to ProcessSample() until it reaches the number of samples you want the attack to last, then set the state to decay and start counting again.

Simple logic and counting in 1/48,000 second steps enables you to produce any manner of envelope behaviours.

The counter values just track timing so do a parallel additive iteration using floating point scaled to the desired output values you want.

Once you've got a linear envelope working you can adjust the algorithm for fancy curves using polynomials or whatever strategy you want.
bcgreen24
Posts: 13
Joined: Mon Oct 04, 2021 9:25 pm

Re: ADSR - How to Trigger Envelope?

Post by bcgreen24 »

Sounds good...I'll give that a go.

Thanks!
Bryan
Cherry Dan
Site Admin
Posts: 256
Joined: Fri Jul 27, 2018 5:36 pm

Re: ADSR - How to Trigger Envelope?

Post by Cherry Dan »

bcgreen24 wrote: Wed Oct 06, 2021 7:57 pm However--- when a gate signal is applied, I see '5.0', and when the gate is 'released', I see '0.0' (rather than changing values).
Your code should work fine, but chances are you haven't set an attack or release time. In your Initialize() function, add:

envelope.SetAttackTime(2500.0);
envelope.SetReleaseTime(5000.0);

..then run your project again, and you should see the numbers correctly rise and fall.

Also -- this test is fine for debugging, but in an actual module, you shouldn't be altering a text field in the ProcessSample() function. GUI operations like that should be done in the GUI_Update_Timer notification. So, in your Initialize function, add a call to StartGuiUpdateTimer(). Then move your SetText() call to the "case GUI_Update_Timer:" section of the Notify() function.

Now you'll have a module that operates the exact same way, but it won't be trying to modify the GUI in the time-critical ProcessSample() function.
I'm not a fan of the way ADSR is handled in VM anyway as the attack phase aborts as soon as the gate drops so they are useless when driven by a trigger.
Really, it's quite easy to use the envelope generator with a trigger. All you'd have to do to accomplish this is something like:

if (!gate && envelope.GetStage() != ADSREnvelope::ENV_STAGE::ADSR_Stage_Attack)
envelope.SetGate(false);

..that way you delay turning off the envelope's gate until it's completed the Attack stage.

- Dan @ Cherry Audio
bcgreen24
Posts: 13
Joined: Mon Oct 04, 2021 9:25 pm

Re: ADSR - How to Trigger Envelope?

Post by bcgreen24 »

Yeah, I was wondering about updating the text in ProcessAudio() (even though it's just a test)-- however, I had seen an LED being updated in ProcessAudio() in one of the SDK demo modules (the LFO demo).


And, yeah, I flaked out on actually setting an attack/release/etc. time; I'm setting variables for that when the knobs are twiddled, but haven't applied them to the ADSR stages yet. :)

Bryan
bcgreen24
Posts: 13
Joined: Mon Oct 04, 2021 9:25 pm

Re: ADSR - How to Trigger Envelope?

Post by bcgreen24 »

OK, I have things almost working. Here's my ProcessSample() code so far:

Code: Select all

 if(outputJack.IsConnected()){

     if(inputJackGate.GetValue() > 0.0){
       envelope.Reset();
       envelope.SetGate(true);
     }
   
     if(inputJackGate.GetValue() <= 0.0 && envelope.GetStage() == ADSREnvelope.ENV_STAGE.ADSR_Stage_Sustain){
       envelope.SetGate(false);
     }
   
     if(envelope.GetStage() == ADSREnvelope.ENV_STAGE.ADSR_Stage_Off) {
       envelope.Reset();
     }

     if(inputJackV.IsConnected()){
       cv = Math.pow(2, inputJackV.GetValue() + 0.25) * 55.0;
     }
     
    myosc.SetFrequency(cv + freq);
    outputJack.SetValue(myosc.GetSawtoothValue() * envelope.GetValue());
    myosc.AdvanceSample();
    envelope.AdvanceSample();
   }
The problem is that even though I have the envelope's AttackHoldTime set to 0.0, the envelope gets 'stuck' at the 'Attack_Hold' stage of the envelope. I hold down a key, the envelope goes to the Attack_Hold stage, and I don't hear a sound until I release the key...any ideas, anyone?

Thanks!
Bryan
ColinP
Posts: 939
Joined: Mon Aug 03, 2020 7:46 pm

Re: ADSR - How to Trigger Envelope?

Post by ColinP »

Hi Bryan,

My guess would be that the problem might be because you reset the envelope every sample when the gate is high...

Code: Select all

     if(inputJackGate.GetValue() > 0.0){
       envelope.Reset();
       envelope.SetGate(true);
     }
...rather than doing a rising edge detection.

By rising edge detection I mean something along these lines...

Code: Select all

	if( input > THRESHOLD )
	{
		// gate is high
		if( edgeDetected == false )
		{
			edgeDetected = true;
			reset();
		}
	}
	else
	{
		// gate is low
		edgeDetected = false;
	}
So reset() is only called once when the gate transitions from low to high.

But I have never used this class and have no idea about its internal operation so this is all just conjecture.
ColinP
Posts: 939
Joined: Mon Aug 03, 2020 7:46 pm

Re: ADSR - How to Trigger Envelope?

Post by ColinP »

Cherry Dan wrote: Thu Oct 07, 2021 12:06 am
I'm not a fan of the way ADSR is handled in VM anyway as the attack phase aborts as soon as the gate drops so they are useless when driven by a trigger.
Really, it's quite easy to use the envelope generator with a trigger. All you'd have to do to accomplish this is something like:

if (!gate && envelope.GetStage() != ADSREnvelope::ENV_STAGE::ADSR_Stage_Attack)
envelope.SetGate(false);
Hi Dan,

It's always great to see you getting involved in the forum.

But actually that part of my comment was about how the Envelope Generator modules behave rather than coding technique using the class. Although presumably the modules are built on the class. Sorry, my phrasing was totally rubbish as the "they" was meant to refer to the modules rather than the class and it wasn't at all clear.

Terminating the attack phase when a gate drops is a perfectly reasonable design choice especially when the gate is from a keyboard and the attack is slow as you can generate nice crescendo effects by repeatedly pressing a key for increasing lengths of time, but being able to use triggers to fire envelope generators is also very useful. Personally, if the module doesn't offer the user an option of one or the other then I'd opt for making triggers work.

I had hoped that the Percussion-EG would do the trick but it can't produce a slow attack from a trigger signal. So the only option appears to be to use the Trig to Gate module to convert a trigger to a gate, but this is far from easy as getting the gate time to match the attack time is incredibly fiddly.
bcgreen24
Posts: 13
Joined: Mon Oct 04, 2021 9:25 pm

Re: ADSR - How to Trigger Envelope?

Post by bcgreen24 »

I was using a bool to check for 'gate active/gate non active' but was havning issues--- but I see that it is definitely necessary, so I did some tweakin' and made it work:

Code: Select all

 // add your own code here
     if(outputJack.IsConnected()){
     if(inputJackGate.GetValue() > 0.0 && gateSet == false){
       envelope.SetGate(true);
       gateSet = true;
     }
   
     if(inputJackGate.GetValue() <= 0.0 && envelope.GetStage() == ADSREnvelope.ENV_STAGE.ADSR_Stage_Sustain){
       envelope.SetGate(false);
     }
   
     if(envelope.GetStage() == ADSREnvelope.ENV_STAGE.ADSR_Stage_Off) {
       envelope.Reset();
       gateSet = false;
     }
     
     if(inputJackV.IsConnected()){
       cv = Math.pow(2, inputJackV.GetValue() + 0.25) * 55.0;
     }
     
     myosc.SetFrequency(cv + freq);
     outputJack.SetValue(myosc.GetSawtoothValue() * envelope.GetValue());
     myosc.AdvanceSample();
     envelope.AdvanceSample();
   }
...so now the only problem is now i can't trigger a new note until after the envelope is complete; i.e., if i press a key during the 'decay' or 'sustain' stage, nothing happens, and the original envelope continues until the end, and THEN i can press a key to generate a new note (envelope)...

And to all of you-- thanks for helping a DSP newb out! :)
Post Reply

Return to “Module Designer”