shithub: leaf

Download patch

ref: 344bac374e6642c371111b47cdc36ca143adb3d0
parent: 98cb700b9e5af25336a1818612a8933d97587139
author: Jeff Snyder <jeff@snyderphonics.com>
date: Sat Apr 13 16:21:58 EDT 2019

added living string to oversampler leaf branch
;

--- a/LEAF/Inc/leaf-delay.h
+++ b/LEAF/Inc/leaf-delay.h
@@ -75,6 +75,8 @@
 float       tDelayL_tapOut      (tDelayL*  const, float tapDelay);
 float       tDelayL_addTo       (tDelayL*  const, float value, uint32_t tapDelay);
 float       tDelayL_tick        (tDelayL*  const, float sample);
+void   		tDelayL_tickIn 		(tDelayL* const d, float input);
+float   	tDelayL_tickOut 	(tDelayL* const d);
 float       tDelayL_getLastOut  (tDelayL*  const);
 float       tDelayL_getLastIn   (tDelayL*  const);
 
--- a/LEAF/Inc/leaf-mempool.h
+++ b/LEAF/Inc/leaf-mempool.h
@@ -50,7 +50,7 @@
     
 //==============================================================================
 
-#define MPOOL_POOL_SIZE   3000000
+#define MPOOL_POOL_SIZE   300000
 #define MPOOL_ALIGN_SIZE (8)
 
 //#define size_t unsigned long
--- a/LEAF/Inc/leaf-utilities.h
+++ b/LEAF/Inc/leaf-utilities.h
@@ -1,24 +1,28 @@
-/*==============================================================================
+/*
+  ==============================================================================
 
-    leaf-utilities.h
+    LEAFUtilities.h
     Created: 20 Jan 2017 12:02:17pm
     Author:  Michael R Mulshine
 
-==============================================================================*/
+  ==============================================================================
+*/
 
