shithub: leaf

Download patch

ref: 96b6a59a61f0a4203e9c5720f378f45ad7d616fd
parent: 3c8c5ec4983d71b56b8c1f969c4aa7eb87ada9cd
author: Jeffrey Snyder <jeffsnyder@jeffreys-mbp.mynetworksettings.com>
date: Thu Dec 1 09:10:23 EST 2022

testing string simulations

--- a/TestPlugin/LEAF.jucer
+++ b/TestPlugin/LEAF.jucer
@@ -10,7 +10,7 @@
               pluginRTASCategory="" aaxIdentifier="com.pumusic.LEAF" pluginAAXCategory="2"
               companyName="Princeton University" companyEmail="mrmulshine@gmail.com"
               displaySplashScreen="1" reportAppUsage="1" splashScreenColour="Dark"
-              buildStandalone="1" enableIAA="0" cppLanguageStandard="11" companyCopyright="Princeton University"
+              buildStandalone="1" enableIAA="0" companyCopyright="Princeton University"
               pluginFormats="buildAU,buildStandalone" pluginCharacteristicsValue="pluginWantsMidiIn"
               jucerFormatVersion="1">
   <MAINGROUP id="F7Bywq" name="LEAF">
--- a/TestPlugin/Source/MyTest.cpp
+++ b/TestPlugin/Source/MyTest.cpp
@@ -14,100 +14,27 @@
 static void run_pool_test(void);
 
 tMBSaw bsaw;
-tMBTriangle btri;
-tMBPulse bpulse;
 
-tPhasor phasor;
-
-tSVF lp, hp;
-
-tPeriodDetection pd;
-
-tZeroCrossingCounter zc;
-tEnvelopeFollower ef;
-
-tTriangle tri;
-
-tNoise noise;
-tButterworth bw;
-
-tWaveTable wt;
-tWaveTableS cwt;
-tWaveOsc ws;
-
-tBuffer samp;
-tMBSampler sampler;
-
-const int numWavetables = 4;
-tWaveTable wavetables[numWavetables];
-tSimpleRetune retune;
-
-float gain;
-float dtime;
-bool buttonState;
-int ratio = 2;
-float x = 0.0f;
-float y = 0.0f;
-float a, b, c, d;
-
-float* bufIn;
-float* bufOut;
-
 LEAF leaf;
 
 #define MSIZE 2048000
 char memory[MSIZE];
 
-int lastLoadedAudioSize = 0;
 
 void    LEAFTest_init            (float sampleRate, int blockSize)
 {
     LEAF_init(&leaf, sampleRate, memory, MSIZE, &getRandomFloat);
-    
-    tSimpleRetune_init(&retune, 1, 100, 1200, 2048, &leaf);
-    tSimpleRetune_setMode(&retune, 1);
-    
-//    tWaveTable_init(&wt, __leaf_table_sawtooth[0], 2048, 10000.f, &leaf);
-//    tWaveTableS_init(&cwt, __leaf_table_sawtooth[0], 2048, 10000.f, &leaf);
-    
-    tMBTriangle_init(&btri, &leaf);
-    tMBPulse_init(&bpulse, &leaf);
+
     tMBSaw_init(&bsaw, &leaf);
-    tMBSaw_setSyncMode(&bsaw, 1);
-    tMBTriangle_setSyncMode(&btri, 0);
-    tMBPulse_setSyncMode(&bpulse, 0);
-    
-    tPhasor_init(&phasor, &leaf);
-    tPhasor_setFreq(&phasor, 220.f);
-    
-    tWaveTable_init(&wavetables[0], (float*)__leaf_table_sinewave, 2048, 20000.f, &leaf);
-    tWaveTable_init(&wavetables[1], (float*)__leaf_table_triangle, 2048, 20000.f, &leaf);
-    tWaveTable_init(&wavetables[2], (float*)__leaf_table_squarewave, 2048, 20000.f, &leaf);
-    tWaveTable_init(&wavetables[3], (float*)__leaf_table_sawtooth, 2048, 20000.f, &leaf);
-    
-    tWaveOsc_init(&ws, wavetables, numWavetables, &leaf);
-    
-    lastLoadedAudioSize = 0;
-    loadedAudio.clear();
+    //tMBSaw_setSyncMode(&bsaw, 1);
 }
 
