shithub: leaf

Download patch

ref: 32b90cfcbaa02028922bc30afb0ad99db5a0cdfe
parent: 1bea5d76d5b4ad8ec19112d07008fc5f0e46509c
author: spiricom <jeff@snyderphonics.com>
date: Wed May 20 17:14:37 EDT 2020

updated ADSR

binary files a/.DS_Store b/.DS_Store differ
binary files a/LEAF/.DS_Store b/LEAF/.DS_Store differ
--- a/LEAF/Inc/leaf-envelopes.h
+++ b/LEAF/Inc/leaf-envelopes.h
@@ -105,47 +105,144 @@
     };
 
     /* ADSR */
-    typedef struct _tADSR
-    {
-        float sampleRateInMs;
-        int state;
-        float output;
-        float attackRate;
-        float decayRate;
-        float releaseRate;
-        float attackCoef;
-        float decayCoef;
-        float releaseCoef;
-        float sustainLevel;
-        float targetRatioA;
-        float targetRatioDR;
-        float attackBase;
-        float decayBase;
-        float releaseBase;
-        float leakFactor;
-        float gain;
+        typedef struct _tADSR
+        {
+           const float *exp_buff;
+           const float *inc_buff;
+            uint32_t buff_size;
 
-    } _tADSR;
+            float next;
+
+            float attackInc, decayInc, releaseInc, rampInc;
+
+            oBool inAttack, inDecay, inSustain, inRelease, inRamp;
+
+            float sustain, gain, rampPeak, releasePeak;
+
+            float attackPhase, decayPhase, releasePhase, rampPhase;
+
+            float leakFactor;
+
+
+        } _tADSR;
+
+        typedef _tADSR* tADSR;
+
+        void    tADSR_init          (tADSR* const, float attack, float decay, float sustain, float release);
+        void    tADSR_free          (tADSR* const);
+        void    tADSR_initToPool    (tADSR* const, float attack, float decay, float sustain, float release, tMempool* const);
+        void    tADSR_freeFromPool  (tADSR* const, tMempool* const);
+
+        float   tADSR_tick          (tADSR* const);
+        void    tADSR_setAttack     (tADSR* const, float attack);
+        void    tADSR_setDecay      (tADSR* const, float decay);
+        void    tADSR_setSustain    (tADSR* const, float sustain);
+        void    tADSR_setRelease    (tADSR* const, float release);
+        void    tADSR_setLeakFactor (tADSR* const, float leakFactor);
+        void    tADSR_on            (tADSR* const, float velocity);
+        void    tADSR_off           (tADSR* const);
+
+
     
-    typedef _tADSR* tADSR;
     
-    void    tADSR_init          (tADSR* const, float attack, float decay, float sustain, float release);
-    void    tADSR_free          (tADSR* const);
-    void    tADSR_initToPool    (tADSR* const, float attack, float decay, float sustain, float release, tMempool* const);
-    void    tADSR_freeFromPool  (tADSR* const, tMempool* const);
-    
-    float   tADSR_tick          (tADSR* const);
-    void    tADSR_setAttack     (tADSR* const, float attack);
-    void    tADSR_setDecay      (tADSR* const, float decay);
-    void    tADSR_setSustain    (tADSR* const, float sustain);
-    void    tADSR_setRelease    (tADSR* const, float release);
-    void    tADSR_setLeakFactor (tADSR* const, float leakFactor);
-    void    tADSR_on            (tADSR* const, float velocity);
-    void    tADSR_off           (tADSR* const);
-    
-    
     // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     
