shithub: leaf

Download patch

ref: a0b0b7915cce3792ea00f06d0a6861be1a73d609
parent: c4f8a19b78c9ed28aec07f0abbaa4764b22f8a44
author: Jeffrey Snyder <jeffsnyder@jeffreys-mbp.mynetworksettings.com>
date: Thu Dec 15 16:48:01 EST 2022

added sawpulse and sinetri MB oscillators and changed all oscillators to avoid bursty behavior

--- a/leaf/Inc/leaf-oscillators.h
+++ b/leaf/Inc/leaf-oscillators.h
@@ -730,9 +730,16 @@
         float    syncdir;
         int      softsync;
         float   _p, _w, _b, _x, _z;
+        float _inv_w;
         int     _j, _k;
-        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;
+
     } _tMBPulse;
     
     typedef _tMBPulse* tMBPulse;
@@ -740,7 +747,11 @@
     void tMBPulse_init(tMBPulse* const osc, LEAF* const leaf);
     void tMBPulse_initToPool(tMBPulse* const osc, tMempool* const mempool);
     void tMBPulse_free(tMBPulse* const osc);
-    
+#ifdef ITCMRAM
+void __attribute__ ((section(".itcmram"))) __attribute__ ((aligned (32))) tMBPulse_place_step_dd_noBuffer(tMBPulse* const osc, int index, float phase, float inv_w, float scale);
+#else
+void tMBPulse_place_step_dd_noBuffer(tMBPulse* const osc, int index, float phase, float inv_w, float scale);
+#endif
     float tMBPulse_tick(tMBPulse* const osc);
     void tMBPulse_setFreq(tMBPulse* const osc, float f);
     void tMBPulse_setWidth(tMBPulse* const osc, float w);
@@ -803,7 +814,15 @@
         int      softsync;
         float   _p, _w, _b, _z, quarterwaveoffset;
         int     _j, _k;
-        float   _f [FILLEN + LONGEST_DD_PULSE_LENGTH];
+        float _inv_w;
+        float 	shape;
+        float   _f [8];
+        uint16_t numBLEPs;
+        uint16_t mostRecentBLEP;
+        uint16_t maxBLEPphase;
+        uint16_t maxBLEPphaseSlope;
+        uint16_t BLEPindices[64];
+        float 	BLEPproperties[64][3];
         float invSampleRate;
     } _tMBTriangle;
     
@@ -812,7 +831,11 @@
     void tMBTriangle_init(tMBTriangle* const osc, LEAF* const leaf);
     void tMBTriangle_initToPool(tMBTriangle* const osc, tMempool* const mempool);
     void tMBTriangle_free(tMBTriangle* const osc);
-    
+#ifdef ITCMRAM
+void __attribute__ ((section(".itcmram"))) __attribute__ ((aligned (32))) tMBTriangle_place_dd_noBuffer(tMBTriangle* const osc, int index, float phase, float inv_w, float scale, float stepOrSlope, float w);
+#else
+void tMBTriangle_place_dd_noBuffer(tMBTriangle* const osc, int index, float phase, float inv_w, float scale, float stepOrSlope, float w);
+#endif
     float tMBTriangle_tick(tMBTriangle* const osc);
     void tMBTriangle_setFreq(tMBTriangle* const osc, float f);
     void tMBTriangle_setWidth(tMBTriangle* const osc, float w);
@@ -823,6 +846,57 @@
     void tMBTriangle_setSampleRate (tMBTriangle* const osc, float sr);
     
     
+
+
+
+    typedef struct _tMBSineTri
+    {
+
+        tMempool mempool;
+        float    out;
+        float    freq;
+        float    waveform;    // duty cycle, must be in [-1, 1]
+        float    lastsyncin;
+        float    sync;
+        float    syncdir;
+        int      softsync;
+        float   _p, _w, _b, _z;
+        float _sinPhase;
+        float shape;
+        int     _j, _k;
+        float _inv_w;
+        float   _f [8];
+        uint16_t numBLEPs;
+        uint16_t mostRecentBLEP;
+        uint16_t maxBLEPphase;
+        uint16_t maxBLEPphaseSlope;
+        uint16_t BLEPindices[64];
+        float 	BLEPproperties[64][3];
+        float invSampleRate;
+        uint32_t sineMask;
+    } _tMBSineTri;
+
+    typedef _tMBSineTri* tMBSineTri;
+
+    void tMBSineTri_init(tMBSineTri* const osc, LEAF* const leaf);
+    void tMBSineTri_initToPool(tMBSineTri* const osc, tMempool* const mempool);
+    void tMBSineTri_free(tMBSineTri* const osc);
+#ifdef ITCMRAM
+void __attribute__ ((section(".itcmram"))) __attribute__ ((aligned (32))) tMBSineTri_place_dd_noBuffer(tMBSineTri* const osc, int index, float phase, float inv_w, float scale, float stepOrSlope, float w);
+#else
+void tMBSineTri_place_dd_noBuffer(tMBSineTri* const osc, int index, float phase, float inv_w, float scale, float stepOrSlope, float w);
+#endif
+    float tMBSineTri_tick(tMBSineTri* const osc);
+    void tMBSineTri_setFreq(tMBSineTri* const osc, float f);
+    void tMBSineTri_setWidth(tMBSineTri* const osc, float w);
+    float tMBSineTri_sync(tMBSineTri* const osc, float sync);
+    void tMBSineTri_setPhase(tMBSineTri* const osc, float phase);
+    void tMBSineTri_setShape(tMBSineTri* const osc, float shape);
+    void tMBSineTri_setSyncMode(tMBSineTri* const osc, int hardOrSoft);
+    void tMBSineTri_setBufferOffset(tMBSineTri* const osc, uint32_t offset);
+    void tMBSineTri_setSampleRate (tMBSineTri* const osc, float sr);
+
+
     /*!
      @defgroup tmbsaw tMBSaw
      @ingroup oscillators
@@ -956,9 +1030,16 @@
         float    waveform;
         float   _p, _w, _b, _x, _z, _k;
         int     _j;
-        float   _f [FILLEN + STEP_DD_PULSE_LENGTH];
+        float _inv_w;
         float invSampleRate;
         float 	shape;
+        float   _f [8];
+        uint16_t numBLEPs;
+        uint16_t mostRecentBLEP;
+        uint16_t maxBLEPphase;
+        uint16_t BLEPindices[64];
+        float 	BLEPproperties[64][2];
+
     } _tMBSawPulse;
     
     typedef _tMBSawPulse* tMBSawPulse;
@@ -966,8 +1047,11 @@
     void tMBSawPulse_init(tMBSawPulse* const osc, LEAF* const leaf);
     void tMBSawPulse_initToPool(tMBSawPulse* const osc, tMempool* const mempool);
     void tMBSawPulse_free(tMBSawPulse* const osc);
-
-
+#ifdef ITCMRAM
+void __attribute__ ((section(".itcmram"))) __attribute__ ((aligned (32))) tMBSawPulse_place_step_dd_noBuffer(tMBSawPulse* const osc, int index, float phase, float inv_w, float scale);
+#else
+void tMBSawPulse_place_step_dd_noBuffer(tMBSawPulse* const osc, int index, float phase, float inv_w, float scale);
+#endif
     float tMBSawPulse_tick(tMBSawPulse* const osc);
     void tMBSawPulse_setFreq(tMBSawPulse* const osc, float f);
     float tMBSawPulse_sync(tMBSawPulse* const osc, float sync);
--- a/leaf/Src/leaf-oscillators.c
+++ b/leaf/Src/leaf-oscillators.c
@@ -954,8 +954,12 @@
     c->_b = 0.5f * (1.0f + c->waveform);  /* duty cycle (0, 1) */
     c->_x = 0.5f;  /* temporary output variable */
     c->_k = 0.0f;  /* output state, 0 = high (0.5f), 1 = low (-0.5f) */