-#ifndef LEAF_UTILITIES_H_INCLUDED
-#define LEAF_UTILITIES_H_INCLUDED
-
-#ifdef __cplusplus
-extern "C" {
+#ifndef LEAFUTILITIES_H_INCLUDED
+#define LEAFUTILITIES_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
 #endif
 
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
 
 #include "leaf-globals.h"
 #include "leaf-math.h"
+#include "leaf-filter.h"
+#include "leaf-delay.h"
 
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
 
 // STACK implementation (fixed size)
 #define STACK_SIZE 128
@@ -46,7 +50,7 @@
 int     tStack_next                 (tStack* const);
 int     tStack_get                  (tStack* const, int which);
 
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
 
 /* Ramp */
 typedef struct _tRamp {
@@ -68,8 +72,148 @@
 int     tRamp_setDest   (tRamp* const, float dest);
 int     tRamp_setVal    (tRamp* const, float val);
 
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
 
+/* Exponential Smoother */
+typedef struct _tExpSmooth {
+    float factor, oneminusfactor;
+    float curr,dest;
+
+} tExpSmooth;
+
+void    tExpSmooth_init      (tExpSmooth* const, float val, float factor);
+void    tExpSmooth_free      (tExpSmooth* const);
+
+float   tExpSmooth_tick      (tExpSmooth* const);
+float   tExpSmooth_sample    (tExpSmooth* const);
+int     tExpSmooth_setFactor   (tExpSmooth* const, float factor);
+int     tExpSmooth_setDest   (tExpSmooth* const, float dest);
+int     tExpSmooth_setVal    (tExpSmooth* const, float val);
+
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+/* PowerEnvelopeFollower */
+typedef struct _tPwrFollow {
+    float factor, oneminusfactor;
+    float curr;
+
+} tPwrFollow;
+
+void    tPwrFollow_init      (tPwrFollow* const, float factor);
+void    tPwrFollow_free      (tPwrFollow* const);
+float   tPwrFollow_tick      (tPwrFollow* const, float input);
+float   tPwrFollow_sample    (tPwrFollow* const);
+int     tPwrFollow_setFactor   (tPwrFollow* const, float factor);
+
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+//Reed Table - borrowed from STK
+typedef struct _tReedTable {
+    float offset, slope;
+} tReedTable;
+
+void    tReedTable_init      (tReedTable* const, float offset, float slope);
+void    tReedTable_free      (tReedTable* const);
+float   tReedTable_tick      (tReedTable* const, float input);
+void     tReedTable_setOffset   (tReedTable* const, float offset);
+void     tReedTable_setSlope (tReedTable* const, float slope);
+
+
+
+///
+/* Feedback leveller */
+// An auto VCA that you put into a feedback circuit to make it stay at the same level.
+// It can enforce level bidirectionally (amplifying and attenuating as needed) or
+// just attenutating. The former option allows for infinite sustain strings, for example, while
+// The latter option allows for decaying strings, which can never exceed
+// a specific level.
+
+typedef struct _tFBleveller {
+    float targetLevel;	// target power level
+    float strength;		// how strongly level difference affects the VCA
+    int	  mode;			// 0 for upwards limiting only, 1 for biderctional limiting
+    float curr;
+    tPwrFollow pwrFlw;	// internal power follower needed for level tracking
+
+} tFBleveller;
+
+void    tFBleveller_init      (tFBleveller* const, float targetLevel, float factor, float strength, int mode);
+void    tFBleveller_free      (tFBleveller* const);
+
+float   tFBleveller_tick      (tFBleveller* const, float input);
+float   tFBleveller_sample    (tFBleveller* const);
+int     tFBleveller_setTargetLevel   (tFBleveller* const, float TargetLevel);
+int     tFBleveller_setFactor   (tFBleveller* const, float factor);
+int     tFBleveller_setMode   (tFBleveller* const, int mode); // 0 for upwards limiting only, 1 for biderctional limiting
+int     tFBleveller_setStrength   (tFBleveller* const, float strength);
+
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+/* Simple Living String */
+typedef struct _tSimpleLivingString {
+	float freq, waveLengthInSamples;		// the frequency of the string, determining delay length
+	float dampFreq;	// frequency for the bridge LP filter, in Hz
+	float decay; // amplitude damping factor for the string (only active in mode 0)
+	float levMode;
+	float curr;
+	tDelayL delayLine;
+	tOnePole bridgeFilter;
+	tHighpass DCblocker;
+	tFBleveller fbLev;
+	tExpSmooth wlSmooth;
+} tSimpleLivingString;
+
+void    tSimpleLivingString_init      (tSimpleLivingString* const, float freq, float dampFreq, float decay, float targetLev, float levSmoothFactor, float levStrength, int levMode);
+void    tSimpleLivingString_free      (tSimpleLivingString* const);
+float   tSimpleLivingString_tick      (tSimpleLivingString* const, float input);
+float   tSimpleLivingString_sample    (tSimpleLivingString* const);
+int     tSimpleLivingString_setFreq   			(tSimpleLivingString* const, float freq);
+int     tSimpleLivingString_setWaveLength		(tSimpleLivingString* const, float waveLength); // in samples
+int     tSimpleLivingString_setDampFreq   		(tSimpleLivingString* const, float dampFreq);
+int     tSimpleLivingString_setDecay     		(tSimpleLivingString* const, float decay); // should be near 1.0
+int     tSimpleLivingString_setTargetLev		(tSimpleLivingString* const, float targetLev);
+int     tSimpleLivingString_setLevSmoothFactor  (tSimpleLivingString* const, float levSmoothFactor);
+int     tSimpleLivingString_setLevStrength		(tSimpleLivingString* const, float levStrength);
+int     tSimpleLivingString_setLevMode		(tSimpleLivingString* const, int levMode);
+
+
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+/* Living String */
+typedef struct _tLivingString {
+	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 prepIndex;	// the amount of pressure on the pickpoint of the string (near 0=soft obj, near 1=hard obj)
+	float dampFreq;	// frequency for the bridge LP filter, in Hz
+	float decay; // amplitude damping factor for the string (only active in mode 0)
+	float levMode;
+	float curr;
+	tDelayL delLF,delUF,delUB,delLB;	// delay for lower/upper/forward/backward part of the waveguide model
+	tOnePole bridgeFilter, nutFilter, prepFilterU, prepFilterL;
+	tHighpass DCblockerL, DCblockerU;
+	tFBleveller fbLevU, fbLevL;
+	tExpSmooth wlSmooth, ppSmooth;
+} tLivingString;
+
+void    tLivingString_init      (tLivingString* const, float freq, float pickPos, float prepIndex, float dampFreq, float decay,
+												float targetLev, float levSmoothFactor, float levStrength, int levMode);
+void    tLivingString_free      (tLivingString* const);
+float   tLivingString_tick      (tLivingString* const, float input);
+float   tLivingString_sample    (tLivingString* const);
+int     tLivingString_setFreq   			(tLivingString* const, float freq);
+int     tLivingString_setWaveLength			(tLivingString* const, float waveLength); // in samples
+int     tLivingString_setPickPos   			(tLivingString* const, float pickPos);
+int     tLivingString_setPrepIndex 			(tLivingString* const, float prepIndex);
+int     tLivingString_setDampFreq   		(tLivingString* const, float dampFreq);
+int     tLivingString_setDecay     			(tLivingString* const, float decay); // should be near 1.0
+int     tLivingString_setTargetLev			(tLivingString* const, float targetLev);
+int     tLivingString_setLevSmoothFactor  	(tLivingString* const, float levSmoothFactor);
+int     tLivingString_setLevStrength		(tLivingString* const, float levStrength);
+int     tLivingString_setLevMode			(tLivingString* const, int levMode);
+
+
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
 /* Compressor */
 typedef struct _tCompressor
 {
@@ -86,7 +230,7 @@
 void    tCompressor_free    (tCompressor* const);
 float   tCompressor_tick    (tCompressor* const, float input);
 
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
 
 /* Attack-Decay envelope */
 typedef struct _tEnvelope {
@@ -118,7 +262,7 @@
 int     tEnvelope_loop      (tEnvelope*  const, oBool loop);
 int     tEnvelope_on        (tEnvelope*  const, float velocity);
 
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
 
 /* ADSR */
 typedef struct _tADSR
@@ -150,7 +294,7 @@
 int     tADSR_on        (tADSR*  const, float velocity);
 int     tADSR_off       (tADSR*  const);
 
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
 
 /* Envelope Follower */
 typedef struct _tEnvelopeFollower
@@ -168,7 +312,7 @@
 int     tEnvelopeFollower_decayCoeff     (tEnvelopeFollower*  const, float decayCoeff);
 int     tEnvelopeFollower_attackThresh   (tEnvelopeFollower*  const, float attackThresh);
 
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
 
 /* tAtkDtk */
 #define DEFBLOCKSIZE 1024
@@ -219,7 +363,7 @@
 // find largest transient in input block, return index of attack
 int     tAtkDtk_detect          (tAtkDtk* const, float *in);
 
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
 
 // ENV~ from PD, modified for LEAF
 #define MAXOVERLAP 32
@@ -244,14 +388,12 @@
 float   tEnv_tick           (tEnv* const);
 void    tEnv_processBlock   (tEnv* const, float* in);
 
-//==============================================================================
-    
-#ifdef __cplusplus
-}
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+    
+#ifdef __cplusplus
+}
 #endif
 
-#endif  // LEAF_UTILITIES_H_INCLUDED
-
-//==============================================================================
+#endif  // LEAFUTILITIES_H_INCLUDED
 
 
--- a/LEAF/Src/leaf-delay.c
+++ b/LEAF/Src/leaf-delay.c
@@ -178,6 +178,30 @@
     return d->lastOut;
 }
 
+void   tDelayL_tickIn (tDelayL* const d, float input)
+{
+    d->buff[d->inPoint] = input * d->gain;
+
+    // Increment input pointer modulo length.
+    if (++(d->inPoint) == d->maxDelay )    d->inPoint = 0;
+}
+
+float   tDelayL_tickOut (tDelayL* const d)
+{
+    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);
+
+    // Increment output pointer modulo length
+    if ( (++d->outPoint) >= d->maxDelay )   d->outPoint = 0;
+
+    return d->lastOut;
+}
+
 int     tDelayL_setDelay (tDelayL* const d, float delay)
 {
     d->delay = LEAF_clip(0.0f, delay,  d->maxDelay);
--- a/LEAF/Src/leaf-utilities.c
+++ b/LEAF/Src/leaf-utilities.c
@@ -1,10 +1,12 @@
-/*==============================================================================
+/*
+  ==============================================================================
 
-    leaf-utilities.c
+    LEAFUtilities.c
     Created: 20 Jan 2017 12:02:17pm
     Author:  Michael R Mulshine
 
-==============================================================================*/
+  ==============================================================================
+*/
 
 
 #if _WIN32 || _WIN64
@@ -29,12 +31,12 @@
 {
     if (f <= -1500.0f) return(0);
     else if (f > 1499.0f) return(mtof(1499.0f));
-    else return (8.17579891564f * exp(0.0577622650f * f));
+    else return (8.17579891564f * expf(0.0577622650f * f));
 }
 
 float ftom(float f)
 {
-    return (f > 0 ? 17.3123405046f * log(.12231220585f * f) : -1500.0f);
+    return (f > 0 ? 17.3123405046f * logf(.12231220585f * f) : -1500.0f);
 }
 
 float powtodb(float f)
@@ -42,7 +44,7 @@
     if (f <= 0) return (0);
     else
     {
-        float val = 100 + 10.f/LOGTEN * log(f);
+        float val = 100 + 10.f/LOGTEN * logf(f);
         return (val < 0 ? 0 : val);
     }
 }