+
+
+    /* ADSR2 */
+     typedef struct _tADSR2
+     {
+         float sampleRateInMs;
+         float attack;
+         float decay;
+         float release;
+         float attackLambda;
+         float decayLambda;
+         float releaseLambda;
+         float sustain;
+         float leakGain;
+         float leakFactor;
+         float targetGainSquared;
+         float factor;
+         float oneMinusFactor;
+         float gain;
+         uint8_t attacking;
+         uint8_t gate;
+         float env;
+         float envTarget;
+     } _tADSR2;
+
+     typedef _tADSR2* tADSR2;
+
+     void    tADSR2_init          (tADSR2* const, float attack, float decay, float sustain, float release);
+     void    tADSR2_free          (tADSR2* const);
+     void    tADSR2_initToPool    (tADSR2* const, float attack, float decay, float sustain, float release, tMempool* const);
+     void    tADSR2_freeFromPool  (tADSR2* const, tMempool* const);
+
+     float   tADSR2_tick          (tADSR2* const);
+     void    tADSR2_setAttack     (tADSR2* const, float attack);
+     void    tADSR2_setDecay      (tADSR2* const, float decay);
+     void    tADSR2_setSustain    (tADSR2* const, float sustain);
+     void    tADSR2_setRelease    (tADSR2* const, float release);
+     void    tADSR2_setLeakFactor (tADSR2* const, float leakFactor);
+     void    tADSR2_on            (tADSR2* const, float velocity);
+     void    tADSR2_off           (tADSR2* const);
+
+
+     // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+     // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+     enum envState {
+         env_idle = 0,
+         env_attack,
+         env_decay,
+         env_sustain,
+         env_release
+     };
+
+     /* ADSR3 */
+     typedef struct _tADSR3
+     {
+         float sampleRateInMs;
+         int state;
+         float output;
+         float attackRate;
+         float decayRate;
+         float releaseRate;
+         float attackCoef;
+         float decayCoef;
+         float releaseCoef;
+         float sustainLevel;
+         float targetRatioA;
+         float targetRatioDR;
+         float attackBase;
+         float decayBase;
+         float releaseBase;
+         float leakFactor;
+         float targetGainSquared;
+         float factor;
+         float oneMinusFactor;
+         float gain;
+
+     } _tADSR3;
+
+     typedef _tADSR3* tADSR3;
+
+     void    tADSR3_init          (tADSR3* const, float attack, float decay, float sustain, float release);
+     void    tADSR3_free          (tADSR3* const);
+     void    tADSR3_initToPool    (tADSR3* const, float attack, float decay, float sustain, float release, tMempool* const);
+     void    tADSR3_freeFromPool  (tADSR3* const, tMempool* const);
+
+     float   tADSR3_tick          (tADSR3* const);
+     void    tADSR3_setAttack     (tADSR3* const, float attack);
+     void    tADSR3_setDecay      (tADSR3* const, float decay);
+     void    tADSR3_setSustain    (tADSR3* const, float sustain);
+     void    tADSR3_setRelease    (tADSR3* const, float release);
+     void    tADSR3_setLeakFactor (tADSR3* const, float leakFactor);
+     void    tADSR3_on            (tADSR3* const, float velocity);
+     void    tADSR3_off           (tADSR3* const);
+
+    // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     
     /* Ramp */
--- a/LEAF/Src/leaf-envelopes.c
+++ b/LEAF/Src/leaf-envelopes.c
@@ -254,46 +254,480 @@
 }
 
 
+
 /* ADSR */