-inline double getSawFall(double angle) {
-    
-    angle = fmod(angle + double_Pi, 2*double_Pi); // shift x
-    double sample = angle/double_Pi - double(1); // computer as remainder
-    
-    return sample;
-    
-}
 
+
 float   LEAFTest_tick            (float input)
 {
     float out = tMBSaw_tick(&bsaw);
     
-//    for (int i = 0; i < numWavetables; ++i)
-//    {
-//        out += tWaveOsc_tick(&ws);
-//    }
     return out;
 }
 
@@ -116,31 +43,11 @@
 void    LEAFTest_block           (void)
 {
     float val = getSliderValue("slider1");
-    tMBTriangle_setFreq(&btri, val * 440.f);
-    tMBPulse_setFreq(&bpulse, val * 160000.f - 80000.0f);
     tMBSaw_setFreq(&bsaw, -val * 2000.f + 1000.0f);
-//    tWaveTable_setFreq(&wt, val * 160000.f - 80000.0f);
-//    tWaveTableS_setFreq(&cwt, val * 10000.);
-    tWaveOsc_setFreq(&ws, val * 2000.f);
-//    tRetune_tuneVoice(&retune, 0, val * 3.0f + 0.5f);
-//    tSimpleRetune_tuneVoice(&sretune, 0, 300);
 
     val = getSliderValue("slider2");
-    tWaveOsc_setIndex(&ws, val);
-//    tRetune_setPitchFactor(&retune, val * 3.0f + 0.5f, 1);
     
     val = getSliderValue("slider3");
-//    tRetune_setPitchFactor(&retune, val * 3.0f + 0.5f, 2);
-        
-    if (lastLoadedAudioSize < loadedAudio.size())
-    {
-        int i = (loadedAudio.size() - 1) % numWavetables;
-        if (loadedAudio.size() - 1 >= numWavetables) tWaveTable_free(&wavetables[i]);
-        tWaveTable_init(&wavetables[i], (float*)loadedAudio[loadedAudio.size() - 1].getReadPointer(0), loadedAudio[loadedAudio.size() - 1].getNumSamples(), 20000, &leaf);
-        
-        lastLoadedAudioSize = loadedAudio.size();
-    }
-//    if (loadedAudio.size() > 0) tWaveTable_setFreq(&wavetables[0], 220);//val * 10000.f);
 }
 
 void    LEAFTest_controllerInput (int cnum, float cval)
--- a/TestPlugin/Source/UIComponent.cpp
+++ b/TestPlugin/Source/UIComponent.cpp
@@ -163,7 +163,7 @@
     {
         juce::FileChooser chooser ("Select a Wave file to play...", {}, "*.wav");
         
-        if (chooser.browseForFileToOpen())
+        /*if (chooser.browseForFileToOpen())
         {
             auto file = chooser.getResult();
             auto* reader = formatManager.createReaderFor (file);
@@ -177,5 +177,6 @@
                 loadedAudio.add(buffer);
             }
         }
+         */
     }
 }
--- a/leaf/Inc/leaf-oscillators.h
+++ b/leaf/Inc/leaf-oscillators.h
@@ -873,8 +873,14 @@
         float    syncdir;
         int      softsync;
         float   _p, _w, _z;
+        float   _inv_w;
         int     _j;
-        float   _f [FILLEN + STEP_DD_PULSE_LENGTH];
+        float   _f[8];
+        uint16_t numBLEPs;
+        uint16_t mostRecentBLEP;
+        uint16_t maxBLEPphase;
+        uint16_t 	BLEPindices[64];
+        float 	BLEPproperties[64][2];
         float invSampleRate;
     } _tMBSaw;
     