-
-    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 tMBPulse_free(tMBPulse* const osc)
@@ -964,6 +968,28 @@
     mpool_free((char*)c, c->mempool);
 }
 
+#ifdef ITCMRAM
+void __attribute__ ((section(".itcmram"))) __attribute__ ((aligned (32))) tMBPulse_place_step_dd_noBuffer(tMBPulse* const osc, int index, float phase, float inv_w, float scale)
+#else
+void tMBPulse_place_step_dd_noBuffer(tMBPulse* const osc, int index, float phase, float inv_w, float scale)
+#endif
+{
+	_tMBPulse* 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 tMBPulse_tick(tMBPulse* const osc)
 {
     _tMBPulse* c = *osc;
@@ -987,6 +1013,7 @@
     if (sync > 0.0f && c->softsync > 0) c->syncdir = -c->syncdir;
     
     sw = w * c->syncdir;
+    float inv_sw = c->_inv_w * c->syncdir;
     p += sw - (int)sw;
     
     if (sync > 0.0f && c->softsync == 0) {  /* sync to master */
@@ -1001,13 +1028,13 @@
             if (sw > 0)
             {
                 if (p_at_reset >= b) {
-                    place_step_dd(c->_f, j, p_at_reset - b + eof_offset, sw, -1.0f);
+                	tMBPulse_place_step_dd_noBuffer(osc, j, p_at_reset - b + eof_offset, inv_sw, -1.0f);
                     k = 1;
                     x = -0.5f;
                 }
                 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);
+                    tMBPulse_place_step_dd_noBuffer(osc, j, p_at_reset + eof_offset, inv_sw, 1.0f);
                     k = 0;
                     x = 0.5f;
                 }
@@ -1016,12 +1043,12 @@
             {
                 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);
+                    tMBPulse_place_step_dd_noBuffer(osc, j, 1.0f - p_at_reset - eof_offset, -inv_sw, -1.0f);
                     k = 1;
                     x = -0.5f;
                 }
                 if (k && p_at_reset < b) {
-                    place_step_dd(c->_f, j, b - p_at_reset - eof_offset, -sw, 1.0f);
+                	tMBPulse_place_step_dd_noBuffer(osc, j, b - p_at_reset - eof_offset, -inv_sw, 1.0f);
                     k = 0;
                     x = 0.5f;
                 }
@@ -1031,12 +1058,12 @@
             {
                 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);
+                    tMBPulse_place_step_dd_noBuffer(osc, j, p_at_reset + eof_offset, inv_sw, 1.0f);
                     k = 0;
                     x = 0.5f;
                 }
                 if (!k && p_at_reset >= b) {
-                    place_step_dd(c->_f, j, p_at_reset - b + eof_offset, sw, -1.0f);
+                	tMBPulse_place_step_dd_noBuffer(osc, j, p_at_reset - b + eof_offset, inv_sw, -1.0f);
                     k = 1;
                     x = -0.5f;
                 }
@@ -1044,13 +1071,13 @@
             else if (sw < 0)
             {
                 if (p_at_reset < b) {
-                    place_step_dd(c->_f, j, b - p_at_reset - eof_offset, -sw, 1.0f);
+                	tMBPulse_place_step_dd_noBuffer(osc, j, b - p_at_reset - eof_offset, -inv_sw, 1.0f);
                     k = 0;
                     x = 0.5f;
                 }
                 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);
+                    tMBPulse_place_step_dd_noBuffer(osc, j, 1.0f - p_at_reset - eof_offset, -inv_sw, -1.0f);
                     k = 1;
                     x = -0.5f;
                 }
@@ -1061,12 +1088,12 @@
         if (sw > 0)
         {
             if (k) {
-                place_step_dd(c->_f, j, p, sw, 1.0f);
+            	tMBPulse_place_step_dd_noBuffer(osc, j, p, inv_sw, 1.0f);
                 k = 0;
                 x = 0.5f;
             }
             if (p >= b) {
-                place_step_dd(c->_f, j, p - b, sw, -1.0f);
+            	tMBPulse_place_step_dd_noBuffer(osc, j, p - b, inv_sw, -1.0f);
                 k = 1;
                 x = -0.5f;
             }
@@ -1074,12 +1101,12 @@
         else if (sw < 0)
         {
             if (!k) {
-                place_step_dd(c->_f, j, 1.0f - p, -sw, -1.0f);
+            	tMBPulse_place_step_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, -1.0f);
                 k = 1;
                 x = -0.5f;
             }
             if (p < b) {
-                place_step_dd(c->_f, j, b - p, -sw, 1.0f);
+            	tMBPulse_place_step_dd_noBuffer(osc, j, b - p, -inv_sw, 1.0f);
                 k = 0;
                 x = 0.5f;
             }
@@ -1089,13 +1116,13 @@
         if (sw > 0)
         {
             if (p >= b) {
-                place_step_dd(c->_f, j, p - b, sw, -1.0f);
+            	tMBPulse_place_step_dd_noBuffer(osc, j, p - b, inv_sw, -1.0f);
                 k = 1;
                 x = -0.5f;
             }
             if (p >= 1.0f) {
                 p -= 1.0f;
-                place_step_dd(c->_f, j, p, sw, 1.0f);
+                tMBPulse_place_step_dd_noBuffer(osc, j, p, inv_sw, 1.0f);
                 k = 0;
                 x = 0.5f;
             }
@@ -1104,12 +1131,12 @@
         {
             if (p < 0.0f) {
                 p += 1.0f;
-                place_step_dd(c->_f, j, 1.0f - p, -sw, -1.0f);
+                tMBPulse_place_step_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, -1.0f);
                 k = 1;
                 x = -0.5f;
             }
             if (k && p < b) {
-                place_step_dd(c->_f, j, b - p, -sw, 1.0f);
+            	tMBPulse_place_step_dd_noBuffer(osc, j, b - p, -inv_sw, 1.0f);
                 k = 0;
                 x = 0.5f;
             }
@@ -1121,12 +1148,12 @@
         {
             if (p >= 1.0f) {
                 p -= 1.0f;
-                place_step_dd(c->_f, j, p, sw, 1.0f);
+                tMBPulse_place_step_dd_noBuffer(osc, j, p, inv_sw, 1.0f);
                 k = 0;
                 x = 0.5f;
             }
             if (!k && p >= b) {
-                place_step_dd(c->_f, j, p - b, sw, -1.0f);
+            	tMBPulse_place_step_dd_noBuffer(osc, j, p - b, inv_sw, -1.0f);
                 k = 1;
                 x = -0.5f;
             }
@@ -1134,30 +1161,47 @@
         else if (sw < 0)
         {
             if (p < b) {
-                place_step_dd(c->_f, j, b - p, -sw, 1.0f);
+            	tMBPulse_place_step_dd_noBuffer(osc, j, b - p, -inv_sw, 1.0f);
                 k = 0;
                 x = 0.5f;
             }
             if (p < 0.0f) {
                 p += 1.0f;
-                place_step_dd(c->_f, j, 1.0f - p, -sw, -1.0f);
+                tMBPulse_place_step_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, -1.0f);
                 k = 1;
                 x = -0.5f;
             }
         }
     }
-    c->_f[j + DD_SAMPLE_DELAY] += x;
+
+    int currentSamp = (j + DD_SAMPLE_DELAY) & 7;
     
+    c->_f[currentSamp] = x;
+
+    volatile uint8_t numBLEPsAtLoopStart = c->numBLEPs;
+	for (int i = 0; i < numBLEPsAtLoopStart; i++)
+	{
+		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);
     c->out = z;
-    
-    if (++j == FILLEN)
-    {
-        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));
-    }
-    
+
+    j = (j+1) & 7;
+
     c->_p = p;
     c->_w = w;
     c->_b = b;