-//replaced our older ADSR that relied on a large lookup table with this one by Nigel Redmon from his blog. Thanks, Nigel!
+void    tADSR_init(tADSR* const adsrenv, float attack, float decay, float sustain, float release)
+{
+    tADSR_initToPool(adsrenv, attack, decay, sustain, release, &leaf.mempool);
+}
+
+void tADSR_free(tADSR* const adsrenv)
+{
+    tADSR_freeFromPool(adsrenv, &leaf.mempool);
+}
+
+void    tADSR_initToPool    (tADSR* const adsrenv, float attack, float decay, float sustain, float release, tMempool* const mp)
+{
+    _tMempool* m = *mp;
+    _tADSR* adsr = *adsrenv = (_tADSR*) mpool_alloc(sizeof(_tADSR), m);
+
+    adsr->exp_buff = __leaf_table_exp_decay;
+    adsr->inc_buff = __leaf_table_attack_decay_inc;
+    adsr->buff_size = sizeof(__leaf_table_exp_decay);
+
+    if (attack > 8192.0f)
+        attack = 8192.0f;
+    if (attack < 0.0f)
+        attack = 0.0f;
+
+    if (decay > 8192.0f)
+        decay = 8192.0f;
+    if (decay < 0.0f)
+        decay = 0.0f;
+
+    if (sustain > 1.0f)
+        sustain = 1.0f;
+    if (sustain < 0.0f)
+        sustain = 0.0f;
+
+    if (release > 8192.0f)
+        release = 8192.0f;
+    if (release < 0.0f)
+        release = 0.0f;
+
+    int16_t attackIndex = ((int16_t)(attack * 8.0f))-1;
+    int16_t decayIndex = ((int16_t)(decay * 8.0f))-1;
+    int16_t releaseIndex = ((int16_t)(release * 8.0f))-1;
+    int16_t rampIndex = ((int16_t)(2.0f * 8.0f))-1;
+
+    if (attackIndex < 0)
+        attackIndex = 0;
+    if (decayIndex < 0)
+        decayIndex = 0;
+    if (releaseIndex < 0)
+        releaseIndex = 0;
+    if (rampIndex < 0)
+        rampIndex = 0;
+
+    adsr->next = 0.0f;
+
+    adsr->inRamp = OFALSE;
+    adsr->inAttack = OFALSE;
+    adsr->inDecay = OFALSE;
+    adsr->inSustain = OFALSE;
+    adsr->inRelease = OFALSE;
+
+    adsr->sustain = sustain;
+
+    adsr->attackInc = adsr->inc_buff[attackIndex];
+    adsr->decayInc = adsr->inc_buff[decayIndex];
+    adsr->releaseInc = adsr->inc_buff[releaseIndex];
+    adsr->rampInc = adsr->inc_buff[rampIndex];
+
+    adsr->leakFactor = 1.0f;
+}
+
+void    tADSR_freeFromPool  (tADSR* const adsrenv, tMempool* const mp)
+{
+    _tMempool* m = *mp;
+    _tADSR* adsr = *adsrenv;
+    mpool_free(adsr, m);
+}
+
+void     tADSR_setAttack(tADSR* const adsrenv, float attack)
+{
+    _tADSR* adsr = *adsrenv;
+
+    int32_t attackIndex;
+
+    if (attack < 0.0f) {
+        attackIndex = 0.0f;
+    } else if (attack < 8192.0f) {
+        attackIndex = ((int32_t)(attack * 8.0f))-1;
+    } else {
+        attackIndex = ((int32_t)(8192.0f * 8.0f))-1;
+    }
+
+    adsr->attackInc = adsr->inc_buff[attackIndex];
+}
+
+void     tADSR_setDecay(tADSR* const adsrenv, float decay)
+{
+    _tADSR* adsr = *adsrenv;
+
+    int32_t decayIndex;
+
+    if (decay < 0.0f) {
+        decayIndex = 0.0f;
+    } else if (decay < 8192.0f) {
+        decayIndex = ((int32_t)(decay * 8.0f)) - 1;
+    } else {
+        decayIndex = ((int32_t)(8192.0f * 8.0f)) - 1;
+    }
+
+    adsr->decayInc = adsr->inc_buff[decayIndex];
+}
+
+void     tADSR_setSustain(tADSR* const adsrenv, float sustain)
+{
+    _tADSR* adsr = *adsrenv;
+
+    if (sustain > 1.0f)      adsr->sustain = 1.0f;
+    else if (sustain < 0.0f) adsr->sustain = 0.0f;
+    else                     adsr->sustain = sustain;
+}
+
+void     tADSR_setRelease(tADSR* const adsrenv, float release)
+{
+    _tADSR* adsr = *adsrenv;
+
+    int32_t releaseIndex;
+
+    if (release < 0.0f) {
+        releaseIndex = 0.0f;
+    } else if (release < 8192.0f) {
+        releaseIndex = ((int32_t)(release * 8.0f)) - 1;
+    } else {
+        releaseIndex = ((int32_t)(8192.0f * 8.0f)) - 1;
+    }
+
+    adsr->releaseInc = adsr->inc_buff[releaseIndex];
+}
+
+// 0.999999 is slow leak, 0.9 is fast leak
+void     tADSR_setLeakFactor(tADSR* const adsrenv, float leakFactor)
+{
+    _tADSR* adsr = *adsrenv;
+
+
+    adsr->leakFactor = leakFactor;
+}
+
+void tADSR_on(tADSR* const adsrenv, float velocity)
+{
+    _tADSR* adsr = *adsrenv;
+
+    if ((adsr->inAttack || adsr->inDecay) || (adsr->inSustain || adsr->inRelease)) // In case ADSR retriggered while it is still happening.
+    {
+        adsr->rampPhase = 0;
+        adsr->inRamp = OTRUE;
+        adsr->rampPeak = adsr->next;
+    }
+    else // Normal start.
+    {
+        adsr->inAttack = OTRUE;
+    }
+
+    adsr->attackPhase = 0;
+    adsr->decayPhase = 0;
+    adsr->releasePhase = 0;
+    adsr->inDecay = OFALSE;
+    adsr->inSustain = OFALSE;
+    adsr->inRelease = OFALSE;
+    adsr->gain = velocity;
+}
+
+void tADSR_off(tADSR* const adsrenv)
+{
+    _tADSR* adsr = *adsrenv;
+
+    if (adsr->inRelease) return;
+
+    adsr->inAttack = OFALSE;
+    adsr->inDecay = OFALSE;
+    adsr->inSustain = OFALSE;
+    adsr->inRelease = OTRUE;
+
+    adsr->releasePeak = adsr->next;
+}
+
+float   tADSR_tick(tADSR* const adsrenv)
+{
+    _tADSR* adsr = *adsrenv;
+
+
+    if (adsr->inRamp)
+    {
+        if (adsr->rampPhase > UINT16_MAX)
+        {
+            adsr->inRamp = OFALSE;
+            adsr->inAttack = OTRUE;
+            adsr->next = 0.0f;
+        }
+        else
+        {
+            adsr->next = adsr->rampPeak * adsr->exp_buff[(uint32_t)adsr->rampPhase];
+        }
+
+        adsr->rampPhase += adsr->rampInc;
+    }
+
+    if (adsr->inAttack)
+    {
+
+        // If attack done, time to turn around.
+        if (adsr->attackPhase > UINT16_MAX)
+        {
+            adsr->inDecay = OTRUE;
+            adsr->inAttack = OFALSE;
+            adsr->next = adsr->gain * 1.0f;
+        }
+        else
+        {
+            // do interpolation !
+            adsr->next = adsr->gain * adsr->exp_buff[UINT16_MAX - (uint32_t)adsr->attackPhase]; // inverted and backwards to get proper rising exponential shape/perception
+        }
+
+        // Increment ADSR attack.
+        adsr->attackPhase += adsr->attackInc;
+
+    }
+
+    if (adsr->inDecay)
+    {
+
+        // If decay done, sustain.
+        if (adsr->decayPhase >= UINT16_MAX)
+        {
+            adsr->inDecay = OFALSE;
+            adsr->inSustain = OTRUE;
+            adsr->next = adsr->gain * adsr->sustain;
+        }
+
+        else
+        {
+            adsr->next = adsr->gain * (adsr->sustain + ((adsr->exp_buff[(uint32_t)adsr->decayPhase]) * (1 - adsr->sustain))); // do interpolation !
+        }
+
+        // Increment ADSR decay.
+        adsr->decayPhase += adsr->decayInc;
+    }
+
+    if (adsr->inSustain)
+    {
+        adsr->next = adsr->next * adsr->leakFactor;
+    }
+
+    if (adsr->inRelease)
+    {
+        // If release done, finish.
+        if (adsr->releasePhase >= UINT16_MAX)
+        {
+            adsr->inRelease = OFALSE;
+            adsr->next = 0.0f;
+        }
+        else {
+
+            adsr->next = adsr->releasePeak * (adsr->exp_buff[(uint32_t)adsr->releasePhase]); // do interpolation !
+        }
+
+        // Increment envelope release;
+        adsr->releasePhase += adsr->releaseInc;
+    }
+
+
+    return adsr->next;
+}
+
+
+
+
+/* ADSR 2*/
+//This one is adapted from the VCV Rack code
 //-JS
 
 
 
