shithub: leaf

Download patch

ref: a98552ba09ba07ed88553aae41b503870ad56140
parent: c4d857d556d623d99b8517002b267f6c0a8f60bd
parent: 71e82bd67cba20a233a7c5313dc803965899db97
author: Matthew Wang <mjw7@princeton.edu>
date: Tue May 19 06:15:22 EDT 2020

Merge branch 'master' of github.com:spiricom/LEAF

binary files a/.DS_Store b/.DS_Store differ
--- a/LEAF/Inc/leaf-envelopes.h
+++ b/LEAF/Inc/leaf-envelopes.h
@@ -95,26 +95,36 @@
     // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
 
 
+    // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+    enum envState {
+        env_idle = 0,
+        env_attack,
+        env_decay,
+        env_sustain,
+        env_release
+    };
+
     /* ADSR */
     typedef struct _tADSR
     {
-       const float *exp_buff;
-       const float *inc_buff;
-        uint32_t buff_size;
-        
-        float next;
-        
-        float attackInc, decayInc, releaseInc, rampInc;
-        
-        oBool inAttack, inDecay, inSustain, inRelease, inRamp;
-        
-        float sustain, gain, rampPeak, releasePeak;
-        
-        float attackPhase, decayPhase, releasePhase, rampPhase;
-
+        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;
 
-
     } _tADSR;
     
     typedef _tADSR* tADSR;
@@ -129,9 +139,10 @@
     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_setLeakFactor (tADSR* const, float leakFactor);
     void    tADSR_on            (tADSR* const, float velocity);
     void    tADSR_off           (tADSR* const);
+    
     
     // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     
--- a/LEAF/Src/leaf-envelopes.c
+++ b/LEAF/Src/leaf-envelopes.c
@@ -253,7 +253,19 @@
     return env->next;
 }
 
+
 /* ADSR */
+//replaced our older ADSR that relied on a large lookup table with this one by Nigel Redmon from his blog. Thanks, Nigel!
+//-JS
+
+
+
+float calcADSRCoef(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)
 {
     tADSR_initToPool(adsrenv, attack, decay, sustain, release, &leaf.mempool);
@@ -268,60 +280,27 @@
 {
     _tMempool* m = *mp;
     _tADSR* adsr = *adsrenv = (_tADSR*) mpool_alloc(sizeof(_tADSR), 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->attackBase = (1.0f + adsr->targetRatioA) * (1.0f - adsr->attackCoef);
     
-    adsr->exp_buff = __leaf_table_exp_decay;
-    adsr->inc_buff = __leaf_table_attack_decay_inc;
-    adsr->buff_size = sizeof(__leaf_table_exp_decay);
+    adsr->decayRate = decay * adsr->sampleRateInMs;
+    adsr->decayCoef = calcADSRCoef(decay * adsr->sampleRateInMs,adsr-> targetRatioDR);
+    adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef);
     
-    if (attack > 8192.0f)
-        attack = 8192.0f;
-    if (attack < 0.0f)
-        attack = 0.0f;
+    adsr->sustainLevel = sustain;
+    adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef);
     
-    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->releaseRate = release * adsr->sampleRateInMs;
+    adsr->releaseCoef = calcADSRCoef(release * adsr->sampleRateInMs, adsr->targetRatioDR);
+    adsr->releaseBase = -adsr->targetRatioDR * (1.0f - adsr->releaseCoef);
 
+    adsr->state = env_idle;
+    adsr->gain = 1.0f;
+    adsr->output = 0.0f;
     adsr->leakFactor = 1.0f;
 }
 
@@ -335,18 +314,10 @@
 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];
+
+    adsr->attackRate = attack * adsr->sampleRateInMs;
+    adsr->attackCoef = calcADSRCoef(adsr->attackRate, adsr->targetRatioA);
+    adsr->attackBase = (1.0f + adsr->targetRatioA) * (1.0f - adsr->attackCoef);
 }
 
 void     tADSR_setDecay(tADSR* const adsrenv, float decay)
@@ -353,17 +324,9 @@
 {
     _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];
+    adsr->decayRate = decay * adsr->sampleRateInMs;
+    adsr->decayCoef = calcADSRCoef(adsr->decayRate,adsr-> targetRatioDR);
+    adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef);
 }
 
 void     tADSR_setSustain(tADSR* const adsrenv, float sustain)
@@ -370,9 +333,8 @@
 {
     _tADSR* adsr = *adsrenv;
     
-    if (sustain > 1.0f)      adsr->sustain = 1.0f;
-    else if (sustain < 0.0f) adsr->sustain = 0.0f;
-    else                     adsr->sustain = sustain;
+    adsr->sustainLevel = sustain;
+    adsr->decayBase = (adsr->sustainLevel - adsr->targetRatioDR) * (1.0f - adsr->decayCoef);
 }
 
 void     tADSR_setRelease(tADSR* const adsrenv, float release)
@@ -379,17 +341,9 @@
 {
     _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];
+    adsr->releaseRate = release * adsr->sampleRateInMs;
+    adsr->releaseCoef = calcADSRCoef(adsr->releaseRate, adsr->targetRatioDR);
+    adsr->releaseBase = -adsr->targetRatioDR * (1.0f - adsr->releaseCoef);
 }
 
 // 0.999999 is slow leak, 0.9 is fast leak