@@ -1174,6 +1218,7 @@
     _tMBPulse* c = *osc;
     c->freq = f;
     c->_w = c->freq * c->invSampleRate;  /* phase increment */
+    c->_inv_w = 1.0f / c->_w;
 }
 
 void tMBPulse_setWidth(tMBPulse* const osc, float w)
@@ -1249,12 +1294,17 @@
     c->waveform = 0.0f;
     c->_z = 0.0f;
     c->_j = 0;
-    c->_p = 0.0f;  /* phase [0, 1) */
+    c->_p = 0.25f;  /* phase [0, 1) */
     c->_w = c->freq * c->invSampleRate;  /* phase increment */
-    c->quarterwaveoffset = c->_w * 0.25f;
     c->_b = 0.5f * (1.0f + c->waveform);  /* duty cycle (0, 1) */
     c->_k = 0.0f;  /* output state, 0 = high (0.5f), 1 = low (-0.5f) */
-    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;
+    c->maxBLEPphaseSlope = MINBLEP_PHASES * SLOPE_DD_PULSE_LENGTH;
+    memset (c->BLEPindices, 0, 64 * sizeof (uint16_t));
+    memset (c->_f, 0, 8 * sizeof (float));
 }
 
 void tMBTriangle_free(tMBTriangle* const osc)
@@ -1263,6 +1313,29 @@
     mpool_free((char*)c, c->mempool);
 }
 
+#ifdef ITCMRAM
+void __attribute__ ((section(".itcmram"))) __attribute__ ((aligned (32))) tMBTriangle_place_dd_noBuffer(tMBTriangle* const osc, int index, float phase, float inv_w, float scale, float stepOrSlope, float w)
+#else
+void tMBTriangle_place_dd_noBuffer(tMBTriangle* const osc, int index, float phase, float inv_w, float scale, float stepOrSlope, float w)
+#endif
+{
+	_tMBTriangle* 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 */
+	scale *= w;
+	c->mostRecentBLEP = (c->mostRecentBLEP + 1) & 63;
+	c->BLEPindices[c->mostRecentBLEP] = i;
+    c->BLEPproperties[c->mostRecentBLEP][0] = r;
+    c->BLEPproperties[c->mostRecentBLEP][1] = scale;
+    c->BLEPproperties[c->mostRecentBLEP][2] = stepOrSlope;
+    c->numBLEPs = (c->numBLEPs + 1) & 63;
+}
+
 float tMBTriangle_tick(tMBTriangle* const osc)
 {
     _tMBTriangle* c = *osc;
@@ -1290,6 +1363,7 @@
     if (sync > 0.0f && c->softsync > 0) c->syncdir = -c->syncdir;
     
     sw = w * c->syncdir;
+    float inv_sw = c->_inv_w * c->syncdir;
     p += sw - (int)sw;
     
     if (sync > 0.0f && c->softsync == 0) {  /* sync to master */
@@ -1307,13 +1381,13 @@
             {
                 if (p_at_reset >= b) {
                     x = 0.5f - (p_at_reset - b) * invB1;
-                    place_slope_dd(c->_f, j, p_at_reset - b + eof_offset, sw, -invB1 - invB);
+                    tMBTriangle_place_dd_noBuffer(osc, j, p_at_reset - b + eof_offset, inv_sw, -invB1 - invB, 1.0f, sw);
                     k = 1;
                 }
                 if (p_at_reset >= 1.0f) {
                     p_at_reset -= 1.0f;
                     x = -0.5f + p_at_reset * invB;
-                    place_slope_dd(c->_f, j, p_at_reset + eof_offset, sw, invB + invB1);
+                    tMBTriangle_place_dd_noBuffer(osc, j, p_at_reset + eof_offset, inv_sw, invB + invB1, 1.0f, sw);
                     k = 0;
                 }
             }
@@ -1322,12 +1396,12 @@
                 if (p_at_reset < 0.0f) {
                     p_at_reset += 1.0f;
                     x = 0.5f - (p_at_reset - b)  * invB1;
-                    place_slope_dd(c->_f, j, 1.0f - p_at_reset - eof_offset, -sw, invB + invB1);
+                    tMBTriangle_place_dd_noBuffer(osc, j, 1.0f - p_at_reset - eof_offset, -inv_sw, invB + invB1, 1.0f, -sw);
                     k = 1;
                 }
                 if (k && p_at_reset < b) {
                     x = -0.5f + p_at_reset * invB;
-                    place_slope_dd(c->_f, j, b - p_at_reset - eof_offset, -sw, -invB1 - invB);
+                    tMBTriangle_place_dd_noBuffer(osc, j, b - p_at_reset - eof_offset, -inv_sw, -invB1 - invB, 1.0f, -sw);
                     k = 0;
                 }
             }
@@ -1338,12 +1412,12 @@
                 if (p_at_reset >= 1.0f) {
                     p_at_reset -= 1.0f;
                     x = -0.5f + p_at_reset * invB;
-                    place_slope_dd(c->_f, j, p_at_reset + eof_offset, sw, invB + invB1);
+                    tMBTriangle_place_dd_noBuffer(osc, j, p_at_reset + eof_offset, inv_sw, invB + invB1, 1.0f, sw);
                     k = 0;
                 }
                 if (!k && p_at_reset >= b) {
                     x = 0.5f - (p_at_reset - b) * invB1;
-                    place_slope_dd(c->_f, j, p_at_reset - b + eof_offset, sw, -invB1 - invB);
+                    tMBTriangle_place_dd_noBuffer(osc, j, p_at_reset - b + eof_offset, inv_sw, -invB1 - invB, 1.0f, sw);
                     k = 1;
                 }
             }