-float calcADSRCoef(double rate, double targetRatio)
+const float ADSR2_MIN_TIME = 1e-3f;
+const float ADSR2_INV_MIN_TIME = 1000.0f;
+const float ADSR2_MAX_TIME = 10.f;
+const float ADSR2_LAMBDA_BASE = 10000.0f;
+
+
+void    tADSR2_init(tADSR2* const adsrenv, float attack, float decay, float sustain, float release)
 {
+    tADSR2_initToPool(adsrenv, attack, decay, sustain, release, &leaf.mempool);
+}
+
+void tADSR2_free(tADSR2* const adsrenv)
+{
+    tADSR2_freeFromPool(adsrenv, &leaf.mempool);
+}
+
+void    tADSR2_initToPool    (tADSR2* const adsrenv, float attack, float decay, float sustain, float release, tMempool* const mp)
+{
+    _tMempool* m = *mp;
+    _tADSR2* adsr = *adsrenv = (_tADSR2*) mpool_alloc(sizeof(_tADSR2), m);
+    adsr->sampleRateInMs =  leaf.sampleRate * 0.001f;
+    adsr->attack = LEAF_clip(0.0f, attack * 0.001f, 1.0f);
+    adsr->attackLambda = powf(ADSR2_LAMBDA_BASE, -adsr->attack) * ADSR2_INV_MIN_TIME;
+
+
+    adsr->decay = LEAF_clip(0.0f, decay * 0.001f, 1.0f);
+    adsr->decayLambda = powf(ADSR2_LAMBDA_BASE, -adsr->decay) * ADSR2_INV_MIN_TIME;
+
+
+    adsr->sustain= sustain;
+
+
+    adsr->release = LEAF_clip(0.0f, release * 0.001f, 1.0f);
+    adsr->releaseLambda = powf(ADSR2_LAMBDA_BASE, -adsr->release) * ADSR2_INV_MIN_TIME;
+
+    adsr->attacking = 0;
+    adsr->gate = 0;
+    adsr->gain = 1.0f;
+    adsr->targetGainSquared = 1.0f;
+    adsr->factor = 0.01f;
+    adsr->oneMinusFactor = 0.99f;
+    adsr->env = 0.0f;
+    adsr->leakFactor = 1.0f;
+}
+
+void    tADSR2_freeFromPool  (tADSR2* const adsrenv, tMempool* const mp)
+{
+    _tMempool* m = *mp;
+    _tADSR2* adsr = *adsrenv;
+    mpool_free(adsr, m);
+}
+
+void     tADSR2_setAttack(tADSR2* const adsrenv, float attack)
+{
+    _tADSR2* adsr = *adsrenv;
+    adsr->attack = LEAF_clip(0.0f, attack * 0.001f, 1.0f);
+    adsr->attackLambda = fastPow(ADSR2_LAMBDA_BASE, -adsr->attack) * ADSR2_INV_MIN_TIME;
+
+}
+
+void     tADSR2_setDecay(tADSR2* const adsrenv, float decay)
+{
+    _tADSR2* adsr = *adsrenv;
+    adsr->decay = LEAF_clip(0.0f, decay * 0.001f, 1.0f);
+    adsr->decayLambda = fastPow(ADSR2_LAMBDA_BASE, -adsr->decay) * ADSR2_INV_MIN_TIME;
+
+}
+
+void     tADSR2_setSustain(tADSR2* const adsrenv, float sustain)
+{
+    _tADSR2* adsr = *adsrenv;
+    adsr->sustain = sustain;
+
+    if (adsr->gate)
+    {
+        if (adsr->attacking == 0)
+        {
+            adsr->envTarget = sustain;
+        }
+    }
+}
+
+void     tADSR2_setRelease(tADSR2* const adsrenv, float release)
+{
+    _tADSR2* adsr = *adsrenv;
+    adsr->release = LEAF_clip(0.0f, release * 0.001f, 1.0f);
+    adsr->releaseLambda = fastPow(ADSR2_LAMBDA_BASE, -adsr->release) * ADSR2_INV_MIN_TIME;
+}
+
+// 0.999999 is slow leak, 0.9 is fast leak
+void     tADSR2_setLeakFactor(tADSR2* const adsrenv, float leakFactor)
+{
+    _tADSR2* adsr = *adsrenv;
+
+    adsr->leakFactor = leakFactor;
+}
+
+void tADSR2_on(tADSR2* const adsrenv, float velocity)
+{
+    _tADSR2* adsr = *adsrenv;
+    adsr->targetGainSquared = velocity * velocity;
+    adsr->envTarget = 1.2f;
+    adsr->leakGain = 1.0f;
+    adsr->gate = 1;
+    adsr->attacking = 1;
+}
+
+void tADSR2_off(tADSR2* const adsrenv)
+{
+    _tADSR2* adsr = *adsrenv;
+    adsr->gate = 0;
+    adsr->envTarget = 0.0f;
+}
+
+float   tADSR2_tick(tADSR2* const adsrenv)
+{
+    _tADSR2* adsr = *adsrenv;
+    float lambda;
+
+    if (adsr->gate)
+    {
+        if (adsr->attacking)
+        {
+            lambda = adsr->attackLambda;
+        }
+        else
+        {
+            lambda = adsr->decayLambda;
+        }
+    }
+    else
+    {
+        lambda = adsr->releaseLambda;
+    }
+
+
+    // Adjust env
+    adsr->env += (adsr->envTarget - adsr->env) * lambda * leaf.invSampleRate;
+
+    // Turn off attacking state if envelope is HIGH
+    if (adsr->env >= 1.0f)
+    {
+        adsr->attacking = 0;
+        adsr->envTarget = adsr->sustain;
+    }
+
+    //smooth the gain value   -- this is not ideal, a retrigger while the envelope is still going with a new gain will cause a jump, although it will be smoothed quickly. Maybe doing the math so the range is computed based on the gain rather than 0.->1. is preferable? But that's harder to get the exponential curve right without a lookup.
+    adsr->gain = ((adsr->factor*adsr->targetGainSquared)+(adsr->oneMinusFactor*adsr->gain));
+    adsr->leakGain *= adsr->leakFactor;
+    return adsr->env * adsr->gain * adsr->leakGain;
+}
+
+/* ADSR 3*/
+//This one doesn't use any lookup table - by Nigel Redmon from his blog. Thanks, Nigel!
+//-JS
+
+float calcADSR3Coef(double rate, double targetRatio)
+{
     return (rate <= 0.0f) ? 0.0f : exp(-log((1.0 + targetRatio) / targetRatio) / rate);
 }
 
 
