shithub: leaf

Download patch

ref: 23faeb0a9edc0b46299231e9f78a40705aab2e4d
parent: 53a6d7a024d9b38c86da84fe5fc43d3fc2753621
author: spiricom <jeff@snyderphonics.com>
date: Thu Sep 10 13:04:04 EDT 2020

added some fixes to several leaf objects and major bug fix to tSampler

--- a/leaf/Inc/leaf-filters.h
+++ b/leaf/Inc/leaf-filters.h
@@ -1012,13 +1012,16 @@
     float   tVZFilter_tick               (tVZFilter* const, float input);
     float   tVZFilter_tickEfficient               (tVZFilter* const vf, float in);
     void    tVZFilter_calcCoeffs           (tVZFilter* const);
+    void    tVZFilter_calcCoeffsEfficientBP           (tVZFilter* const);
     void    tVZFilter_setBandwidth            (tVZFilter* const, float bandWidth);
     void    tVZFilter_setFreq           (tVZFilter* const, float freq);
     void    tVZFilter_setFreqAndBandwidth    (tVZFilter* const vf, float freq, float bw);
+    void    tVZFilter_setFreqAndBandwidthEfficientBP    (tVZFilter* const vf, float freq, float bw);
     void    tVZFilter_setGain                  (tVZFilter* const, float gain);
+
     void    tVZFilter_setType                  (tVZFilter* const, VZFilterType type);
     float   tVZFilter_BandwidthToR        (tVZFilter* const vf, float B);
-    
+    float   tVZFilter_BandwidthToREfficientBP(tVZFilter* const vf, float B);
     
     /*!
      @defgroup tdiodefilter tDiodeFilter
--- a/leaf/Inc/leaf-math.h
+++ b/leaf/Inc/leaf-math.h
@@ -138,6 +138,13 @@
     float fastcosf(float fAngle);
 
     float fastercosf(float fAngle);
+
+    float fasttanf (float fAngle);
+
+
+    float fastertanf(float fAngle);
+
+
     // alternative implementation for abs()
     // REQUIRES: 32 bit integers
     int fastabs_int(int in);
--- a/leaf/Inc/leaf-mempool.h
+++ b/leaf/Inc/leaf-mempool.h
@@ -112,7 +112,7 @@
      @param size The size of the chuck of memory to be used as a mempool.
      @param poolTo A pointer to the tMempool to which a tMempool should be initialized.
      */
-    void    tMempool_initToPool     (tMempool* const pool, char* memory, size_t size, tMempool* const poolTo);
+    void    tMempool_initToPool     (tMempool* const mp, char* memory, size_t size, tMempool* const mem, LEAF* const leaf);
 
     /*!
      @} */
--- a/leaf/Inc/leaf-sampling.h
+++ b/leaf/Inc/leaf-sampling.h
@@ -122,6 +122,8 @@
         uint32_t idx;
         uint32_t bufferLength;
         uint32_t recordedLength;
+        uint32_t channels;
+        uint32_t sampleRate;
         RecordMode mode;
         
         int active;
@@ -130,7 +132,7 @@
     typedef _tBuffer* tBuffer;
     
     void  tBuffer_init                  (tBuffer* const, uint32_t length, LEAF* const leaf);
-    void  tBuffer_initToPool            (tBuffer* const, uint32_t length, tMempool* const);
+    void  tBuffer_initToPool 			(tBuffer* const sb, uint32_t length, tMempool* const mp, LEAF* const leaf);
     void  tBuffer_free                  (tBuffer* const);
     
     void  tBuffer_tick                  (tBuffer* const, float sample);
@@ -138,6 +140,7 @@
     float tBuffer_get                   (tBuffer* const, int idx);
     void  tBuffer_record                (tBuffer* const);
     void  tBuffer_stop                  (tBuffer* const);
+    void tBuffer_setBuffer				(tBuffer* const sb, float* externalBuffer, int length, int channels, int sampleRate);
     int   tBuffer_getRecordPosition     (tBuffer* const);
     void   tBuffer_setRecordPosition    (tBuffer* const, int pos);
     void  tBuffer_setRecordMode         (tBuffer* const, RecordMode mode);
@@ -224,6 +227,12 @@
         
         tBuffer samp;
         
+        float leafInvSampleRate;
+        float leafSampleRate;
+        float ticksPerSevenMs;
+        float rateFactor;
+        uint32_t channels;
+
         tRamp gain;
         
         float idx;
@@ -254,10 +263,11 @@
     typedef _tSampler* tSampler;
     
     void    tSampler_init               (tSampler* const, tBuffer* const, LEAF* const leaf);