@@ -1351,13 +1425,13 @@
             {
                 if (p_at_reset < b) {
                     x = -0.5f + p_at_reset * invB;
-                    place_slope_dd(c->_f, j, b - p_at_reset - eof_offset, -sw, -invB1 - invB);
+                    tMBTriangle_place_dd_noBuffer(osc, j, b - p_at_reset - eof_offset, -inv_sw, -invB1 - invB, 1.0f, -sw);
                     k = 0;
                 }
                 if (p_at_reset < 0.0f) {
                     p_at_reset += 1.0f;
                     x = 0.5f - (p_at_reset - b) * invB1;
-                    place_slope_dd(c->_f, j, 1.0f - p_at_reset - eof_offset, -sw, invB + invB1);
+                    tMBTriangle_place_dd_noBuffer(osc, j, 1.0f - p_at_reset - eof_offset, -inv_sw, invB + invB1, 1.0f, -sw);
                     k = 1;
                 }
             }
@@ -1367,13 +1441,13 @@
         if (sw > 0)
         {
             if (k)
-                place_slope_dd(c->_f, j, p, sw, invB + invB1);
-            place_step_dd(c->_f, j, p, sw, -0.5f - x);
+            	tMBTriangle_place_dd_noBuffer(osc, j, p, inv_sw, invB + invB1, 1.0f, sw);
+            tMBTriangle_place_dd_noBuffer(osc, j, p, inv_sw, -0.5f - x, 0.0f, sw);
             x = -0.5f + p * invB;
             k = 0;
             if (p >= b) {
                 x = 0.5f - (p - b) * invB1;
-                place_slope_dd(c->_f, j, p - b, sw, -invB1 - invB);
+                tMBTriangle_place_dd_noBuffer(osc, j, p - b, inv_sw, -invB1 - invB, 1.0f, sw);
                 k = 1;
             }
         }
@@ -1380,13 +1454,13 @@
         else if (sw < 0)
         {
             if (!k)
-                place_slope_dd(c->_f, j, 1.0f - p, -sw, invB + invB1);
-            place_step_dd(c->_f, j, 1.0f - p, -sw, -0.5f - x);
+            	tMBTriangle_place_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, invB + invB1, 1.0f, -sw);
+            tMBTriangle_place_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, -0.5f - x, 0.0f, -sw);
             x = 0.5f - (p - b) * invB1;
             k = 1;
             if (p < b) {
                 x = -0.5f + p * invB;
-                place_slope_dd(c->_f, j, b - p, -sw, -invB1 - invB);
+                tMBTriangle_place_dd_noBuffer(osc, j, b - p, -inv_sw, -invB1 - invB, 1.0f, -sw);
                 k = 0;
             }
         }
@@ -1397,13 +1471,13 @@
         {
             if (p >= b) {
                 x = 0.5f - (p - b) * invB1;;
-                place_slope_dd(c->_f, j, p - b, sw, -invB1 - invB);
+                tMBTriangle_place_dd_noBuffer(osc, j, p - b, inv_sw, -invB1 - invB, 1.0f, sw);
                 k = 1;
             }
             if (p >= 1.0f) {
                 p -= 1.0f;
                 x = -0.5f + p * invB;
-                place_slope_dd(c->_f, j, p, sw, invB + invB1);
+                tMBTriangle_place_dd_noBuffer(osc, j, p, inv_sw, invB + invB1, 1.0f, sw);
                 k = 0;
             }
         }
@@ -1412,12 +1486,12 @@
             if (p < 0.0f) {
                 p += 1.0f;
                 x = 0.5f - (p - b) * invB1;
-                place_slope_dd(c->_f, j, 1.0f - p, -sw, invB + invB1);
+                tMBTriangle_place_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, invB + invB1, 1.0f, -sw);
                 k = 1;
             }
             if (k && p < b) {
                 x = -0.5f + p * invB;
-                place_slope_dd(c->_f, j, b - p, -sw, -invB1 - invB);
+                tMBTriangle_place_dd_noBuffer(osc, j, b - p, -inv_sw, -invB1 - invB, 1.0f, -sw);
                 k = 0;
             }
         }
@@ -1430,12 +1504,12 @@
             if (p >= 1.0f) {
                 p -= 1.0f;
                 x = -0.5f + p * invB;
-                place_slope_dd(c->_f, j, p, sw, invB + invB1);
+                tMBTriangle_place_dd_noBuffer(osc, j, p, inv_sw, invB + invB1, 1.0f, sw);
                 k = 0;
             }
             if (!k && p >= b) {
                 x = 0.5f - (p - b) * invB1;
-                place_slope_dd(c->_f, j, p - b, sw, -invB1 - invB);
+                tMBTriangle_place_dd_noBuffer(osc, j, p - b, inv_sw, -invB1 - invB, 1.0f, sw);
                 k = 1;
             }
         }
@@ -1443,29 +1517,56 @@
         {
             if (p < b) {
                 x = -0.5f + p * invB;
-                place_slope_dd(c->_f, j, b - p, -sw, -invB1 - invB);
+                tMBTriangle_place_dd_noBuffer(osc, j, b - p, -inv_sw, -invB1 - invB, 1.0f, -sw);
                 k = 0;
             }
             if (p < 0.0f) {
                 p += 1.0f;
                 x = 0.5f - (p - b) * invB1;
-                place_slope_dd(c->_f, j, 1.0f - p, -sw, invB + invB1);
+                tMBTriangle_place_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, invB + invB1, 1.0f, -sw);
                 k = 1;
             }
         }
     }
-    c->_f[j + DD_SAMPLE_DELAY] += x;
+    int currentSamp = (j + DD_SAMPLE_DELAY) & 7;
     
-    z += 0.5f * (c->_f[j] - z);
-    c->out = z;
-    
-    if (++j == FILLEN)
+    c->_f[currentSamp] = x;
+
+    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
+
+    	if (c->BLEPproperties[whichBLEP][2] < 0.5f) //step blep
+    	{
+    		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--;
+    		}
+    	}
+    	else  // slope blep
+    	{
+    		c->_f[j] += c->BLEPproperties[whichBLEP][1] * (slope_dd_table[c->BLEPindices[whichBLEP]] + c->BLEPproperties[whichBLEP][0] * (slope_dd_table[c->BLEPindices[whichBLEP]+1] - slope_dd_table[c->BLEPindices[whichBLEP]]));
+        	//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->maxBLEPphaseSlope)) //slope bleps are 71 length instead of 72 length so subtract 1
+    		{
+    			c->numBLEPs--;
+    		}
+    	}
     }