@@ -883,7 +889,6 @@
     void tMBSaw_init(tMBSaw* const osc, LEAF* const leaf);
     void tMBSaw_initToPool(tMBSaw* const osc, tMempool* const mempool);
     void tMBSaw_free(tMBSaw* const osc);
-    
     float tMBSaw_tick(tMBSaw* const osc);
     void tMBSaw_setFreq(tMBSaw* const osc, float f);
     float tMBSaw_sync(tMBSaw* const osc, float sync);
@@ -891,7 +896,13 @@
     void tMBSaw_setSyncMode(tMBSaw* const osc, int hardOrSoft);
     void tMBSaw_setBufferOffset(tMBSaw* const osc, uint32_t offset);
     void tMBSaw_setSampleRate (tMBSaw* const osc, float sr);
-    
+#ifdef ITCMRAM
+    void __attribute__ ((section(".itcmram"))) __attribute__ ((aligned (32))) tMBSaw_place_step_dd_noBuffer(tMBSaw* const osc, int index, float phase, float w, float scale);
+#else
+		void tMBSaw_place_step_dd_noBuffer(tMBSaw* const osc, int index, float phase, float w, float scale);
+#endif
+
+
     //==============================================================================
     /*!
      @defgroup tmbsaw tMBSawPulse
@@ -958,7 +969,6 @@
 
 
     float tMBSawPulse_tick(tMBSawPulse* const osc);
-    float tMBSawPulse_sync(tMBSawPulse* const osc, float value);
     void tMBSawPulse_setFreq(tMBSawPulse* const osc, float f);
     float tMBSawPulse_sync(tMBSawPulse* const osc, float sync);
     void tMBSawPulse_setPhase(tMBSawPulse* const osc, float phase);
--- a/leaf/Inc/leaf-physical.h
+++ b/leaf/Inc/leaf-physical.h
@@ -339,6 +339,55 @@
     
 
 
+
+typedef struct _tSimpleLivingString3
+{
+    
+    tMempool mempool;
+    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)
+    int levMode;
+    float curr;
+    float Uout;
+    float Lout;
+    tLinearDelay delayLineU;
+    tLinearDelay delayLineL;
+    tOnePole bridgeFilter;
+    tHighpass DCblocker;
+    tFeedbackLeveler fbLev;
+    tExpSmooth wlSmooth;
+    float sampleRate;
+} _tSimpleLivingString3;
+
+typedef _tSimpleLivingString3* tSimpleLivingString3;
+
+void    tSimpleLivingString3_init                (tSimpleLivingString3* const, float freq, float dampFreq,
+                                                 float decay, float targetLev, float levSmoothFactor,
+                                                 float levStrength, int levMode, LEAF* const leaf);
+void    tSimpleLivingString3_initToPool          (tSimpleLivingString3* const, float freq, float dampFreq,
+                                                 float decay, float targetLev, float levSmoothFactor,
+                                                 float levStrength, int levMode, tMempool* const);
+void    tSimpleLivingString3_free                (tSimpleLivingString3* const);
+
+float   tSimpleLivingString3_pluck              (tSimpleLivingString3* const pl, float input, float position);
+float   tSimpleLivingString3_tick                (tSimpleLivingString3* const, float input);
+float   tSimpleLivingString3_sample              (tSimpleLivingString3* const);
+void    tSimpleLivingString3_setFreq             (tSimpleLivingString3* const, float freq);
+void    tSimpleLivingString3_setWaveLength       (tSimpleLivingString3* const, float waveLength); // in samples
+void    tSimpleLivingString3_setDampFreq         (tSimpleLivingString3* const, float dampFreq);
+void    tSimpleLivingString3_setDecay            (tSimpleLivingString3* const, float decay); // should be near 1.0
+void    tSimpleLivingString3_setTargetLev        (tSimpleLivingString3* const, float targetLev);
+void    tSimpleLivingString3_setLevSmoothFactor  (tSimpleLivingString3* const, float levSmoothFactor);
+void    tSimpleLivingString3_setLevStrength      (tSimpleLivingString3* const, float levStrength);
+void    tSimpleLivingString3_setLevMode          (tSimpleLivingString3* const, int levMode);
+void    tSimpleLivingString3_setSampleRate       (tSimpleLivingString3* const, float sr);
+
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+
+
+
     typedef struct _tSimpleLivingString2
     {
 
--- a/leaf/Src/leaf-math.c
+++ b/leaf/Src/leaf-math.c
@@ -887,6 +887,10 @@
 		index++;
 	}
 }
+
+
+
+
 #ifdef ITCMRAM
 void __attribute__ ((section(".itcmram"))) __attribute__ ((aligned (32))) place_slope_dd(float *buffer, int index, float phase, float w, float slope_delta)
 #else
--- a/leaf/Src/leaf-oscillators.c
+++ b/leaf/Src/leaf-oscillators.c
@@ -1289,7 +1289,7 @@
     invB1 = 1.0f / b1;
     if (sync > 0.0f && c->softsync > 0) c->syncdir = -c->syncdir;
     
-    sw = w * c->syncdir + c->quarterwaveoffset;
+    sw = w * c->syncdir;
     p += sw - (int)sw;
     
     if (sync > 0.0f && c->softsync == 0) {  /* sync to master */