-    void    tSampler_initToPool         (tSampler* const, tBuffer* const, tMempool* const);
+    void 	tSampler_initToPool			(tSampler* const sp, tBuffer* const b, tMempool* const mp, LEAF* const leaf);
     void    tSampler_free               (tSampler* const);
     
     float   tSampler_tick               (tSampler* const);
+    float	tSampler_tickStereo        	(tSampler* const sp, float* outputArray);
     void    tSampler_setSample          (tSampler* const, tBuffer* const);
     void    tSampler_setMode            (tSampler* const, PlayMode mode);
     void    tSampler_play               (tSampler* const);
@@ -343,7 +353,7 @@
     typedef _tAutoSampler* tAutoSampler;
     
     void    tAutoSampler_init               (tAutoSampler* const, tBuffer* const, LEAF* const leaf);
-    void    tAutoSampler_initToPool         (tAutoSampler* const, tBuffer* const, tMempool* const);
+    void    tAutoSampler_initToPool 		(tAutoSampler* const as, tBuffer* const b, tMempool* const mp, LEAF* const leaf);
     void    tAutoSampler_free               (tAutoSampler* const);
     
     float   tAutoSampler_tick               (tAutoSampler* const, float input);
--- a/leaf/Src/leaf-analysis.c
+++ b/leaf/Src/leaf-analysis.c
@@ -10,7 +10,7 @@
 
 #include "..\Inc\leaf-analysis.h"
 #include "..\Externals\d_fft_mayer.h"
-
+#include <intrin.h>
 #else
 
 #include "../Inc/leaf-analysis.h"
--- a/leaf/Src/leaf-effects.c
+++ b/leaf/Src/leaf-effects.c
@@ -288,7 +288,7 @@
         {
 //            for(i=0; i<n; i++)
 //            {
-                buf[i] = 0.0f;
+                buf[0] = 0.0f;
                 return;
 //            }
         }
@@ -618,7 +618,7 @@
         {
 //            for(i=0; i<n; i++)
 //            {
-                buf[i] = 0.0f;
+                buf[0] = 0.0f;
                 return;
 //            }
         }
--- a/leaf/Src/leaf-filters.c
+++ b/leaf/Src/leaf-filters.c
@@ -1321,38 +1321,7 @@
 
 }
 
-float   tVZFilter_tickEfficientBP               (tVZFilter* const vf, float in)
-{
-    _tVZFilter* f = *vf;
 
-    float yL, yB, yH;
-
-    // compute highpass output via Eq. 5.1:
-    yH = (in - f->R2*f->s1 - f->g*f->s1 - f->s2) * f->h;
-
-    // compute bandpass output by applying 1st integrator to highpass output:
-    yB = (f->g*yH) + f->s1;
-    f->s1 = f->g*yH + yB; // state update in 1st integrator
-
-    // compute lowpass output by applying 2nd integrator to bandpass output:
-    yL = (f->g*yB) + f->s2;
-    f->s2 = f->g*yB + yL; // state update in 2nd integrator
-
-    //according to the Vadim paper, we could add saturation to this model by adding a tanh in the integration stage.
-    //
-    //seems like that might look like this:
-    // y = tanh(g*x) + s; // output computation
-    // s = g*x + y; // state update
-
-    //instead of this:
-    // y = g*x + s; // output computation
-    // s = g*x + y; // state update
-
-    return f->cL*yL + f->cB*yB + f->cH*yH;
-
-}
-
-
 void   tVZFilter_calcCoeffs           (tVZFilter* const vf)
 {
 
@@ -1458,7 +1427,19 @@
       f->h = 1.0f / (1.0f + f->R2*f->g + f->g*f->g);  // factor for feedback precomputation
 }
 
+void   tVZFilter_calcCoeffsEfficientBP           (tVZFilter* const vf)
+{
 
+    _tVZFilter* f = *vf;
+    f->g = fastertanf(PI * f->fc * f->inv_sr);  // embedded integrator gain (Fig 3.11)
+    f->R2 = 2.0f*tVZFilter_BandwidthToR(vf, f->B);
+    f->cB = f->R2;
+    f->h = 1.0f / (1.0f + f->R2*f->g + f->g*f->g);  // factor for feedback precomputation
+}
+
+
+
+
 void   tVZFilter_setBandwidth               (tVZFilter* const vf, float B)
 {
     _tVZFilter* f = *vf;
@@ -1483,14 +1464,28 @@
     tVZFilter_calcCoeffs(vf);
 }
 