-void    tADSR_init(tADSR* const adsrenv, float attack, float decay, float sustain, float release)
+void    tADSR3_init(tADSR3* const adsrenv, float attack, float decay, float sustain, float release)
 {
-    tADSR_initToPool(adsrenv, attack, decay, sustain, release, &leaf.mempool);
+    tADSR3_initToPool(adsrenv, attack, decay, sustain, release, &leaf.mempool);
 }
 
-void tADSR_free(tADSR* const adsrenv)
+void tADSR3_free(tADSR3* const adsrenv)
 {
-    tADSR_freeFromPool(adsrenv, &leaf.mempool);
+    tADSR3_freeFromPool(adsrenv, &leaf.mempool);
 }
 
-void    tADSR_initToPool    (tADSR* const adsrenv, float attack, float decay, float sustain, float release, tMempool* const mp)
+void    tADSR3_initToPool    (tADSR3* const adsrenv, float attack, float decay, float sustain, float release, tMempool* const mp)
 {
     _tMempool* m = *mp;
-    _tADSR* adsr = *adsrenv = (_tADSR*) mpool_alloc(sizeof(_tADSR), m);
+    _tADSR3* adsr = *adsrenv = (_tADSR3*) mpool_alloc(sizeof(_tADSR3), m);
     adsr->sampleRateInMs =  leaf.sampleRate * 0.001f;
     adsr->targetRatioA = 0.3f;
     adsr->targetRatioDR = 0.0001f;
     adsr->attackRate = attack * adsr->sampleRateInMs;
-    adsr->attackCoef = calcADSRCoef(attack * adsr->sampleRateInMs, adsr->targetRatioA);
+    adsr->attackCoef = calcADSR3Coef(attack * adsr->sampleRateInMs, adsr->targetRatioA);
     adsr->attackBase = (1.0f + adsr->targetRatioA) * (1.0f - adsr->attackCoef);
-    
+
     adsr->decayRate = decay * adsr->sampleRateInMs;
     adsr->decayCoef = calcADSRCoef(decay * adsr->sampleRateInMs,adsr-> targetRatioDR);
     adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef);