@@ -1481,7 +1481,7 @@
     _tMBTriangle* c = *osc;
     c->freq = f;
     c->_w = c->freq * c->invSampleRate;  /* phase increment */
-    c->quarterwaveoffset = c->_w * 0.25f;
+    //c->quarterwaveoffset = c->_w * 0.25f;
 }
 
 void tMBTriangle_setWidth(tMBTriangle* const osc, float w)
@@ -1558,8 +1558,12 @@
     c->_j = 0;
     c->_p = 0.0f;  /* phase [0, 1) */
     c->_w = c->freq * c->invSampleRate;  /* phase increment */
-
-    memset (c->_f, 0, (FILLEN + STEP_DD_PULSE_LENGTH) * sizeof (float));
+    c->_inv_w = 1.0f / c->_w;
+    c->numBLEPs = 0;
+    c->mostRecentBLEP = 0;
+    c->maxBLEPphase = MINBLEP_PHASES * STEP_DD_PULSE_LENGTH;
+    memset (c->BLEPindices, 0, 64 * sizeof (uint16_t));
+    memset (c->_f, 0, 8 * sizeof (float));
 }
 
 void tMBSaw_free(tMBSaw* const osc)
@@ -1568,39 +1572,65 @@
     mpool_free((char*)c, c->mempool);
 }
 
