shithub: leaf

Download patch

ref: 6dcc4aeffb487193bb072797444d354fd7ded848
parent: 64d544f911536439cbc718aeb3d46cc6ad2732cb
author: spiricom <jeff@snyderphonics.com>
date: Tue May 26 18:22:42 EDT 2020

replaced multiple booleans in ADSR4 with single state variable, and added some functions to tSimplePoly to allow handling of release envelopes

--- a/LEAF/Inc/leaf-envelopes.h
+++ b/LEAF/Inc/leaf-envelopes.h
@@ -239,8 +239,10 @@
 
     // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
     
-     /* ADSR 4*/
-         typedef struct _tADSR4
+    //ADSR4
+
+     
+     typedef struct _tADSR4
          {
             const float *exp_buff;
              uint32_t buff_size;
@@ -250,7 +252,7 @@
 
              float attackInc, decayInc, releaseInc, rampInc;
 
-             oBool inAttack, inDecay, inSustain, inRelease, inRamp;
+             uint32_t whichStage;
 
              float sustain, gain, rampPeak, releasePeak;
 
--- a/LEAF/Inc/leaf-midi.h
+++ b/LEAF/Inc/leaf-midi.h
@@ -364,14 +364,6 @@
     //==============================================================================
 
     
-    // simplified version of poly for more efficiency when we don't need ramps and pitch glide
-    /*!
-     * @defgroup tpoly tPoly
-     * @ingroup midi
-     * @brief An object for polyphonic handling.
-     * @{
-     */
-
     /* tPoly */
     typedef struct _tSimplePoly
     {
@@ -380,6 +372,8 @@
         int numVoices;
         int maxNumVoices;
         int** voices;
+        int stealing_on;
+        int recover_stolen;
 
         int notes[128][2];
     } _tSimplePoly;
@@ -435,6 +429,15 @@
     int     tSimplePoly_noteOff               (tSimplePoly* const poly, uint8_t note);
 
 
+
+    void tSimplePoly_deactivateVoice(tSimplePoly* const polyh, uint8_t voice);
+
+    int tSimplePoly_markPendingNoteOff(tSimplePoly* const polyh, uint8_t note);
+
+
+    //find if there is a voice with that note -- useful for note offs where you want to wait to remove it from the poly until the release phase of the envelope is finished
+    int tSimplePoly_findVoiceAssignedToNote(tSimplePoly* const polyh, uint8_t note);
+
     //! Set the number of voices available to play notes.
     /*!
      @param poly A pointer to the relevant tPoly.
@@ -470,6 +473,11 @@
      */
 
     int   tSimplePoly_getPitch              (tSimplePoly* const poly, uint8_t voice);
+
+
+    //this one returns negative one if the voice is inactive
+    int tSimplePoly_getPitchAndCheckActive(tSimplePoly* const polyh, uint8_t voice);
+
 
     //! Get the current MIDI velocity of a given voice.
     /*!
--- a/LEAF/Src/leaf-envelopes.c
+++ b/LEAF/Src/leaf-envelopes.c
@@ -850,7 +850,7 @@
 // use this if the size of the big ADSR tables is too much.
 void    tADSR4_init    (tADSR4* const adsrenv, float attack, float decay, float sustain, float release, float* expBuffer, int bufferSize)
 {
-	tADSR4_initToPool    (adsrenv, attack, decay, sustain, release, expBuffer, bufferSize, &leaf.mempool);
+    tADSR4_initToPool    (adsrenv, attack, decay, sustain, release, expBuffer, bufferSize, &leaf.mempool);
 }
 
 void tADSR4_free(tADSR4* const adsrenv)
@@ -887,11 +887,7 @@
 
     adsr->next = 0.0f;
 
-    adsr->inRamp = OFALSE;
-    adsr->inAttack = OFALSE;
-    adsr->inDecay = OFALSE;
-    adsr->inSustain = OFALSE;
-    adsr->inRelease = OFALSE;
+    adsr->whichStage = env_idle;
 
     adsr->sustain = sustain;
 
@@ -916,7 +912,7 @@
 
     if (attack < 0.0f)
     {
-    	attack = 0.0f;
+        attack = 0.0f;
     }
 
     adsr->attackInc = adsr->bufferSizeDividedBySampleRateInMs / attack;
@@ -928,7 +924,7 @@
 
     if (decay < 0.0f)
     {
-    	decay = 0.0f;
+        decay = 0.0f;
     }
     adsr->decayInc = adsr->bufferSizeDividedBySampleRateInMs / decay;
 }
@@ -948,7 +944,7 @@
 
     if (release < 0.0f)
     {
-    	release = 0.0f;
+        release = 0.0f;
     }
     adsr->releaseInc = adsr->bufferSizeDividedBySampleRateInMs / release;
 }
@@ -966,23 +962,20 @@
 {
     _tADSR4* adsr = *adsrenv;
 
-    if ((adsr->inAttack || adsr->inDecay) || (adsr->inSustain || adsr->inRelease)) // In case ADSR retriggered while it is still happening.
+    if (adsr->whichStage != env_idle) // In case ADSR retriggered while it is still happening.
     {
         adsr->rampPhase = 0;
-        adsr->inRamp = OTRUE;
+        adsr->whichStage = env_ramp;
         adsr->rampPeak = adsr->next;
     }
     else // Normal start.
     {
-        adsr->inAttack = OTRUE;
+        adsr->whichStage = env_attack;
     }
 
     adsr->attackPhase = 0;
     adsr->decayPhase = 0;
     adsr->releasePhase = 0;
-    adsr->inDecay = OFALSE;
-    adsr->inSustain = OFALSE;
-    adsr->inRelease = OFALSE;
     adsr->gain = velocity;
 }
 
@@ -990,14 +983,15 @@
 {
     _tADSR4* adsr = *adsrenv;
 
-    if (adsr->inRelease) return;
-
-    adsr->inAttack = OFALSE;
-    adsr->inDecay = OFALSE;
-    adsr->inSustain = OFALSE;
-    adsr->inRelease = OTRUE;
-
-    adsr->releasePeak = adsr->next;
+    if (adsr->whichStage == env_idle)
+    {
+        return;
+    }
+    else
+    {
+        adsr->whichStage = env_release;
+        adsr->releasePeak = adsr->next;
+    }
 }
 
 float   tADSR4_tick(tADSR4* const adsrenv)
@@ -1004,132 +998,124 @@
 {
     _tADSR4* adsr = *adsrenv;
 
-
-    if (adsr->inRamp)
+    switch (adsr->whichStage)
     {
-        if (adsr->rampPhase > adsr->buff_sizeMinusOne)
-        {
-            adsr->inRamp = OFALSE;
-            adsr->inAttack = OTRUE;
-            adsr->next = 0.0f;
-        }
-        else
-        {
-        	uint32_t intPart = (uint32_t)adsr->rampPhase;
-        	float floatPart = adsr->rampPhase - intPart;
-        	float secondValue;
-        	if (adsr->rampPhase + 1.0f > adsr->buff_sizeMinusOne)
-        	{
-        		secondValue = 0.0f;
-        	}
-        	else
-        	{
-        		secondValue = adsr->exp_buff[(uint32_t)((adsr->rampPhase)+1)];
-        	}
-        	adsr->next = adsr->rampPeak * LEAF_interpolation_linear(adsr->exp_buff[(uint32_t)(adsr->rampPhase)], secondValue, floatPart);
-        }
+        case env_ramp:
+            if (adsr->rampPhase > adsr->buff_sizeMinusOne)
+            {
+                adsr->whichStage = env_attack;
+                adsr->next = 0.0f;
+            }
+            else
+            {
+                uint32_t intPart = (uint32_t)adsr->rampPhase;
+                float floatPart = adsr->rampPhase - intPart;
+                float secondValue;
+                if (adsr->rampPhase + 1.0f > adsr->buff_sizeMinusOne)
+                {
+                    secondValue = 0.0f;
+                }
+                else
+                {
+                    secondValue = adsr->exp_buff[(uint32_t)((adsr->rampPhase)+1)];
+                }
+                adsr->next = adsr->rampPeak * LEAF_interpolation_linear(adsr->exp_buff[intPart], secondValue, floatPart);
+            }
 
-        adsr->rampPhase += adsr->rampInc;
-    }
+            adsr->rampPhase += adsr->rampInc;
+            break;
 
-    if (adsr->inAttack)
-    {
 
-        // If attack done, time to turn around.
-        if (adsr->attackPhase > adsr->buff_sizeMinusOne)
-        {
-            adsr->inDecay = OTRUE;
-            adsr->inAttack = OFALSE;
-            adsr->next = adsr->gain;
-        }
-        else
-        {
-            // do interpolation !
-        	uint32_t intPart = (uint32_t)adsr->attackPhase;
-        	float floatPart = adsr->attackPhase - intPart;
-        	float secondValue;
-        	if (adsr->attackPhase + 1.0f > adsr->buff_sizeMinusOne)
-        	{
-        		secondValue = 0.0f;
-        	}
-        	else
-        	{
-        		secondValue = adsr->exp_buff[(uint32_t)((adsr->attackPhase)+1)];
-        	}
+        case env_attack:
 
-            adsr->next = adsr->gain * (1.0f - LEAF_interpolation_linear(adsr->exp_buff[(uint32_t)(adsr->attackPhase)], secondValue, floatPart)); // inverted and backwards to get proper rising exponential shape/perception
-        }
+            // If attack done, time to turn around.
+            if (adsr->attackPhase > adsr->buff_sizeMinusOne)
+            {
+                adsr->whichStage = env_decay;
+                adsr->next = adsr->gain;
+            }
+            else
+            {
+                // do interpolation !
+                uint32_t intPart = (uint32_t)adsr->attackPhase;
+                float floatPart = adsr->attackPhase - intPart;
+                float secondValue;
+                if (adsr->attackPhase + 1.0f > adsr->buff_sizeMinusOne)
+                {
+                    secondValue = 0.0f;
+                }
+                else
+                {
+                    secondValue = adsr->exp_buff[(uint32_t)((adsr->attackPhase)+1)];
+                }
 
-        // Increment ADSR attack.
-        adsr->attackPhase += adsr->attackInc;
+                adsr->next = adsr->gain * (1.0f - LEAF_interpolation_linear(adsr->exp_buff[intPart], secondValue, floatPart)); // inverted and backwards to get proper rising exponential shape/perception
+            }
 
-    }
+            // Increment ADSR attack.
+            adsr->attackPhase += adsr->attackInc;
+            break;
 
-    if (adsr->inDecay)
-    {
+        case env_decay:
 
-        // If decay done, sustain.
-        if (adsr->decayPhase > adsr->buff_sizeMinusOne)
-        {
-            adsr->inDecay = OFALSE;
-            adsr->inSustain = OTRUE;
-            adsr->next = adsr->gain * adsr->sustain;
-        }
+            // If decay done, sustain.
+            if (adsr->decayPhase > adsr->buff_sizeMinusOne)
+            {
+                adsr->whichStage = env_sustain;
+                adsr->next = adsr->gain * adsr->sustain;
+            }
 
-        else
-        {
-        	uint32_t intPart = (uint32_t)adsr->decayPhase;
-			float floatPart = adsr->decayPhase - intPart;
-			float secondValue;
-			if (adsr->decayPhase + 1.0f > adsr->buff_sizeMinusOne)
-			{
-				secondValue = 0.0f;
-			}
-			else
-			{
-				secondValue = adsr->exp_buff[(uint32_t)((adsr->decayPhase)+1)];
-			}
-			float interpValue = (LEAF_interpolation_linear(adsr->exp_buff[(uint32_t)(adsr->decayPhase)], secondValue, floatPart));
-        	adsr->next = (adsr->gain * (adsr->sustain + (interpValue * (1.0f - adsr->sustain)))) * adsr->leakFactor; // do interpolation !
-        }
+            else
+            {
+                uint32_t intPart = (uint32_t)adsr->decayPhase;
+                float floatPart = adsr->decayPhase - intPart;
+                float secondValue;
+                if (adsr->decayPhase + 1.0f > adsr->buff_sizeMinusOne)
+                {
+                    secondValue = 0.0f;
+                }
+                else
+                {
+                    secondValue = adsr->exp_buff[(uint32_t)((adsr->decayPhase)+1)];
+                }
+                float interpValue = (LEAF_interpolation_linear(adsr->exp_buff[intPart], secondValue, floatPart));
+                adsr->next = (adsr->gain * (adsr->sustain + (interpValue * (1.0f - adsr->sustain)))) * adsr->leakFactor; // do interpolation !
+            }
 
-        // Increment ADSR decay.
-        adsr->decayPhase += adsr->decayInc;
-    }
+            // Increment ADSR decay.
+            adsr->decayPhase += adsr->decayInc;
+            break;
 
-    if (adsr->inSustain)
-    {
-        adsr->next = adsr->next * adsr->leakFactor;
-    }
+        case env_sustain:
+            adsr->next = adsr->next * adsr->leakFactor;
+            break;
 
-    if (adsr->inRelease)
-    {
-        // If release done, finish.
-        if (adsr->releasePhase > adsr->buff_sizeMinusOne)
-        {
-            adsr->inRelease = OFALSE;
-            adsr->next = 0.0f;
-        }
-        else {
-        	uint32_t intPart = (uint32_t)adsr->releasePhase;
-			float floatPart = adsr->releasePhase - intPart;
-			float secondValue;
-			if (adsr->releasePhase + 1.0f > adsr->buff_sizeMinusOne)
-			{
-				secondValue = 0.0f;
-			}
-			else
-			{
-				secondValue = adsr->exp_buff[(uint32_t)((adsr->releasePhase)+1)];
-			}
-            adsr->next = adsr->releasePeak * (LEAF_interpolation_linear(adsr->exp_buff[(uint32_t)(adsr->releasePhase)], secondValue, floatPart)); // do interpolation !
-        }
+        case env_release:
+            // If release done, finish.
+            if (adsr->releasePhase > adsr->buff_sizeMinusOne)
+            {
+                adsr->whichStage = env_idle;
+                adsr->next = 0.0f;
+            }
+            else {
+                uint32_t intPart = (uint32_t)adsr->releasePhase;
+                float floatPart = adsr->releasePhase - intPart;
+                float secondValue;
+                if (adsr->releasePhase + 1.0f > adsr->buff_sizeMinusOne)
+                {
+                    secondValue = 0.0f;
+                }
+                else
+                {
+                    secondValue = adsr->exp_buff[(uint32_t)((adsr->releasePhase)+1)];
+                }
+                adsr->next = adsr->releasePeak * (LEAF_interpolation_linear(adsr->exp_buff[intPart], secondValue, floatPart)); // do interpolation !
+            }
 
-        // Increment envelope release;
-        adsr->releasePhase += adsr->releaseInc;
+            // Increment envelope release;
+            adsr->releasePhase += adsr->releaseInc;
+            break;
     }
-
-
     return adsr->next;
 }
 
@@ -1137,87 +1123,77 @@
 {
     _tADSR4* adsr = *adsrenv;
 
-
-    if (adsr->inRamp)
+    switch (adsr->whichStage)
     {
-        if (adsr->rampPhase > adsr->buff_sizeMinusOne)
-        {
-            adsr->inRamp = OFALSE;
-            adsr->inAttack = OTRUE;
-            adsr->next = 0.0f;
-        }
-        else
-        {
-        	adsr->next = adsr->rampPeak * adsr->exp_buff[(uint32_t)(adsr->rampPhase)];
-        }
+        case env_ramp:
+            if (adsr->rampPhase > adsr->buff_sizeMinusOne)
+            {
+                adsr->whichStage = env_attack;
+                adsr->next = 0.0f;
+            }
+            else
+            {
+                adsr->next = adsr->rampPeak * adsr->exp_buff[(uint32_t)adsr->rampPhase];
+            }
 
-        adsr->rampPhase += adsr->rampInc;
-    }
+            adsr->rampPhase += adsr->rampInc;
+            break;
 
-    if (adsr->inAttack)
-    {
 
-        // If attack done, time to turn around.
-        if (adsr->attackPhase > adsr->buff_sizeMinusOne)
-        {
-            adsr->inDecay = OTRUE;
-            adsr->inAttack = OFALSE;
-            adsr->next = adsr->gain;
-        }
-        else
-        {
-            // do interpolation !
-            adsr->next = adsr->gain * (1.0f - adsr->exp_buff[(uint32_t)(adsr->attackPhase)]); // inverted and backwards to get proper rising exponential shape/perception
-        }
+        case env_attack:
 
-        // Increment ADSR attack.
-        adsr->attackPhase += adsr->attackInc;
+            // If attack done, time to turn around.
+            if (adsr->attackPhase > adsr->buff_sizeMinusOne)
+            {
+                adsr->whichStage = env_decay;
+                adsr->next = adsr->gain;
+            }
+            else
+            {
+                adsr->next = adsr->gain * (1.0f - adsr->exp_buff[(uint32_t)adsr->attackPhase]); // inverted and backwards to get proper rising exponential shape/perception
+            }
 
-    }
+            // Increment ADSR attack.
+            adsr->attackPhase += adsr->attackInc;
+            break;
 
-    if (adsr->inDecay)
-    {
+        case env_decay:
 
-        // If decay done, sustain.
-        if (adsr->decayPhase > adsr->buff_sizeMinusOne)
-        {
-            adsr->inDecay = OFALSE;
-            adsr->inSustain = OTRUE;
-            adsr->next = adsr->gain * adsr->sustain;
-        }
+            // If decay done, sustain.
+            if (adsr->decayPhase > adsr->buff_sizeMinusOne)
+            {
+                adsr->whichStage = env_sustain;
+                adsr->next = adsr->gain * adsr->sustain;
+            }
 
-        else
-        {
-            adsr->next = (adsr->gain * (adsr->sustain + ((adsr->exp_buff[(uint32_t)(adsr->decayPhase)]) * (1.0f - adsr->sustain)))) * adsr->leakFactor; // do interpolation !
-        }
+            else
+            {
+                adsr->next = (adsr->gain * (adsr->sustain + (adsr->exp_buff[(uint32_t)adsr->decayPhase] * (1.0f - adsr->sustain)))) * adsr->leakFactor;
+            }
 
-        // Increment ADSR decay.
-        adsr->decayPhase += adsr->decayInc;
-    }
+            // Increment ADSR decay.
+            adsr->decayPhase += adsr->decayInc;
+            break;
 
-    if (adsr->inSustain)
-    {
-        adsr->next = adsr->next * adsr->leakFactor;
-    }
+        case env_sustain:
+            adsr->next = adsr->next * adsr->leakFactor;
+            break;
 
-    if (adsr->inRelease)
-    {
-        // If release done, finish.
-        if (adsr->releasePhase > adsr->buff_sizeMinusOne)
-        {
-            adsr->inRelease = OFALSE;
-            adsr->next = 0.0f;
-        }
-        else {
+        case env_release:
+            // If release done, finish.
+            if (adsr->releasePhase > adsr->buff_sizeMinusOne)
+            {
+                adsr->whichStage = env_idle;
+                adsr->next = 0.0f;
+            }
+            else {
+                adsr->next = adsr->releasePeak * adsr->exp_buff[(uint32_t)adsr->releasePhase];
+            }
 
-            adsr->next = adsr->releasePeak * (adsr->exp_buff[(uint32_t)(adsr->releasePhase)]); // do interpolation !
-        }
-
-        // Increment envelope release;
-        adsr->releasePhase += adsr->releaseInc;
+            // Increment envelope release;
+            adsr->releasePhase += adsr->releaseInc;
+            break;
     }
-
-
     return adsr->next;
 }
 
--- a/LEAF/Src/leaf-midi.c
+++ b/LEAF/Src/leaf-midi.c
@@ -682,9 +682,10 @@
 
 
 
-//tSimplePoly = more efficient implementation without ramps and glide
 
+//tSimplePoly = much more efficient implementation without ramps and glide
 
+
 // SIMPLE POLY
 void tSimplePoly_init(tSimplePoly* const polyh, int maxNumVoices)
 {
@@ -709,7 +710,8 @@
         poly->notes[i][0] = -1;
         poly->notes[i][1] = 0;
     }
-
+    poly->stealing_on = 1;
+    poly->recover_stolen = 1;
     poly->voices = (int**) mpool_alloc(sizeof(int*) * poly->maxNumVoices, m);
 
     for (int i = 0; i < poly->maxNumVoices; ++i)
@@ -743,13 +745,11 @@
     if (tStack_contains(&poly->stack, note) >= 0) return -1;
     else
     {
-        tStack_add(&poly->stack, note);
-
         alteredVoice = -1;
         oBool found = OFALSE;
         for (int i = 0; i < poly->numVoices; i++)
         {
-            if (poly->voices[i][0] < 0)    // if inactive voice, give this note to voice
+            if (poly->voices[i][0] == -1)    // if inactive voice, give this note to voice
             {
 
                 found = OTRUE;
@@ -757,15 +757,37 @@
                 poly->voices[i][0] = note;
                 poly->voices[i][1] = vel;
                 poly->notes[note][0] = i;
-
+                poly->notes[note][1] = vel;
                 poly->voices[i][2] = note; // voices[i][2] is the output midi note, (avoiding the -1 when a voice is inactive)
 
                 alteredVoice = i;
+                tStack_add(&poly->stack, note);
                 break;
             }
         }
+        if (!found)
+        {
+            //second preference is grabbing one that is in release phase but not finished sounding yet
+            for (int i = 0 ; i < poly->numVoices; i++)
+            {
+                if (poly->voices[i][0] == -2)    // if voice is released but still sounding, take over this voice
+                {
 
-        if (!found) //steal
+                    found = OTRUE;
+
+                    poly->voices[i][0] = note;
+                    poly->voices[i][1] = vel;
+                    poly->notes[note][0] = i;
+                    poly->notes[note][1] = vel;
+                    poly->voices[i][2] = note; // voices[i][2] is the output midi note, (avoiding the -1 when a voice is inactive)
+
+                    alteredVoice = i;
+                    tStack_add(&poly->stack, note);
+                    break;
+                }
+            }
+        }
+        if ((!found) && (poly->stealing_on)) //steal
         {
             for (int j = tStack_getSize(&poly->stack) - 1; j >= 0; j--)
             {
@@ -776,7 +798,7 @@
                     oldNote = poly->voices[whichVoice][0];
                     poly->voices[whichVoice][0] = note;
                     poly->voices[whichVoice][1] = vel;
-                    poly->notes[oldNote][0] = -1; //mark the stolen voice as inactive (in the second dimension of the notes array)
+                    poly->notes[oldNote][0] = -3; //mark the stolen voice as stolen (in the second dimension of the notes array)
                     poly->notes[note][0] = whichVoice;
                     poly->notes[note][1] = vel;
 
@@ -783,7 +805,7 @@
                     poly->voices[whichVoice][2] = note;
 
                     alteredVoice = whichVoice;
-
+                    tStack_add(&poly->stack, note);
                     break;
                 }
             }
@@ -794,17 +816,14 @@
 
 
 
-
 int tSimplePoly_noteOff(tSimplePoly* const polyh, uint8_t note)
 {
     _tSimplePoly* poly = *polyh;
     int16_t noteToTest = -1;
 
-
     tStack_remove(&poly->stack, note);
     poly->notes[note][0] = -1;
 
-
     int deactivatedVoice = -1;
     for (int i = 0; i < poly->maxNumVoices; i++)
     {
@@ -817,23 +836,101 @@
         }
     }
 
-    //grab old notes off the stack if there are notes waiting to replace the free voice
-    if (deactivatedVoice >= 0)
+    if (poly->recover_stolen)
     {
-        for (int j = 0; j < tStack_getSize(&poly->stack); ++j)
+        //grab old notes off the stack if there are notes waiting to replace the free voice
+        if (deactivatedVoice >= 0)
         {
-            noteToTest = tStack_get(&poly->stack, j);
+            for (int j = 0; j < tStack_getSize(&poly->stack); ++j)
+            {
+                noteToTest = tStack_get(&poly->stack, j);
 
-            if (poly->notes[noteToTest][0] < 0) //if there is a stolen note waiting (marked inactive but on the stack)
+                if (poly->notes[noteToTest][0] == -3) //if there is a stolen note waiting (marked inactive but on the stack)
+                {
+                    poly->voices[deactivatedVoice][0] = noteToTest; //set the newly free voice to use the old stolen note
+                    poly->voices[deactivatedVoice][1] = poly->notes[noteToTest][1]; // set the velocity of the voice to be the velocity of that note
+                    poly->voices[deactivatedVoice][2] = noteToTest;
+                    poly->notes[noteToTest][0] = deactivatedVoice; //mark that it is no longer stolen and is now active
+                    return -1;
+                }
+            }
+        }
+    }
+    return deactivatedVoice;
+}
+
+
+void tSimplePoly_deactivateVoice(tSimplePoly* const polyh, uint8_t voice)
+{
+    _tSimplePoly* poly = *polyh;
+
+    if (poly->voices[voice][0] == -2) //only do this if the voice is waiting for deactivation (not already reassigned while waiting)
+    {
+        poly->voices[voice][0] = -1;
+        poly->voices[voice][1] = 0;
+        if (poly->recover_stolen)
+        {
+            //grab old notes off the stack if there are notes waiting to replace the free voice
+            for (int j = 0; j < tStack_getSize(&poly->stack); ++j)
             {
-                poly->voices[deactivatedVoice][0] = noteToTest; //set the newly free voice to use the old stolen note
-                poly->voices[deactivatedVoice][1] = poly->notes[noteToTest][1]; // set the velocity of the voice to be the velocity of that note
-                poly->voices[deactivatedVoice][2] = noteToTest;
-                poly->notes[noteToTest][0] = deactivatedVoice; //mark that it is no longer stolen and is now active
-                return -1;
+                noteToTest = tStack_get(&poly->stack, j);
+
+                if (poly->notes[noteToTest][0] == -3) //if there is a stolen note waiting (marked inactive but on the stack)
+                {
+                    poly->voices[voice][0] = noteToTest; //set the newly free voice to use the old stolen note
+                    poly->voices[voice][1] = poly->notes[noteToTest][1]; // set the velocity of the voice to be the velocity of that note
+                    poly->voices[voice][2] = noteToTest;
+                    poly->notes[noteToTest][0] = voice; //mark that it is no longer stolen and is now active
+                }
             }
         }
     }
+}
+
+int tSimplePoly_findVoiceAssignedToNote(tSimplePoly* const polyh, uint8_t note)
+{
+    _tSimplePoly* poly = *polyh;
+
+
+    int voiceWithThatNote = -1;
+    for (int i = 0; i < poly->maxNumVoices; i++)
+    {
+        if (poly->voices[i][0] == note)
+        {
+            voiceWithThatNote = i;
+            break;
+        }
+    }
+    return voiceWithThatNote;
+}
+
+
+int tSimplePoly_markPendingNoteOff(tSimplePoly* const polyh, uint8_t note)
+{
+    _tSimplePoly* poly = *polyh;
+    int deactivatedVoice = -1;
+
+    if (tStack_remove(&poly->stack, note))
+
+    {
+
+        poly->notes[note][0] = -2;
+
+
+
+        for (int i = 0; i < poly->maxNumVoices; i++)
+        {
+            if (poly->voices[i][0] == note)
+            {
+                poly->voices[i][0] = -2;
+                poly->voices[i][1] = 0;
+                deactivatedVoice = i;
+                break;
+            }
+        }
+
+
+    }
     return deactivatedVoice;
 }
 
@@ -861,6 +958,13 @@
 {
     _tSimplePoly* poly = *polyh;
     return poly->voices[voice][2];
+}
+
+//this one returns negative one if the voice is inactive
+int tSimplePoly_getPitchAndCheckActive(tSimplePoly* const polyh, uint8_t voice)
+{
+    _tSimplePoly* poly = *polyh;
+    return poly->voices[voice][0];
 }
 
 int tSimplePoly_getVelocity(tSimplePoly* const polyh, uint8_t voice)