shithub: leaf

Download patch

ref: debdbb428d99c40ca143dc6d475d13d3e6f05d61
parent: a1615efe9c4e692b81776df864c1244b218e433a
author: spiricom <jeff@snyderphonics.com>
date: Fri Feb 19 14:11:50 EST 2021

added tLivingString2 and changed tHermiteDelay to use only power of 2 buffer sizes and do an integer mask to avoid modulo operators

--- a/leaf/Inc/leaf-delay.h
+++ b/leaf/Inc/leaf-delay.h
@@ -317,6 +317,13 @@
      @param position
      @return
      
+     @fn float   tHermiteDelay_tapOutInterpolated (tHermiteDelay* const dl, uint32_t tapDelay, float alpha);
+     @brief
+     @param delay A pointer to the relevant tHermiteDelay.
+     @param integer position
+     @param fractional portion of the position
+     @return
+     
      @fn void     tHermiteDelay_tapIn         (tHermiteDelay* const dl, float value, uint32_t tapDelay)
      @brief
      @param delay A pointer to the relevant tHermiteDelay.
@@ -361,15 +368,14 @@
     {
         tMempool mempool;
         
-        float gain;
         float* buff;
-        
+        uint32_t bufferMask;
         float lastOut, lastIn;
         
         uint32_t inPoint, outPoint;
         
         uint32_t maxDelay;
-        
+        float gain;
         float delay;
         
         float alpha, omAlpha;
@@ -387,6 +393,7 @@
     float   tHermiteDelay_tickOut       (tHermiteDelay* const dl);
     void    tHermiteDelay_setDelay      (tHermiteDelay* const dl, float delay);
     float   tHermiteDelay_tapOut        (tHermiteDelay* const dl, uint32_t tapDelay);
+    float   tHermiteDelay_tapOutInterpolated (tHermiteDelay* const dl, uint32_t tapDelay, float alpha);
     void    tHermiteDelay_tapIn         (tHermiteDelay* const dl, float value, uint32_t tapDelay);
     float   tHermiteDelay_addTo         (tHermiteDelay* const dl, float value, uint32_t tapDelay);
     float   tHermiteDelay_getDelay      (tHermiteDelay* const dl);
--- a/leaf/Inc/leaf-physical.h
+++ b/leaf/Inc/leaf-physical.h
@@ -444,7 +444,133 @@
     void    tLivingString_setLevMode            (tLivingString* const, int levMode);
     
 
+  // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+   
+    
+    /*!
+     @defgroup tlivingstring2 tLivingString2
+     @ingroup physical
+     @brief String model with preparation and pick position separated.
+     @{
+     
+     @fn void    tLivingString2_init                  (tLivingString2* const, float freq, float pickPos, float prepIndex, float dampFreq, float decay, float targetLev, float levSmoothFactor, float levStrength, int levMode, LEAF* const leaf)
+     @brief Initialize a tLivingString to the default mempool of a LEAF instance.
+     @param string A pointer to the tLivingString2 to initialize.
+     @param leaf A pointer to the leaf instance.
+     
+     @fn void    tLivingString2_initToPool            (tLivingString2* const, float freq, float pickPos, float prepIndex, float dampFreq, float decay, float targetLev, float levSmoothFactor, float levStrength, int levMode, tMempool* const)
+     @brief Initialize a tLivingString2 to a specified mempool.
+     @param string A pointer to the tLivingString2 to initialize.
+     @param mempool A pointer to the tMempool to use.
+     
+     @fn void    tLivingString2_free                  (tLivingString2* const)
+     @brief Free a tLivingString2 from its mempool.
+     @param string A pointer to the tLivingString2 to free.
+     
+     @fn float   tLivingString2_tick                  (tLivingString2* const, float input)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     
+     @fn float   tLivingString2_sample                (tLivingString2* const)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     
+     @fn void    tLivingString2_setFreq               (tLivingString2* const, float freq)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     
+     @fn void    tLivingString2_setWaveLength         (tLivingString2* const, float waveLength)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     
+     @fn void    tLivingString2_setPickPos            (tLivingString2* const, float pickPos)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     
+     @fn void    tLivingString2_setPrepPosition          (tLivingString2* const, float prepPos)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     
+     
+     @fn void    tLivingString2_setPrepIndex          (tLivingString2* const, float prepIndex)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     
+     @fn void    tLivingString2_setBrightness           (tLivingString2* const, float brightness)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     @param float The brightness parameter from 0 to 1.
+     
+     @fn void    tLivingString2_setDecay              (tLivingString2* const, float decay)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     @param float The decay parameter from 0 to 1.
+     
+     @fn void    tLivingString2_setTargetLev          (tLivingString2* const, float targetLev)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     
+     @fn void    tLivingString2_setLevSmoothFactor    (tLivingString2* const, float levSmoothFactor)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     
+     @fn void    tLivingString2_setLevStrength        (tLivingString2* const, float levStrength)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     
+     @fn void    tLivingString2_setLevMode            (tLivingString2* const, int levMode)
+     @brief
+     @param string A pointer to the relevant tLivingString2.
+     
+     @} */
+    typedef struct _tLivingString2
+        {
 
+            tMempool mempool;
+            float freq, waveLengthInSamples;        // the frequency of the whole string, determining delay length
+            float pickPos;    // the pick position, dividing the string in two, in ratio
+            float prepPos;    // the preparation position, dividing the string in two, in ratio
+            float pickupPos;    // the preparation position, dividing the string in two, in ratio
+            float prepIndex;    // the amount of pressure on the preparation position of the string (near 0=soft obj, near 1=hard obj)
+            float decay; // amplitude damping factor for the string (only active in mode 0)
+            int levMode;
+            float brightness;
+            float curr;
+            tHermiteDelay delLF,delUF,delUB,delLB;    // delay for lower/upper/forward/backward part of the waveguide model
+            tTwoZero bridgeFilter, nutFilter, prepFilterU, prepFilterL;
+            tHighpass DCblockerL, DCblockerU;
+            tFeedbackLeveler fbLevU, fbLevL;
+            tExpSmooth wlSmooth, ppSmooth, prpSmooth, puSmooth;
+        } _tLivingString2;
+
+        typedef _tLivingString2* tLivingString2;
+
+        void    tLivingString2_init                  (tLivingString2* const, float freq, float pickPos, float prepPos, float pickupPos, float prepIndex,
+                                                     float brightness, float decay, float targetLev, float levSmoothFactor,
+                                                     float levStrength, int levMode, LEAF* const leaf);
+        void    tLivingString2_initToPool            (tLivingString2* const, float freq, float pickPos, float prepPos, float pickupPos, float prepIndex,
+                                                     float brightness, float decay, float targetLev, float levSmoothFactor,
+                                                     float levStrength, int levMode, tMempool* const);
+        void    tLivingString2_free                  (tLivingString2* const);
+
+        float   tLivingString2_tick                  (tLivingString2* const, float input);
+        float   tLivingString2_sample                (tLivingString2* const);
+        void    tLivingString2_setFreq               (tLivingString2* const, float freq);
+        void    tLivingString2_setWaveLength         (tLivingString2* const, float waveLength); // in samples
+        void    tLivingString2_setPickPos            (tLivingString2* const, float pickPos);
+        void    tLivingString2_setPrepPos            (tLivingString2* const, float prepPos);
+        void    tLivingString2_setPickupPos            (tLivingString2* const, float pickupPos);
+        void    tLivingString2_setPrepIndex          (tLivingString2* const, float prepIndex);
+        void    tLivingString2_setBrightness         (tLivingString2* const, float brightness);
+        void    tLivingString2_setDecay              (tLivingString2* const, float decay); // from 0 to 1, gets converted to real decay factor
+        void    tLivingString2_setTargetLev          (tLivingString2* const, float targetLev);
+        void    tLivingString2_setLevSmoothFactor    (tLivingString2* const, float levSmoothFactor);
+        void    tLivingString2_setLevStrength        (tLivingString2* const, float levStrength);
+        void    tLivingString2_setLevMode            (tLivingString2* const, int levMode);
+
+
+
+            // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
         // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
 
     /*!
--- a/leaf/Src/leaf-delay.c
+++ b/leaf/Src/leaf-delay.c
@@ -368,6 +368,25 @@
     else if (delay < 0.0f)  d->delay = 0.0f;
     else                    d->delay = delay;
 
+    
+    if ((maxDelay != 0) && ((maxDelay & (maxDelay - 1)) == 0))
+    {
+        d->maxDelay = maxDelay;
+        d->bufferMask = maxDelay - 1;
+    }
+    else
+    {
+        //make the delay size into a power of 2
+        maxDelay--;
+        maxDelay |= maxDelay >> 1;
+        maxDelay |= maxDelay >> 2;
+        maxDelay |= maxDelay >> 4;
+        maxDelay |= maxDelay >> 8;
+        maxDelay |= maxDelay >> 16;
+        maxDelay++;
+        d->maxDelay = maxDelay;
+        d->bufferMask = maxDelay - 1;
+    }
     d->buff = (float*) mpool_alloc(sizeof(float) * maxDelay, m);
 
     d->gain = 1.0f;
@@ -405,19 +424,20 @@
 
     d->buff[d->inPoint] = input * d->gain;
 
+    
     // Increment input pointer modulo length.
-    if (++(d->inPoint) == d->maxDelay )    d->inPoint = 0;
-
-
+    d->inPoint = (d->inPoint + 1) & d->bufferMask;
+    
+    
     uint32_t idx = (uint32_t) d->outPoint;
-    d->lastOut =    LEAF_interpolate_hermite (d->buff[((idx - 1) + d->maxDelay) % d->maxDelay],
-                                              d->buff[idx],
-                                              d->buff[(idx + 1) % d->maxDelay],
-                                              d->buff[(idx + 2) % d->maxDelay],
-                                              d->alpha);
-
+    d->lastOut =    LEAF_interpolate_hermite_x (d->buff[((idx - 1) + d->maxDelay) & d->bufferMask],
+                                                d->buff[idx],
+                                                d->buff[(idx + 1) & d->bufferMask],
+                                                d->buff[(idx + 2) & d->bufferMask],
+                                                d->alpha);
+    
     // Increment output pointer modulo length
-    if ( (++d->outPoint) >= d->maxDelay )   d->outPoint = 0;
+    d->outPoint = (d->outPoint + 1) & d->bufferMask;
 
     return d->lastOut;
 }
@@ -425,30 +445,30 @@
 void   tHermiteDelay_tickIn (tHermiteDelay* const dl, float input)
 {
     _tHermiteDelay* d = *dl;
-
-    d->buff[d->inPoint] = input * d->gain;
-
+    
+    d->buff[d->inPoint] = input;
+    
     // Increment input pointer modulo length.
-    if (++(d->inPoint) == d->maxDelay )    d->inPoint = 0;
+    d->inPoint = (d->inPoint + 1) & d->bufferMask;
 }
 
 float   tHermiteDelay_tickOut (tHermiteDelay* const dl)
 {
     _tHermiteDelay* d = *dl;
-
+    
     uint32_t idx = (uint32_t) d->outPoint;
-
-
-
-    d->lastOut =    LEAF_interpolate_hermite (d->buff[((idx - 1) + d->maxDelay) % d->maxDelay],
-                                              d->buff[idx],
-                                              d->buff[(idx + 1) % d->maxDelay],
-                                              d->buff[(idx + 2) % d->maxDelay],
-                                              d->alpha);
-
+    
+    
+    
+    d->lastOut =    LEAF_interpolate_hermite_x (d->buff[((idx - 1) + d->maxDelay) & d->bufferMask],
+                                                d->buff[idx],
+                                                d->buff[(idx + 1) & d->bufferMask],
+                                                d->buff[(idx + 2) & d->bufferMask],
+                                                d->alpha);
+    
     // Increment output pointer modulo length
-    if ( (++d->outPoint) >= d->maxDelay )   d->outPoint = 0;
-
+    d->outPoint = (d->outPoint + 1) & d->bufferMask;
+    
     return d->lastOut;
 }
 
@@ -455,44 +475,49 @@
 void tHermiteDelay_setDelay (tHermiteDelay* const dl, float delay)
 {
     _tHermiteDelay* d = *dl;
-
-    d->delay = LEAF_clip(0.0f, delay,  d->maxDelay);
-
+    //d->delay = LEAF_clip(0.0f, delay,  d->maxDelay);
+    d->delay = delay; // not safe but faster
     float outPointer = d->inPoint - d->delay;
-
     while ( outPointer < 0 )
         outPointer += d->maxDelay; // modulo maximum length
-
+    
     d->outPoint = (uint32_t) outPointer;   // integer part
-
+    
     d->alpha = outPointer - d->outPoint; // fractional part
     d->omAlpha = 1.0f - d->alpha;
-
-    if ( d->outPoint == d->maxDelay ) d->outPoint = 0;
+    
+    d->outPoint &= d->bufferMask;
 }
 
 float tHermiteDelay_tapOut (tHermiteDelay* const dl, uint32_t tapDelay)
 {
     _tHermiteDelay* d = *dl;
-
-    int32_t tap = d->inPoint - tapDelay - 1;
-
-    // Check for wraparound.
-    while ( tap < 0 )   tap += d->maxDelay;
-
+    
+    int32_t tap = (d->inPoint - tapDelay - 1) & d->bufferMask;
+    
     return d->buff[tap];
 
 }
 
-void tHermiteDelay_tapIn (tHermiteDelay* const dl, float value, uint32_t tapDelay)
+float   tHermiteDelay_tapOutInterpolated (tHermiteDelay* const dl, uint32_t tapDelay, float alpha)
 {
     _tHermiteDelay* d = *dl;
+    
+    int32_t idx = (d->inPoint - tapDelay - 1) & d->bufferMask;
+    
+    return    LEAF_interpolate_hermite_x (d->buff[((idx - 1) + d->maxDelay) & d->bufferMask],
+                                          d->buff[idx],
+                                          d->buff[(idx + 1) & d->bufferMask],
+                                          d->buff[(idx + 2) & d->bufferMask],
+                                          alpha);
+}
 
-    int32_t tap = d->inPoint - tapDelay - 1;
-
-    // Check for wraparound.
-    while ( tap < 0 )   tap += d->maxDelay;
-
+void tHermiteDelay_tapIn (tHermiteDelay* const dl, float value, uint32_t tapDelay)
+{
+    _tHermiteDelay* d = *dl;
+    
+    int32_t tap = (d->inPoint - tapDelay - 1)  & d->bufferMask;
+    
     d->buff[tap] = value;
 }
 
@@ -499,12 +524,9 @@
 float tHermiteDelay_addTo (tHermiteDelay* const dl, float value, uint32_t tapDelay)
 {
     _tHermiteDelay* d = *dl;
-
-    int32_t tap = d->inPoint - tapDelay - 1;
-
-    // Check for wraparound.
-    while ( tap < 0 )   tap += d->maxDelay;
-
+    
+    int32_t tap = (d->inPoint - tapDelay - 1)  & d->bufferMask;
+    
     return (d->buff[tap] += value);
 }
 
--- a/leaf/Src/leaf-physical.c
+++ b/leaf/Src/leaf-physical.c
@@ -660,6 +660,309 @@
 
 
 //////////---------------------------
+/* Version of Living String with Hermite Interpolation */
+/*Living String experiment 2 */
+
+/* Living String*/
+
+void    tLivingString2_init(tLivingString2* const pl, float freq, float pickPos, float prepPos, float pickupPos, float prepIndex,
+                           float brightness, float decay, float targetLev, float levSmoothFactor,
+                           float levStrength, int levMode, LEAF* const leaf)
+{
+    tLivingString2_initToPool(pl, freq, pickPos, prepPos, pickupPos, prepIndex, brightness, decay, targetLev, levSmoothFactor, levStrength, levMode, &leaf->mempool);
+}
+
+void    tLivingString2_initToPool    (tLivingString2* const pl, float freq, float pickPos, float prepPos, float pickupPos, float prepIndex,
+                                     float brightness, float decay, float targetLev, float levSmoothFactor,
+                                     float levStrength, int levMode, tMempool* const mp)
+{
+    _tMempool* m = *mp;
+    _tLivingString2* p = *pl = (_tLivingString2*) mpool_alloc(sizeof(_tLivingString2), m);
+    p->mempool = m;
+    LEAF* leaf = p->mempool->leaf;
+
+    p->curr=0.0f;
+    tExpSmooth_initToPool(&p->wlSmooth, leaf->sampleRate/freq, 0.01f, mp); // smoother for string wavelength (not freq, to avoid expensive divisions)
+    tLivingString2_setFreq(pl, freq);
+    p->freq = freq;
+    p->prepPos = prepPos;
+    tExpSmooth_initToPool(&p->ppSmooth, pickPos, 0.01f, mp); // smoother for pick position
+    tExpSmooth_initToPool(&p->prpSmooth, prepPos, 0.01f, mp); // smoother for prep position
+    tExpSmooth_initToPool(&p->puSmooth, pickupPos, 0.01f, mp); // smoother for pickup position
+    tLivingString2_setPickPos(pl, pickPos);
+    tLivingString2_setPrepPos(pl, prepPos);
+    p->prepIndex=prepIndex;
+    p->pickupPos = pickupPos;
+    tHermiteDelay_initToPool(&p->delLF,p->waveLengthInSamples, 2400, mp);
+    tHermiteDelay_initToPool(&p->delUF,p->waveLengthInSamples, 2400, mp);
+    tHermiteDelay_initToPool(&p->delUB,p->waveLengthInSamples, 2400, mp);
+    tHermiteDelay_initToPool(&p->delLB,p->waveLengthInSamples, 2400, mp);
+    tHermiteDelay_clear(&p->delLF);
+    tHermiteDelay_clear(&p->delUF);
+    tHermiteDelay_clear(&p->delUB);
+    tHermiteDelay_clear(&p->delLB);
+    p->brightness = brightness;
+    tTwoZero_initToPool(&p->bridgeFilter, mp);
+    tTwoZero_initToPool(&p->nutFilter, mp);
+    tTwoZero_initToPool(&p->prepFilterU, mp);
+    tTwoZero_initToPool(&p->prepFilterL, mp);
+    tHighpass_initToPool(&p->DCblockerU,13, mp);
+    tHighpass_initToPool(&p->DCblockerL,13, mp);
+    p->decay=decay;
+    p->prepIndex = prepIndex;
+    tFeedbackLeveler_initToPool(&p->fbLevU, targetLev, levSmoothFactor, levStrength, levMode, mp);
+    tFeedbackLeveler_initToPool(&p->fbLevL, targetLev, levSmoothFactor, levStrength, levMode, mp);
+    p->levMode=levMode;
+}
+
+void    tLivingString2_free (tLivingString2* const pl)
+{
+    _tLivingString2* p = *pl;
+
+    tExpSmooth_free(&p->wlSmooth);
+    tExpSmooth_free(&p->ppSmooth);
+    tExpSmooth_free(&p->prpSmooth);
+    tExpSmooth_free(&p->puSmooth);
+    tHermiteDelay_free(&p->delLF);
+    tHermiteDelay_free(&p->delUF);
+    tHermiteDelay_free(&p->delUB);
+    tHermiteDelay_free(&p->delLB);
+    tTwoZero_free(&p->bridgeFilter);
+    tTwoZero_free(&p->nutFilter);
+    tTwoZero_free(&p->prepFilterU);
+    tTwoZero_free(&p->prepFilterL);
+    tHighpass_free(&p->DCblockerU);
+    tHighpass_free(&p->DCblockerL);
+    tFeedbackLeveler_free(&p->fbLevU);
+    tFeedbackLeveler_free(&p->fbLevL);
+
+    mpool_free((char*)p, p->mempool);
+}
+
+void     tLivingString2_setFreq(tLivingString2* const pl, float freq)
+{    // NOTE: It is faster to set wavelength in samples directly
+    _tLivingString2* p = *pl;
+    LEAF* leaf = p->mempool->leaf;
+    if (freq<20.f) freq=20.f;
+    else if (freq>10000.f) freq=10000.f;
+    p->freq = freq;
+    p->waveLengthInSamples = (leaf->sampleRate/freq) - 1;
+    tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+}
+
+void     tLivingString2_setWaveLength(tLivingString2* const pl, float waveLength)
+{
+    _tLivingString2* p = *pl;
+    LEAF* leaf = p->mempool->leaf;
+    if (waveLength<4.8f) waveLength=4.8f;
+    else if (waveLength>2400.f) waveLength=2400.f;
+    p->waveLengthInSamples = waveLength - 1;
+    p->freq = leaf->sampleRate / waveLength;
+    tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+}
+
+void     tLivingString2_setPickPos(tLivingString2* const pl, float pickPos)
+{    // between 0 and 1
+    _tLivingString2* p = *pl;
+    if (pickPos<0.f) pickPos=0.f;
+    else if (pickPos>1.f) pickPos=1.f;
+    p->pickPos = pickPos;
+    tExpSmooth_setDest(&p->ppSmooth, p->pickPos);
+}
+
+void     tLivingString2_setPrepPos(tLivingString2* const pl, float prepPos)
+{    // between 0 and 1
+    _tLivingString2* p = *pl;
+    if (prepPos<0.f) prepPos=0.f;
+    else if (prepPos>1.f) prepPos=1.f;
+    p->prepPos = prepPos;
+    tExpSmooth_setDest(&p->prpSmooth, p->prepPos);
+}
+
+void     tLivingString2_setPickupPos(tLivingString2* const pl, float pickupPos)
+{    // between 0 and 1
+    _tLivingString2* p = *pl;
+    if (pickupPos<0.f) pickupPos=0.f;
+    else if (pickupPos>1.f) pickupPos=1.f;
+    p->pickupPos = pickupPos;
+    tExpSmooth_setDest(&p->puSmooth, p->pickupPos);
+}
+
+void     tLivingString2_setPrepIndex(tLivingString2* const pl, float prepIndex)
+{    // between 0 and 1
+    _tLivingString2* p = *pl;
+    if (prepIndex<0.f) prepIndex=0.f;
+    else if (prepIndex>1.f) prepIndex=1.f;
+    p->prepIndex = prepIndex;
+}
+
+void     tLivingString2_setBrightness(tLivingString2* const pl, float brightness)
+{
+    _tLivingString2* p = *pl;
+    float h0=(1.0 + brightness) * 0.5f;
+    float h1=(1.0 - brightness) * 0.25f;
+
+    tTwoZero_setCoefficients(&p->bridgeFilter, h1, h0, h1);
+    tTwoZero_setCoefficients(&p->nutFilter, h1, h0, h1);
+    tTwoZero_setCoefficients(&p->prepFilterU, h1, h0, h1);
+    tTwoZero_setCoefficients(&p->prepFilterL, h1, h0, h1);
+}
+
+void     tLivingString2_setDecay(tLivingString2* const pl, float decay)
+{
+    _tLivingString2* p = *pl;
+    p->decay=powf(0.001f,1.0f/(p->freq*decay));
+}
+
+void     tLivingString2_setTargetLev(tLivingString2* const pl, float targetLev)
+{
+    _tLivingString2* p = *pl;
+    tFeedbackLeveler_setTargetLevel(&p->fbLevU, targetLev);
+    tFeedbackLeveler_setTargetLevel(&p->fbLevL, targetLev);
+}
+
+void     tLivingString2_setLevSmoothFactor(tLivingString2* const pl, float levSmoothFactor)
+{
+    _tLivingString2* p = *pl;
+    tFeedbackLeveler_setFactor(&p->fbLevU, levSmoothFactor);
+    tFeedbackLeveler_setFactor(&p->fbLevL, levSmoothFactor);
+}
+
+void     tLivingString2_setLevStrength(tLivingString2* const pl, float levStrength)
+{
+    _tLivingString2* p = *pl;
+    tFeedbackLeveler_setStrength(&p->fbLevU, levStrength);
+    tFeedbackLeveler_setStrength(&p->fbLevL, levStrength);
+}
+
+void     tLivingString2_setLevMode(tLivingString2* const pl, int levMode)
+{
+    _tLivingString2* p = *pl;
+    tFeedbackLeveler_setMode(&p->fbLevU, levMode);
+    tFeedbackLeveler_setMode(&p->fbLevL, levMode);
+    p->levMode=levMode;
+}
+
+float   tLivingString2_tick(tLivingString2* const pl, float input)
+{
+    _tLivingString2* p = *pl;
+
+    input = input * 0.5f; // drop gain by half since we'll be equally adding it at half amplitude to forward and backward waveguides
+    // from prepPos upwards=forwards
+    float wLen=tExpSmooth_tick(&p->wlSmooth);
+
+    float pickP=tExpSmooth_tick(&p->ppSmooth);
+
+    //float pickupPos=tExpSmooth_tick(&p->puSmooth);
+
+    //need to determine which delay line to put it into (should be half amplitude into forward and backward lines for the correct portion of string)
+    float prepP=tExpSmooth_tick(&p->prpSmooth);
+    float lowLen=prepP*wLen;
+    float upLen=(1.0f-prepP)*wLen;
+    uint32_t pickPInt;
+/*
+    if (pickP > prepP)
+    {
+        float fullPickPoint =  ((pickP*wLen) - lowLen);
+        pickPInt = (uint) fullPickPoint; // where does the input go? that's the pick point
+        float pickPFloat = fullPickPoint - pickPInt;
+
+        tHermiteDelay_addTo(&p->delUF, input * (1.0f - pickPFloat), pickPInt);
+        tHermiteDelay_addTo(&p->delUF, input * pickPFloat, pickPInt + 1);
+        tHermiteDelay_addTo(&p->delUB, input * (1.0f - pickPFloat), (uint) (upLen - pickPInt));
+        tHermiteDelay_addTo(&p->delUB, input * pickPFloat, (uint) (upLen - pickPInt - 1));
+    }
+    else
+    {
+         float fullPickPoint =  pickP * wLen;
+        pickPInt = (uint) fullPickPoint; // where does the input go? that's the pick point
+        float pickPFloat = fullPickPoint - pickPInt;
+
+        tHermiteDelay_addTo(&p->delLF, input * (1.0f - pickPFloat), pickPInt);
+        tHermiteDelay_addTo(&p->delLF, input * pickPFloat, pickPInt + 1);
+        tHermiteDelay_addTo(&p->delLB, input * (1.0f - pickPFloat), (uint) (lowLen - pickPInt));
+        tHermiteDelay_addTo(&p->delLB, input * pickPFloat, (uint) (lowLen - pickPInt - 1));
+    }
+*/
+    if (pickP > prepP)
+    {
+        float fullPickPoint =  ((pickP*wLen) - lowLen);
+        pickPInt = (uint32_t) fullPickPoint; // where does the input go? that's the pick point
+
+        tHermiteDelay_addTo(&p->delUF, input, pickPInt);
+        tHermiteDelay_addTo(&p->delUB, input, (uint32_t) (upLen - pickPInt));
+    }
+    else
+    {
+        float fullPickPoint =  pickP * wLen;
+        pickPInt = (uint32_t) fullPickPoint; // where does the input go? that's the pick point
+
+        tHermiteDelay_addTo(&p->delLF, input, pickPInt);
+        tHermiteDelay_addTo(&p->delLB, input, (uint32_t) (lowLen - pickPInt));
+    }
+
+    float fromLF=tHermiteDelay_tickOut(&p->delLF);
+    float fromUF=tHermiteDelay_tickOut(&p->delUF);
+    float fromUB=tHermiteDelay_tickOut(&p->delUB);
+    float fromLB=tHermiteDelay_tickOut(&p->delLB);
+    // into upper half of string, from bridge, going backwards
+    float fromBridge=-tFeedbackLeveler_tick(&p->fbLevU, (p->levMode==0?p->decay:1)*tHighpass_tick(&p->DCblockerU, tTwoZero_tick(&p->bridgeFilter, fromUF)));
+    tHermiteDelay_tickIn(&p->delUB, fromBridge);
+    // into lower half of string, from prepPoint, going backwards
+    float fromLowerPrep=-tTwoZero_tick(&p->prepFilterL, fromLF);
+    float intoLower=p->prepIndex*fromLowerPrep+(1.0f - p->prepIndex)*fromUB; //used to add input here
+    tHermiteDelay_tickIn(&p->delLB, intoLower);
+    // into lower half of string, from nut
+    float fromNut=-tFeedbackLeveler_tick(&p->fbLevL, (p->levMode==0?p->decay:1.0f)*tHighpass_tick(&p->DCblockerL, tTwoZero_tick(&p->nutFilter, fromLB)));
+    tHermiteDelay_tickIn(&p->delLF, fromNut);
+    // into upper half of string, from prepPoint, going forwards/upwards
+    float fromUpperPrep=-tTwoZero_tick(&p->prepFilterU, fromUB);
+    float intoUpper=p->prepIndex*fromUpperPrep+(1.0f - p->prepIndex)*fromLF;
+    tHermiteDelay_tickIn(&p->delUF, intoUpper);
+    // update all delay lengths
+
+    tHermiteDelay_setDelay(&p->delLF, lowLen);
+    tHermiteDelay_setDelay(&p->delLB, lowLen);
+    tHermiteDelay_setDelay(&p->delUF, upLen);
+    tHermiteDelay_setDelay(&p->delUB, upLen);
+
+    /*
+    uint pickupPosInt;
+    float pickupOut = 0.0f;
+    if (pickupPos < 0.9f)
+    {
+        float fullPickupPos = (pickupPos*upLen);
+        pickupPosInt = (uint) fullPickupPos;
+        float pickupPosFloat = fullPickupPos - pickupPosInt;
+        if (pickupPosInt == 0)
+        {
+            pickupPosInt = 1;
+        }
+        pickupOut = tHermiteDelay_tapOutInterpolated(&p->delUF, pickupPosInt, pickupPosFloat);
+        pickupOut += tHermiteDelay_tapOutInterpolated(&p->delUB, (uint) (upLen - pickupPosInt), pickupPosFloat);
+        p->curr = pickupOut;
+    }
+    else
+    */
+    //{
+        p->curr = fromBridge;
+    //}
+
+    //p->curr = fromBridge;
+    //p->curr += fromNut;
+
+    return p->curr;
+}
+
+float   tLivingString2_sample(tLivingString2* const pl)
+{
+    _tLivingString2* p = *pl;
+    return p->curr;
+}
+
+
+
+//////////---------------------------
 
 /* Complex Living String (has pick position and preparation position separated) */
 
--- a/leaf/leaf-config.h
+++ b/leaf/leaf-config.h
@@ -58,7 +58,7 @@
 
 #define LEAF_USE_CMSIS 0
 
-//! Use stdlib malloc() and free() internally instead of  LEAF's normal mempool behavior for when you want to avoid being limited to and managing mempool a fixed mempool size. Usage of all object remains essentially the same.
+//! Use stdlib malloc() and free() internally instead of LEAF's normal mempool behavior for when you want to avoid being limited to and managing mempool a fixed mempool size. Usage of all object remains essentially the same.
 #define LEAF_USE_DYNAMIC_ALLOCATION 1
 
 //==============================================================================