+
+#ifdef ITCMRAM
+void __attribute__ ((section(".itcmram"))) __attribute__ ((aligned (32))) tMBSaw_place_step_dd_noBuffer(tMBSaw* const osc, int index, float phase, float inv_w, float scale)
+#else
+void tMBSaw_place_step_dd_noBuffer(tMBSaw* const osc, int index, float phase, float inv_w, float scale)
+#endif
+{
+	_tMBSaw* c = *osc;
+	float r;
+	long i;
+
+	r = MINBLEP_PHASES * phase * inv_w;
+	i = lrintf(r - 0.5f);
+	r -= (float)i;
+	i &= MINBLEP_PHASE_MASK;  /* extreme modulation can cause i to be out-of-range */
+	c->mostRecentBLEP = (c->mostRecentBLEP + 1) & 63;
+	c->BLEPindices[c->mostRecentBLEP] = i;
+    c->BLEPproperties[c->mostRecentBLEP][0] = r;
+    c->BLEPproperties[c->mostRecentBLEP][1] = scale;
+    c->numBLEPs = (c->numBLEPs + 1) & 63;
+}
+
+
+
 float tMBSaw_tick(tMBSaw* const osc)
 {
     _tMBSaw* c = *osc;
-    
+
     int    j;
     float  sync;
-    float  p, w, sw, z;
-    
+    float  p, sw, z;
+
     sync = c->sync;
 
-    
+
     p = c->_p;  /* phase [0, 1) */
-    w = c->_w;  /* phase increment */
     z = c->_z;  /* low pass filter state */
     j = c->_j;  /* index into buffer _f */
 
 
     if (sync > 0.0f && c->softsync > 0) c->syncdir = -c->syncdir;
+    sw = c->_w * c->syncdir;
+    float inv_sw = c->_inv_w * c->syncdir;
+    p += sw - (int)sw;
+
+   //if (sync > 0.0f && c->softsync > 0) {
     // Should insert minblep for softsync?
 	//	if (p_at_reset >= 1.0f) {
 	//		p_at_reset -= (int)p_at_reset;
-	//		place_slope_dd(c->_f, j, p_at_reset + eof_offset, sw, 2.0f);
+	//		place_slope_dd(osc, j, p_at_reset + eof_offset, inv_sw, 2.0f);
 	//	}
 	//	if (p_at_reset < 0.0f) {
 	//		p_at_reset += 1.0f - (int)p_at_reset;
-	//		place_slope_dd(c->_f, j, 1.0f - p_at_reset - eof_offset, -sw, -2.0f);
+	//		place_slope_dd(osc, j, 1.0f - p_at_reset - eof_offset, -inv_sw, -2.0f);
 	//	}
-	//	if (sw > 0) place_slope_dd(c->_f, j, p, sw, 2.0f);
-	//	else if (sw < 0) place_slope_dd(c->_f, j, 1.0f - p, -sw, -2.0f);
-    
-    sw = w * c->syncdir;
-    p += sw - (int)sw;
-    
+	//	if (sw > 0) place_slope_dd(osc, j, p, inv_sw, 2.0f);
+	//	else if (sw < 0) place_slope_dd(osc, j, 1.0f - p, -inv_sw, -2.0f);
+    //}
+
     if (sync > 0.0f && c->softsync == 0) {  /* sync to master */
         float eof_offset = sync * sw;
         float p_at_reset = p - eof_offset;
@@ -1607,48 +1637,66 @@
 
         if (sw > 0) p = eof_offset;
         else if (sw < 0) p = 1.0f - eof_offset;
-        
+
         /* place any DD that may have occurred in subsample before reset */
         if (p_at_reset >= 1.0f) {
             p_at_reset -= 1.0f;
-            place_step_dd(c->_f, j, p_at_reset + eof_offset, sw, 1.0f);
+            tMBSaw_place_step_dd_noBuffer(osc, j, p_at_reset + eof_offset, inv_sw, 1.0f);
         }
         if (p_at_reset < 0.0f) {
             p_at_reset += 1.0f;
-            place_step_dd(c->_f, j, 1.0f - p_at_reset - eof_offset, -sw, -1.0f);
+            tMBSaw_place_step_dd_noBuffer(osc, j, 1.0f - p_at_reset - eof_offset, -inv_sw, -1.0f);
         }
-        
+
         /* now place reset DD */
         if (sw > 0)
-            place_step_dd(c->_f, j, p, sw, p_at_reset);
+        	tMBSaw_place_step_dd_noBuffer(osc, j, p, inv_sw, p_at_reset);
         else if (sw < 0)
-            place_step_dd(c->_f, j, 1.0f - p, -sw, -p_at_reset);
+        	tMBSaw_place_step_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, -p_at_reset);
 
     } else if (p >= 1.0f) {  /* normal phase reset */
         p -= 1.0f;
-        place_step_dd(c->_f, j, p, sw, 1.0f);
-        
+        tMBSaw_place_step_dd_noBuffer(osc, j, p, inv_sw, 1.0f);
+
     } else if (p < 0.0f) {
         p += 1.0f;
-        place_step_dd(c->_f, j, 1.0f - p, -sw, -1.0f);
+        tMBSaw_place_step_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, -1.0f);
     }