+void   tVZFilter_setFreqAndBandwidthEfficientBP           (tVZFilter* const vf, float freq, float bw)
+{
+    _tVZFilter* f = *vf;
+    LEAF* leaf = f->mempool->leaf;
+
+    f->B = LEAF_clip(0.0f,bw, 100.0f);
+    f->fc = LEAF_clip(0.0f, freq, 0.5f * leaf->sampleRate);
+    tVZFilter_calcCoeffsEfficientBP(vf);
+}
+
+
+
 void   tVZFilter_setGain                (tVZFilter* const vf, float gain)
 {
     _tVZFilter* f = *vf;
-    f->G = LEAF_clip(0.000001f, gain, 100.0f);
+    f->G = LEAF_clip(0.000001f, gain, 4000.0f);
     f->invG = 1.0f/f->G;
     tVZFilter_calcCoeffs(vf);
 }
 
+
+
 void   tVZFilter_setMorph               (tVZFilter* const vf, float morph)
 {
     _tVZFilter* f = *vf;
@@ -1516,8 +1511,20 @@
   return sqrtf((1.0f-r*r)*(1.0f-r*r)/(4.0f*r*r));
 }
 
+float tVZFilter_BandwidthToREfficientBP(tVZFilter* const vf, float B)
+{
+    _tVZFilter* f = *vf;
+  float fl = f->fc*fastPowf(2.0f, -B * 0.5f); // lower bandedge frequency (in Hz)
+  float gl = fastertanf(PI*fl*f->inv_sr);   // warped radian lower bandedge frequency /(2*fs)
+  float r  = gl/f->g;            // ratio between warped lower bandedge- and center-frequencies
+                               // unwarped: r = pow(2, -B/2) -> approximation for low
+                               // center-frequencies
+  return fastsqrtf((1.0f-r*r)*(1.0f-r*r)/(4.0f*r*r));
+}
 
 
+
+
 void    tDiodeFilter_init           (tDiodeFilter* const vf, float cutoff, float resonance, LEAF* const leaf)
 {
     tDiodeFilter_initToPool(vf, cutoff, resonance, &leaf->mempool);
@@ -1557,7 +1564,15 @@
 {
     float a = x*x;
     // IIRC I got this as Pade-approx for tanh(sqrt(x))/sqrt(x)
-    return ((a + 105.0f)*a + 945.0f) / ((15.0f*a + 420.0f)*a + 945.0f);
+    float testVal = ((15.0f*a + 420.0f)*a + 945.0f);
+    float output = 1.0f;
+
+    if (testVal!= 0.0f)
+    {
+    	output = testVal;
+
+    }
+    return ((a + 105.0f)*a + 945.0f) / output;
 }
 
 float   tDiodeFilter_tick               (tDiodeFilter* const vf, float in)
@@ -1564,6 +1579,7 @@
 {
     _tDiodeFilter* f = *vf;
 
+    int errorCheck = 0;
     // the input x[n+1] is given by 'in', and x[n] by zi
     // input with half delay
     float ih = 0.5f * (in + f->zi);
@@ -1579,24 +1595,24 @@
 
     // This formula gives the result for y3 thanks to MATLAB
     float y3 = (f->s2 + f->s3 + t2*(f->s1 + f->s2 + f->s3 + t1*(f->s0 + f->s1 + f->s2 + f->s3 + t0*in)) + t1*(2.0f*f->s2 + 2.0f*f->s3))*t3 + f->s3 + 2.0f*f->s3*t1 + t2*(2.0f*f->s3 + 3.0f*f->s3*t1);
-//    if (isnan(y3))
-//    {
-//        __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400);
-//    }
+    if (isnan(y3))
+    {
+    	errorCheck = 1;
+    }
     float tempy3denom = (t4 + t1*(2.0f*t4 + 4.0f) + t2*(t4 + t1*(t4 + f->r*t0 + 4.0f) + 3.0f) + 2.0f)*t3 + t4 + t1*(2.0f*t4 + 2.0f) + t2*(2.0f*t4 + t1*(3.0f*t4 + 3.0f) + 2.0f) + 1.0f;
-//    if (isnan(tempy3denom))
-//    {
-//        __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400);
-//    }
+    if (isnan(tempy3denom))
+    {
+    	errorCheck = 2;
+    }
     if (tempy3denom == 0.0f)
     {
         tempy3denom = 0.000001f;
     }
     y3 = y3 / tempy3denom;
-//    if (isnan(y3))
-//    {
-//        __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400);
-//    }
+    if (isnan(y3))
+    {
+    	errorCheck = 3;
+    }
     if (t1 == 0.0f)
     {
         t1 = 0.000001f;
@@ -1617,15 +1633,15 @@
 
     // update state
     f->s0 += 2.0f * (t0*xx + t1*(y1-y0));
-//    if (isnan(f->s0))
-//    {
-//        __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400);
-//    }
+    if (isnan(f->s0))
+    {
+    	errorCheck = 4;
+    }
 
-//    if (isinf(f->s0))
-//    {
-//        __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400);
-//    }
+    if (isinf(f->s0))
+    {
+    	errorCheck = 5;
+    }
     f->s1 += 2.0f * (t2*(y2-y1) - t1*(y1-y0));
     f->s2 += 2.0f * (t3*(y3-y2) - t2*(y2-y1));
     f->s3 += 2.0f * (-t4*(y3) - t3*(y3-y2));
--- a/leaf/Src/leaf-math.c
+++ b/leaf/Src/leaf-math.c
@@ -99,8 +99,48 @@
     return fResult;
 }
 
+float fasttanf (float fAngle)
+{
+    float fASqr = fAngle*fAngle;
+    float fResult = 9.5168091e-03f;
+    fResult *= fASqr;
+    fResult += 2.900525e-03f;
+    fResult *= fASqr;
+    fResult += 2.45650893e-02f;
+    fResult *= fASqr;
+    fResult += 5.33740603e-02f;
+    fResult *= fASqr;
+    fResult += 1.333923995e-01f;
+    fResult *= fASqr;
+    fResult += 3.333314036e-01f;
+    fResult *= fASqr;
+    fResult += 1.0f;
+    fResult *= fAngle;
+    return fResult;
+}
 
+float fastertanf(float fAngle)
+{
+    float fASqr = fAngle*fAngle;
+    float fResult = 2.033e-01f;
+    fResult *= fASqr;
+    fResult += 3.1755e-01f;
+    fResult *= fASqr;
+    fResult += 1.0f;
+    fResult *= fAngle;
+    return fResult;
+}
 
+// from Heng Li, a combination of inverse square root (see wiki) and inversion: https://bits.stephan-brumme.com/inverse.html
+float fastsqrtf(float x)
+{
+	union { float f; uint32_t i; } z = { x };
+	z.i  = 0x5f3759df - (z.i >> 1);
+	z.f *= (1.5f - (x * 0.5f * z.f * z.f));
+	z.i = 0x7EEEEEEE - z.i;
+	return z.f;
+}
+
 // alternative implementation for abs()
 // REQUIRES: 32 bit integers
 int fastabs_int(int in){
@@ -114,7 +154,7 @@
 
 // alternative implementation for abs()
 // REQUIRES: 32 bit floats
-inline float fastabsf(float f)
+float fastabsf(float f)
 {
     union
     {
--- a/leaf/Src/leaf-mempool.c
+++ b/leaf/Src/leaf-mempool.c
@@ -410,7 +410,7 @@
 
 void tMempool_init(tMempool* const mp, char* memory, size_t size, LEAF* const leaf)
 {
-    tMempool_initToPool(mp, memory, size, &leaf->mempool);
+    tMempool_initToPool(mp, memory, size, &leaf->mempool, leaf);
 }
 
 void tMempool_free(tMempool* const mp)
@@ -420,10 +420,11 @@
     mpool_free((char*)m, m->mempool);
 }
 
-void    tMempool_initToPool     (tMempool* const mp, char* memory, size_t size, tMempool* const mem)
+void    tMempool_initToPool     (tMempool* const mp, char* memory, size_t size, tMempool* const mem, LEAF* const leaf)
 {
     _tMempool* mm = *mem;
     _tMempool* m = *mp = (_tMempool*) mpool_alloc(sizeof(_tMempool), mm);
-    
+    m->leaf = leaf;
     mpool_create (memory, size, m);
 }
+
--- a/leaf/Src/leaf-sampling.c
+++ b/leaf/Src/leaf-sampling.c
@@ -26,10 +26,10 @@
 
 void  tBuffer_init (tBuffer* const sb, uint32_t length, LEAF* const leaf)
 {
-    tBuffer_initToPool(sb, length, &leaf->mempool);
+    tBuffer_initToPool(sb, length, &leaf->mempool, leaf);
 }
 
-void  tBuffer_initToPool (tBuffer* const sb, uint32_t length, tMempool* const mp)
+void  tBuffer_initToPool (tBuffer* const sb, uint32_t length, tMempool* const mp, LEAF* const leaf)
 {
     _tMempool* m = *mp;
     _tBuffer* s = *sb = (_tBuffer*) mpool_alloc(sizeof(_tBuffer), m);
@@ -36,7 +36,8 @@
     s->mempool = m;
     
     s->buff = (float*) mpool_alloc( sizeof(float) * length, m);
-    
+    s->sampleRate = leaf->sampleRate;
+    s->channels = 1;
     s->bufferLength = length;
     s->recordedLength = 0;
     s->active = 0;
@@ -136,6 +137,17 @@
 
 }
 
+void tBuffer_setBuffer(tBuffer* const sb, float* externalBuffer, int length, int channels, int sampleRate)
+{
+    _tBuffer* s = *sb;
+
+    s->buff = externalBuffer;
+    s->channels = channels;
+    s->sampleRate = sampleRate;
+    s->recordedLength = length/channels;
+    s->bufferLength = s->recordedLength;
+}
+
 uint32_t tBuffer_getBufferLength(tBuffer* const sb)
 {
     _tBuffer* s = *sb;
@@ -168,10 +180,10 @@
 
 void tSampler_init(tSampler* const sp, tBuffer* const b, LEAF* const leaf)
 {
-    tSampler_initToPool(sp, b, &leaf->mempool);
+    tSampler_initToPool(sp, b, &leaf->mempool, leaf);
 }
 
-void tSampler_initToPool(tSampler* const sp, tBuffer* const b, tMempool* const mp)
+void tSampler_initToPool(tSampler* const sp, tBuffer* const b, tMempool* const mp, LEAF* const leaf)
 {
     _tMempool* m = *mp;
     _tSampler* p = *sp = (_tSampler*) mpool_alloc(sizeof(_tSampler), m);
@@ -179,18 +191,36 @@
     
     _tBuffer* s = *b;
     
+    p->leafInvSampleRate = leaf->invSampleRate;
+    p->leafSampleRate = leaf->sampleRate;
+    p->ticksPerSevenMs = 0.007f * p->leafSampleRate;
+    p->rateFactor = s->sampleRate * p->leafInvSampleRate;
+    p->channels = s->channels;
+
+
     p->samp = s;
     
     p->active = 0;
     
     p->start = 0;
-    p->end = 0;
+    p->end = p->samp->bufferLength - 1;
     
     p->len = p->end - p->start;
     
     p->idx = 0.f;
-    p->inc = 1.f;
-    p->iinc = 1.f;
+    float rate = p->rateFactor; //adjust for sampling rate of buffer (may be different from leaf.sampleRate if audio file was loaded form SD card)
+    if (rate < 0.f)
+    {
+        rate = -rate;
+        p->dir = -1;
+    }
+    else
+    {
+        p->dir = 1;
+    }
+
+    p->inc = rate;
+    p->iinc = 1.f / p->inc;
     
     p->dir = 1;
     p->flip = 1;
@@ -200,7 +230,7 @@
     
     p->cfxlen = 500; // default 300 sample crossfade
     
-    tRamp_initToPool(&p->gain, 7.0f, 1, mp);
+    tRamp_initToPool(&p->gain, 5.0f, 1, mp);
     tRamp_setVal(&p->gain, 0.f);
     
     p->targetstart = -1;
@@ -226,6 +256,10 @@
     
     p->samp = s;
     
+
+    p->rateFactor = s->sampleRate * p->leafInvSampleRate;
+    p->channels = s->channels;
+
     p->start = 0;
     p->end = p->samp->bufferLength - 1;
     
@@ -452,7 +486,7 @@
             p->idx = myEnd;
         }
         float ticksToEnd = rev ? ((idx - myStart) * p->iinc) : ((myEnd - idx) * p->iinc);
-        if (ticksToEnd < (0.007f * leaf->sampleRate))
+        if ((ticksToEnd < p->ticksPerSevenMs) && (p->active == 1))
         {
             tRamp_setDest(&p->gain, 0.f);
             p->active = -1;
@@ -500,6 +534,299 @@
     return p->last;
 }
 
+
+float tSampler_tickStereo        (tSampler* const sp, float* outputArray)
+{
+    _tSampler* p = *sp;
+    LEAF* leaf = p->mempool->leaf;
+
+    attemptStartEndChange(sp);
+
+    if (p->active == 0)         return 0.f;
+
+    if ((p->inc == 0.0f) || (p->len < 2))
+    {
+        //        p->inCrossfade = 1;
+        return p->last;
+    }
+
+    float cfxsample[2] = {0.0f, 0.0f};
+    float crossfadeMix = 0.0f;
+    float flipsample[2] = {0.0f, 0.0f};
+    float flipMix = 0.0f;
+
+    float* buff = p->samp->buff;
+
+    // Variables so start is also before end
+    int myStart = p->start;
+    int myEnd = p->end;
+    if (p->flip < 0)
+    {
+        myStart = p->end;
+        myEnd = p->start;
+    }
+
+    // Get the direction and a reverse flag for some calcs
+    int dir = p->bnf * p->dir * p->flip;
+    int rev = 0;
+    if (dir < 0) rev = 1;
+
+    // Get the current integer index and alpha for interpolation
+    int idx = (int) p->idx;
+    float alpha = rev + (p->idx - idx) * dir;
+    idx += rev;
+
+    // Get the indexes for interpolation
+    int i1 = idx-(1*dir);
+    int i2 = idx;
+    int i3 = idx+(1*dir);
+    int i4 = idx+(2*dir);
+
+    int length = p->samp->recordedLength;
+
+    // Wrap as needed
+    i1 = (i1 < length*rev) ? i1 + (length * (1-rev)) : i1 - (length * rev);
+    i2 = (i2 < length*rev) ? i2 + (length * (1-rev)) : i2 - (length * rev);
+    i3 = (i3 < length*(1-rev)) ? i3 + (length * rev) : i3 - (length * (1-rev));
+    i4 = (i4 < length*(1-rev)) ? i4 + (length * rev) : i4 - (length * (1-rev));
+
+    outputArray[0] = LEAF_interpolate_hermite_x (buff[i1 * p->channels],
+                                         buff[i2 * p->channels],
+                                         buff[i3 * p->channels],
+                                         buff[i4 * p->channels],
+                                         alpha);
+
+    outputArray[1] = LEAF_interpolate_hermite_x (buff[(i1 * p->channels) + 1],
+                                         buff[(i2 * p->channels) + 1],
+                                         buff[(i3 * p->channels) + 1],
+                                         buff[(i4 * p->channels) + 1],
+                                         alpha);
+
+    int32_t cfxlen = p->cfxlen;
+    if (p->len * 0.25f < cfxlen) cfxlen = p->len * 0.25f;
+
+    // Determine crossfade points
+    int32_t fadeLeftStart = 0;
+    if (myStart >= cfxlen) fadeLeftStart = myStart - cfxlen;
+    int32_t fadeLeftEnd = fadeLeftStart + cfxlen;
+
+    int32_t fadeRightEnd = myEnd;// + (fadeLeftEnd - start);
+    //    if (fadeRightEnd >= length) fadeRightEnd = length - 1;
+    int32_t fadeRightStart = fadeRightEnd - cfxlen;
+
+
+
+
+    if (p->mode == PlayLoop)
+    {
+
+        int offset = 0;
+        int cdx = 0;
+        if ((fadeLeftStart <= idx) && (idx <= fadeLeftEnd))
+        {
+            offset = fadeLeftEnd - idx;
+            cdx = fadeRightEnd - offset;
+            p->inCrossfade = 1;
+        }
+        else if ((fadeRightStart <= idx) && (idx <= fadeRightEnd))
+        {
+            offset = idx - fadeRightStart;
+            cdx = fadeLeftStart + offset;
+            p->inCrossfade = 1;
+        }
+        else p->inCrossfade = 0;
+
+        if (p->inCrossfade)
+        {
+            int c1 = cdx-(1*dir);
+            int c2 = cdx;
+            int c3 = cdx+(1*dir);
+            int c4 = cdx+(2*dir);
+
+            // Wrap as needed
+            c1 = (c1 < length * rev) ? c1 + (length * (1-rev)) : c1 - (length * rev);
+            c2 = (c2 < length * rev) ? c2 + (length * (1-rev)) : c2 - (length * rev);
+            c3 = (c3 < length * (1-rev)) ? c3 + (length * rev) : c3 - (length * (1-rev));
+            c4 = (c4 < length * (1-rev)) ? c4 + (length * rev) : c4 - (length * (1-rev));
+
+            cfxsample[0] = LEAF_interpolate_hermite_x (buff[c1 * p->channels],
+                                                    buff[c2 * p->channels],
+                                                    buff[c3 * p->channels],
+                                                    buff[c4 * p->channels],
+                                                    alpha);
+
+            cfxsample[1] = LEAF_interpolate_hermite_x (buff[(c1 * p->channels) + 1],
+                                                                buff[(c2 * p->channels) + 1],
+                                                                buff[(c3 * p->channels) + 1],
+                                                                buff[(c4 * p->channels) + 1],
+                                                                alpha);
+
+            crossfadeMix = (float) offset / (float) cfxlen;
+        }
+
+        float flipLength = fabsf(p->flipIdx - p->flipStart);
+        if (flipLength > cfxlen)
+        {
+            p->flipStart = -1;
+            p->flipIdx = -1;
+        }
+        if (p->flipIdx >= 0)
+        {
+            if (p->flipStart == -1)
+            {
+                p->flipStart = p->idx;
+                p->flipIdx = p->idx;
+            }
+            flipLength = fabsf(p->flipIdx - p->flipStart);
+
+            int fdx = (int) p->flipIdx;
+            float falpha = (1-rev) - (p->flipIdx - fdx) * dir;
+            idx += (1-rev);
+
+            // Get the indexes for interpolation
+            int f1 = fdx+(1*dir);
+            int f2 = fdx;
+            int f3 = fdx-(1*dir);
+            int f4 = fdx-(2*dir);
+
+            // Wrap as needed
+            f1 = (f1 < length*(1-rev)) ? f1 + (length * rev) : f1 - (length * (1-rev));
+            f2 = (f2 < length*(1-rev)) ? f2 + (length * rev) : f2 - (length * (1-rev));
+            f3 = (f3 < length*rev) ? f3 + (length * (1-rev)) : f3 - (length * rev);
+            f4 = (f4 < length*rev) ? f4 + (length * (1-rev)) : f4 - (length * rev);
+
+            flipsample[0] = LEAF_interpolate_hermite_x (buff[f1 * p->channels],
+                                                     buff[f2 * p->channels],
+                                                     buff[f3 * p->channels],
+                                                     buff[f4 * p->channels],
+                                                     falpha);
+
+            flipsample[1] = LEAF_interpolate_hermite_x (buff[(f1 * p->channels) + 1],
+                                                     buff[(f2 * p->channels) + 1],
+                                                     buff[(f3 * p->channels) + 1],
+                                                     buff[(f4 * p->channels) + 1],
+                                                     falpha);
+
+            flipMix = (float) (cfxlen - flipLength) / (float) cfxlen;
+        }
+    }
+
+    float inc = fmodf(p->inc, (float)p->len);
+    p->idx += (dir * inc);
+    if (p->flipStart >= 0)
+    {
+        p->flipIdx += (-dir * inc);
+        if((int)p->flipIdx < 0)
+        {
+            p->idx += (float)length;
+        }
+        if((int)p->idx >= length)
+        {
+
+            p->idx -= (float)length;
+        }
+    }
+
+
+
+    attemptStartEndChange(sp);
+
+
+    if (p->mode == PlayLoop)
+    {
+        if((int)p->idx < myStart)
+        {
+            p->idx += (float)(fadeRightEnd - fadeLeftEnd);
+        }
+        if((int)p->idx > myEnd)
+        {
+
+            p->idx -= (float)(fadeRightEnd - fadeLeftEnd);
+        }
+    }
+    else if (p->mode == PlayBackAndForth)
+    {
+        if (p->idx < myStart)
+        {
+            p->bnf = -p->bnf;
+            p->idx = myStart + 1;
+        }
+        else if (p->idx > myEnd)
+        {
+            p->bnf = -p->bnf;
+            p->idx = myEnd - 1;
+        }
+    }
+
+
+    if (p->mode == PlayNormal)
+    {
+        if (p->idx < myStart)
+        {
+            p->idx = myStart;
+        }
+        else if (p->idx > myEnd)
+        {
+            p->idx = myEnd;
+        }
+        float ticksToEnd = rev ? ((idx - myStart) * p->iinc) : ((myEnd - idx) * p->iinc);
+        if ((ticksToEnd < p->ticksPerSevenMs) && (p->active == 1))
+        {
+            tRamp_setDest(&p->gain, 0.f);
+            p->active = -1;
+        }
+    }
+
+    float sampleGain = tRamp_tick(&p->gain);
+    for (int i = 0; i < p->channels; i++)
+    {
+        outputArray[i] = ((outputArray[i] * (1.0f - crossfadeMix)) + (cfxsample[i] * crossfadeMix)) * (1.0f - flipMix) + (flipsample[i] * flipMix);
+        outputArray[i]  = outputArray[i] * sampleGain;
+    }
+
+
+
+
+    if (p->active < 0)
+    {
+        //if was fading out and reached silence
+    	if (tRamp_sample(&p->gain) <= 0.0001f)
+        {
+            if (p->retrigger == 1)
+            {
+                p->active = 1;
+                p->retrigger = 0;
+                tRamp_setDest(&p->gain, 1.f);
+
+                if (p->dir > 0)
+                {
+                    if (p->flip > 0)    p->idx = p->start;
+                    else                p->idx = p->end;
+                }
+                else
+                {
+                    if (p->flip > 0)    p->idx = p->end;
+                    else                p->idx = p->start;
+                }
+                handleStartEndChange(&p);
+            }
+            else
+            {
+                p->active = 0;
+            }
+
+        }
+    }
+
+
+
+    p->last =  outputArray[0];
+
+
+    return p->last;
+}
+
+
 void tSampler_setMode      (tSampler* const sp, PlayMode mode)
 {
     _tSampler* p = *sp;
@@ -518,8 +845,8 @@
 void tSampler_play         (tSampler* const sp)
 {
     _tSampler* p = *sp;
-    
-    if (p->active != 0)
+
+    if (p->active > 0)
     {
         p->active = -1;
         p->retrigger = 1;
@@ -526,6 +853,15 @@
         
         tRamp_setDest(&p->gain, 0.f);
     }
+
+    else if (p->active < 0)
+    {
+        p->active = -1;
+        p->retrigger = 1;
+
+        //tRamp_setDest(&p->gain, 0.f);
+    }
+
     else
     {
         p->active = 1;
@@ -740,6 +1076,7 @@
 {
     _tSampler* p = *sp;
     
+    rate = rate * p->rateFactor; //adjust for sampling rate of buffer (may be different from leaf.sampleRate if audio file was loaded form SD card)
     if (rate < 0.f)
     {
         rate = -rate;
@@ -758,10 +1095,10 @@
 
 void    tAutoSampler_init   (tAutoSampler* const as, tBuffer* const b, LEAF* const leaf)
 {
-    tAutoSampler_initToPool(as, b, &leaf->mempool);
+    tAutoSampler_initToPool(as, b, &leaf->mempool, leaf);
 }
 
-void    tAutoSampler_initToPool (tAutoSampler* const as, tBuffer* const b, tMempool* const mp)
+void    tAutoSampler_initToPool (tAutoSampler* const as, tBuffer* const b, tMempool* const mp, LEAF* const leaf)
 {
     _tMempool* m = *mp;
     _tAutoSampler* a = *as = (_tAutoSampler*) mpool_alloc(sizeof(_tAutoSampler), m);
@@ -768,7 +1105,7 @@
     a->mempool = m;
     
     tBuffer_setRecordMode(b, RecordOneShot);
-    tSampler_initToPool(&a->sampler, b, mp);
+    tSampler_initToPool(&a->sampler, b, mp, leaf);
     tSampler_setMode(&a->sampler, PlayLoop);
     tEnvelopeFollower_initToPool(&a->ef, 0.05f, 0.9999f, mp);
 }
@@ -939,7 +1276,7 @@
 {
     _tMBSampler* c = *sp;
     
-    if (c->gain->curr == 0.0f && !c->active) return 0.0f;
+    if ((c->gain->curr == 0.0f) && (!c->active)) return 0.0f;
     if (c->_w == 0.0f)
     {
         c->_last_w = 0.0f;
@@ -1031,8 +1368,10 @@
             float nextSlope = (afterNext - next) / w;
             float lastSlope = (last - beforeLast) / w;
             place_slope_dd(c->_f, j, p - start, w, nextSlope - lastSlope);
-            
-            if (c->mode == PlayNormal) c->active = 0;
+            if (c->mode == PlayNormal)
+            {
+            	c->active = 0;
+            }
             else if (c->mode == PlayBackAndForth) w = -w;
         }
 //        else if (p < (float) start) { /* start has been set ahead of the current phase */
@@ -1056,6 +1395,13 @@
             int i = (int) f;
             f -= i;
             next = buff[i] * (1.0f - f) + buff[i+1] * f;
+
+
+            if ((end - p) < 480) // 480 samples should be enough to let the tExpSmooth go from 1 to 0 (10ms at 48k, 5ms at 192k)
+            if (c->mode == PlayNormal)
+            {
+            	tExpSmooth_setDest(&c->gain, 0.0f);
+            }
         }
 
         if (c->_last_w < 0.0f)
@@ -1095,7 +1441,10 @@
             float lastSlope = (last - beforeLast) / w;
             place_slope_dd(c->_f, j, end - p, w, nextSlope - lastSlope);
             
-            if (c->mode == PlayNormal) c->active = 0;
+            if (c->mode == PlayNormal)
+            {
+            	c->active = 0;
+            }
             else if (c->mode == PlayBackAndForth) w = -w;
         }
 //        else if (p > (float) end) {
@@ -1115,6 +1464,11 @@
 //        }
         else {
             
+            if ((p - start) < 480) // 480 samples should be enough to let the tExpSmooth go from 1 to 0 (10ms at 48k, 5ms at 192k)
+            if (c->mode == PlayNormal)
+            {
+            	tExpSmooth_setDest(&c->gain, 0.0f);
+            }
             float f = p;
             int i = (int) f;
             f -= i;