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
//==============================================================================