-    c->_f[j + DD_SAMPLE_DELAY] += 0.5f - p;
-    
-    z += 0.5f * (c->_f[j] - z); // LP filtering
-    c->out = z;
-    
-    if (++j == FILLEN)
+
+    //construct the current output sample based on the state of the active BLEPs
+
+    int currentSamp = (j + DD_SAMPLE_DELAY) & 7;
+
+    c->_f[currentSamp] = 0.5f - p;
+
+    volatile uint8_t numBLEPsAtLoopStart = c->numBLEPs;
+    for (int i = 0; i < numBLEPsAtLoopStart; i++)
     {
-        j = 0;
-        memcpy (c->_f, c->_f + FILLEN, STEP_DD_PULSE_LENGTH * sizeof (float));
-        memset (c->_f + STEP_DD_PULSE_LENGTH, 0,  FILLEN * sizeof (float));
+    	volatile uint16_t whichBLEP = (c->mostRecentBLEP - i);
+    	whichBLEP &= 63;
+
+    	//use the scale and r values from the BLEPproperties array to compute the current state of each active BLEP and add it to the output value
+    	c->_f[j] += c->BLEPproperties[whichBLEP][1] * (step_dd_table[c->BLEPindices[whichBLEP]].value + c->BLEPproperties[whichBLEP][0] * step_dd_table[c->BLEPindices[whichBLEP]].delta);
+
+    	//increment the position in the BLEP table
+		c->BLEPindices[whichBLEP] += MINBLEP_PHASES;
+		//check if this BLEP is finished and if so mark it as inactive so it isn't computed anymore.
+		if (c->BLEPindices[whichBLEP] >= c->maxBLEPphase)
+		{
+			c->numBLEPs--;
+		}
+
     }
-    
+
+    z += 0.5f * (c->_f[j] - z); // LP filtering
+    c->out = z;
+    j = (j+1) & 7; //don't need 128 sample buffer just for lowpass, so only using the first 16 values before wrapping around (probably only need 4 or 8)
+
     c->_p = p;
-    c->_w = w;
     c->_z = z;
     c->_j = j;
-    
+
+
     return -c->out;
 }
 
@@ -1658,6 +1706,7 @@
     c->freq = f;
 
     c->_w = c->freq * c->invSampleRate;
+    c->_inv_w = 1.0f / c->_w;
 }
 
 float tMBSaw_sync(tMBSaw* const osc, float value)
--- a/leaf/Src/leaf-physical.c
+++ b/leaf/Src/leaf-physical.c
@@ -499,7 +499,188 @@
 }
 
 
+/* Simple Living String*/
 
