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)
{