-    
+
+    z += 0.5f * (c->_f[j] - z);
+    c->out = z;
+    j = (j+1) & 7;
     c->_p = p;
     c->_w = w;
     c->_b = b;
@@ -1481,6 +1582,7 @@
     _tMBTriangle* c = *osc;
     c->freq = f;
     c->_w = c->freq * c->invSampleRate;  /* phase increment */
+    c->_inv_w = 1.0f / c->_w;
     //c->quarterwaveoffset = c->_w * 0.25f;
 }
 
@@ -1533,6 +1635,420 @@
     c->invSampleRate = 1.0f/sr;
 }
 
+
+
+//==========================================================================================================
+
+void tMBSineTri_init(tMBSineTri* const osc, LEAF* const leaf)
+{
+    tMBSineTri_initToPool(osc, &leaf->mempool);
+}
+
+void tMBSineTri_initToPool(tMBSineTri* const osc, tMempool* const pool)
+{
+    _tMempool* m = *pool;
+    _tMBSineTri* c = *osc = (_tMBSineTri*) mpool_alloc(sizeof(_tMBSineTri), m);
+    c->mempool = m;
+    LEAF* leaf = c->mempool->leaf;
+
+    c->invSampleRate = leaf->invSampleRate;
+    c->freq = 440.f;
+    c->lastsyncin = 0.0f;
+    c->sync = 0.0f;
+    c->syncdir = 1.0f;
+    c->softsync = 0;
+    c->waveform = 0.0f;
+    c->shape = 0.0f;
+    c->_z = 0.0f;
+    c->_j = 0;
+    c->_sinPhase = 0.0f;
+    c->_p = 0.25f;  /* phase [0, 1) */
+    c->_w = c->freq * c->invSampleRate;  /* phase increment */
+    c->_b = 0.5f * (1.0f + c->waveform);  /* duty cycle (0, 1) */
+    c->_k = 0.0f;  /* output state, 0 = high (0.5f), 1 = low (-0.5f) */
+    c->_inv_w = 1.0f / c->_w;
+    c->numBLEPs = 0;
+    c->mostRecentBLEP = 0;
+    c->maxBLEPphase = MINBLEP_PHASES * STEP_DD_PULSE_LENGTH;
+    c->maxBLEPphaseSlope = MINBLEP_PHASES * SLOPE_DD_PULSE_LENGTH;
+    c->sineMask = 2047;
+    memset (c->BLEPindices, 0, 64 * sizeof (uint16_t));
+    memset (c->_f, 0, 8 * sizeof (float));
+}
+
+void tMBSineTri_free(tMBSineTri* const osc)
+{
+    _tMBSineTri* c = *osc;
+    mpool_free((char*)c, c->mempool);
+}
+
+#ifdef ITCMRAM
+void __attribute__ ((section(".itcmram"))) __attribute__ ((aligned (32))) tMBSineTri_place_dd_noBuffer(tMBSineTri* const osc, int index, float phase, float inv_w, float scale, float stepOrSlope, float w)
+#else
+void tMBSineTri_place_dd_noBuffer(tMBSineTri* const osc, int index, float phase, float inv_w, float scale, float stepOrSlope, float w)
+#endif
+{
+	_tMBSineTri* 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 */
+	scale *= w;
+	c->mostRecentBLEP = (c->mostRecentBLEP + 1) & 63;
+	c->BLEPindices[c->mostRecentBLEP] = i;
+    c->BLEPproperties[c->mostRecentBLEP][0] = r;
+    c->BLEPproperties[c->mostRecentBLEP][1] = scale;
+    c->BLEPproperties[c->mostRecentBLEP][2] = stepOrSlope;
+    c->numBLEPs = (c->numBLEPs + 1) & 63;
+}
+
+float tMBSineTri_tick(tMBSineTri* const osc)
+{
+    _tMBSineTri* c = *osc;
+
+    int    j, k;
+    float  sync;
+    float  b, b1, invB, invB1, p, sinPhase, w, sw, z;
+    float  x = 0.5f;
+
+    sync = c->sync;
+
+    p = c->_p;  /* phase [0, 1) */
+    sinPhase = c->_sinPhase;
+    w = c->_w;  /* phase increment */
+    b = c->_b;  /* duty cycle (0, 1) */
+    invB = 1.0f / b;
+    z = c->_z;  /* low pass filter state */
+    j = c->_j;  /* index into buffer _f */
+    k = c->_k;  /* output state, 0 = positive slope, 1 = negative slope */
+
+
+
+    b = 0.5f * (1.0f + c->waveform);
+    b1 = 1.0f - b;
+    invB1 = 1.0f / b1;
+    if (sync > 0.0f && c->softsync > 0) c->syncdir = -c->syncdir;
+
+    sw = w * c->syncdir;
+    float inv_sw = c->_inv_w * c->syncdir;
+    float inc_amount = sw - (int)sw;
+    p += inc_amount;
+    sinPhase += inc_amount;
+
+    if (sync > 0.0f && c->softsync == 0) {  /* sync to master */
+        float eof_offset = sync * sw;
+        float p_at_reset = p - eof_offset;
+        if (sw > 0)
+        {
+        	p = eof_offset + 0.25f;
+        	sinPhase = eof_offset;
+        }
+        else if (sw < 0)
+        {
+        	p = (1.0f - eof_offset) + 0.25f;
+        	sinPhase = (1.0f - eof_offset);
+        }
+        //
+        /* place any DDs that may have occurred in subsample before reset */
+
+        if (!k) {
+            x = -0.5f + p_at_reset * invB;
+            if (sw > 0)
+            {
+                if (p_at_reset >= b) {
+                    x = 0.5f - (p_at_reset - b) * invB1;
+                    tMBSineTri_place_dd_noBuffer(osc, j, p_at_reset - b + eof_offset, inv_sw, -invB1 - invB, 1.0f, sw);
+                    k = 1;
+                }
+                if (p_at_reset >= 1.0f) {
+                    p_at_reset -= 1.0f;
+                    x = -0.5f + p_at_reset * invB;
+                    tMBSineTri_place_dd_noBuffer(osc, j, p_at_reset + eof_offset, inv_sw, invB + invB1, 1.0f, sw);
+                    k = 0;
+                }
+            }
+            else if (sw < 0)
+            {
+                if (p_at_reset < 0.0f) {
+                    p_at_reset += 1.0f;
+                    x = 0.5f - (p_at_reset - b)  * invB1;
+                    tMBSineTri_place_dd_noBuffer(osc, j, 1.0f - p_at_reset - eof_offset, -inv_sw, invB + invB1, 1.0f, -sw);
+                    k = 1;
+                }
+                if (k && p_at_reset < b) {
+                    x = -0.5f + p_at_reset * invB;
+                    tMBSineTri_place_dd_noBuffer(osc, j, b - p_at_reset - eof_offset, -inv_sw, -invB1 - invB, 1.0f, -sw);
+                    k = 0;
+                }
+            }
+        } else {
+            x = 0.5f - (p_at_reset - b) * invB1;
+            if (sw > 0)
+            {
+                if (p_at_reset >= 1.0f) {
+                    p_at_reset -= 1.0f;
+                    x = -0.5f + p_at_reset * invB;
+                    tMBSineTri_place_dd_noBuffer(osc, j, p_at_reset + eof_offset, inv_sw, invB + invB1, 1.0f, sw);
+                    k = 0;
+                }
+                if (!k && p_at_reset >= b) {
+                    x = 0.5f - (p_at_reset - b) * invB1;
+                    tMBSineTri_place_dd_noBuffer(osc, j, p_at_reset - b + eof_offset, inv_sw, -invB1 - invB, 1.0f, sw);
+                    k = 1;
+                }
+            }
+            else if (sw < 0)
+            {
+                if (p_at_reset < b) {
+                    x = -0.5f + p_at_reset * invB;
+                    tMBSineTri_place_dd_noBuffer(osc, j, b - p_at_reset - eof_offset, -inv_sw, -invB1 - invB, 1.0f, -sw);
+                    k = 0;
+                }
+                if (p_at_reset < 0.0f) {
+                    p_at_reset += 1.0f;
+                    x = 0.5f - (p_at_reset - b) * invB1;
+                    tMBSineTri_place_dd_noBuffer(osc, j, 1.0f - p_at_reset - eof_offset, -inv_sw, invB + invB1, 1.0f, -sw);
+                    k = 1;
+                }
+            }
+        }
+
+        /* now place reset DDs */
+        if (sw > 0)
+        {
+            if (k)
+            	tMBSineTri_place_dd_noBuffer(osc, j, p, inv_sw, invB + invB1, 1.0f, sw);
+            tMBSineTri_place_dd_noBuffer(osc, j, p, inv_sw, 0.0f - x, 0.0f, sw);
+            x = -0.5f + p * invB;
+            k = 0;
+            if (p >= b) {
+                x = 0.5f - (p - b) * invB1;
+                tMBSineTri_place_dd_noBuffer(osc, j, p - b, inv_sw, -invB1 - invB, 1.0f, sw);
+                k = 1;
+            }
+        }
+        else if (sw < 0)
+        {
+            if (!k)
+            	tMBSineTri_place_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, invB + invB1, 1.0f, -sw);
+            tMBSineTri_place_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, 0.0f - x, 0.0f, -sw);
+            x = 0.5f - (p - b) * invB1;
+            k = 1;
+            if (p < b) {
+                x = -0.5f + p * invB;
+                tMBSineTri_place_dd_noBuffer(osc, j, b - p, -inv_sw, -invB1 - invB, 1.0f, -sw);
+                k = 0;
+            }
+        }
+    } else if (!k) {  /* normal operation, slope currently up */
+
+        x = -0.5f + p * invB;
+        if (sw > 0)
+        {
+            if (p >= b) {
+                x = 0.5f - (p - b) * invB1;;
+                tMBSineTri_place_dd_noBuffer(osc, j, p - b, inv_sw, -invB1 - invB, 1.0f, sw);
+                k = 1;
+            }
+            if (p >= 1.0f) {
+                p -= 1.0f;
+                x = -0.5f + p * invB;
+                tMBSineTri_place_dd_noBuffer(osc, j, p, inv_sw, invB + invB1, 1.0f, sw);
+                k = 0;
+            }
+        }
+        else if (sw < 0)
+        {
+            if (p < 0.0f) {
+                p += 1.0f;
+                x = 0.5f - (p - b) * invB1;
+                tMBSineTri_place_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, invB + invB1, 1.0f, -sw);
+                k = 1;
+            }
+            if (k && p < b) {
+                x = -0.5f + p * invB;
+                tMBSineTri_place_dd_noBuffer(osc, j, b - p, -inv_sw, -invB1 - invB, 1.0f, -sw);
+                k = 0;
+            }
+        }
+
+    } else {  /* normal operation, slope currently down */
+
+        x = 0.5f - (p - b) * invB1;
+        if (sw > 0)
+        {
+            if (p >= 1.0f) {
+                p -= 1.0f;
+                x = -0.5f + p * invB;
+                tMBSineTri_place_dd_noBuffer(osc, j, p, inv_sw, invB + invB1, 1.0f, sw);
+                k = 0;
+            }
+            if (!k && p >= b) {
+                x = 0.5f - (p - b) * invB1;
+                tMBSineTri_place_dd_noBuffer(osc, j, p - b, inv_sw, -invB1 - invB, 1.0f, sw);
+                k = 1;
+            }
+        }
+        else if (sw < 0)
+        {
+            if (p < b) {
+                x = -0.5f + p * invB;
+                tMBSineTri_place_dd_noBuffer(osc, j, b - p, -inv_sw, -invB1 - invB, 1.0f, -sw);
+                k = 0;
+            }
+            if (p < 0.0f) {
+                p += 1.0f;
+                x = 0.5f - (p - b) * invB1;
+                tMBSineTri_place_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, invB + invB1, 1.0f, -sw);
+                k = 1;
+            }
+        }
+    }
+    int currentSamp = (j + DD_SAMPLE_DELAY) & 7;
+
+    c->_f[currentSamp] = x * c->shape; //add the triangle
+
+
+    float tempFrac;
+    uint32_t idx;
+    float samp0;
+    float samp1;
+
+    // Wavetable synthesis
+    while (sinPhase >= 1.0f)
+    {
+    	sinPhase -= 1.0f;
+    }
+
+    while (sinPhase < 0.0f)
+    {
+    	sinPhase += 1.0f;
+    }
+    float tempPhase = (sinPhase * 2048.0f);
+    idx = (uint32_t)tempPhase; //11 bit table
+    tempFrac = tempPhase - idx;
+    samp0 = __leaf_table_sinewave[idx];
+    idx = (idx + 1) & c->sineMask;
+    samp1 = __leaf_table_sinewave[idx];
+
+    float sinOut = (samp0 + (samp1 - samp0) * tempFrac) * 0.5f; // 1/2097151
+
+    c->_f[currentSamp] += sinOut * (1.0f - c->shape); //add the sine
+
+
+    volatile uint8_t numBLEPsAtLoopStart = c->numBLEPs;
+    for (int i = 0; i < numBLEPsAtLoopStart; i++)
+    {
+    	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
+
+    	if (c->BLEPproperties[whichBLEP][2] < 0.5f) //step blep
+    	{
+    		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--;
+    		}
+    	}
+    	else  // slope blep
+    	{
+    		c->_f[j] += (c->BLEPproperties[whichBLEP][1] * (slope_dd_table[c->BLEPindices[whichBLEP]] + c->BLEPproperties[whichBLEP][0] * (slope_dd_table[c->BLEPindices[whichBLEP]+1] - slope_dd_table[c->BLEPindices[whichBLEP]]))) * c->shape;
+        	//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->maxBLEPphaseSlope)) //slope bleps are 71 length instead of 72 length so subtract 1
+    		{
+    			c->numBLEPs--;
+    		}
+    	}
+    }
+
+    z += 0.5f * (c->_f[j] - z);
+    j = (j+1) & 7;
+    c->out = z;
+    c->_p = p;
+    c->_w = w;
+    c->_b = b;
+    c->_z = z;
+    c->_j = j;
+    c->_k = k;
+    c->_sinPhase = sinPhase;
+
+    return -c->out;
+}
+
+void tMBSineTri_setFreq(tMBSineTri* const osc, float f)
+{
+    _tMBSineTri* c = *osc;
+    c->freq = f;
+    c->_w = c->freq * c->invSampleRate;  /* phase increment */
+    c->_inv_w = 1.0f / c->_w;
+    //c->quarterwaveoffset = c->_w * 0.25f;
+}
+
+void tMBSineTri_setWidth(tMBSineTri* const osc, float w)
+{
+    _tMBSineTri* c = *osc;
+    w = LEAF_clip(0.0f, w, 0.99f);
+    c->waveform = w;
+}
+
+float tMBSineTri_sync(tMBSineTri* const osc, float value)
+{
+    _tMBSineTri* c = *osc;
+
+    //based on https://github.com/VCVRack/Fundamental/blob/5799ee2a9b21492b42ebcb9b65d5395ef5c1cbe2/src/VCO.cpp#L123
+    float last = c->lastsyncin;
+    float delta = value - last;
+    float crossing = -last / delta;
+    c->lastsyncin = value;
+    if ((0.f < crossing) && (crossing <= 1.f) && (value >= 0.f))
+        c->sync = (1.f - crossing) * delta;
+    else c->sync = 0.f;
+
+    return value;
+}
+
+void tMBSineTri_setPhase(tMBSineTri* const osc, float phase)
+{
+    _tMBSineTri* c = *osc;
+    c->_p = phase;
+}
+
+void tMBSineTri_setShape(tMBSineTri* const osc, float shape)
+{
+    _tMBSineTri* c = *osc;
+    c->shape = shape;
+}
+
+
+void tMBSineTri_setSyncMode(tMBSineTri* const osc, int hardOrSoft)
+{
+    _tMBSineTri* c = *osc;
+    c->softsync = hardOrSoft > 0 ? 1 : 0;
+}
+
+//useful if you have several oscillators so the buffer refill is not synchronized
+void tMBSineTri_setBufferOffset(tMBSineTri* const osc, uint32_t offset)
+{
+	_tMBSineTri* c = *osc;
+	offset = offset & (FILLEN-1);
+	c->_j = offset;
+}
+
+void tMBSineTri_setSampleRate(tMBSineTri* const osc, float sr)
+{
+    _tMBSineTri* c = *osc;
+    c->invSampleRate = 1.0f/sr;
+}
 //==================================================================================================
 //==================================================================================================
 