+void    tSimpleLivingString3_init(tSimpleLivingString3* const pl, float freq, float dampFreq,
+                                 float decay, float targetLev, float levSmoothFactor,
+                                 float levStrength, int levMode, LEAF* const leaf)
+{
+    tSimpleLivingString3_initToPool(pl, freq, dampFreq, decay, targetLev, levSmoothFactor, levStrength, levMode, &leaf->mempool);
+}
+
+void    tSimpleLivingString3_initToPool  (tSimpleLivingString3* const pl, float freq, float dampFreq,
+                                         float decay, float targetLev, float levSmoothFactor,
+                                         float levStrength, int levMode, tMempool* const mp)
+{
+    _tMempool* m = *mp;
+    _tSimpleLivingString3* p = *pl = (_tSimpleLivingString3*) mpool_alloc(sizeof(_tSimpleLivingString3), m);
+    p->mempool = m;
+    LEAF* leaf = p->mempool->leaf;
+    
+    p->sampleRate = leaf->sampleRate;
+    p->curr=0.0f;
+    tExpSmooth_initToPool(&p->wlSmooth, p->sampleRate/freq/2.0f, 0.01f, mp); // smoother for string wavelength (not freq, to avoid expensive divisions)
+    tLinearDelay_initToPool(&p->delayLineU,p->waveLengthInSamples, 2400, mp);
+    tLinearDelay_initToPool(&p->delayLineL,p->waveLengthInSamples, 2400, mp);
+    tSimpleLivingString3_setFreq(pl, freq);
+    tLinearDelay_setDelay(&p->delayLineU, p->waveLengthInSamples);
+    tLinearDelay_setDelay(&p->delayLineL, p->waveLengthInSamples);
+    //tSimpleLivingString3_setWaveLength(pl, 4800);
+    tLinearDelay_clear(&p->delayLineU);
+    tLinearDelay_clear(&p->delayLineL);
+    p->dampFreq = dampFreq;
+    p->freq = freq;
+    tOnePole_initToPool(&p->bridgeFilter, dampFreq, mp);
+    tHighpass_initToPool(&p->DCblocker,13, mp);
+    p->decay=decay;
+    tFeedbackLeveler_initToPool(&p->fbLev, targetLev, levSmoothFactor, levStrength, levMode, mp);
+    p->levMode=levMode;
+}
+
+void    tSimpleLivingString3_free (tSimpleLivingString3* const pl)
+{
+    _tSimpleLivingString3* p = *pl;
+    
+    tExpSmooth_free(&p->wlSmooth);
+    tLinearDelay_free(&p->delayLineU);
+    tLinearDelay_free(&p->delayLineL);
+    tOnePole_free(&p->bridgeFilter);
+    tHighpass_free(&p->DCblocker);
+    tFeedbackLeveler_free(&p->fbLev);
+    
+    mpool_free((char*)p, p->mempool);
+}
+
+void     tSimpleLivingString3_setFreq(tSimpleLivingString3* const pl, float freq)
+{
+    _tSimpleLivingString3* p = *pl;
+    
+    if (freq<20) freq=20;
+    else if (freq>10000) freq=10000;
+    p->waveLengthInSamples = (p->sampleRate/freq) * 0.5f;
+    tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+}
+
+void     tSimpleLivingString3_setWaveLength(tSimpleLivingString3* const pl, float waveLength)
+{
+    _tSimpleLivingString3* p = *pl;
+    
+    if (waveLength<4.8) waveLength=4.8f;
+    else if (waveLength>4800) waveLength=4800;
+    p->waveLengthInSamples = waveLength * 0.5f;
+    tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+}
+
+void     tSimpleLivingString3_setDampFreq(tSimpleLivingString3* const pl, float dampFreq)
+{
+    _tSimpleLivingString3* p = *pl;
+    tOnePole_setFreq(&p->bridgeFilter, dampFreq);
+}
+
+void     tSimpleLivingString3_setDecay(tSimpleLivingString3* const pl, float decay)
+{
+    _tSimpleLivingString3* p = *pl;
+    p->decay=decay;
+}
+
+void     tSimpleLivingString3_setTargetLev(tSimpleLivingString3* const pl, float targetLev)
+{
+    _tSimpleLivingString3* p = *pl;
+    tFeedbackLeveler_setTargetLevel(&p->fbLev, targetLev);
+}
+
+void     tSimpleLivingString3_setLevSmoothFactor(tSimpleLivingString3* const pl, float levSmoothFactor)
+{
+    _tSimpleLivingString3* p = *pl;
+    tFeedbackLeveler_setFactor(&p->fbLev, levSmoothFactor);
+}
+
+void     tSimpleLivingString3_setLevStrength(tSimpleLivingString3* const pl, float levStrength)
+{
+    _tSimpleLivingString3* p = *pl;
+    tFeedbackLeveler_setStrength(&p->fbLev, levStrength);
+}
+
+void     tSimpleLivingString3_setLevMode(tSimpleLivingString3* const pl, int levMode)
+{
+    _tSimpleLivingString3* p = *pl;
+    tFeedbackLeveler_setMode(&p->fbLev, levMode);
+    p->levMode=levMode;
+}
+
+float   tSimpleLivingString3_pluck(tSimpleLivingString3* const pl, float input, float position)
+{
+    _tSimpleLivingString3* p = *pl;
+    int length = p->waveLengthInSamples;
+    int pluckPoint = (int)(length * position);
+    int remainder = length-pluckPoint;
+    for (int i = 0; i < length; i++)
+    {
+        float val = 0.0f;
+        if (i <= pluckPoint)
+        {
+            val = input * ((float)i/(float)pluckPoint);
+        }
+        else
+        {
+            val = input * (1.0f - (((float)i-(float)pluckPoint)/(float)remainder));
+                                 
+        }
+        int bufWritePoint = (i+p->delayLineU->outPoint)%p->delayLineU->maxDelay;
+        p->delayLineU->buff[bufWritePoint] = val;
+    }
+    for (int i = 0; i < length; i++)
+    {
+        int currentBufWritePoint = (i+p->delayLineL->outPoint) % p->delayLineL->maxDelay;
+        int currentBufReadPoint = ((length-i)+p->delayLineU->outPoint);
+        int currentBufReadPointMod = currentBufReadPoint % p->delayLineU->maxDelay;
+        p->delayLineL->buff[currentBufWritePoint] = p->delayLineU->buff[currentBufReadPointMod];
+    }
+    
+}
+
+float   tSimpleLivingString3_tick(tSimpleLivingString3* const pl, float input)
+{
+    _tSimpleLivingString3* p = *pl;
+    tLinearDelay_setDelay(&p->delayLineU, tExpSmooth_tick(&p->wlSmooth));
+    tLinearDelay_setDelay(&p->delayLineL, tExpSmooth_tick(&p->wlSmooth));
+    //tLinearDelay_setDelay(&p->delayLineU, p->waveLengthInSamples);
+    //tLinearDelay_setDelay(&p->delayLineL, p->waveLengthInSamples);
+    
+    p->Uout = tOnePole_tick(&p->bridgeFilter,tLinearDelay_tickOut(&p->delayLineU));
+    p->Lout = tLinearDelay_tickOut(&p->delayLineL);
+    
+    tLinearDelay_tickIn(&p->delayLineU, -1.0f * p->Lout);
+    tLinearDelay_tickIn(&p->delayLineL, -1.0f * p->Uout);
+    
+    
+    //float stringInput=tHighpass_tick(&p->DCblocker, tFeedbackLeveler_tick(&p->fbLev, (p->levMode==0?p->decay*stringOut:stringOut)+input));
+    //tLinearDelay_tickIn(&p->delayLine, stringInput);
+    //tLinearDelay_setDelay(&p->delayLine, tExpSmooth_tick(&p->wlSmooth));
+    //p->curr = d1 + d2;
+    return p->curr;
+}
+
+float   tSimpleLivingString3_sample(tSimpleLivingString3* const pl)
+{
+    _tSimpleLivingString3* p = *pl;
+    return p->curr;
+}
+
+void   tSimpleLivingString3_setSampleRate(tSimpleLivingString3* const pl, float sr)
+{
+    _tSimpleLivingString3* p = *pl;
+    float freq = p->sampleRate/p->waveLengthInSamples;
+    p->sampleRate = sr;
+    p->waveLengthInSamples = p->sampleRate/freq;
+    tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+    tOnePole_setSampleRate(&p->bridgeFilter, p->sampleRate);
+    tHighpass_setSampleRate(&p->DCblocker, p->sampleRate);
+}
+
+
+
+
 /* Simple Living String*/
 
 void    tSimpleLivingString2_init(tSimpleLivingString2* const pl, float freq, float brightness,
@@ -568,8 +749,8 @@
 void     tSimpleLivingString2_setBrightness(tSimpleLivingString2* const pl, float brightness)
 {
     _tSimpleLivingString2* p = *pl;
-    float h0=(1.0 + brightness) * 0.5f;
-    float h1=(1.0 - brightness) * 0.25f;
+    float h0 = (1.0f + brightness) * 0.5f;
+    float h1 = (1.0f - brightness) * 0.25f;
     tTwoZero_setCoefficients(&p->bridgeFilter, h1, h0, h1);
 }