ADSR - How to Trigger Envelope?
ADSR - How to Trigger Envelope?
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
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
Re: ADSR - How to Trigger Envelope?
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
Bryan
Re: ADSR - How to Trigger Envelope?
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.
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.
Re: ADSR - How to Trigger Envelope?
Sounds good...I'll give that a go.
Thanks!
Bryan
Thanks!
Bryan
-
- Site Admin
- Posts: 257
- Joined: Fri Jul 27, 2018 5:36 pm
Re: ADSR - How to Trigger Envelope?
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.
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: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.
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
Re: ADSR - How to Trigger Envelope?
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
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
Re: ADSR - How to Trigger Envelope?
OK, I have things almost working. Here's my ProcessSample() code so far:
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
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();
}
Thanks!
Bryan
Re: ADSR - How to Trigger Envelope?
Hi Bryan,
My guess would be that the problem might be because you reset the envelope every sample when the gate is high...
...rather than doing a rising edge detection.
By rising edge detection I mean something along these lines...
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.
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);
}
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;
}
But I have never used this class and have no idea about its internal operation so this is all just conjecture.
Re: ADSR - How to Trigger Envelope?
Hi Dan,Cherry Dan wrote: ↑Thu Oct 07, 2021 12:06 am
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: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.
if (!gate && envelope.GetStage() != ADSREnvelope::ENV_STAGE::ADSR_Stage_Attack)
envelope.SetGate(false);
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.
Re: ADSR - How to Trigger Envelope?
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:
...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!
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();
}
And to all of you-- thanks for helping a DSP newb out!