@@ -1780,8 +2296,13 @@
     c->_b = 0.5f * (1.0f + c->waveform);  /* duty cycle (0, 1) */
     c->_x = 0.5f;  /* temporary output variable */
     c->_k = 0.0f;  /* output state, 0 = high (0.5f), 1 = low (-0.5f) */
+    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));
 
-    memset (c->_f, 0, (FILLEN + STEP_DD_PULSE_LENGTH) * sizeof (float));
 }
 
 void tMBSawPulse_free(tMBSawPulse* const osc)
@@ -1791,7 +2312,26 @@
 }
 
 
+#ifdef ITCMRAM
+void __attribute__ ((section(".itcmram"))) __attribute__ ((aligned (32))) tMBSawPulse_place_step_dd_noBuffer(tMBSawPulse* const osc, int index, float phase, float inv_w, float scale)
+#else
+void tMBSawPulse_place_step_dd_noBuffer(tMBSawPulse* const osc, int index, float phase, float inv_w, float scale)
+#endif
+{
+    _tMBSawPulse* 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;
+}
 
 
 
@@ -1820,6 +2360,7 @@
     if (sync > 0.0f && c->softsync > 0) c->syncdir = -c->syncdir;
 
     sw = w * c->syncdir;
+    float inv_sw = c->_inv_w * c->syncdir;
     p += sw - (int)sw;
 
     if (sync > 0.0f && c->softsync == 0)