@@ -65,7 +67,7 @@
     {
         if (f > 870.0f)
             f = 870.0f;
-        return (exp((LOGTEN * 0.1f) * (f-100.0f)));
+        return (expf((LOGTEN * 0.1f) * (f-100.0f)));
     }
 }
 
@@ -78,7 +80,7 @@
         if (f > 485.0f)
             f = 485.0f;
     }
-    return (exp((LOGTEN * 0.05f) * (f-100.0f)));
+    return (expf((LOGTEN * 0.05f) * (f-100.0f)));
 }
 
 /* ---------------- env~ - simple envelope follower. ----------------- */
@@ -103,7 +105,7 @@
     
     for (i = 0; i < MAXOVERLAP; i++) x->x_sumbuf[i] = 0;
     for (i = 0; i < npoints; i++)
-        x->buf[i] = (1.0f - cos((2 * PI * i) / npoints))/npoints;
+        x->buf[i] = (1.0f - cosf((2 * PI * i) / npoints))/npoints;
     for (; i < npoints+INITVSTAKEN; i++) x->buf[i] = 0;
     
     x->x_f = 0;
@@ -761,6 +763,8 @@
     r->curr += r->inc;
     
     if (((r->curr >= r->dest) && (r->inc > 0.0f)) || ((r->curr <= r->dest) && (r->inc < 0.0f))) r->inc = 0.0f;
