ref: d5135fd5088e6f126bd165f9fe29dcdecdcb938d
parent: 77f3ac50b351c86af9a608e402428cc73b0cd726
author: spiricom <jeff@snyderphonics.com>
date: Tue Jun 2 20:33:53 EDT 2020
added complex living string with separate pluck and prep position
--- a/leaf/Inc/leaf-physical.h
+++ b/leaf/Inc/leaf-physical.h
@@ -228,7 +228,55 @@
void tLivingString_setLevStrength (tLivingString* const, float levStrength);
void tLivingString_setLevMode (tLivingString* const, int levMode);
+
+
+ // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+ /* Complex Living String */
+ typedef struct _tComplexLivingString {
+ float freq, waveLengthInSamples; // the frequency of the whole string, determining delay length
+ float pickPos; // the pick position, dividing the string, in ratio
+ float prepPos; // preparation position, in ratio
+ int prepLower;
+ 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;
+ tLinearDelay delLF,delUF, delMF, delMB, delUB,delLB; // delay for lower/upper/forward/backward part of the waveguide model
+ tOnePole bridgeFilter, nutFilter, prepFilterU, prepFilterL;
+ tHighpass DCblockerL, DCblockerU;
+ tFeedbackLeveler fbLevU, fbLevL;
+ tExpSmooth wlSmooth, pickPosSmooth, prepPosSmooth;
+ } _tComplexLivingString;
+
+ typedef _tComplexLivingString* tComplexLivingString;
+
+ void tComplexLivingString_init (tComplexLivingString* const, float freq, float pickPos, float prepPos, float prepIndex,
+ float dampFreq, float decay, float targetLev, float levSmoothFactor,
+ float levStrength, int levMode);
+ void tComplexLivingString_free (tComplexLivingString* const);
+ void tComplexLivingString_initToPool (tComplexLivingString* const, float freq, float pickPos, float prepPos, float prepIndex,
+ float dampFreq, float decay, float targetLev, float levSmoothFactor,
+ float levStrength, int levMode, tMempool* const);
+ void tComplexLivingString_freeFromPool (tComplexLivingString* const, tMempool* const);
+
+ float tComplexLivingString_tick (tComplexLivingString* const, float input);
+ float tComplexLivingString_sample (tComplexLivingString* const);
+ void tComplexLivingString_setFreq (tComplexLivingString* const, float freq);
+ void tComplexLivingString_setWaveLength (tComplexLivingString* const, float waveLength); // in samples
+ void tComplexLivingString_setPickPos (tComplexLivingString* const, float pickPos);
+ void tComplexLivingString_setPrepPos (tComplexLivingString* const, float prepPos);
+ void tComplexLivingString_setPrepIndex (tComplexLivingString* const, float prepIndex);
+ void tComplexLivingString_setDampFreq (tComplexLivingString* const, float dampFreq);
+ void tComplexLivingString_setDecay (tComplexLivingString* const, float decay); // should be near 1.0
+ void tComplexLivingString_setTargetLev (tComplexLivingString* const, float targetLev);
+ void tComplexLivingString_setLevSmoothFactor (tComplexLivingString* const, float levSmoothFactor);
+ void tComplexLivingString_setLevStrength (tComplexLivingString* const, float levStrength);
+ void tComplexLivingString_setLevMode (tComplexLivingString* const, int levMode);
// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+
//Reed Table - borrowed from STK
typedef struct _tReedTable {
--- a/leaf/Src/leaf-physical.c
+++ b/leaf/Src/leaf-physical.c
@@ -668,6 +668,252 @@
return p->curr;
}
+
+//////////---------------------------
+
+/* Complex Living String (has pick position and preparation position separated) */
+
+void tComplexLivingString_init(tComplexLivingString* const pl, float freq, float pickPos, float prepPos, float prepIndex,
+ float dampFreq, float decay, float targetLev, float levSmoothFactor,
+ float levStrength, int levMode)
+{
+ tComplexLivingString_initToPool(pl, freq, pickPos, prepPos, prepIndex, dampFreq, decay, targetLev, levSmoothFactor, levStrength, levMode, &leaf.mempool);
+}
+
+void tComplexLivingString_free(tComplexLivingString* const pl)
+{
+ tComplexLivingString_freeFromPool(pl, &leaf.mempool);
+}
+
+void tComplexLivingString_initToPool (tComplexLivingString* const pl, float freq, float pickPos, float prepPos, float prepIndex,
+ float dampFreq, float decay, float targetLev, float levSmoothFactor,
+ float levStrength, int levMode, tMempool* const mp)
+{
+ _tMempool* m = *mp;
+ _tComplexLivingString* p = *pl = (_tComplexLivingString*) mpool_alloc(sizeof(_tComplexLivingString), m);
+
+ p->curr=0.0f;
+ tExpSmooth_initToPool(&p->wlSmooth, leaf.sampleRate/freq, 0.01, mp); // smoother for string wavelength (not freq, to avoid expensive divisions)
+ tComplexLivingString_setFreq(pl, freq);
+ p->freq = freq;
+ tExpSmooth_initToPool(&p->pickPosSmooth, pickPos, 0.01f, mp); // smoother for pick position
+ tExpSmooth_initToPool(&p->prepPosSmooth, prepPos, 0.01f, mp); // smoother for pick position
+
+ tComplexLivingString_setPickPos(pl, pickPos);
+ tComplexLivingString_setPrepPos(pl, prepPos);
+
+ p->prepPos=prepPos;
+ p->pickPos=pickPos;
+ tLinearDelay_initToPool(&p->delLF,p->waveLengthInSamples, 2400, mp);
+ tLinearDelay_initToPool(&p->delMF,p->waveLengthInSamples, 2400, mp);
+ tLinearDelay_initToPool(&p->delUF,p->waveLengthInSamples, 2400, mp);
+ tLinearDelay_initToPool(&p->delUB,p->waveLengthInSamples, 2400, mp);
+ tLinearDelay_initToPool(&p->delMB,p->waveLengthInSamples, 2400, mp);
+ tLinearDelay_initToPool(&p->delLB,p->waveLengthInSamples, 2400, mp);
+ tLinearDelay_clear(&p->delLF);
+ tLinearDelay_clear(&p->delMF);
+ tLinearDelay_clear(&p->delUF);
+ tLinearDelay_clear(&p->delUB);
+ tLinearDelay_clear(&p->delMB);
+ tLinearDelay_clear(&p->delLB);
+ p->dampFreq = dampFreq;
+ tOnePole_initToPool(&p->bridgeFilter, dampFreq, mp);
+ tOnePole_initToPool(&p->nutFilter, dampFreq, mp);
+ tOnePole_initToPool(&p->prepFilterU, dampFreq, mp);
+ tOnePole_initToPool(&p->prepFilterL, dampFreq, 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 tComplexLivingString_freeFromPool (tComplexLivingString* const pl, tMempool* const mp)
+{
+ _tMempool* m = *mp;
+ _tComplexLivingString* p = *pl;
+
+ tExpSmooth_freeFromPool(&p->wlSmooth, mp);
+ tExpSmooth_freeFromPool(&p->pickPosSmooth, mp);
+ tExpSmooth_freeFromPool(&p->prepPosSmooth, mp);
+ tLinearDelay_freeFromPool(&p->delLF, mp);
+ tLinearDelay_freeFromPool(&p->delMF, mp);
+ tLinearDelay_freeFromPool(&p->delUF, mp);
+ tLinearDelay_freeFromPool(&p->delUB, mp);
+ tLinearDelay_freeFromPool(&p->delMB, mp);
+ tLinearDelay_freeFromPool(&p->delLB, mp);
+ tOnePole_freeFromPool(&p->bridgeFilter, mp);
+ tOnePole_freeFromPool(&p->nutFilter, mp);
+ tOnePole_freeFromPool(&p->prepFilterU, mp);
+ tOnePole_freeFromPool(&p->prepFilterL, mp);
+ tHighpass_freeFromPool(&p->DCblockerU, mp);
+ tHighpass_freeFromPool(&p->DCblockerL, mp);
+ tFeedbackLeveler_freeFromPool(&p->fbLevU, mp);
+ tFeedbackLeveler_freeFromPool(&p->fbLevL, mp);
+
+ mpool_free((char*)p, m);
+}
+
+void tComplexLivingString_setFreq(tComplexLivingString* const pl, float freq)
+{ // NOTE: It is faster to set wavelength in samples directly
+ _tComplexLivingString* p = *pl;
+ if (freq<20.0f) freq=20.0f;
+ else if (freq>10000.0f) freq=10000.0f;
+ p->waveLengthInSamples = leaf.sampleRate/freq;
+ tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+}
+
+void tComplexLivingString_setWaveLength(tComplexLivingString* const pl, float waveLength)
+{
+ _tComplexLivingString* p = *pl;
+ if (waveLength<4.8f) waveLength=4.8f;
+ else if (waveLength>2400.0f) waveLength=2400.0f;
+ p->waveLengthInSamples = waveLength;
+ tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+}
+
+void tComplexLivingString_setPickPos(tComplexLivingString* const pl, float pickPos)
+{ // between 0 and 1
+ _tComplexLivingString* p = *pl;
+ if (pickPos<0.5f) pickPos=0.5f;
+ else if (pickPos>1.f) pickPos=1.f;
+ p->pickPos = pickPos;
+ tExpSmooth_setDest(&p->pickPosSmooth, p->pickPos);
+}
+
+void tComplexLivingString_setPrepPos(tComplexLivingString* const pl, float prepPos)
+{ // between 0 and 1
+ _tComplexLivingString* p = *pl;
+ if (prepPos<0.f) prepPos=0.f;
+ else if (prepPos>0.5f) prepPos=0.5f;
+ p->prepPos = prepPos;
+ tExpSmooth_setDest(&p->prepPosSmooth, p->prepPos);
+}
+
+void tComplexLivingString_setPrepIndex(tComplexLivingString* const pl, float prepIndex)
+{ // between 0 and 1
+ _tComplexLivingString* p = *pl;
+ if (prepIndex<0.f) prepIndex=0.f;
+ else if (prepIndex>1.f) prepIndex=1.f;
+ p->prepIndex = prepIndex;
+}
+
+void tComplexLivingString_setDampFreq(tComplexLivingString* const pl, float dampFreq)
+{
+ _tComplexLivingString* p = *pl;
+ tOnePole_setFreq(&p->bridgeFilter, dampFreq);
+ tOnePole_setFreq(&p->nutFilter, dampFreq);
+ tOnePole_setFreq(&p->prepFilterU, dampFreq);
+ tOnePole_setFreq(&p->prepFilterL, dampFreq);
+}
+
+void tComplexLivingString_setDecay(tComplexLivingString* const pl, float decay)
+{
+ _tComplexLivingString* p = *pl;
+ p->decay=decay;
+}
+
+void tComplexLivingString_setTargetLev(tComplexLivingString* const pl, float targetLev)
+{
+ _tComplexLivingString* p = *pl;
+ tFeedbackLeveler_setTargetLevel(&p->fbLevU, targetLev);
+ tFeedbackLeveler_setTargetLevel(&p->fbLevL, targetLev);
+}
+
+void tComplexLivingString_setLevSmoothFactor(tComplexLivingString* const pl, float levSmoothFactor)
+{
+ _tComplexLivingString* p = *pl;
+ tFeedbackLeveler_setFactor(&p->fbLevU, levSmoothFactor);
+ tFeedbackLeveler_setFactor(&p->fbLevL, levSmoothFactor);
+}
+
+void tComplexLivingString_setLevStrength(tComplexLivingString* const pl, float levStrength)
+{
+ _tComplexLivingString* p = *pl;
+ tFeedbackLeveler_setStrength(&p->fbLevU, levStrength);
+ tFeedbackLeveler_setStrength(&p->fbLevL, levStrength);
+}
+
+void tComplexLivingString_setLevMode(tComplexLivingString* const pl, int levMode)
+{
+ _tComplexLivingString* p = *pl;
+ tFeedbackLeveler_setMode(&p->fbLevU, levMode);
+ tFeedbackLeveler_setMode(&p->fbLevL, levMode);
+ p->levMode=levMode;
+}
+
+float tComplexLivingString_tick(tComplexLivingString* const pl, float input)
+{
+ _tComplexLivingString* p = *pl;
+
+ // from pickPos upwards=forwards
+ float fromLF=tLinearDelay_tickOut(&p->delLF);
+ float fromMF=tLinearDelay_tickOut(&p->delMF);
+ float fromUF=tLinearDelay_tickOut(&p->delUF);
+ float fromUB=tLinearDelay_tickOut(&p->delUB);
+ float fromMB=tLinearDelay_tickOut(&p->delMB);
+ float fromLB=tLinearDelay_tickOut(&p->delLB);
+
+ // into upper part of string, from bridge, going backwards
+ float fromBridge=-tFeedbackLeveler_tick(&p->fbLevU, (p->levMode==0?p->decay:1)*tHighpass_tick(&p->DCblockerU, tOnePole_tick(&p->bridgeFilter, fromUF)));
+ tLinearDelay_tickIn(&p->delUB, fromBridge);
+
+ // into pick position, take input and add it into the waveguide, going to come out of middle segment
+ tLinearDelay_tickIn(&p->delMB, fromUB+input);
+
+ // into lower part of string, from prepPos, going backwards
+ float fromLowerPrep=-tOnePole_tick(&p->prepFilterL, fromLF);
+ float intoLower=p->prepIndex*fromLowerPrep+(1.0f - p->prepIndex)*fromMB;
+ tLinearDelay_tickIn(&p->delLB, intoLower);
+
+ // into lower part of string, from nut, going forwards toward prep position
+ float fromNut=-tFeedbackLeveler_tick(&p->fbLevL, (p->levMode==0?p->decay:1.0f)*tHighpass_tick(&p->DCblockerL, tOnePole_tick(&p->nutFilter, fromLB)));
+ tLinearDelay_tickIn(&p->delLF, fromNut);
+
+ // into middle part of string, from prep going toward pick position
+ float fromUpperPrep=-tOnePole_tick(&p->prepFilterU, fromUB);
+ float intoMiddle=p->prepIndex*fromUpperPrep+(1.0f - p->prepIndex)*fromLF;
+
+ //pick position, take input and add it into the waveguide, going to come out of middle segment
+ tLinearDelay_tickIn(&p->delMF, intoMiddle + input);
+
+ //take output of middle segment and put it into upper segment connecting to the bridge
+ tLinearDelay_tickIn(&p->delUF, fromMF);
+
+ // update all delay lengths
+ float pickP=tExpSmooth_tick(&p->pickPosSmooth);
+ float prepP=tExpSmooth_tick(&p->prepPosSmooth);
+ float wLen=tExpSmooth_tick(&p->wlSmooth);
+
+ float midLen = (pickP-prepP) * wLen; // the length between the pick and the prep;
+ float lowLen = prepP*wLen; // the length from prep to nut
+ float upLen = (1.0f-pickP)*wLen; // the length from pick to bridge
+
+
+ tLinearDelay_setDelay(&p->delLF, lowLen);
+ tLinearDelay_setDelay(&p->delLB, lowLen);
+
+ tLinearDelay_setDelay(&p->delMF, midLen);
+ tLinearDelay_setDelay(&p->delMB, midLen);
+
+ tLinearDelay_setDelay(&p->delUF, upLen);
+ tLinearDelay_setDelay(&p->delUB, upLen);
+
+ //update this to allow pickup position variation
+ p->curr = fromBridge;
+ return p->curr;
+}
+
+float tComplexLivingString_sample(tComplexLivingString* const pl)
+{
+ _tComplexLivingString* p = *pl;
+ return p->curr;
+}
+
+
+
///Reed Table model
//default values from STK are 0.6 offset and -0.8 slope