@@ -1838,7 +2379,7 @@
 			 {
 				 if (p_at_reset >= b)
 				 {
-					 place_step_dd(c->_f, j, p_at_reset - b + eof_offset, sw, -1.0f * shape);
+					 tMBSawPulse_place_step_dd_noBuffer(osc, j, p_at_reset - b + eof_offset, inv_sw, -1.0f * shape);
 					 k = 1;
 					 x = -0.5f;
 				 }
@@ -1845,7 +2386,7 @@
 				 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);
+					 tMBSawPulse_place_step_dd_noBuffer(osc, j, p_at_reset + eof_offset, inv_sw, 1.0f);
 					 k = 0;
 					 x = 0.5f;
 				 }
@@ -1855,13 +2396,13 @@
 				 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);
+					 tMBSawPulse_place_step_dd_noBuffer(osc, j, 1.0f - p_at_reset - eof_offset, -inv_sw, -1.0f);
 					 k = 1;
 					 x = -0.5f;
 				 }
 				 if (k && p_at_reset < b)
 				 {
-					 place_step_dd(c->_f, j, b - p_at_reset - eof_offset, -sw, 1.0f * shape);
+					 tMBSawPulse_place_step_dd_noBuffer(osc, j, b - p_at_reset - eof_offset, -inv_sw, 1.0f * shape);
 					 k = 0;
 					 x = 0.5f;
 				 }
@@ -1874,13 +2415,13 @@
 				 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);
+					 tMBSawPulse_place_step_dd_noBuffer(osc, j, p_at_reset + eof_offset, inv_sw, 1.0f);
 					 k = 0;
 					 x = 0.5f;
 				 }
 				 if (!k && p_at_reset >= b)
 				 {
-					 place_step_dd(c->_f, j, p_at_reset - b + eof_offset, sw, -1.0f * shape);
+					 tMBSawPulse_place_step_dd_noBuffer(osc, j, p_at_reset - b + eof_offset, inv_sw, -1.0f * shape);
 					 k = 1;
 					 x = -0.5f;
 				 }
