History

added complex living string with separate pluck and prep position

spiricom committed 6 years ago

Commit d5135fd1 parent 77f3ac52 files · +294 −0Browse files

Unified · Split

2 files
  1. leaf/Inc/leaf-physical.h +48 −0
  2. leaf/Src/leaf-physical.c +246 −0
leaf/Inc/leaf-physical.h+48−0
@@ -228,7 +228,55 @@ extern "C" { · 227 unmodified lines
228228 void tLivingString_setLevStrength (tLivingString* const, float levStrength);
229229 void tLivingString_setLevMode (tLivingString* const, int levMode);
230230
231+
232+
233+ // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
234+
235+ /* Complex Living String */
236+ typedef struct _tComplexLivingString {
237+ float freq, waveLengthInSamples; // the frequency of the whole string, determining delay length
238+ float pickPos; // the pick position, dividing the string, in ratio
239+ float prepPos; // preparation position, in ratio
240+ int prepLower;
241+ float prepIndex; // the amount of pressure on the pickpoint of the string (near 0=soft obj, near 1=hard obj)
242+ float dampFreq; // frequency for the bridge LP filter, in Hz
243+ float decay; // amplitude damping factor for the string (only active in mode 0)
244+ float levMode;
245+ float curr;
246+ tLinearDelay delLF,delUF, delMF, delMB, delUB,delLB; // delay for lower/upper/forward/backward part of the waveguide model
247+ tOnePole bridgeFilter, nutFilter, prepFilterU, prepFilterL;
248+ tHighpass DCblockerL, DCblockerU;
249+ tFeedbackLeveler fbLevU, fbLevL;
250+ tExpSmooth wlSmooth, pickPosSmooth, prepPosSmooth;
251+ } _tComplexLivingString;
252+
253+ typedef _tComplexLivingString* tComplexLivingString;
254+
255+ void tComplexLivingString_init (tComplexLivingString* const, float freq, float pickPos, float prepPos, float prepIndex,
256+ float dampFreq, float decay, float targetLev, float levSmoothFactor,
257+ float levStrength, int levMode);
258+ void tComplexLivingString_free (tComplexLivingString* const);
259+ void tComplexLivingString_initToPool (tComplexLivingString* const, float freq, float pickPos, float prepPos, float prepIndex,
260+ float dampFreq, float decay, float targetLev, float levSmoothFactor,
261+ float levStrength, int levMode, tMempool* const);
262+ void tComplexLivingString_freeFromPool (tComplexLivingString* const, tMempool* const);
263+
264+ float tComplexLivingString_tick (tComplexLivingString* const, float input);
265+ float tComplexLivingString_sample (tComplexLivingString* const);
266+ void tComplexLivingString_setFreq (tComplexLivingString* const, float freq);
267+ void tComplexLivingString_setWaveLength (tComplexLivingString* const, float waveLength); // in samples
268+ void tComplexLivingString_setPickPos (tComplexLivingString* const, float pickPos);
269+ void tComplexLivingString_setPrepPos (tComplexLivingString* const, float prepPos);
270+ void tComplexLivingString_setPrepIndex (tComplexLivingString* const, float prepIndex);
271+ void tComplexLivingString_setDampFreq (tComplexLivingString* const, float dampFreq);
272+ void tComplexLivingString_setDecay (tComplexLivingString* const, float decay); // should be near 1.0
273+ void tComplexLivingString_setTargetLev (tComplexLivingString* const, float targetLev);
274+ void tComplexLivingString_setLevSmoothFactor (tComplexLivingString* const, float levSmoothFactor);
275+ void tComplexLivingString_setLevStrength (tComplexLivingString* const, float levStrength);
276+ void tComplexLivingString_setLevMode (tComplexLivingString* const, int levMode);
231277 // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
278+
279+
232280
233281 //Reed Table - borrowed from STK
234282 typedef struct _tReedTable {
leaf/Src/leaf-physical.c+246−0
@@ -668,6 +668,252 @@ float tLivingString_sample(tLivingString* const pl) · 667 unmodified lines
668668 return p->curr;
669669 }
670670
671+
672+//////////---------------------------
673+
674+/* Complex Living String (has pick position and preparation position separated) */
675+
676+void tComplexLivingString_init(tComplexLivingString* const pl, float freq, float pickPos, float prepPos, float prepIndex,
677+ float dampFreq, float decay, float targetLev, float levSmoothFactor,
678+ float levStrength, int levMode)
679+{
680+ tComplexLivingString_initToPool(pl, freq, pickPos, prepPos, prepIndex, dampFreq, decay, targetLev, levSmoothFactor, levStrength, levMode, &leaf.mempool);
681+}
682+
683+void tComplexLivingString_free(tComplexLivingString* const pl)
684+{
685+ tComplexLivingString_freeFromPool(pl, &leaf.mempool);
686+}
687+
688+void tComplexLivingString_initToPool (tComplexLivingString* const pl, float freq, float pickPos, float prepPos, float prepIndex,
689+ float dampFreq, float decay, float targetLev, float levSmoothFactor,
690+ float levStrength, int levMode, tMempool* const mp)
691+{
692+ _tMempool* m = *mp;
693+ _tComplexLivingString* p = *pl = (_tComplexLivingString*) mpool_alloc(sizeof(_tComplexLivingString), m);
694+
695+ p->curr=0.0f;
696+ tExpSmooth_initToPool(&p->wlSmooth, leaf.sampleRate/freq, 0.01, mp); // smoother for string wavelength (not freq, to avoid expensive divisions)
697+ tComplexLivingString_setFreq(pl, freq);
698+ p->freq = freq;
699+ tExpSmooth_initToPool(&p->pickPosSmooth, pickPos, 0.01f, mp); // smoother for pick position
700+ tExpSmooth_initToPool(&p->prepPosSmooth, prepPos, 0.01f, mp); // smoother for pick position
701+
702+ tComplexLivingString_setPickPos(pl, pickPos);
703+ tComplexLivingString_setPrepPos(pl, prepPos);
704+
705+ p->prepPos=prepPos;
706+ p->pickPos=pickPos;
707+ tLinearDelay_initToPool(&p->delLF,p->waveLengthInSamples, 2400, mp);
708+ tLinearDelay_initToPool(&p->delMF,p->waveLengthInSamples, 2400, mp);
709+ tLinearDelay_initToPool(&p->delUF,p->waveLengthInSamples, 2400, mp);
710+ tLinearDelay_initToPool(&p->delUB,p->waveLengthInSamples, 2400, mp);
711+ tLinearDelay_initToPool(&p->delMB,p->waveLengthInSamples, 2400, mp);
712+ tLinearDelay_initToPool(&p->delLB,p->waveLengthInSamples, 2400, mp);
713+ tLinearDelay_clear(&p->delLF);
714+ tLinearDelay_clear(&p->delMF);
715+ tLinearDelay_clear(&p->delUF);
716+ tLinearDelay_clear(&p->delUB);
717+ tLinearDelay_clear(&p->delMB);
718+ tLinearDelay_clear(&p->delLB);
719+ p->dampFreq = dampFreq;
720+ tOnePole_initToPool(&p->bridgeFilter, dampFreq, mp);
721+ tOnePole_initToPool(&p->nutFilter, dampFreq, mp);
722+ tOnePole_initToPool(&p->prepFilterU, dampFreq, mp);
723+ tOnePole_initToPool(&p->prepFilterL, dampFreq, mp);
724+ tHighpass_initToPool(&p->DCblockerU,13, mp);
725+ tHighpass_initToPool(&p->DCblockerL,13, mp);
726+ p->decay=decay;
727+ p->prepIndex = prepIndex;
728+ tFeedbackLeveler_initToPool(&p->fbLevU, targetLev, levSmoothFactor, levStrength, levMode, mp);
729+ tFeedbackLeveler_initToPool(&p->fbLevL, targetLev, levSmoothFactor, levStrength, levMode, mp);
730+ p->levMode=levMode;
731+}
732+
733+void tComplexLivingString_freeFromPool (tComplexLivingString* const pl, tMempool* const mp)
734+{
735+ _tMempool* m = *mp;
736+ _tComplexLivingString* p = *pl;
737+
738+ tExpSmooth_freeFromPool(&p->wlSmooth, mp);
739+ tExpSmooth_freeFromPool(&p->pickPosSmooth, mp);
740+ tExpSmooth_freeFromPool(&p->prepPosSmooth, mp);
741+ tLinearDelay_freeFromPool(&p->delLF, mp);
742+ tLinearDelay_freeFromPool(&p->delMF, mp);
743+ tLinearDelay_freeFromPool(&p->delUF, mp);
744+ tLinearDelay_freeFromPool(&p->delUB, mp);
745+ tLinearDelay_freeFromPool(&p->delMB, mp);
746+ tLinearDelay_freeFromPool(&p->delLB, mp);
747+ tOnePole_freeFromPool(&p->bridgeFilter, mp);
748+ tOnePole_freeFromPool(&p->nutFilter, mp);
749+ tOnePole_freeFromPool(&p->prepFilterU, mp);
750+ tOnePole_freeFromPool(&p->prepFilterL, mp);
751+ tHighpass_freeFromPool(&p->DCblockerU, mp);
752+ tHighpass_freeFromPool(&p->DCblockerL, mp);
753+ tFeedbackLeveler_freeFromPool(&p->fbLevU, mp);
754+ tFeedbackLeveler_freeFromPool(&p->fbLevL, mp);
755+
756+ mpool_free((char*)p, m);
757+}
758+
759+void tComplexLivingString_setFreq(tComplexLivingString* const pl, float freq)
760+{ // NOTE: It is faster to set wavelength in samples directly
761+ _tComplexLivingString* p = *pl;
762+ if (freq<20.0f) freq=20.0f;
763+ else if (freq>10000.0f) freq=10000.0f;
764+ p->waveLengthInSamples = leaf.sampleRate/freq;
765+ tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
766+}
767+
768+void tComplexLivingString_setWaveLength(tComplexLivingString* const pl, float waveLength)
769+{
770+ _tComplexLivingString* p = *pl;
771+ if (waveLength<4.8f) waveLength=4.8f;
772+ else if (waveLength>2400.0f) waveLength=2400.0f;
773+ p->waveLengthInSamples = waveLength;
774+ tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
775+}
776+
777+void tComplexLivingString_setPickPos(tComplexLivingString* const pl, float pickPos)
778+{ // between 0 and 1
779+ _tComplexLivingString* p = *pl;
780+ if (pickPos<0.5f) pickPos=0.5f;
781+ else if (pickPos>1.f) pickPos=1.f;
782+ p->pickPos = pickPos;
783+ tExpSmooth_setDest(&p->pickPosSmooth, p->pickPos);
784+}
785+
786+void tComplexLivingString_setPrepPos(tComplexLivingString* const pl, float prepPos)
787+{ // between 0 and 1
788+ _tComplexLivingString* p = *pl;
789+ if (prepPos<0.f) prepPos=0.f;
790+ else if (prepPos>0.5f) prepPos=0.5f;
791+ p->prepPos = prepPos;
792+ tExpSmooth_setDest(&p->prepPosSmooth, p->prepPos);
793+}
794+
795+void tComplexLivingString_setPrepIndex(tComplexLivingString* const pl, float prepIndex)
796+{ // between 0 and 1
797+ _tComplexLivingString* p = *pl;
798+ if (prepIndex<0.f) prepIndex=0.f;
799+ else if (prepIndex>1.f) prepIndex=1.f;
800+ p->prepIndex = prepIndex;
801+}
802+
803+void tComplexLivingString_setDampFreq(tComplexLivingString* const pl, float dampFreq)
804+{
805+ _tComplexLivingString* p = *pl;
806+ tOnePole_setFreq(&p->bridgeFilter, dampFreq);
807+ tOnePole_setFreq(&p->nutFilter, dampFreq);
808+ tOnePole_setFreq(&p->prepFilterU, dampFreq);
809+ tOnePole_setFreq(&p->prepFilterL, dampFreq);
810+}
811+
812+void tComplexLivingString_setDecay(tComplexLivingString* const pl, float decay)
813+{
814+ _tComplexLivingString* p = *pl;
815+ p->decay=decay;
816+}
817+
818+void tComplexLivingString_setTargetLev(tComplexLivingString* const pl, float targetLev)
819+{
820+ _tComplexLivingString* p = *pl;
821+ tFeedbackLeveler_setTargetLevel(&p->fbLevU, targetLev);
822+ tFeedbackLeveler_setTargetLevel(&p->fbLevL, targetLev);
823+}
824+
825+void tComplexLivingString_setLevSmoothFactor(tComplexLivingString* const pl, float levSmoothFactor)
826+{
827+ _tComplexLivingString* p = *pl;
828+ tFeedbackLeveler_setFactor(&p->fbLevU, levSmoothFactor);
829+ tFeedbackLeveler_setFactor(&p->fbLevL, levSmoothFactor);
830+}
831+
832+void tComplexLivingString_setLevStrength(tComplexLivingString* const pl, float levStrength)
833+{
834+ _tComplexLivingString* p = *pl;
835+ tFeedbackLeveler_setStrength(&p->fbLevU, levStrength);
836+ tFeedbackLeveler_setStrength(&p->fbLevL, levStrength);
837+}
838+
839+void tComplexLivingString_setLevMode(tComplexLivingString* const pl, int levMode)
840+{
841+ _tComplexLivingString* p = *pl;
842+ tFeedbackLeveler_setMode(&p->fbLevU, levMode);
843+ tFeedbackLeveler_setMode(&p->fbLevL, levMode);
844+ p->levMode=levMode;
845+}
846+
847+float tComplexLivingString_tick(tComplexLivingString* const pl, float input)
848+{
849+ _tComplexLivingString* p = *pl;
850+
851+ // from pickPos upwards=forwards
852+ float fromLF=tLinearDelay_tickOut(&p->delLF);
853+ float fromMF=tLinearDelay_tickOut(&p->delMF);
854+ float fromUF=tLinearDelay_tickOut(&p->delUF);
855+ float fromUB=tLinearDelay_tickOut(&p->delUB);
856+ float fromMB=tLinearDelay_tickOut(&p->delMB);
857+ float fromLB=tLinearDelay_tickOut(&p->delLB);
858+
859+ // into upper part of string, from bridge, going backwards
860+ float fromBridge=-tFeedbackLeveler_tick(&p->fbLevU, (p->levMode==0?p->decay:1)*tHighpass_tick(&p->DCblockerU, tOnePole_tick(&p->bridgeFilter, fromUF)));
861+ tLinearDelay_tickIn(&p->delUB, fromBridge);
862+
863+ // into pick position, take input and add it into the waveguide, going to come out of middle segment
864+ tLinearDelay_tickIn(&p->delMB, fromUB+input);
865+
866+ // into lower part of string, from prepPos, going backwards
867+ float fromLowerPrep=-tOnePole_tick(&p->prepFilterL, fromLF);
868+ float intoLower=p->prepIndex*fromLowerPrep+(1.0f - p->prepIndex)*fromMB;
869+ tLinearDelay_tickIn(&p->delLB, intoLower);
870+
871+ // into lower part of string, from nut, going forwards toward prep position
872+ float fromNut=-tFeedbackLeveler_tick(&p->fbLevL, (p->levMode==0?p->decay:1.0f)*tHighpass_tick(&p->DCblockerL, tOnePole_tick(&p->nutFilter, fromLB)));
873+ tLinearDelay_tickIn(&p->delLF, fromNut);
874+
875+ // into middle part of string, from prep going toward pick position
876+ float fromUpperPrep=-tOnePole_tick(&p->prepFilterU, fromUB);
877+ float intoMiddle=p->prepIndex*fromUpperPrep+(1.0f - p->prepIndex)*fromLF;
878+
879+ //pick position, take input and add it into the waveguide, going to come out of middle segment
880+ tLinearDelay_tickIn(&p->delMF, intoMiddle + input);
881+
882+ //take output of middle segment and put it into upper segment connecting to the bridge
883+ tLinearDelay_tickIn(&p->delUF, fromMF);
884+
885+ // update all delay lengths
886+ float pickP=tExpSmooth_tick(&p->pickPosSmooth);
887+ float prepP=tExpSmooth_tick(&p->prepPosSmooth);
888+ float wLen=tExpSmooth_tick(&p->wlSmooth);
889+
890+ float midLen = (pickP-prepP) * wLen; // the length between the pick and the prep;
891+ float lowLen = prepP*wLen; // the length from prep to nut
892+ float upLen = (1.0f-pickP)*wLen; // the length from pick to bridge
893+
894+
895+ tLinearDelay_setDelay(&p->delLF, lowLen);
896+ tLinearDelay_setDelay(&p->delLB, lowLen);
897+
898+ tLinearDelay_setDelay(&p->delMF, midLen);
899+ tLinearDelay_setDelay(&p->delMB, midLen);
900+
901+ tLinearDelay_setDelay(&p->delUF, upLen);
902+ tLinearDelay_setDelay(&p->delUB, upLen);
903+
904+ //update this to allow pickup position variation
905+ p->curr = fromBridge;
906+ return p->curr;
907+}
908+
909+float tComplexLivingString_sample(tComplexLivingString* const pl)
910+{
911+ _tComplexLivingString* p = *pl;
912+ return p->curr;
913+}
914+
915+
916+
671917 ///Reed Table model
672918 //default values from STK are 0.6 offset and -0.8 slope
673919

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t