-    
+
     adsr->sustainLevel = sustain;
     adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef);
-    
+
     adsr->releaseRate = release * adsr->sampleRateInMs;
     adsr->releaseCoef = calcADSRCoef(release * adsr->sampleRateInMs, adsr->targetRatioDR);
     adsr->releaseBase = -adsr->targetRatioDR * (1.0f - adsr->releaseCoef);
@@ -300,70 +734,73 @@
 
     adsr->state = env_idle;
     adsr->gain = 1.0f;
+    adsr->targetGainSquared = 1.0f;
+    adsr->factor = 0.01f;
+    adsr->oneMinusFactor = 0.99f;
     adsr->output = 0.0f;
     adsr->leakFactor = 1.0f;
 }
 
-void    tADSR_freeFromPool  (tADSR* const adsrenv, tMempool* const mp)
+void    tADSR3_freeFromPool  (tADSR3* const adsrenv, tMempool* const mp)
 {
     _tMempool* m = *mp;
-    _tADSR* adsr = *adsrenv;
+    _tADSR3* adsr = *adsrenv;
     mpool_free(adsr, m);
 }
 
-void     tADSR_setAttack(tADSR* const adsrenv, float attack)
+void     tADSR3_setAttack(tADSR3* const adsrenv, float attack)
 {
-    _tADSR* adsr = *adsrenv;
+    _tADSR3* adsr = *adsrenv;
 
     adsr->attackRate = attack * adsr->sampleRateInMs;
-    adsr->attackCoef = calcADSRCoef(adsr->attackRate, adsr->targetRatioA);
+    adsr->attackCoef = calcADSR3Coef(adsr->attackRate, adsr->targetRatioA);
     adsr->attackBase = (1.0f + adsr->targetRatioA) * (1.0f - adsr->attackCoef);
 }
 