@@ -1889,7 +2430,7 @@
 			 {
 				 if (p_at_reset < b)
 				 {
-					 place_step_dd(c->_f, j, b - p_at_reset - eof_offset, -sw, 1.0f * shape);
+					 tMBSawPulse_place_step_dd_noBuffer(osc, j, b - p_at_reset - eof_offset, -inv_sw, 1.0f * shape);
 					 k = 0;
 					 x = 0.5f;
 				 }
@@ -1896,7 +2437,7 @@
 				 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);
+					 tMBSawPulse_place_step_dd_noBuffer(osc, j, 1.0f - p_at_reset - eof_offset, -inv_sw, -1.0f);
 					 k = 1;
 					 x = -0.5f;
 				 }
@@ -1908,15 +2449,15 @@
 		if (sw > 0)
 		{
 			/* now place reset DD for saw*/
-			place_step_dd(c->_f, j, p, sw, p_at_reset * sawShape);
+			tMBSawPulse_place_step_dd_noBuffer(osc, j, p, inv_sw, p_at_reset * sawShape);
             /* now place reset DD for pulse */
             if (k) {
-				place_step_dd(c->_f, j, p, sw, 1.0f * shape);
+            	tMBSawPulse_place_step_dd_noBuffer(osc, j, p, inv_sw, 1.0f * shape);
 				k = 0;
 				x = 0.5f;
 			}
 			if (p >= b) {
-				place_step_dd(c->_f, j, p - b, sw, -1.0f * shape);
+				tMBSawPulse_place_step_dd_noBuffer(osc, j, p - b, inv_sw, -1.0f * shape);
 				k = 1;
 				x = -0.5f;
 			}
@@ -1924,15 +2465,15 @@
 		else if (sw < 0)
 		{
 	        /* now place reset DD for saw*/
-			place_step_dd(c->_f, j, 1.0f - p, -sw, -p_at_reset * sawShape);
+			tMBSawPulse_place_step_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, -p_at_reset * sawShape);
 			 /* now place reset DD for pulse */
 			if (!k) {
-				place_step_dd(c->_f, j, 1.0f - p, -sw, -1.0f * shape);
+				tMBSawPulse_place_step_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, -1.0f * shape);
 				k = 1;
 				x = -0.5f;
 			}
 			if (p < b) {
-				place_step_dd(c->_f, j, b - p, -sw, 1.0f * shape);
+				tMBSawPulse_place_step_dd_noBuffer(osc, j, b - p, -inv_sw, 1.0f * shape);
 				k = 0;
 				x = 0.5f;
 			}
@@ -1948,13 +2489,13 @@
 		if (sw > 0)
 		{
 			if (p >= b) {
-				place_step_dd(c->_f, j, p - b, sw, -1.0f * shape);
+				tMBSawPulse_place_step_dd_noBuffer(osc, j, p - b, inv_sw, -1.0f * shape);
 				k = 1;
 				x = -0.5f;
 			}
 			if (p >= 1.0f) {
 				p -= 1.0f;
-				place_step_dd(c->_f, j, p, sw, 1.0f);
+				tMBSawPulse_place_step_dd_noBuffer(osc, j, p, inv_sw, 1.0f);
 				k = 0;
 				x = 0.5f;
 			}
@@ -1963,12 +2504,12 @@
 		{
 			if (p < 0.0f) {
 				p += 1.0f;
-				place_step_dd(c->_f, j, 1.0f - p, -sw, -1.0f);
+				tMBSawPulse_place_step_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, -1.0f);
 				k = 1;
 				x = -0.5f;
 			}
 			if (k && p < b) {
-				place_step_dd(c->_f, j, b - p, -sw, 1.0f * shape);
+				tMBSawPulse_place_step_dd_noBuffer(osc, j, b - p, -inv_sw, 1.0f * shape);
 				k = 0;
 				x = 0.5f;
 			}
@@ -1980,12 +2521,12 @@
 		{
 			if (p >= 1.0f) {
 				p -= 1.0f;
-				place_step_dd(c->_f, j, p, sw, 1.0f);
+				tMBSawPulse_place_step_dd_noBuffer(osc, j, p, inv_sw, 1.0f);
 				k = 0;
 				x = 0.5f;
 			}
 			if (!k && p >= b) {
-				place_step_dd(c->_f, j, p - b, sw, -1.0f * shape);
+				tMBSawPulse_place_step_dd_noBuffer(osc, j, p - b, inv_sw, -1.0f * shape);
 				k = 1;
 				x = -0.5f;
 			}
@@ -1993,33 +2534,47 @@
 		else if (sw < 0)
 		{
 			if (p < b) {
-				place_step_dd(c->_f, j, b - p, -sw, 1.0f * shape);
+				tMBSawPulse_place_step_dd_noBuffer(osc, j, b - p, -inv_sw, 1.0f * shape);
 				k = 0;
 				x = 0.5f;
 			}
 			if (p < 0.0f) {
 				p += 1.0f;
-				place_step_dd(c->_f, j, 1.0f - p, -sw, -1.0f);
+				tMBSawPulse_place_step_dd_noBuffer(osc, j, 1.0f - p, -inv_sw, -1.0f);
 				k = 1;
 				x = -0.5f;
 			}
 		}
 	}
+    int currentSamp = (j + DD_SAMPLE_DELAY) & 7;
+    c->_f[currentSamp] = ((0.5f - p) * sawShape); //saw
 
-    c->_f[j + DD_SAMPLE_DELAY] += ((0.5f - p) * sawShape); //saw
+    c->_f[currentSamp] += (x * shape);//pulse
 
-    c->_f[j + DD_SAMPLE_DELAY] += (x * shape);//pulse
+    volatile uint8_t numBLEPsAtLoopStart = c->numBLEPs;
+    for (int i = 0; i < numBLEPsAtLoopStart; i++)
+    {
+    	volatile uint16_t whichBLEP = (c->mostRecentBLEP - i);
+    	whichBLEP &= 63;
 
-    z += 0.5f * (c->_f[j] - z); // LP filtering
-    c->out = z;
+    	//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);
 
-    if (++j == FILLEN)
-    {
-        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));
+    	//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;
+
     c->_p = p;
     c->_w = w;
     c->_b = b;
@@ -2030,12 +2585,16 @@
 
     return -c->out;
 }
-
+#ifdef ITCMRAM
+void __attribute__ ((section(".itcmram"))) __attribute__ ((aligned (32)))  tMBSawPulse_setFreq(tMBSawPulse* const osc, float f)
+#else
 void tMBSawPulse_setFreq(tMBSawPulse* const osc, float f)
+#endif
 {
     _tMBSawPulse* c = *osc;
     c->freq = f;
     c->_w = c->freq * c->invSampleRate;  /* phase increment */
+    c->_inv_w = 1.0f / c->_w;
 }
 
 #ifdef ITCMRAM