+    // Palle: There is a slight risk that you overshoot here and stay on dest+inc, which with a large inc value could be a real problem
+    // I suggest you add: r->curr=r->dest in the true if case
     
     return r->curr;
 }
@@ -775,6 +779,407 @@
     r->inv_sr_ms = 1.0f / (leaf.sampleRate * 0.001f);
     r->inc = ((r->dest-r->curr)/r->time * r->inv_sr_ms)*((float)r->samples_per_tick);
 }
+
+
+/* Exponential Smoother */
+void    tExpSmooth_init(tExpSmooth* const smooth, float val, float factor)
+{	// factor is usually a value between 0 and 0.1. Lower value is slower. 0.01 for example gives you a smoothing time of about 10ms
+	smooth->curr=val;
+	smooth->dest=val;
+	if (factor<0) factor=0;
+	if (factor>1) factor=1;
+	smooth->factor=factor;
+	smooth->oneminusfactor=1.0f-factor;
+}
+
+int     tExpSmooth_setFactor(tExpSmooth* const smooth, float factor)
+{	// factor is usually a value between 0 and 0.1. Lower value is slower. 0.01 for example gives you a smoothing time of about 10ms
+	if (factor<0)
+		factor=0;
+	else
+		if (factor>1) factor=1;
+	smooth->factor=factor;
+	smooth->oneminusfactor=1.0f-factor;
+    return 0;
+}
+
+int     tExpSmooth_setDest(tExpSmooth* const smooth, float dest)
+{
+	smooth->dest=dest;
+    return 0;
+}
+
+int     tExpSmooth_setVal(tExpSmooth* const smooth, float val)
+{
+	smooth->curr=val;
+    return 0;
+}
+
+float   tExpSmooth_tick(tExpSmooth* const smooth)
+{
+    smooth->curr = smooth->factor*smooth->dest+smooth->oneminusfactor*smooth->curr;
+    return smooth->curr;
+}
+
+float   tExpSmooth_sample(tExpSmooth* const smooth)
+{
+    return smooth->curr;
+}
+
+
+/* Power Follower */
+
+void    tPwrFollow_init(tPwrFollow* const p, float factor)
+{
+	p->curr=0.0f;
+	p->factor=factor;
+	p->oneminusfactor=1.0f-factor;
+}
+
+int     tPwrFollow_setFactor(tPwrFollow* const p, float factor)
+{
+	if (factor<0) factor=0;
+	if (factor>1) factor=1;
+	p->factor=factor;
+	p->oneminusfactor=1.0f-factor;
+    return 0;
+}
+
+float   tPwrFollow_tick(tPwrFollow* const p, float input)
+{
+    p->curr = p->factor*input*input+p->oneminusfactor*p->curr;
+    return p->curr;
+}
+
+float   tPwrFollow_sample(tPwrFollow* const p)
+{
+    return p->curr;
+}
+
+/* Feedback Leveler */
+
+void    tFBleveller_init(tFBleveller* const p, float targetLevel, float factor, float strength, int mode)
+{
+	p->curr=0.0f;
+	p->targetLevel=targetLevel;
+	tPwrFollow_init(&p->pwrFlw,factor);
+	p->mode=mode;
+	p->strength=strength;
+}
+
+int     tFBleveller_setStrength(tFBleveller* const p, float strength)
+{	// strength is how strongly level diff is affecting the amp ratio
+	// try 0.125 for a start
+	p->strength=strength;
+    return 0;
+}
+
+int     tFBleveller_setFactor(tFBleveller* const p, float factor)
+{
+	tPwrFollow_setFactor(&p->pwrFlw,factor);
+    return 0;
+}
+
+int     tFBleveller_setMode(tFBleveller* const p, int mode)
+{	// 0 for decaying with upwards lev limiting, 1 for constrained absolute level (also downwards limiting)
+	p->mode=mode;
+    return 0;
+}
+
+float   tFBleveller_tick(tFBleveller* const p, float input)
+{
+	float levdiff=(tPwrFollow_tick(&p->pwrFlw, input)-p->targetLevel);
+	if (p->mode==0 && levdiff<0) levdiff=0;
+	p->curr=input*(1-p->strength*levdiff);
+	return p->curr;
+}
+
+float   tFBleveller_sample(tFBleveller* const p)
+{
+    return p->curr;
+}
+
+
+int     tFBleveller_setTargetLevel   (tFBleveller* const p, float TargetLevel)
+{
+	p->targetLevel=TargetLevel;
+}
+
+///Reed Table model
+//default values from STK are 0.6 offset and -0.8 slope
+
+void    tReedTable_init      (tReedTable* const p, float offset, float slope)
+{
+	p->offset = offset;
+	p->slope = slope;
+}
+
+void    tReedTable_free      (tReedTable* const p)
+{
+	;
+}
+
+float   tReedTable_tick      (tReedTable* const p, float input)
+{
+	// The input is differential pressure across the reed.
+	float output = p->offset + (p->slope * input);
+
+	// If output is > 1, the reed has slammed shut and the
+	// reflection function value saturates at 1.0.
+	if ( output > 1.0f) output = 1.0f;
+
+	// This is nearly impossible in a physical system, but
+	// a reflection function value of -1.0 corresponds to
+	// an open end (and no discontinuity in bore profile).
+	if ( output < -1.0f) output = -1.0f;
+
+	return output;
+}
+
+void     tReedTable_setOffset   (tReedTable* const p, float offset)
+{
+	p->offset = offset;
+}
+
+void     tReedTable_setSlope   (tReedTable* const p, float slope)
+{
+	p->slope = slope;
+}
+
+
+
+/* Simple Living String*/
+
+void    tSimpleLivingString_init(tSimpleLivingString* const p, float freq, float dampFreq, float decay, float targetLev, float levSmoothFactor, float levStrength, int levMode)
+{
+	p->curr=0.0f;
+	tExpSmooth_init(&p->wlSmooth, leaf.sampleRate/freq, 0.01); // smoother for string wavelength (not freq, to avoid expensive divisions)
+	tSimpleLivingString_setFreq(p, freq);
+	tDelayL_init(&p->delayLine,p->waveLengthInSamples, 2400);
+	tOnePole_init(&p->bridgeFilter, dampFreq);
+	tHighpass_init(&p->DCblocker,13);
+	p->decay=decay;
+	tFBleveller_init(&p->fbLev, targetLev, levSmoothFactor, levStrength, levMode);
+	p->levMode=levMode;
+}
+
+int     tSimpleLivingString_setFreq(tSimpleLivingString* const p, float freq)
+{
+	if (freq<20) freq=20;
+	else if (freq>10000) freq=10000;
+	p->waveLengthInSamples = leaf.sampleRate/freq;
+	tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+    return 0;
+}
+
+int     tSimpleLivingString_setWaveLength(tSimpleLivingString* const p, float waveLength)
+{
+	if (waveLength<4.8) waveLength=4.8;
+	else if (waveLength>2400) waveLength=2400;
+	p->waveLengthInSamples = waveLength;
+	tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+    return 0;
+}
+
+int     tSimpleLivingString_setDampFreq(tSimpleLivingString* const p, float dampFreq)
+{
+	tOnePole_setFreq(&p->bridgeFilter, dampFreq);
+    return 0;
+}
+
+int     tSimpleLivingString_setDecay(tSimpleLivingString* const p, float decay)
+{
+	p->decay=decay;
+    return 0;
+}
+
+int     tSimpleLivingString_setTargetLev(tSimpleLivingString* const p, float targetLev)
+{
+	tFBleveller_setTargetLevel(&p->fbLev, targetLev);
+    return 0;
+}
+
+int     tSimpleLivingString_setLevSmoothFactor(tSimpleLivingString* const p, float levSmoothFactor)
+{
+	tFBleveller_setFactor(&p->fbLev, levSmoothFactor);
+    return 0;
+}
+
+int     tSimpleLivingString_setLevStrength(tSimpleLivingString* const p, float levStrength)
+{
+	tFBleveller_setStrength(&p->fbLev, levStrength);
+    return 0;
+}
+
+int     tSimpleLivingString_setLevMode(tSimpleLivingString* const p, int levMode)
+{
+	tFBleveller_setMode(&p->fbLev, levMode);
+	p->levMode=levMode;
+    return 0;
+}
+
+float   tSimpleLivingString_tick(tSimpleLivingString* const p, float input)
+{
+	float stringOut=tOnePole_tick(&p->bridgeFilter,tDelayL_tickOut(&p->delayLine));
+	float stringInput=tHighpass_tick(&p->DCblocker, tFBleveller_tick(&p->fbLev, (p->levMode==0?p->decay*stringOut:stringOut)+input));
+	tDelayL_tickIn(&p->delayLine, stringInput);
+	tDelayL_setDelay(&p->delayLine, tExpSmooth_tick(&p->wlSmooth));
+    p->curr = stringOut;
+    return p->curr;
+}
+
+float   tSimpleLivingString_sample(tSimpleLivingString* const p)
+{
+    return p->curr;
+}
+
+/* Living String*/
+
+void    tLivingString_init(tLivingString* const p, float freq, float pickPos, float prepIndex, float dampFreq, float decay, float targetLev, float levSmoothFactor, float levStrength, int levMode)
+{
+	p->curr=0.0f;
+	tExpSmooth_init(&p->wlSmooth, leaf.sampleRate/freq, 0.01); // smoother for string wavelength (not freq, to avoid expensive divisions)
+	tLivingString_setFreq(p, freq);
+	tExpSmooth_init(&p->ppSmooth, pickPos, 0.01); // smoother for pick position
+	tLivingString_setPickPos(p, pickPos);
+	p->prepIndex=prepIndex;
+	tDelayL_init(&p->delLF,p->waveLengthInSamples, 2400);
+	tDelayL_init(&p->delUF,p->waveLengthInSamples, 2400);
+	tDelayL_init(&p->delUB,p->waveLengthInSamples, 2400);
+	tDelayL_init(&p->delLB,p->waveLengthInSamples, 2400);
+	tOnePole_init(&p->bridgeFilter, dampFreq);
+	tOnePole_init(&p->nutFilter, dampFreq);
+	tOnePole_init(&p->prepFilterU, dampFreq);
+	tOnePole_init(&p->prepFilterL, dampFreq);
+	tHighpass_init(&p->DCblockerU,13);
+	tHighpass_init(&p->DCblockerL,13);
+	p->decay=decay;
+	tFBleveller_init(&p->fbLevU, targetLev, levSmoothFactor, levStrength, levMode);
+	tFBleveller_init(&p->fbLevL, targetLev, levSmoothFactor, levStrength, levMode);
+	p->levMode=levMode;
+}
+
+int     tLivingString_setFreq(tLivingString* const p, float freq)
+{	// NOTE: It is faster to set wavelength in samples directly
+	if (freq<20) freq=20;
+	else if (freq>10000) freq=10000;
+	p->waveLengthInSamples = leaf.sampleRate/freq;
+	tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+    return 0;
+}
+
+int     tLivingString_setWaveLength(tLivingString* const p, float waveLength)
+{
+	if (waveLength<4.8) waveLength=4.8;
+	else if (waveLength>2400) waveLength=2400;
+	p->waveLengthInSamples = waveLength;
+	tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+    return 0;
+}
+
+int     tLivingString_setPickPos(tLivingString* const p, float pickPos)
+{	// between 0 and 1
+	if (pickPos<0.f) pickPos=0.f;
+	else if (pickPos>1.f) pickPos=1.f;
+	p->pickPos = pickPos;
+	tExpSmooth_setDest(&p->ppSmooth, p->pickPos);
+    return 0;
+}
+
+int     tLivingString_setPrepIndex(tLivingString* const p, float prepIndex)
+{	// between 0 and 1
+	if (prepIndex<0.f) prepIndex=0.f;
+	else if (prepIndex>1.f) prepIndex=1.f;
+	p->prepIndex = prepIndex;
+    return 0;
+}
+
+int     tLivingString_setDampFreq(tLivingString* const p, float dampFreq)
+{
+	tOnePole_setFreq(&p->bridgeFilter, dampFreq);
+	tOnePole_setFreq(&p->nutFilter, dampFreq);
+	tOnePole_setFreq(&p->prepFilterU, dampFreq);
+	tOnePole_setFreq(&p->prepFilterL, dampFreq);
+    return 0;
+}
+
+int     tLivingString_setDecay(tLivingString* const p, float decay)
+{
+	p->decay=decay;
+    return 0;
+}
+
+int     tLivingString_setTargetLev(tLivingString* const p, float targetLev)
+{
+	tFBleveller_setTargetLevel(&p->fbLevU, targetLev);
+	tFBleveller_setTargetLevel(&p->fbLevL, targetLev);
+    return 0;
+}
+
+int     tLivingString_setLevSmoothFactor(tLivingString* const p, float levSmoothFactor)
+{
+	tFBleveller_setFactor(&p->fbLevU, levSmoothFactor);
+	tFBleveller_setFactor(&p->fbLevL, levSmoothFactor);
+    return 0;
+}
+
+int     tLivingString_setLevStrength(tLivingString* const p, float levStrength)
+{
+	tFBleveller_setStrength(&p->fbLevU, levStrength);
+	tFBleveller_setStrength(&p->fbLevL, levStrength);
+    return 0;
+}
+
+int     tLivingString_setLevMode(tLivingString* const p, int levMode)
+{
+	tFBleveller_setMode(&p->fbLevU, levMode);
+	tFBleveller_setMode(&p->fbLevL, levMode);
+	p->levMode=levMode;
+    return 0;
+}
+
+float   tLivingString_tick(tLivingString* const p, float input)
+{
+	// from pickPos upwards=forwards
+	float fromLF=tDelayL_tickOut(&p->delLF);
+	float fromUF=tDelayL_tickOut(&p->delUF);
+	float fromUB=tDelayL_tickOut(&p->delUB);
+	float fromLB=tDelayL_tickOut(&p->delLB);
+	// into upper half of string, from nut, going backwards
+	float fromNut=-tFBleveller_tick(&p->fbLevU, (p->levMode==0?p->decay:1)*tHighpass_tick(&p->DCblockerU, tOnePole_tick(&p->nutFilter, fromUF)));
+	tDelayL_tickIn(&p->delUB, fromNut);
+	// into lower half of string, from pickpoint, going backwards
+	float fromLowerPrep=-tOnePole_tick(&p->prepFilterL, fromLF);
+	float intoLower=p->prepIndex*fromLowerPrep+(1-p->prepIndex)*fromUB+input;
+	tDelayL_tickIn(&p->delLB, intoLower);
+	// into lower half of string, from bridge
+	float fromBridge=-tFBleveller_tick(&p->fbLevL, (p->levMode==0?p->decay:1)*tHighpass_tick(&p->DCblockerL, tOnePole_tick(&p->bridgeFilter, fromLB)));
+	tDelayL_tickIn(&p->delLF, fromBridge);
+	// into upper half of string, from pickpoint, going forwards/upwards
+	float fromUpperPrep=-tOnePole_tick(&p->prepFilterU, fromUB);
+	float intoUpper=p->prepIndex*fromUpperPrep+(1-p->prepIndex)*fromLF+input;
+	tDelayL_tickIn(&p->delUF, intoUpper);
+	// update all delay lengths
+	float pickP=tExpSmooth_tick(&p->ppSmooth);
+	float wLen=tExpSmooth_tick(&p->wlSmooth);
+	float lowLen=pickP*wLen;
+	float upLen=(1-pickP)*wLen;
+	tDelayL_setDelay(&p->delLF, lowLen);
+	tDelayL_setDelay(&p->delLB, lowLen);
+	tDelayL_setDelay(&p->delUF, upLen);
+	tDelayL_setDelay(&p->delUB, upLen);
+    p->curr = fromBridge;
+    return p->curr;
+}
+
+float   tLivingString_sample(tLivingString* const p)
+{
+    return p->curr;
+}
+
+
+/* Stack */
+
 // If stack contains note, returns index. Else returns -1;
 int tStack_contains(tStack* const ns, uint16_t noteVal)
 {