-void     tADSR_setDecay(tADSR* const adsrenv, float decay)
+void     tADSR3_setDecay(tADSR3* const adsrenv, float decay)
 {
-    _tADSR* adsr = *adsrenv;
-    
+    _tADSR3* adsr = *adsrenv;
+
     adsr->decayRate = decay * adsr->sampleRateInMs;
-    adsr->decayCoef = calcADSRCoef(adsr->decayRate,adsr-> targetRatioDR);
+    adsr->decayCoef = calcADSR3Coef(adsr->decayRate,adsr-> targetRatioDR);
     adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef);
 }
 
-void     tADSR_setSustain(tADSR* const adsrenv, float sustain)
+void     tADSR3_setSustain(tADSR3* const adsrenv, float sustain)
 {
-    _tADSR* adsr = *adsrenv;
-    
+    _tADSR3* adsr = *adsrenv;
+
     adsr->sustainLevel = sustain;
     adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef);
 }
 
-void     tADSR_setRelease(tADSR* const adsrenv, float release)
+void     tADSR3_setRelease(tADSR3* const adsrenv, float release)
 {
-    _tADSR* adsr = *adsrenv;
-    
+    _tADSR3* adsr = *adsrenv;
+
     adsr->releaseRate = release * adsr->sampleRateInMs;
-    adsr->releaseCoef = calcADSRCoef(adsr->releaseRate, adsr->targetRatioDR);
+    adsr->releaseCoef = calcADSR3Coef(adsr->releaseRate, adsr->targetRatioDR);
     adsr->releaseBase = -adsr->targetRatioDR * (1.0f - adsr->releaseCoef);
 }
 
 // 0.999999 is slow leak, 0.9 is fast leak
-void     tADSR_setLeakFactor(tADSR* const adsrenv, float leakFactor)
+void     tADSR3_setLeakFactor(tADSR3* const adsrenv, float leakFactor)
 {
-    _tADSR* adsr = *adsrenv;
+    _tADSR3* adsr = *adsrenv;
 
     adsr->leakFactor = leakFactor;
 }
 
-void tADSR_on(tADSR* const adsrenv, float velocity)
+void tADSR3_on(tADSR3* const adsrenv, float velocity)
 {
-    _tADSR* adsr = *adsrenv;
+    _tADSR3* adsr = *adsrenv;
     adsr->state = env_attack;
-    adsr->gain = velocity;
+    adsr->targetGainSquared = velocity * velocity;
 }
 
-void tADSR_off(tADSR* const adsrenv)
+void tADSR3_off(tADSR3* const adsrenv)
 {
-    _tADSR* adsr = *adsrenv;
+    _tADSR3* adsr = *adsrenv;
 
     if (adsr->state != env_idle)
     {
@@ -371,11 +808,11 @@
     }
 }
 
-float   tADSR_tick(tADSR* const adsrenv)
+float   tADSR3_tick(tADSR3* const adsrenv)
 {
-    _tADSR* adsr = *adsrenv;
-    
+    _tADSR3* adsr = *adsrenv;
 
+
     switch (adsr->state) {
         case env_idle:
             break;
@@ -403,11 +840,10 @@
                 adsr->state = env_idle;
             }
     }
+    //smooth the gain value   -- this is not ideal, a retrigger while the envelope is still going with a new gain will cause a jump, although it will be smoothed quickly. Maybe doing the math so the range is computed based on the gain rather than 0.->1. is preferable? But that's harder to get the exponential curve right without a lookup.
+    adsr->gain = (adsr->factor*adsr->targetGainSquared)+(adsr->oneMinusFactor*adsr->gain);
     return adsr->output * adsr->gain;
 }
-
-
-