@@ -397,7 +351,6 @@
 {
     _tADSR* adsr = *adsrenv;
 
-
     adsr->leakFactor = leakFactor;
 }
 
@@ -404,24 +357,7 @@
 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->state = env_attack;
     adsr->gain = velocity;
 }
 
@@ -428,15 +364,11 @@
 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;
+
+    if (adsr->state != env_idle)
+    {
+        adsr->state = env_release;
+    }
 }
 
 float   tADSR_tick(tADSR* const adsrenv)
@@ -444,89 +376,42 @@
     _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;
+    switch (adsr->state) {
+        case env_idle:
+            break;
+        case env_attack:
+            adsr->output = adsr->attackBase + adsr->output * adsr->attackCoef;
+            if (adsr->output >= 1.0f) {
+                adsr->output = 1.0f;
+                adsr->state = env_decay;
+            }
+            break;
+        case env_decay:
+            adsr->output = adsr->decayBase + adsr->output * adsr->decayCoef * adsr->leakFactor;
+            if (adsr->output <= adsr->sustainLevel) {
+                adsr->output = adsr->sustainLevel;
+                adsr->state = env_sustain;
+            }
+            break;
+        case env_sustain:
+            adsr->output = adsr->output * adsr->leakFactor;
+            break;
+        case env_release:
+            adsr->output = adsr->releaseBase + adsr->output * adsr->releaseCoef;
+            if (adsr->output <= 0.0f) {
+                adsr->output = 0.0f;
+                adsr->state = env_idle;
+            }
     }
-    
-    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;
-    }
+    return adsr->output * adsr->gain;
+}
 
-    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;
-}
 
+
+/////-----------------
 /* Ramp */
 void    tRamp_init(tRamp* const r, float time, int samples_per_tick)
 {
--- a/LEAF/Src/leaf-mempool.c
+++ b/LEAF/Src/leaf-mempool.c
@@ -68,10 +68,13 @@
     
     pool->head = create_node(pool->mpool, NULL, NULL, pool->msize-leaf.header_size);
     
+    /*
     for (int i = 0; i < pool->head->size; i++)
     {
         memory[i+leaf.header_size]=0;
     }
+    */
+    //is zeroing out the memory necessary? This takes a long time on large pools - JS
 }
 
 void leaf_pool_init(char* memory, size_t size)
@@ -87,7 +90,11 @@
 void* mpool_alloc(size_t asize, _tMempool* pool)
 {
     // If the head is NULL, the mempool is full
-    if (pool->head == NULL) return NULL;
+    if (pool->head == NULL)
+    {
+        leaf_mempool_overrun();
+        return NULL;
+    }
     
     // Should we alloc the first block large enough or check all blocks and pick the one closest in size?
     size_t size_to_alloc = mpool_align(asize);
@@ -100,7 +107,11 @@
         
         // If we reach the end of the free list, there
         // are no blocks large enough, return NULL
-        if (node_to_alloc == NULL) return NULL;
+        if (node_to_alloc == NULL)
+        {
+            leaf_mempool_overrun();
+            return NULL;
+        }
     }
     
     // Create a new node after the node to be allocated if there is enough space
@@ -153,7 +164,11 @@
 void* mpool_calloc(size_t asize, _tMempool* pool)
 {
     // If the head is NULL, the mempool is full
-    if (pool->head == NULL) return NULL;
+    if (pool->head == NULL)
+    {
+        leaf_mempool_overrun();
+        return NULL;
+    }
     
     // Should we alloc the first block large enough or check all blocks and pick the one closest in size?
     size_t size_to_alloc = mpool_align(asize);
@@ -166,7 +181,11 @@
         
         // If we reach the end of the free list, there
         // are no blocks large enough, return NULL
-        if (node_to_alloc == NULL) return NULL;
+        if (node_to_alloc == NULL)
+        {
+            leaf_mempool_overrun();
+            return NULL;
+        }
     }
     
     // Create a new node after the node to be allocated if there is enough space
@@ -362,7 +381,8 @@
 
 void leaf_mempool_overrun(void)
 {
-    //TODO: we should set up some kind of leaf_error method that reaches user space to notify library users of things that failed.
+    LEAF_error(1);
+    //TODO: we should make a set of real error codes that are in an enum type
 }
 
 void tMempool_init(tMempool* const mp, char* memory, size_t size)
--- a/LEAF/Src/leaf.c
+++ b/LEAF/Src/leaf.c
@@ -52,3 +52,9 @@
     return leaf.sampleRate;
 }
 
+
+//implement a function called this in your user code to catch errors
+__attribute__((weak)) uint8_t LEAF_error(uint8_t whichone)
+{
+    return whichone;
+}
--- a/LEAF/leaf.h
+++ b/LEAF/leaf.h
@@ -125,6 +125,9 @@
      */
     float       LEAF_getSampleRate   (void);
     
+
+    __attribute__((weak)) uint8_t LEAF_error(uint8_t whichone);
+    
     /*!
      * @}
      */