diff --git a/components/bl00mbox/CMakeLists.txt b/components/bl00mbox/CMakeLists.txt
index 9688b0e0fc0f85a8fa1c7bdc867c19bcaabd9a4f..997418c9d80dc95467e0f13d6bcdd5985d0745ed 100644
--- a/components/bl00mbox/CMakeLists.txt
+++ b/components/bl00mbox/CMakeLists.txt
@@ -20,6 +20,8 @@ idf_component_register(
         plugins/distortion.c
         plugins/lowpass.c
         plugins/mixer.c
+        plugins/range_shifter.c
+        plugins/poly_squeeze.c
         plugins/slew_rate_limiter.c
         plugins/bl00mbox/bl00mbox_line_in.c
         radspa/radspa_helpers.c
diff --git a/components/bl00mbox/bl00mbox_audio.c b/components/bl00mbox/bl00mbox_audio.c
index 9571af71362be1114973c981256bff34b5902cda..a14efa73bc18f5eb30bca2a3461d1269e5e901ee 100644
--- a/components/bl00mbox/bl00mbox_audio.c
+++ b/components/bl00mbox/bl00mbox_audio.c
@@ -133,6 +133,7 @@ void bl00mbox_channels_init(){
         chan->is_active = true;
         chan->is_free = true;
         chan->name = NULL;
+        chan->dc = 0;
     }
     is_initialized = true;
 }
@@ -167,81 +168,102 @@ void bl00mbox_audio_bud_render(bl00mbox_bud_t * bud){
     bud->render_pass_id = render_pass_id;
 }
 
-static void bl00mbox_audio_channel_render(bl00mbox_channel_t * chan, int16_t * out, bool adding){
-    if(render_pass_id == chan->render_pass_id) return;
+static bool bl00mbox_audio_channel_render(bl00mbox_channel_t * chan, int16_t * out, bool adding){
+    if(render_pass_id == chan->render_pass_id) return false;
     chan->render_pass_id = render_pass_id;
 
     bl00mbox_channel_root_t * root = chan->root_list;
 
     // early exit when no sources:
     if((root == NULL) || (!chan->is_active)){
-        if(adding) return; // nothing to do
-        memset(out, 0, full_buffer_len*sizeof(int16_t)); // mute
-        return;
+        return false;
     }
 
-    int32_t acc[256];
-    bool first = true;
+    int32_t acc[full_buffer_len];
+    bool acc_init = false;
 
     while(root != NULL){
         bl00mbox_audio_bud_render(root->con->source_bud);
-        if(first){
-            for(uint16_t i = 0; i < full_buffer_len; i++){
-                acc[i] = root->con->buffer[i];
+        if(root->con->buffer[1] == -32768){
+            if(!acc_init){
+                for(uint16_t i = 0; i < full_buffer_len; i++){
+                    acc[i] = root->con->buffer[0];
+                }
+                acc_init = true;
+            } else if(root->con->buffer[0]){
+                for(uint16_t i = 0; i < full_buffer_len; i++){
+                    acc[i] += root->con->buffer[0];
+                }
             }
         } else {
-            for(uint16_t i = 0; i < full_buffer_len; i++){ // replace this with proper ladspa-style adding function someday
-                acc[i] += root->con->buffer[i];
+            if(!acc_init){
+                for(uint16_t i = 0; i < full_buffer_len; i++){
+                    acc[i] = root->con->buffer[i];
+                }
+                acc_init = true;
+            } else {
+                for(uint16_t i = 0; i < full_buffer_len; i++){
+                    acc[i] += root->con->buffer[i];
+                }
             }
         }
-        first = false;
         root = root->next;
     }
 
     for(uint16_t i = 0; i < full_buffer_len; i++){
-        if(adding){
+        chan->dc = ((chan->dc * ((1<<10) - 1)) >> 10) + acc[i];
+        acc[i] = acc[i] - (chan->dc >> 10);
+    }
+
+    if(adding){
+        for(uint16_t i = 0; i < full_buffer_len; i++){
             out[i] = radspa_add_sat(radspa_mult_shift(acc[i], chan->volume), out[i]);
-        } else {
+        }
+    } else {
+        for(uint16_t i = 0; i < full_buffer_len; i++){
             out[i] = radspa_mult_shift(acc[i], chan->volume);
         }
     }
+    return true;
 }
 
-void bl00mbox_audio_render(int16_t * rx, int16_t * tx, uint16_t len){
-    if(!is_initialized){
-        memset(tx, 0, len*sizeof(int16_t)); // mute
-        return;
-    }
+bool _bl00mbox_audio_render(int16_t * rx, int16_t * tx, uint16_t len){
+    if(!is_initialized) return false;
 
     bl00mbox_audio_do_pointer_change();
     bl00mbox_channel_foreground = last_chan_event;
 
-    if(!bl00mbox_audio_run){
-        memset(tx, 0, len*sizeof(int16_t)); // mute
-        return;
-    }
+    if(!bl00mbox_audio_run) return false;
 
     render_pass_id++; // fresh pass, all relevant sources must be recomputed
     full_buffer_len = len/2;
     bl00mbox_line_in_interlaced = rx;
     int16_t acc[full_buffer_len];
+    bool acc_init = false;
     // system channel always runs non-adding
-    bl00mbox_audio_channel_render(&(channels[0]), acc, 0);
+    acc_init = bl00mbox_audio_channel_render(&(channels[0]), acc, acc_init) || acc_init;
 
     // re-rendering channels is ok, if render_pass_id didn't change it will just exit
-    bl00mbox_audio_channel_render(&(channels[bl00mbox_channel_foreground]), acc, 1);
+    acc_init = bl00mbox_audio_channel_render(&(channels[bl00mbox_channel_foreground]), acc, acc_init) || acc_init;
 
     // TODO: scales poorly if there's many channels
 #ifdef BL00MBOX_BACKGROUND_MUTE_OVERRIDE_ENABLE
     for(uint8_t i = 1; i < (BL00MBOX_CHANNELS); i++){
         if(bl00mbox_channel_background_mute_override[i]){
-            bl00mbox_audio_channel_render(&(channels[i]), acc, 1);
+            acc_init = bl00mbox_audio_channel_render(&(channels[i]), acc, acc_init) || acc_init;
         }
     }
 #endif
 
+    if(!acc_init) return false;
+
     for(uint16_t i = 0; i < full_buffer_len; i++){
         tx[2*i] = acc[i];
         tx[2*i+1] = acc[i];
     }
+    return true;
+}
+
+void bl00mbox_audio_render(int16_t * rx, int16_t * tx, uint16_t len){
+    if(!_bl00mbox_audio_render(rx, tx, len)) memset(tx, 0, len*sizeof(int16_t));
 }
diff --git a/components/bl00mbox/bl00mbox_plugin_registry.c b/components/bl00mbox/bl00mbox_plugin_registry.c
index 80edc3152b850274c131d25667af1d43bf23dc20..1092b3f5afc36920e25f95eb5074062dcb70ad6a 100644
--- a/components/bl00mbox/bl00mbox_plugin_registry.c
+++ b/components/bl00mbox/bl00mbox_plugin_registry.c
@@ -101,6 +101,8 @@ radspa_descriptor_t * bl00mbox_plugin_registry_get_id_from_index(uint32_t index)
 #include "mixer.h"
 #include "multipitch.h"
 #include "slew_rate_limiter.h"
+#include "range_shifter.h"
+#include "poly_squeeze.h"
 #include "bl00mbox_line_in.h"
 
 void bl00mbox_plugin_registry_init(void){
@@ -117,6 +119,8 @@ void bl00mbox_plugin_registry_init(void){
     plugin_add(&noise_burst_desc);
     plugin_add(&distortion_desc);
     plugin_add(&mixer_desc);
+    plugin_add(&range_shifter_desc);
+    plugin_add(&poly_squeeze_desc);
     plugin_add(&slew_rate_limiter_desc);
     plugin_add(&multipitch_desc);
     plugin_add(&bl00mbox_line_in_desc);
diff --git a/components/bl00mbox/bl00mbox_user.c b/components/bl00mbox/bl00mbox_user.c
index 8b73670074d6ccaa1ad863e7611925cec68b1a95..8f224860bb2b4bf6e2c106af3149cc9bd057156b 100644
--- a/components/bl00mbox/bl00mbox_user.c
+++ b/components/bl00mbox/bl00mbox_user.c
@@ -704,7 +704,11 @@ bool bl00mbox_channel_bud_set_signal_value(uint8_t channel, uint32_t bud_index,
     radspa_signal_t * sig = bl00mbox_signal_get_by_index(bud->plugin, bud_signal_index);
     if(sig == NULL) return false;
 
-    sig->value = value;
+    if(value == -32678){
+        sig->value = 0;
+    } else {
+        sig->value = value;
+    }
     bl00mbox_channel_event(channel);
     return true;
 }
diff --git a/components/bl00mbox/include/bl00mbox.h b/components/bl00mbox/include/bl00mbox.h
index 9dd3f1c1ae442ad30f5ca9798f3c51d41b3d681e..8a57cf28469decf67d45b0f52868372d6b514f78 100644
--- a/components/bl00mbox/include/bl00mbox.h
+++ b/components/bl00mbox/include/bl00mbox.h
@@ -8,9 +8,5 @@
 uint16_t bl00mbox_sources_count();
 uint16_t bl00mbox_source_add(void* render_data, void* render_function);
 void bl00mbox_source_remove(uint16_t index);
-void bl00mbox_audio_render(int16_t * rx, int16_t * tx, uint16_t len);
-
-// TEMP
-void bl00mbox_player_function(int16_t * rx, int16_t * tx, uint16_t len);
-
+bool bl00mbox_audio_render(int16_t * rx, int16_t * tx, uint16_t len);
 void bl00mbox_init(void);
diff --git a/components/bl00mbox/include/bl00mbox_audio.h b/components/bl00mbox/include/bl00mbox_audio.h
index 38008c6502a4e2cab86c9caac3e136bd72f1f474..8f5112baa22636bafa876c4093f3a0e733495126 100644
--- a/components/bl00mbox/include/bl00mbox_audio.h
+++ b/components/bl00mbox/include/bl00mbox_audio.h
@@ -55,6 +55,7 @@ typedef struct{
     bool is_free;
     char * name;
     int32_t volume;
+    int32_t dc;
     struct _bl00mbox_channel_root_t * root_list; // list of all roots associated with channels
     uint32_t render_pass_id; // may be used by host to determine whether recomputation is necessary
     struct _bl00mbox_bud_t * buds; // linked list with all channel buds
diff --git a/components/bl00mbox/plugins/env_adsr.c b/components/bl00mbox/plugins/env_adsr.c
index 4e1c9e18d81c1d1b86d52e8ed560d076d0eb8c48..3e31630028ec360feda6fdc472aed212247b50ec 100644
--- a/components/bl00mbox/plugins/env_adsr.c
+++ b/components/bl00mbox/plugins/env_adsr.c
@@ -8,16 +8,15 @@ radspa_descriptor_t env_adsr_desc = {
     .destroy_plugin_instance = radspa_standard_plugin_destroy
 };
 
-#define ENV_ADSR_NUM_SIGNALS 9
+#define ENV_ADSR_NUM_SIGNALS 8
 #define ENV_ADSR_OUTPUT 0
-#define ENV_ADSR_PHASE 1
-#define ENV_ADSR_INPUT 2
-#define ENV_ADSR_TRIGGER 3
-#define ENV_ADSR_ATTACK 4
-#define ENV_ADSR_DECAY 5
-#define ENV_ADSR_SUSTAIN 6
-#define ENV_ADSR_RELEASE 7
-#define ENV_ADSR_GATE 8
+#define ENV_ADSR_INPUT 1
+#define ENV_ADSR_TRIGGER 2
+#define ENV_ADSR_ATTACK 3
+#define ENV_ADSR_DECAY 4
+#define ENV_ADSR_SUSTAIN 5
+#define ENV_ADSR_RELEASE 6
+#define ENV_ADSR_GAIN 7
 
 #define ENV_ADSR_PHASE_OFF 0
 #define ENV_ADSR_PHASE_ATTACK 1
@@ -29,14 +28,13 @@ radspa_t * env_adsr_create(uint32_t init_var){
     radspa_t * env_adsr = radspa_standard_plugin_create(&env_adsr_desc, ENV_ADSR_NUM_SIGNALS, sizeof(env_adsr_data_t),0);
     env_adsr->render = env_adsr_run;
     radspa_signal_set(env_adsr, ENV_ADSR_OUTPUT, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0);
-    radspa_signal_set(env_adsr, ENV_ADSR_PHASE, "phase", RADSPA_SIGNAL_HINT_OUTPUT, 0);
     radspa_signal_set(env_adsr, ENV_ADSR_INPUT, "input", RADSPA_SIGNAL_HINT_INPUT, 32767);
     radspa_signal_set(env_adsr, ENV_ADSR_TRIGGER, "trigger", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_TRIGGER, 0);
     radspa_signal_set(env_adsr, ENV_ADSR_ATTACK, "attack", RADSPA_SIGNAL_HINT_INPUT, 100);
     radspa_signal_set(env_adsr, ENV_ADSR_DECAY, "decay", RADSPA_SIGNAL_HINT_INPUT, 250);
     radspa_signal_set(env_adsr, ENV_ADSR_SUSTAIN, "sustain", RADSPA_SIGNAL_HINT_INPUT, 16000);
     radspa_signal_set(env_adsr, ENV_ADSR_RELEASE, "release", RADSPA_SIGNAL_HINT_INPUT, 50);
-    radspa_signal_set(env_adsr, ENV_ADSR_GATE, "gate", RADSPA_SIGNAL_HINT_INPUT,0);
+    radspa_signal_set(env_adsr, ENV_ADSR_GAIN, "gain", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_GAIN, RADSPA_SIGNAL_VAL_UNITY_GAIN);
     radspa_signal_get_by_index(env_adsr, ENV_ADSR_ATTACK)->unit = "ms";
     radspa_signal_get_by_index(env_adsr, ENV_ADSR_DECAY)->unit = "ms";
     radspa_signal_get_by_index(env_adsr, ENV_ADSR_SUSTAIN)->unit = "ms";
@@ -44,177 +42,131 @@ radspa_t * env_adsr_create(uint32_t init_var){
     env_adsr_data_t * data = env_adsr->plugin_data;
     data->trigger_prev = 0;
     data->env_phase = ENV_ADSR_PHASE_OFF;
+    data->release_prev_ms = -1;
+    data->release_init_val_prev = -1;
+    data->attack_prev_ms = -1;
+    data->sustain_prev = -1;
+    data->decay_prev_ms = -1;
 
     return env_adsr;
 }
 
-static int16_t env_adsr_run_single(env_adsr_data_t * env){
-    uint32_t tmp;
-    switch(env->env_phase){
-        case ENV_ADSR_PHASE_OFF:
-            env->env_counter = 0;;
-            break;
-        case ENV_ADSR_PHASE_ATTACK:
-            tmp = env->env_counter + env->attack;
-            if(tmp < env->env_counter){ // overflow
-                tmp = ~((uint32_t) 0); // max out
-                env->env_phase = ENV_ADSR_PHASE_DECAY;
-            }
-            env->env_counter = tmp;
-            break;
-        case ENV_ADSR_PHASE_DECAY:
-            tmp = env->env_counter - env->decay;
-            if(tmp > env->env_counter){ // underflow
-                tmp = 0; //bottom out
-            }
-            env->env_counter = tmp;
-
-            if(env->env_counter <= env->sustain){
-                env->env_counter = env->sustain;
-                env->env_phase = ENV_ADSR_PHASE_SUSTAIN;
-            } else if(env->env_counter < env->gate){
-                env->env_counter = 0;
-                env->env_phase = ENV_ADSR_PHASE_OFF;
-            }
-            break;
-        case ENV_ADSR_PHASE_SUSTAIN:
-            if(env->sustain == 0) env->env_phase = ENV_ADSR_PHASE_OFF;
-            env->env_counter = env->sustain;
-            break;
-        case ENV_ADSR_PHASE_RELEASE:
-            tmp = env->env_counter - env->release;
-            if(tmp > env->env_counter){ // underflow
-                tmp = 0; //bottom out
-                env->env_phase = ENV_ADSR_PHASE_OFF;
-            }
-            env->env_counter = tmp;
-            /*
-            if(env->env_counter < env->gate){
-                env->env_counter = 0;
-                env->env_phase = ENV_ADSR_PHASE_OFF;
-            }
-            */
-            break;
-    }
-    return env->env_counter >> 17;
-}
-
 #define SAMPLE_RATE_SORRY 48000
 #define ENV_ADSR_UNDERSAMPLING 5
 
 
-static inline uint32_t env_adsr_time_ms_to_val_rise(uint16_t time_ms, uint32_t val){
+static inline uint32_t env_adsr_time_ms_to_val_rise(int16_t time_ms, uint32_t val, uint16_t leftshift){
     if(!time_ms) return UINT32_MAX;
+    if(time_ms < 0) time_ms = -time_ms;
     uint32_t div = time_ms * ((SAMPLE_RATE_SORRY)/1000);
-    return val/div;
+    uint32_t input =  val/div;
+    if(!leftshift) return input; // nothing to do
+    if(input >> (32-leftshift)) return UINT32_MAX; // sat
+    return input << leftshift;
 }
 
-static inline uint32_t uint32_sat_leftshift(uint32_t input, uint16_t left){
-    if(!left) return input; // nothing to do
-    if(input >> (32-left)) return UINT32_MAX; // sat
-    return input << left;
-}
-
-
 void env_adsr_run(radspa_t * env_adsr, uint16_t num_samples, uint32_t render_pass_id){
-    env_adsr_data_t * plugin_data = env_adsr->plugin_data;
+    env_adsr_data_t * data = env_adsr->plugin_data;
     radspa_signal_t * output_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_OUTPUT);
-    radspa_signal_t * phase_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_PHASE);
-    if((output_sig->buffer == NULL) && (phase_sig->buffer == NULL)) return;
-    radspa_signal_t * trigger_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_TRIGGER);
+    if(output_sig->buffer == NULL) return;
     radspa_signal_t * input_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_INPUT);
-    radspa_signal_t * attack_sig = NULL;
-    radspa_signal_t * decay_sig = NULL;
-    radspa_signal_t * sustain_sig = NULL;
-    radspa_signal_t * release_sig = NULL;
-    radspa_signal_t * gate_sig = NULL;
+    radspa_signal_t * trigger_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_TRIGGER);
+    radspa_signal_t * attack_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_ATTACK);
+    radspa_signal_t * decay_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_DECAY);
+    radspa_signal_t * sustain_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_SUSTAIN);
+    radspa_signal_t * release_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_RELEASE);
+    radspa_signal_t * gain_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_GAIN);
 
     int16_t env = 0;
     for(uint16_t i = 0; i < num_samples; i++){
         int16_t ret = 0;
 
         int16_t trigger = radspa_signal_get_value(trigger_sig, i, render_pass_id);
-        int16_t vel = radspa_trigger_get(trigger, &(plugin_data->trigger_prev));
+        int16_t vel = radspa_trigger_get(trigger, &(data->trigger_prev));
 
         if(vel < 0){ // stop
-            if(plugin_data->env_phase != ENV_ADSR_PHASE_OFF){
-                plugin_data->env_phase = ENV_ADSR_PHASE_RELEASE;
-                plugin_data->release_init_val = plugin_data->env_counter;
+            if(data->env_phase != ENV_ADSR_PHASE_OFF){
+                data->env_phase = ENV_ADSR_PHASE_RELEASE;
+                data->release_init_val = data->env_counter;
             }
         } else if(vel > 0 ){ // start
-            plugin_data->env_phase = ENV_ADSR_PHASE_ATTACK;
-            plugin_data->velocity = ((uint32_t) vel) << 17;
+            data->env_phase = ENV_ADSR_PHASE_ATTACK;
+            data->velocity = vel;
         }
 
         if(!(i%(1<<ENV_ADSR_UNDERSAMPLING))){
-            uint16_t time_ms;
-            uint32_t sus;
-            switch(plugin_data->env_phase){
+            uint32_t tmp;
+            int16_t time_ms;
+            switch(data->env_phase){
                 case ENV_ADSR_PHASE_OFF:
+                    data->env_counter = 0;;
                     break;
                 case ENV_ADSR_PHASE_ATTACK:
-                    if(attack_sig == NULL){
-                        attack_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_ATTACK);
-                    }
                     time_ms = radspa_signal_get_value(attack_sig, i, render_pass_id);
-                    if(time_ms != plugin_data->attack_prev_ms){
-                        plugin_data->attack = uint32_sat_leftshift(env_adsr_time_ms_to_val_rise(time_ms, UINT32_MAX), ENV_ADSR_UNDERSAMPLING);
-                        plugin_data->attack_prev_ms = time_ms;
+                    if(data->attack_prev_ms != time_ms){
+                        data->attack_raw = env_adsr_time_ms_to_val_rise(time_ms, UINT32_MAX, ENV_ADSR_UNDERSAMPLING);
+                        data->attack_prev_ms = time_ms;
                     }
+
+                    tmp = data->env_counter + data->attack_raw;
+                    if(tmp < data->env_counter){ // overflow
+                        tmp = ~((uint32_t) 0); // max out
+                        data->env_phase = ENV_ADSR_PHASE_DECAY;
+                    }
+                    data->env_counter = tmp;
                     break;
                 case ENV_ADSR_PHASE_DECAY:
-                    if(sustain_sig == NULL){
-                        sustain_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_SUSTAIN);
+                    data->sustain = radspa_signal_get_value(sustain_sig, i, render_pass_id) << 17UL;
+                    time_ms = radspa_signal_get_value(decay_sig, i, render_pass_id);
+                    if((data->decay_prev_ms != time_ms) || (data->sustain_prev != data->sustain)){
+                        data->decay_raw = env_adsr_time_ms_to_val_rise(time_ms, UINT32_MAX - data->sustain, ENV_ADSR_UNDERSAMPLING);
+                        data->decay_prev_ms = time_ms;
+                        data->sustain_prev = data->sustain;
                     }
-                    sus = radspa_signal_get_value(sustain_sig, i, render_pass_id);
-                    plugin_data->sustain = sus<<17;
-
-                    if(gate_sig == NULL){
-                        gate_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_GATE);
+                    tmp = data->env_counter - data->decay_raw;
+                    if(tmp > data->env_counter){ // underflow
+                        tmp = 0; //bottom out
                     }
-                    sus = radspa_signal_get_value(gate_sig, i, render_pass_id);
-                    plugin_data->gate = sus<<17;
 
-                    if(decay_sig == NULL){
-                        decay_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_DECAY);
-                    }
-                    time_ms = radspa_signal_get_value(decay_sig, i, render_pass_id);
-                    if(time_ms != plugin_data->decay_prev_ms){
-                        plugin_data->decay = uint32_sat_leftshift(env_adsr_time_ms_to_val_rise(time_ms, UINT32_MAX-plugin_data->sustain), ENV_ADSR_UNDERSAMPLING);
-                        plugin_data->decay_prev_ms = time_ms;
+                    if(tmp <= data->sustain){
+                        tmp = data->sustain;
+                        data->env_phase = ENV_ADSR_PHASE_SUSTAIN;
                     }
+
+                    data->env_counter = tmp;
+
                     break;
                 case ENV_ADSR_PHASE_SUSTAIN:
-                    if(sustain_sig == NULL){
-                        sustain_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_SUSTAIN);
-                    }
-                    sus = radspa_signal_get_value(sustain_sig, i, render_pass_id);
-                    plugin_data->sustain = sus<<17;
+                    data->sustain = radspa_signal_get_value(sustain_sig, i, render_pass_id) << 17UL;
+                    if(data->sustain == 0) data->env_phase = ENV_ADSR_PHASE_OFF;
+                    data->env_counter = data->sustain;
                     break;
                 case ENV_ADSR_PHASE_RELEASE:
-                    if(gate_sig == NULL){
-                        gate_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_GATE);
-                    }
-                    sus = radspa_signal_get_value(gate_sig, i, render_pass_id);
-                    plugin_data->gate = sus<<17;
-
-                    if(release_sig == NULL){
-                        release_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_RELEASE);
-                    }
                     time_ms = radspa_signal_get_value(release_sig, i, render_pass_id);
-                    if(time_ms != plugin_data->release_prev_ms){
-                        plugin_data->release = uint32_sat_leftshift(env_adsr_time_ms_to_val_rise(time_ms, plugin_data->release_init_val), ENV_ADSR_UNDERSAMPLING);
-                        plugin_data->release_prev_ms = time_ms;
+                    if((data->release_prev_ms != time_ms) || (data->release_init_val_prev != data->release_init_val)){
+                        data->release_raw = env_adsr_time_ms_to_val_rise(time_ms, data->release_init_val, ENV_ADSR_UNDERSAMPLING);
+                        data->release_prev_ms = time_ms;
+                        data->release_init_val_prev = data->release_init_val;;
+                    }
+                    tmp = data->env_counter - data->release_raw;
+                    if(tmp > data->env_counter){ // underflow
+                        tmp = 0; //bottom out
+                        data->env_phase = ENV_ADSR_PHASE_OFF;
                     }
+                    data->env_counter = tmp;
                     break;
             }
-            env = env_adsr_run_single(plugin_data);
+            env = data->env_counter >> 17;
+            env = (env * (env + 1)) >> 15;
+
+            int32_t gain = radspa_signal_get_value(gain_sig, i, render_pass_id);
+            env  = (env * gain) >> 12;
+            env  = (env * data->velocity) >> 15;
         }
         if(env){
             int16_t input = radspa_signal_get_value(input_sig, i, render_pass_id);
             ret = radspa_mult_shift(env, input);
         }
-        radspa_signal_set_value(phase_sig, i, plugin_data->env_phase);
         radspa_signal_set_value(output_sig, i, ret);
     }
 }
diff --git a/components/bl00mbox/plugins/env_adsr.h b/components/bl00mbox/plugins/env_adsr.h
index c2d9534514d9934d095e4668df8e89d3517fb2c1..fae0313f10b96db7ee05a2524c0fb7f33c435b02 100644
--- a/components/bl00mbox/plugins/env_adsr.h
+++ b/components/bl00mbox/plugins/env_adsr.h
@@ -4,19 +4,19 @@
 
 typedef struct {
     uint32_t    env_counter;
-    uint32_t    attack;
-    uint32_t    decay;
+    int16_t    attack_prev_ms;
+    uint32_t    attack_raw;
+    int16_t    decay_prev_ms;
+    uint32_t    decay_raw;
     uint32_t    sustain;
-    uint32_t    release;
+    uint32_t    sustain_prev;
+    int16_t    release_prev_ms;
+    uint32_t    release_raw;
     uint32_t    release_init_val;
-    uint16_t    attack_prev_ms;
-    uint16_t    decay_prev_ms;
-    uint16_t    release_prev_ms;
-    uint32_t    gate;
-    uint32_t    velocity;
-    uint8_t     env_phase;
-    uint8_t     skip_hold;
+    uint32_t    release_init_val_prev;
+    int16_t    velocity;
     int16_t     trigger_prev;
+    uint8_t     env_phase;
 } env_adsr_data_t;
 
 extern radspa_descriptor_t env_adsr_desc;
diff --git a/components/bl00mbox/plugins/mixer.c b/components/bl00mbox/plugins/mixer.c
index d77064e01c95f49b8ed2f91adeb51d36800fdba8..a7e74b4c7b385acb0cbed74b651683ace5804466 100644
--- a/components/bl00mbox/plugins/mixer.c
+++ b/components/bl00mbox/plugins/mixer.c
@@ -19,8 +19,6 @@ void mixer_run(radspa_t * mixer, uint16_t num_samples, uint32_t render_pass_id){
         input_sigs[i] = radspa_signal_get_by_index(mixer, 2 + i);
     }
 
-    int32_t * dc_acc = mixer->plugin_data;
-
     static int32_t ret = 0;
     for(uint16_t i = 0; i < num_samples; i++){
         int16_t gain = radspa_signal_get_value(gain_sig, i, render_pass_id);
@@ -28,9 +26,6 @@ void mixer_run(radspa_t * mixer, uint16_t num_samples, uint32_t render_pass_id){
         for(uint8_t j = 0; j < num_inputs; j++){
             ret += radspa_signal_get_value(input_sigs[j], i, render_pass_id);
         }
-        // remove dc
-        (* dc_acc) = (ret + (* dc_acc)*1023) >> 10;
-        ret -= (* dc_acc);
         ret = radspa_clip(radspa_gain(ret, gain));
         radspa_signal_set_value(output_sig, i, ret);
     }
@@ -39,14 +34,12 @@ void mixer_run(radspa_t * mixer, uint16_t num_samples, uint32_t render_pass_id){
 radspa_t * mixer_create(uint32_t init_var){
     if(init_var == 0) init_var = 4;
     if(init_var > 127) init_var = 127;
-    radspa_t * mixer = radspa_standard_plugin_create(&mixer_desc, 2 + init_var, sizeof(int32_t), 0);
+    radspa_t * mixer = radspa_standard_plugin_create(&mixer_desc, 2 + init_var, 0, 0);
     if(mixer == NULL) return NULL;
     mixer->render = mixer_run;
     radspa_signal_set(mixer, 0, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0);
     int16_t gain = RADSPA_SIGNAL_VAL_UNITY_GAIN/init_var;
     radspa_signal_set(mixer, 1, "gain", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_GAIN, gain);
     radspa_signal_set_group(mixer, init_var, 1, 2, "input", RADSPA_SIGNAL_HINT_INPUT, 0);
-    int32_t * dc_acc = mixer->plugin_data;
-    (* dc_acc) = 0;
     return mixer;
 }
diff --git a/components/bl00mbox/plugins/osc_fm.c b/components/bl00mbox/plugins/osc_fm.c
index a05bec085424055bf1a66ddc25b0eda10e6c8a00..b78610497a21cb22e960e16ac76880a5d7fbdae1 100644
--- a/components/bl00mbox/plugins/osc_fm.c
+++ b/components/bl00mbox/plugins/osc_fm.c
@@ -1,7 +1,5 @@
 #include "osc_fm.h"
 
-static inline int16_t waveshaper(int16_t saw, int16_t shape);
-
 radspa_descriptor_t osc_fm_desc = {
     .name = "osc_fm",
     .id = 420,
@@ -10,11 +8,13 @@ radspa_descriptor_t osc_fm_desc = {
     .destroy_plugin_instance = radspa_standard_plugin_destroy
 };
 
-#define OSC_FM_NUM_SIGNALS 4
+#define OSC_FM_NUM_SIGNALS 6
 #define OSC_FM_OUTPUT 0
 #define OSC_FM_PITCH 1
 #define OSC_FM_WAVEFORM 2
 #define OSC_FM_LIN_FM 3
+#define OSC_FM_PITCH_THRU 4
+#define OSC_FM_PITCH_OFFSET 5
 
 radspa_t * osc_fm_create(uint32_t init_var){
     radspa_t * osc_fm = radspa_standard_plugin_create(&osc_fm_desc, OSC_FM_NUM_SIGNALS, sizeof(osc_fm_data_t), 0);
@@ -23,46 +23,16 @@ radspa_t * osc_fm_create(uint32_t init_var){
     radspa_signal_set(osc_fm, OSC_FM_PITCH, "pitch", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_SCT, 18367);
     radspa_signal_set(osc_fm, OSC_FM_WAVEFORM, "waveform", RADSPA_SIGNAL_HINT_INPUT, -16000);
     radspa_signal_set(osc_fm, OSC_FM_LIN_FM, "lin_fm", RADSPA_SIGNAL_HINT_INPUT, 0);
+    radspa_signal_set(osc_fm, OSC_FM_PITCH_THRU, "fm_pitch_thru", RADSPA_SIGNAL_HINT_OUTPUT | RADSPA_SIGNAL_HINT_SCT, 18367);
+    radspa_signal_set(osc_fm, OSC_FM_PITCH_OFFSET, "fm_pitch_offset", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_SCT, 18367);
     return osc_fm;
 }
 
-void osc_fm_run(radspa_t * osc_fm, uint16_t num_samples, uint32_t render_pass_id){
-    osc_fm_data_t * plugin_data = osc_fm->plugin_data;
-    radspa_signal_t * output_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_OUTPUT);
-    radspa_signal_t * pitch_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_PITCH);
-    radspa_signal_t * waveform_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_WAVEFORM);
-    radspa_signal_t * lin_fm_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_LIN_FM);
-    if(output_sig->buffer == NULL) return;
-
-    int16_t ret = 0;
-    for(uint16_t i = 0; i < num_samples; i++){
-        int16_t pitch = radspa_signal_get_value(pitch_sig, i, render_pass_id);
-        int16_t wave = radspa_signal_get_value(waveform_sig, i, render_pass_id);
-        int32_t lin_fm = radspa_signal_get_value(lin_fm_sig, i, render_pass_id);
-
-        if(pitch != plugin_data->prev_pitch){
-            plugin_data->incr = radspa_sct_to_rel_freq(pitch, 0);
-            plugin_data->prev_pitch = pitch;
-        }
-        plugin_data->counter += plugin_data->incr;
-        if(lin_fm){
-            plugin_data->counter += lin_fm * (plugin_data->incr >> 15);
-        }
-
-        int32_t tmp = (plugin_data->counter) >> 17;
-        tmp = (tmp*2) - 32767;
-        ret = waveshaper(tmp, wave);
-        radspa_signal_set_value(output_sig, i, ret);
-    }
-}
-
-static inline int16_t triangle(int16_t saw){
-    int32_t tmp = saw;
-    tmp += 16384;
-    if(tmp > 32767) tmp -= 65535;
-    if(tmp > 0) tmp = -tmp;
-    tmp = (2 * tmp) + 32767;
-    return tmp;
+static inline int16_t triangle(int32_t saw){
+    saw -= 16384;
+    if(saw < -32767) saw += 65535;
+    if(saw > 0) saw = -saw;
+    return saw * 2 + 32767;
 }
 
 static inline int16_t waveshaper(int16_t saw, int16_t shape){
@@ -75,11 +45,11 @@ static inline int16_t waveshaper(int16_t saw, int16_t shape){
             if(tmp > 0){
                 tmp = 32767 - tmp;
                 tmp = (tmp*tmp)>>15;
-                tmp = 32767. - tmp;
+                tmp = 32767 - tmp;
             } else {
                 tmp = 32767 + tmp;
                 tmp = (tmp*tmp)>>15;
-                tmp = tmp - 32767.;
+                tmp = tmp - 32767;
             }
             break;
         case 1: //tri
@@ -97,3 +67,46 @@ static inline int16_t waveshaper(int16_t saw, int16_t shape){
     }
     return tmp;
 }
+
+void osc_fm_run(radspa_t * osc_fm, uint16_t num_samples, uint32_t render_pass_id){
+    osc_fm_data_t * plugin_data = osc_fm->plugin_data;
+    radspa_signal_t * output_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_OUTPUT);
+    radspa_signal_t * pitch_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_PITCH);
+    radspa_signal_t * waveform_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_WAVEFORM);
+    radspa_signal_t * lin_fm_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_LIN_FM);
+
+    radspa_signal_t * fm_pitch_thru_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_PITCH_THRU);
+    if(fm_pitch_thru_sig->buffer != NULL){
+        radspa_signal_t * fm_pitch_offset_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_PITCH_OFFSET);
+        int32_t thru_acc = 0;
+        for(uint16_t i = 0; i < num_samples; i++){
+            thru_acc = radspa_signal_get_value(pitch_sig, i, render_pass_id);
+            thru_acc += radspa_signal_get_value(fm_pitch_offset_sig, i, render_pass_id);
+            thru_acc = radspa_clip(thru_acc);
+            radspa_signal_set_value(fm_pitch_thru_sig, i, thru_acc);
+        }
+    };
+
+    if(output_sig->buffer == NULL) return;
+
+    int16_t ret = 0;
+    for(uint16_t i = 0; i < num_samples; i++){
+        int16_t pitch = radspa_signal_get_value(pitch_sig, i, render_pass_id);
+        int16_t wave = radspa_signal_get_value(waveform_sig, i, render_pass_id);
+        int32_t lin_fm = radspa_signal_get_value(lin_fm_sig, i, render_pass_id);
+
+        if(pitch != plugin_data->prev_pitch){
+            plugin_data->incr = radspa_sct_to_rel_freq(pitch, 0);
+            plugin_data->prev_pitch = pitch;
+        }
+        plugin_data->counter += plugin_data->incr;
+        if(lin_fm){
+            plugin_data->counter += lin_fm * (plugin_data->incr >> 15);
+        }
+
+        int32_t tmp = (plugin_data->counter) >> 16;
+        tmp = tmp - 32767;
+        ret = waveshaper(tmp, wave);
+        radspa_signal_set_value(output_sig, i, ret);
+    }
+}
diff --git a/components/bl00mbox/plugins/poly_squeeze.c b/components/bl00mbox/plugins/poly_squeeze.c
new file mode 100644
index 0000000000000000000000000000000000000000..2d23836a5c5cc429da2f53f7dce57bb633d3f779
--- /dev/null
+++ b/components/bl00mbox/plugins/poly_squeeze.c
@@ -0,0 +1,245 @@
+#include "poly_squeeze.h"
+
+radspa_descriptor_t poly_squeeze_desc = {
+    .name = "poly_squeeze",
+    .id = 172,
+    .description = "Multiplexes a number of triggerand pitch inputs into a lesser number of trigger pitch output pairs. "
+                   "The latest triggered inputs are forwarded to the output. If such an input receives a stop trigger it is disconnected "
+                   "from its output. If another inputs is in triggered state but not forwarded at the same time it will be connected to that "
+                   "output and the output is triggered. Pitch is constantly streamed to the outputs if they are connected, else the last "
+                   "connected value is being held."
+                   "\ninit_var: lsb: number of outputs, 1..16, default 3; lsb+1: number of inputs, <lsb>..32, default 10; ",
+    .create_plugin_instance = poly_squeeze_create,
+    .destroy_plugin_instance = radspa_standard_plugin_destroy
+};
+
+
+// mpx block 1
+#define POLY_SQUEEZE_TRIGGER_INPUT 0
+#define POLY_SQUEEZE_PITCH_INPUT 1
+// mpx block 2
+#define POLY_SQUEEZE_TRIGGER_OUTPUT 0
+#define POLY_SQUEEZE_PITCH_OUTPUT 1
+
+static void assign_note_voices(poly_squeeze_data_t * data){
+    poly_squeeze_note_t * note = data->active_notes_top;
+    if(note == NULL) return;
+    uint32_t active_voices = 0xFFFFFFFFUL << data->num_voices;
+
+    for(uint8_t i = 0; i < data->num_voices; i++){
+        if(note == NULL) break;
+        if(note->voice >= 0) active_voices = active_voices | (1UL<<note->voice);
+        note = note->lower;
+    }
+    while(note != NULL){
+        note->voice = -1;
+        note = note->lower;
+    }
+    
+    if(active_voices == UINT32_MAX) return;
+
+    note = data->active_notes_top;
+    for(uint8_t i = 0; i < data->num_voices; i++){
+        if(note == NULL) break;
+        if(note->voice < 0){
+            int8_t voice = -1;
+            for(int8_t v = 0; v < data->num_voices; v++){
+                if((~active_voices) & (1<<v)){
+                    active_voices = active_voices | (1<<v);
+                    voice = v;
+                    break;
+                }
+                
+            }
+            note->voice = voice;
+        }
+        note = note->lower;
+    }
+}
+
+static poly_squeeze_note_t * get_note_with_voice(poly_squeeze_data_t * data, int8_t voice){
+    poly_squeeze_note_t * note = data->active_notes_top;
+    for(uint8_t i = 0; i < data->num_voices; i++){
+        if(note == NULL) break;
+        if(note->voice == voice) return note;
+        note = note->lower;
+    }
+    return NULL;
+}
+
+void take_note(poly_squeeze_data_t * data, poly_squeeze_note_t * note){
+    if(note == NULL) return;
+    if(data->free_notes == note) data->free_notes = note->lower;
+    if(data->active_notes_bottom == note) data->active_notes_bottom = note->higher;
+    if(data->active_notes_top == note) data->active_notes_top = note->lower;
+    if(note->higher != NULL) note->higher->lower = note->lower;
+    if(note->lower != NULL) note->lower->higher = note->higher;
+    note->higher = NULL;
+    note->lower = NULL;
+}
+
+static int8_t put_note_on_top(poly_squeeze_data_t * data, poly_squeeze_note_t * note){
+    if(note == NULL) return -1;
+    if(note == data->active_notes_top) return note->voice;
+    take_note(data, note);
+    note->lower = data->active_notes_top;
+    if(note->lower != NULL) note->lower->higher = note;
+    data->active_notes_top = note;
+
+    if(note->lower == NULL) data->active_notes_bottom = note;
+    assign_note_voices(data);
+    return note->voice;
+}
+
+static int8_t free_note(poly_squeeze_data_t * data, poly_squeeze_note_t * note){
+    if(note == NULL) return -1;
+    take_note(data, note);
+    int8_t ret = note->voice;
+    note->voice = -1;
+    note->lower = data->free_notes;
+    if(note->lower != NULL) note->lower->higher = note;
+    data->free_notes = note;
+    assign_note_voices(data);
+    return ret;
+}
+
+static void voice_start(poly_squeeze_voice_t * voice, int16_t pitch, int16_t vol){
+    voice->pitch_out = pitch;
+    int16_t tmp = voice->_start_trigger;
+    radspa_trigger_start(vol, &tmp);
+    voice->trigger_out = tmp;
+} 
+
+static void voice_stop(poly_squeeze_voice_t * voice){
+    int16_t tmp = voice->_start_trigger;
+    radspa_trigger_stop(&tmp);
+    voice->trigger_out = tmp;
+} 
+
+void poly_squeeze_run(radspa_t * poly_squeeze, uint16_t num_samples, uint32_t render_pass_id){
+    poly_squeeze_data_t * data = poly_squeeze->plugin_data;
+    poly_squeeze_note_t * notes = (void *) (&(data[1]));
+    poly_squeeze_input_t * inputs = (void *) (&(notes[data->num_notes]));
+    poly_squeeze_voice_t * voices = (void *) (&(inputs[data->num_inputs]));
+
+    radspa_signal_t * trigger_input_sigs[data->num_inputs];
+    radspa_signal_t * pitch_input_sigs[data->num_inputs];
+    radspa_signal_t * trigger_output_sigs[data->num_voices];
+    radspa_signal_t * pitch_output_sigs[data->num_voices];
+
+    for(uint8_t j = 0; j < data->num_inputs; j++){
+        trigger_input_sigs[j] = radspa_signal_get_by_index(poly_squeeze, POLY_SQUEEZE_TRIGGER_INPUT + 2*j);
+        pitch_input_sigs[j] = radspa_signal_get_by_index(poly_squeeze, POLY_SQUEEZE_PITCH_INPUT + 2*j);
+    }
+    for(uint8_t j = 0; j < data->num_voices; j++){
+        trigger_output_sigs[j] = radspa_signal_get_by_index(poly_squeeze, POLY_SQUEEZE_TRIGGER_OUTPUT + 2*(data->num_inputs+j));
+        pitch_output_sigs[j] = radspa_signal_get_by_index(poly_squeeze, POLY_SQUEEZE_PITCH_OUTPUT + 2*(data->num_inputs+j));
+    }
+
+    for(uint16_t i = 0; i < num_samples; i++){
+        for(uint8_t j = 0; j < data->num_inputs; j++){
+            notes[j].pitch = radspa_signal_get_value(pitch_input_sigs[j], i, render_pass_id);
+            int16_t trigger_in = radspa_trigger_get(radspa_signal_get_value(trigger_input_sigs[j], i, render_pass_id),
+                    &(inputs[j].trigger_in_hist));
+
+            if(trigger_in > 0){
+                notes[j].vol = trigger_in;
+                int8_t voice = put_note_on_top(data, &(notes[j]));
+                if(voice >= 0) voice_start(&(voices[voice]), notes[j].pitch, notes[j].vol);
+            } else if(trigger_in < 0){
+                int8_t voice = free_note(data, &(notes[j]));
+                if(voice >= 0){
+                    poly_squeeze_note_t * note = get_note_with_voice(data, voice);
+                    if(note == NULL){
+                        voice_stop(&(voices[voice]));
+                    } else {
+                        voice_start(&(voices[voice]), note->pitch, note->vol);
+                    }
+                }
+            }
+        }
+        for(uint8_t j = 0; j < data->num_inputs; j++){
+            if((notes[j].voice != -1) && (notes[j].voice < data->num_voices)){
+                voices[notes[j].voice].pitch_out = notes[j].pitch;
+            }
+        }
+        for(uint8_t j = 0; j < data->num_voices; j++){
+            radspa_signal_set_value_check_const(trigger_output_sigs[j], i, voices[j].trigger_out);
+            radspa_signal_set_value_check_const(pitch_output_sigs[j], i, voices[j].pitch_out);
+            voices[j]._start_trigger = voices[j].trigger_out;
+        }
+    }
+}
+
+radspa_t * poly_squeeze_create(uint32_t init_var){
+    if(!init_var) init_var = 3 + (1UL<<8) + 9 * (1UL<<16);
+    uint8_t num_voices = init_var & 0xFF;
+    if(num_voices > 32) num_voices = 32;
+    if(num_voices < 1) num_voices = 1;
+
+    init_var = init_var >> 8;
+    uint8_t num_inputs = init_var & 0xFF;
+    if(num_inputs > 32) num_inputs = 32;
+    if(num_inputs < num_voices) num_inputs = num_voices;
+
+    uint8_t num_notes = num_inputs;
+
+    uint32_t num_signals = num_voices * 2 + num_inputs * 2;
+    size_t data_size = sizeof(poly_squeeze_data_t);
+    data_size += sizeof(poly_squeeze_voice_t) * num_voices;
+    data_size += sizeof(poly_squeeze_note_t) * num_notes;
+    data_size += sizeof(poly_squeeze_input_t) * num_inputs;
+    radspa_t * poly_squeeze = radspa_standard_plugin_create(&poly_squeeze_desc, num_signals, data_size, 0);
+    if(poly_squeeze == NULL) return NULL;
+
+    poly_squeeze->render = poly_squeeze_run;
+
+    radspa_signal_set_group(poly_squeeze, num_inputs, 2, POLY_SQUEEZE_TRIGGER_INPUT, "trigger_in",
+            RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_TRIGGER, 0);
+    radspa_signal_set_group(poly_squeeze, num_inputs, 2, POLY_SQUEEZE_PITCH_INPUT, "pitch_in",
+            RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_SCT, RADSPA_SIGNAL_VAL_SCT_A440);
+    radspa_signal_set_group(poly_squeeze, num_voices, 2, POLY_SQUEEZE_TRIGGER_OUTPUT + 2*num_inputs, "trigger_out",
+            RADSPA_SIGNAL_HINT_OUTPUT | RADSPA_SIGNAL_HINT_TRIGGER, 0);
+    radspa_signal_set_group(poly_squeeze, num_voices, 2, POLY_SQUEEZE_PITCH_OUTPUT + 2*num_inputs, "pitch_out",
+            RADSPA_SIGNAL_HINT_OUTPUT | RADSPA_SIGNAL_HINT_SCT, RADSPA_SIGNAL_VAL_SCT_A440);
+
+    poly_squeeze_data_t * data = poly_squeeze->plugin_data;
+    data->num_voices = num_voices;
+    data->num_notes = num_notes;
+    data->num_inputs = num_inputs;
+
+    poly_squeeze_note_t * notes = (void *) (&(data[1]));
+    poly_squeeze_input_t * inputs = (void *) (&(notes[data->num_notes]));
+    poly_squeeze_voice_t * voices = (void *) (&(inputs[data->num_inputs]));
+
+    data->active_notes_top = NULL;
+    data->active_notes_bottom = NULL;
+    data->free_notes = &(notes[0]);
+
+    for(uint8_t i = 0; i < num_voices; i++){
+        voices[i].trigger_out = 0;
+        voices[i].pitch_out = RADSPA_SIGNAL_VAL_SCT_A440;
+        voices[i]._start_trigger = voices[i].trigger_out;
+    }
+
+    for(uint8_t i = 0; i < num_notes; i++){
+        notes[i].pitch = -32768;
+        notes[i].voice = -1;
+        if(i){
+            notes[i].higher = &(notes[i-1]);
+        } else{
+            notes[i].higher = NULL;
+        }
+        if(i != (num_notes - 1)){
+            notes[i].lower = &(notes[i+1]);
+        } else {
+            notes[i].lower = NULL;
+        }
+    }
+
+    for(uint8_t i = 0; i < num_inputs; i++){
+        inputs[i].trigger_in_hist = 0;
+    }
+
+    return poly_squeeze;
+}
diff --git a/components/bl00mbox/plugins/poly_squeeze.h b/components/bl00mbox/plugins/poly_squeeze.h
new file mode 100644
index 0000000000000000000000000000000000000000..40f954d3cfc25f6b34a17a19fc716eac477eccf9
--- /dev/null
+++ b/components/bl00mbox/plugins/poly_squeeze.h
@@ -0,0 +1,34 @@
+#pragma once
+#include "radspa.h"
+#include "radspa_helpers.h"
+
+typedef struct _poly_squeeze_note_t {
+    int16_t pitch;
+    int16_t vol;
+    int8_t voice;
+    struct _poly_squeeze_note_t * lower;
+    struct _poly_squeeze_note_t * higher;
+} poly_squeeze_note_t;
+
+typedef struct {
+    int16_t trigger_out;
+    int16_t pitch_out;
+    int16_t _start_trigger;
+} poly_squeeze_voice_t;
+
+typedef struct {
+    int16_t trigger_in_hist;
+} poly_squeeze_input_t;
+
+typedef struct {
+    uint8_t num_voices;
+    uint8_t num_notes;
+    uint8_t num_inputs;
+    poly_squeeze_note_t * active_notes_top;
+    poly_squeeze_note_t * active_notes_bottom;
+    poly_squeeze_note_t * free_notes;
+} poly_squeeze_data_t;
+
+extern radspa_descriptor_t poly_squeeze_desc;
+radspa_t * poly_squeeze_create(uint32_t init_var);
+void poly_squeeze_run(radspa_t * osc, uint16_t num_samples, uint32_t render_pass_id);
diff --git a/components/bl00mbox/plugins/range_shifter.c b/components/bl00mbox/plugins/range_shifter.c
new file mode 100644
index 0000000000000000000000000000000000000000..baf57c6842bedf37c078e5727aabf62b48d2f339
--- /dev/null
+++ b/components/bl00mbox/plugins/range_shifter.c
@@ -0,0 +1,76 @@
+#include "range_shifter.h"
+
+radspa_t * range_shifter_create(uint32_t init_var);
+radspa_descriptor_t range_shifter_desc = {
+    .name = "range_shifter",
+    .id = 68,
+    .description = "saturating multiplication and addition",
+    .create_plugin_instance = range_shifter_create,
+    .destroy_plugin_instance = radspa_standard_plugin_destroy
+};
+
+#define RANGE_SHIFTER_NUM_SIGNALS 6
+#define RANGE_SHIFTER_OUTPUT 0
+#define RANGE_SHIFTER_INPUT 1
+#define RANGE_SHIFTER_OUTPUT_A 2
+#define RANGE_SHIFTER_OUTPUT_B 3
+#define RANGE_SHIFTER_INPUT_A 2
+#define RANGE_SHIFTER_INPUT_B 3
+
+void range_shifter_run(radspa_t * range_shifter, uint16_t num_samples, uint32_t render_pass_id){
+    radspa_signal_t * output_sig = radspa_signal_get_by_index(range_shifter, RANGE_SHIFTER_OUTPUT);
+    if(output_sig->buffer == NULL) return;
+    radspa_signal_t * input_sig = radspa_signal_get_by_index(range_shifter, RANGE_SHIFTER_INPUT);
+    int32_t output_a = radspa_signal_get_value(radspa_signal_get_by_index(range_shifter, RANGE_SHIFTER_OUTPUT_A), 0, render_pass_id);
+    int32_t output_b = radspa_signal_get_value(radspa_signal_get_by_index(range_shifter, RANGE_SHIFTER_OUTPUT_B), 0, render_pass_id);
+    int32_t input_a = radspa_signal_get_value(radspa_signal_get_by_index(range_shifter, RANGE_SHIFTER_INPUT_A), 0, render_pass_id);
+    int32_t input_b = radspa_signal_get_value(radspa_signal_get_by_index(range_shifter, RANGE_SHIFTER_INPUT_B), 0, render_pass_id);
+    int32_t output_span = output_b - output_a;
+    if(!output_span){
+        for(uint16_t i = 0; i < num_samples; i++){
+            radspa_signal_set_value(output_sig, i, output_a);
+        }
+        return;
+    }
+    int32_t input_span = input_b - input_a;
+    if(!input_span){
+        int32_t avg = (output_b - output_a)/2;
+        for(uint16_t i = 0; i < num_samples; i++){
+            radspa_signal_set_value(output_sig, i, avg);
+        }
+        return;
+    }
+    int32_t gain = (output_span << 14) / input_span;
+
+    for(uint16_t i = 0; i < num_samples; i++){
+        int32_t ret = radspa_signal_get_value(input_sig, i, render_pass_id);
+        if(ret == input_a){
+            ret = output_a;
+        } else if(ret == input_b){
+            ret = output_b;
+        } else {
+            ret -= input_a;
+            ret = (ret * gain) >> 14;
+            ret += output_a;
+            if(ret > 32767){
+                ret = 32767;
+            } else if(ret < -32767){
+                ret = -32767;
+            }
+        }
+        radspa_signal_set_value(output_sig, i, ret);
+    }
+}
+
+radspa_t * range_shifter_create(uint32_t init_var){
+    radspa_t * range_shifter = radspa_standard_plugin_create(&range_shifter_desc, RANGE_SHIFTER_NUM_SIGNALS, sizeof(char), 0);
+    if(range_shifter == NULL) return NULL;
+    range_shifter->render = range_shifter_run;
+    radspa_signal_set(range_shifter, RANGE_SHIFTER_OUTPUT, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0);
+    radspa_signal_set(range_shifter, RANGE_SHIFTER_INPUT, "input", RADSPA_SIGNAL_HINT_INPUT, 0);
+    radspa_signal_set(range_shifter, RANGE_SHIFTER_OUTPUT_A, "output_a", RADSPA_SIGNAL_HINT_INPUT, -32767);
+    radspa_signal_set(range_shifter, RANGE_SHIFTER_OUTPUT_B, "output_b", RADSPA_SIGNAL_HINT_INPUT, 32767);
+    radspa_signal_set(range_shifter, RANGE_SHIFTER_INPUT_A, "input_a", RADSPA_SIGNAL_HINT_INPUT, -32767);
+    radspa_signal_set(range_shifter, RANGE_SHIFTER_INPUT_B, "input_b", RADSPA_SIGNAL_HINT_INPUT, 32767);
+    return range_shifter;
+}
diff --git a/components/bl00mbox/plugins/range_shifter.h b/components/bl00mbox/plugins/range_shifter.h
new file mode 100644
index 0000000000000000000000000000000000000000..254099f8a2a0be8e175429c1be9fc07e20e55299
--- /dev/null
+++ b/components/bl00mbox/plugins/range_shifter.h
@@ -0,0 +1,7 @@
+#pragma once
+#include <radspa.h>
+#include <radspa_helpers.h>
+
+extern radspa_descriptor_t range_shifter_desc;
+radspa_t * range_shifter_create(uint32_t init_var);
+void range_shifter_run(radspa_t * osc, uint16_t num_samples, uint32_t render_pass_id);
diff --git a/components/bl00mbox/plugins/sampler.c b/components/bl00mbox/plugins/sampler.c
index e2627c54d6e9b68093db834adc34080875a0dbc2..3de9e4349e8877bf2e3a500be25259ddd9c47a81 100644
--- a/components/bl00mbox/plugins/sampler.c
+++ b/components/bl00mbox/plugins/sampler.c
@@ -14,8 +14,8 @@ radspa_descriptor_t sampler_desc = {
 #define SAMPLER_NUM_SIGNALS 5
 #define SAMPLER_OUTPUT 0
 #define SAMPLER_TRIGGER 1
-#define SAMPLER_REC_IN 2
-#define SAMPLER_REC_TRIGGER 3
+#define SAMPLER_REC_TRIGGER 2
+#define SAMPLER_REC_IN 3
 #define SAMPLER_PITCH_SHIFT 4
 
 #define READ_HEAD_POS 0
@@ -27,52 +27,60 @@ radspa_descriptor_t sampler_desc = {
 void sampler_run(radspa_t * sampler, uint16_t num_samples, uint32_t render_pass_id){
     radspa_signal_t * output_sig = radspa_signal_get_by_index(sampler, SAMPLER_OUTPUT);
     if(output_sig->buffer == NULL) return;
+
     sampler_data_t * data = sampler->plugin_data;
     int16_t * buf = sampler->plugin_table;
     uint32_t * buf32 = (uint32_t *) buf;
+    data->read_head_pos = buf32[READ_HEAD_POS/2];
+    uint32_t sample_len = buf32[SAMPLE_LEN/2];
+    uint32_t buffer_size = sampler->plugin_table_len - BUFFER_OFFSET;
+    if(sample_len >= buffer_size) sample_len = buffer_size - 1;
+    if(data->read_head_pos >= buffer_size) data->read_head_pos = buffer_size - 1;
+
     radspa_signal_t * trigger_sig = radspa_signal_get_by_index(sampler, SAMPLER_TRIGGER);
-    radspa_signal_t * rec_in_sig = radspa_signal_get_by_index(sampler, SAMPLER_REC_IN);
     radspa_signal_t * rec_trigger_sig = radspa_signal_get_by_index(sampler, SAMPLER_REC_TRIGGER);
-    radspa_signal_t * pitch_shift_sig = radspa_signal_get_by_index(sampler, SAMPLER_PITCH_SHIFT);
 
-    static int32_t ret = 0;
-    
-    uint32_t buffer_size = sampler->plugin_table_len - BUFFER_OFFSET;
+    int16_t trigger_const = radspa_signal_get_const_value(trigger_sig, render_pass_id);
+    int16_t rec_trigger_const = radspa_signal_get_const_value(rec_trigger_sig, render_pass_id);
+
+    bool output_mute = (data->read_head_pos >= sample_len);
+    if( output_mute && (!data->rec_active) && (trigger_const == data->trigger_prev) && (rec_trigger_const == data->rec_trigger_prev)){
+        radspa_signal_set_const_value(output_sig, 0);
+        return;
+    }
 
-    data->read_head_pos = buf32[READ_HEAD_POS/2];
     uint32_t sample_start = buf32[SAMPLE_START/2];
-    uint32_t sample_len = buf32[SAMPLE_LEN/2];
     uint32_t sample_rate = buf32[SAMPLE_RATE/2];
 
+    radspa_signal_t * rec_in_sig = radspa_signal_get_by_index(sampler, SAMPLER_REC_IN);
+    radspa_signal_t * pitch_shift_sig = radspa_signal_get_by_index(sampler, SAMPLER_PITCH_SHIFT);
+
     if(sample_start >= buffer_size) sample_start = buffer_size - 1;
-    if(sample_len >= buffer_size) sample_len = buffer_size - 1;
-    if(data->read_head_pos >= buffer_size) data->read_head_pos = buffer_size - 1;
     
-    bool buffer_all_zeroes = true;
     for(uint16_t i = 0; i < num_samples; i++){
-        int16_t trigger = radspa_trigger_get(radspa_signal_get_value(trigger_sig, i, render_pass_id), &(data->trigger_prev));
-        int16_t rec_trigger = radspa_trigger_get(radspa_signal_get_value(rec_trigger_sig, i, render_pass_id), &(data->rec_trigger_prev));
-
-        if(rec_trigger > 0){
-            if(!(data->rec_active)){
-                data->read_head_pos = sample_len;
-                data->write_head_pos = 0;
-                data->write_head_pos_long = 0;
-                sample_len = 0;
-                data->rec_active = true;
-            }
-        } else if(rec_trigger < 0){
-            if(data->rec_active){
-                if(sample_len == buffer_size){
-                    sample_start = data->write_head_pos;
-                } else {
-                    sample_start = 0;
+        int32_t ret;
+        if((rec_trigger_const == -32768) || (!i)){
+            int16_t rec_trigger = radspa_trigger_get(radspa_signal_get_value(rec_trigger_sig, i, render_pass_id), &(data->rec_trigger_prev));
+            if(rec_trigger > 0){
+                if(!(data->rec_active)){
+                    data->read_head_pos = sample_len;
+                    data->write_head_pos = 0;
+                    data->write_head_pos_long = 0;
+                    sample_len = 0;
+                    data->rec_active = true;
+                }
+            } else if(rec_trigger < 0){
+                if(data->rec_active){
+                    if(sample_len == buffer_size){
+                        sample_start = data->write_head_pos;
+                    } else {
+                        sample_start = 0;
+                    }
+                    buf[8] = 1;
+                    data->rec_active = false;
                 }
-                buf[8] = 1;
-                data->rec_active = false;
             }
         }
-        
 
         if(data->rec_active){
             int16_t rec_in = radspa_signal_get_value(rec_in_sig, i, render_pass_id);
@@ -93,12 +101,21 @@ void sampler_run(radspa_t * sampler, uint16_t num_samples, uint32_t render_pass_
                 buf32[SAMPLE_LEN] = sample_len;
             }
         } else {
-            if(trigger > 0){
-                data->read_head_pos_long = 0;
-                data->read_head_pos = 0;
-                data->volume = trigger;
-            } else if(trigger < 0){
-                data->read_head_pos = sample_len;
+            if((trigger_const == -32768) || (!i)){
+                int16_t trigger = radspa_trigger_get(radspa_signal_get_value(trigger_sig, i, render_pass_id), &(data->trigger_prev));
+                if(trigger > 0){
+                    data->read_head_pos_long = 0;
+                    data->read_head_pos = 0;
+                    data->volume = trigger;
+                    if(output_mute){
+                        for(uint8_t j = 0; j < i; j++){
+                            radspa_signal_set_value(output_sig, j, 0);
+                        }
+                        output_mute = false;
+                    }
+                } else if(trigger < 0){
+                    data->read_head_pos = sample_len;
+                }
             }
 
 
@@ -106,6 +123,7 @@ void sampler_run(radspa_t * sampler, uint16_t num_samples, uint32_t render_pass_
                 uint32_t sample_offset_pos = data->read_head_pos + sample_start;
                 if(sample_offset_pos >= sample_len) sample_offset_pos -= sample_len;
                 ret = radspa_mult_shift(buf[sample_offset_pos + BUFFER_OFFSET], data->volume);
+                radspa_signal_set_value(output_sig, i, ret);
 
                 int32_t pitch_shift = radspa_signal_get_value(pitch_shift_sig, i, render_pass_id);
                 if(pitch_shift != data->pitch_shift_prev){
@@ -122,17 +140,13 @@ void sampler_run(radspa_t * sampler, uint16_t num_samples, uint32_t render_pass_
                     data->read_head_pos_long += (sample_rate * data->pitch_shift_mult) >> 11;
                     data->read_head_pos = (data->read_head_pos_long * 699) >> 25; // equiv to _/48000 (acc 0.008%)
                 }
-            } else {
-                if(data->buffer_all_zeroes) continue;
-                //ret = (ret * 255)>>8; // avoid dc clicks with bad samples
-                ret = 0;
+            } else if(!output_mute){
+                radspa_signal_set_value(output_sig, i, 0);
             }
 
-            if(ret) buffer_all_zeroes = false;
-            radspa_signal_set_value(output_sig, i, ret);
         }
     }
-    data->buffer_all_zeroes = buffer_all_zeroes;
+    if(output_mute) radspa_signal_set_const_value(output_sig, 0);
     buf32[READ_HEAD_POS/2] = data->read_head_pos;
     buf32[SAMPLE_START/2] = sample_start;
     buf32[SAMPLE_LEN/2] = sample_len;
diff --git a/components/bl00mbox/plugins/sequencer.c b/components/bl00mbox/plugins/sequencer.c
index 3bf2bab82892b119f7e6555e58481f2205666d94..083e43d66cff3b63090af2bccc24509d76a45e56 100644
--- a/components/bl00mbox/plugins/sequencer.c
+++ b/components/bl00mbox/plugins/sequencer.c
@@ -31,18 +31,13 @@ static uint64_t target(uint64_t step_len, uint64_t bpm, uint64_t beat_div){
 void sequencer_run(radspa_t * sequencer, uint16_t num_samples, uint32_t render_pass_id){
     bool output_request = false;
     sequencer_data_t * data = sequencer->plugin_data;
+    sequencer_track_data_t * tracks = (void *) (&data[1]);
     radspa_signal_t * track_sigs[data->num_tracks];
 
-    for(uint8_t j = 0; j < data->num_tracks; j++){
-        track_sigs[j] = radspa_signal_get_by_index(sequencer, SEQUENCER_OUTPUT+j);
-        if(track_sigs[j]->buffer != NULL) output_request = true;
-    }
-
     radspa_signal_t * step_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_STEP);
     if(step_sig->buffer != NULL) output_request = true;
     radspa_signal_t * sync_out_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_SYNC_OUT);
     if(sync_out_sig->buffer != NULL) output_request = true;
-    if(!output_request) return;
 
     radspa_signal_t * sync_in_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_SYNC_IN);
     radspa_signal_t * start_step_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_START_STEP);
@@ -50,6 +45,12 @@ void sequencer_run(radspa_t * sequencer, uint16_t num_samples, uint32_t render_p
     radspa_signal_t * bpm_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_BPM);
     radspa_signal_t * beat_div_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_BEAT_DIV);
 
+    for(uint8_t j = 0; j < data->num_tracks; j++){
+        track_sigs[j] = radspa_signal_get_by_index(sequencer, SEQUENCER_OUTPUT+j);
+        if(track_sigs[j]->buffer != NULL) output_request = true;
+    }
+    if(!output_request) return;
+
     int16_t * table = sequencer->plugin_table;
 
     int16_t s1 = radspa_signal_get_value(end_step_sig, 0, render_pass_id);
@@ -59,6 +60,7 @@ void sequencer_run(radspa_t * sequencer, uint16_t num_samples, uint32_t render_p
 
     int16_t bpm = radspa_signal_get_value(bpm_sig, 0, render_pass_id);
     int16_t beat_div = radspa_signal_get_value(beat_div_sig, 0, render_pass_id);
+
     if((bpm != data->bpm_prev) || (beat_div != data->beat_div_prev)){
         data->counter_target = target(data->track_step_len, bpm, beat_div);
         data->is_stopped = data->counter_target ? false : true;
@@ -99,22 +101,29 @@ void sequencer_run(radspa_t * sequencer, uint16_t num_samples, uint32_t render_p
                 for(uint8_t j = 0; j < data->num_tracks; j++){
                     int16_t type = table[j * (data->track_step_len + 1)];
                     int16_t stage_val = table[data->step + 1 + (1 + data->track_step_len) * j];
+                    if((!tracks[j].changed) && (tracks[j].stage_val_prev != stage_val)){
+                        tracks[j].changed = true;
+                        tracks[j].stage_val_prev = stage_val;
+                        for(uint16_t k = 0; k < i; k++){
+                            radspa_signal_set_value(track_sigs[j], k, tracks[j].track_fill);
+                        }
+                    }
                     if(type == 32767){
-                        data->tracks[j].track_fill = stage_val;
+                        tracks[j].track_fill = stage_val;
                     } else if(type == -32767){
-                        if(stage_val > 0) data->tracks[j].track_fill = radspa_trigger_start(stage_val, &(data->tracks[j].trigger_hist));
-                        if(stage_val < 0) data->tracks[j].track_fill = radspa_trigger_stop(&(data->tracks[j].trigger_hist));
+                        if(stage_val > 0){
+                            radspa_trigger_start(stage_val, &(tracks[j].track_fill));
+                        } else if(stage_val < 0){
+                            radspa_trigger_stop(&(tracks[j].track_fill));
+                        }
                     }
+                    tracks[j].stage_val_prev = stage_val;
                 }
             }
-          
-        } else {
-            for(uint8_t j = 0; j < data->num_tracks; j++){
-                data->tracks[j].track_fill = radspa_trigger_stop(&(data->tracks[j].trigger_hist));
-            }
         }
+
         for(uint8_t j = 0; j < data->num_tracks; j++){
-            radspa_signal_set_value(track_sigs[j], i, data->tracks[j].track_fill);
+            if(tracks[j].changed) radspa_signal_set_value(track_sigs[j], i, tracks[j].track_fill);
         }
 
         int16_t sync_out = 0;
@@ -124,30 +133,37 @@ void sequencer_run(radspa_t * sequencer, uint16_t num_samples, uint32_t render_p
             sync_out = radspa_trigger_stop(&(data->sync_out_hist));
         }
         radspa_signal_set_value(sync_out_sig, i, sync_out);
-        radspa_signal_set_value(step_sig, i, data->step);
     }
+    for(uint8_t j = 0; j < data->num_tracks; j++){
+        if(!tracks[j].changed){
+            radspa_signal_set_const_value(track_sigs[j], tracks[j].track_fill);
+        } else {
+            tracks[j].changed = false;
+        }
+    }
+    radspa_signal_set_const_value(step_sig, data->step);
 }
 
 radspa_t * sequencer_create(uint32_t init_var){
     uint32_t num_tracks = 4;
-    uint32_t num_pixels = 16;
+    uint32_t num_steps = 16;
     if(init_var){
         num_tracks = init_var & 0xFF;
-        num_pixels = (init_var>>8) & 0xFF;
+        num_steps = (init_var>>8) & 0xFF;
     }
     if(!num_tracks) return NULL;
-    if(!num_pixels) return NULL;
+    if(!num_steps) return NULL;
 
-    uint32_t table_size = num_tracks * (num_pixels + 1);
+    uint32_t table_size = num_tracks * (num_steps + 1);
     uint32_t num_signals = num_tracks + SEQUENCER_NUM_SIGNALS; //one for each channel output
-    size_t data_size = sizeof(sequencer_data_t) + sizeof(sequencer_track_data_t) * (num_tracks - 1);
+    size_t data_size = sizeof(sequencer_data_t) + sizeof(sequencer_track_data_t) * num_tracks;
     radspa_t * sequencer = radspa_standard_plugin_create(&sequencer_desc, num_signals, data_size, table_size);
     if(sequencer == NULL) return NULL;
 
     sequencer->render = sequencer_run;
 
     sequencer_data_t * data = sequencer->plugin_data;
-    data->track_step_len = num_pixels;
+    data->track_step_len = num_steps;
     data->num_tracks = num_tracks;
     data->bpm_prev = 120;
     data->beat_div_prev = 16;
@@ -156,7 +172,7 @@ radspa_t * sequencer_create(uint32_t init_var){
     radspa_signal_set(sequencer, SEQUENCER_SYNC_OUT, "sync_out", RADSPA_SIGNAL_HINT_OUTPUT, 0);
     radspa_signal_set(sequencer, SEQUENCER_SYNC_IN, "sync_in", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_TRIGGER, 0);
     radspa_signal_set(sequencer, SEQUENCER_START_STEP, "step_start", RADSPA_SIGNAL_HINT_INPUT, 0);
-    radspa_signal_set(sequencer, SEQUENCER_END_STEP, "step_end", RADSPA_SIGNAL_HINT_INPUT, num_pixels-1);
+    radspa_signal_set(sequencer, SEQUENCER_END_STEP, "step_end", RADSPA_SIGNAL_HINT_INPUT, num_steps-1);
     radspa_signal_set(sequencer, SEQUENCER_BPM, "bpm", RADSPA_SIGNAL_HINT_INPUT, data->bpm_prev);
     radspa_signal_set(sequencer, SEQUENCER_BEAT_DIV, "beat_div", RADSPA_SIGNAL_HINT_INPUT, data->beat_div_prev);
     radspa_signal_set_group(sequencer, data->num_tracks, 1, SEQUENCER_OUTPUT, "track",
@@ -168,6 +184,12 @@ radspa_t * sequencer_create(uint32_t init_var){
     data->sync_out_start = false;
     data->sync_out_stop = false;
     data->is_stopped = false;
+    sequencer_track_data_t * tracks = (void *) (&data[1]);
+    for(uint8_t j = 0; j < data->num_tracks; j++){
+        tracks[j].changed = false;
+        tracks[j].track_fill = 0;
+        tracks[j].stage_val_prev = 0;
+    }
 
     return sequencer;
 }
diff --git a/components/bl00mbox/plugins/sequencer.h b/components/bl00mbox/plugins/sequencer.h
index 970a86f8499b19d5d7dee5015bc98bd6554b6df7..4d53c4f9d5a047680802d094b24c668aa66ddcc0 100644
--- a/components/bl00mbox/plugins/sequencer.h
+++ b/components/bl00mbox/plugins/sequencer.h
@@ -5,7 +5,8 @@
 
 typedef struct {
     int16_t track_fill;
-    int16_t trigger_hist;
+    int16_t stage_val_prev;
+    bool changed;
 } sequencer_track_data_t;
 
 typedef struct {
@@ -23,7 +24,6 @@ typedef struct {
     bool is_stopped;
     int16_t bpm_prev;
     int16_t beat_div_prev;
-    sequencer_track_data_t tracks[];
 } sequencer_data_t;
 
 
diff --git a/components/bl00mbox/radspa/radspa.h b/components/bl00mbox/radspa/radspa.h
index 80c66ddca7034dae04f50d7938068e3f623e0fb0..c3af2f5114de8897811ec5483fb1350c799df2e9 100644
--- a/components/bl00mbox/radspa/radspa.h
+++ b/components/bl00mbox/radspa/radspa.h
@@ -81,6 +81,7 @@ typedef struct _radspa_signal_t{
     int16_t * buffer;
     // static value to be used when buffer is NULL for input signals only
     int16_t value;
+    // when the signal has last requested to render its source
     uint32_t render_pass_id;
     // linked list pointer
     struct _radspa_signal_t * next;
@@ -124,27 +125,28 @@ inline int32_t radspa_mult_shift(int32_t a, int32_t b){ return radspa_clip((a*b)
 inline int32_t radspa_gain(int32_t a, int32_t b){ return radspa_clip((a*b)>>12); }
 
 inline int16_t radspa_trigger_start(int16_t velocity, int16_t * hist){
-    int16_t ret = ((* hist) > 0) ? -velocity : velocity;
-    (* hist) = ret;
-    return ret;
+    if(!velocity) velocity = 1;
+    if(velocity == -32768) velocity = 1;
+    if(velocity < 0) velocity = -velocity;
+    (* hist) = ((* hist) > 0) ? -velocity : velocity;
+    return * hist;
 }
 
 inline int16_t radspa_trigger_stop(int16_t * hist){
     (* hist) = 0;
-    return 0;
+    return * hist;
 }
 
 inline int16_t radspa_trigger_get(int16_t trigger_signal, int16_t * hist){
-    int16_t ret = 0;
-    if((!trigger_signal) && (* hist)){ //stop
-        ret = -1;
-    } else if(trigger_signal > 0 ){
-        if((* hist) <= 0) ret = trigger_signal;
+    if((* hist) == trigger_signal) return 0;
+    (* hist) = trigger_signal;
+    if(!trigger_signal){
+        return  -1;
     } else if(trigger_signal < 0 ){
-        if((* hist) >= 0) ret = -trigger_signal;
+        return -trigger_signal;
+    } else {
+        return trigger_signal;
     }
-    (* hist) = trigger_signal;
-    return ret;
 }
 
 /* REQUIREMENTS
diff --git a/components/bl00mbox/radspa/radspa_helpers.c b/components/bl00mbox/radspa/radspa_helpers.c
index c95e1bde462932a8b134461cce2ae70781c34147..e584935bfbb3e88ea0d3a8542809cfe9fae0fc6c 100644
--- a/components/bl00mbox/radspa/radspa_helpers.c
+++ b/components/bl00mbox/radspa/radspa_helpers.c
@@ -2,7 +2,10 @@
 #include "radspa_helpers.h"
 
 extern inline int16_t radspa_signal_get_value(radspa_signal_t * sig, int16_t index, uint32_t render_pass_id);
-extern inline void radspa_signal_set_value(radspa_signal_t * sig, int16_t index, int16_t value);
+extern inline int16_t radspa_signal_get_const_value(radspa_signal_t * sig, uint32_t render_pass_id);
+extern inline void radspa_signal_set_value(radspa_signal_t * sig, int16_t index, int32_t value);
+extern inline void radspa_signal_set_value_check_const(radspa_signal_t * sig, int16_t index, int32_t value);
+extern inline void radspa_signal_set_const_value(radspa_signal_t * sig, int32_t value);
 extern inline int16_t radspa_clip(int32_t a);
 extern inline int16_t radspa_add_sat(int32_t a, int32_t b);
 extern inline int32_t radspa_mult_shift(int32_t a, int32_t b);
diff --git a/components/bl00mbox/radspa/radspa_helpers.h b/components/bl00mbox/radspa/radspa_helpers.h
index 86a08ee4841b9396b167cccb6fe14c00a58b9720..ff3a912d128e659095191a4118dcd30183a7365d 100644
--- a/components/bl00mbox/radspa/radspa_helpers.h
+++ b/components/bl00mbox/radspa/radspa_helpers.h
@@ -29,15 +29,55 @@ inline int16_t radspa_signal_get_value(radspa_signal_t * sig, int16_t index, uin
             radspa_host_request_buffer_render(sig->buffer);
             sig->render_pass_id = render_pass_id;
         }
+        if(sig->buffer[1] == -32768) return sig->buffer[0];
         return sig->buffer[index];
+    }
+    return sig->value;
+}
+
+inline void radspa_signal_set_value(radspa_signal_t * sig, int16_t index, int32_t val){
+    if(sig->buffer != NULL){
+        sig->buffer[index] = radspa_clip(val);
+    } else if(!index){
+        sig->value = radspa_clip(val);
+    }
+}
+
+inline void radspa_signal_set_value_check_const(radspa_signal_t * sig, int16_t index, int32_t val){
+    if(sig->buffer == NULL){
+        if(!index) sig->value = radspa_clip(val);
+        return;
+    }
+    val = radspa_clip(val);
+    if(index == 0){
+        sig->buffer[0] = val;
+    } else if(index == 1){
+        if(val == sig->buffer[0]) sig->buffer[1] = -32768;
+    } else {
+        if((sig->buffer[1] == -32768) && (val != sig->buffer[0])) sig->buffer[1] = sig->buffer[0];
+        sig->buffer[index] = val;
+    }
+}
+
+inline void radspa_signal_set_const_value(radspa_signal_t * sig, int32_t val){
+    if(sig->buffer == NULL){
+        sig->value = radspa_clip(val);
     } else {
-        return sig->value;
+        sig->buffer[0] = radspa_clip(val);
+        sig->buffer[1] = -32768;
     }
 }
 
-inline void radspa_signal_set_value(radspa_signal_t * sig, int16_t index, int16_t val){
-    if(sig->buffer != NULL) sig->buffer[index] = val;
-    if(!index) sig->value = val;
+inline int16_t radspa_signal_get_const_value(radspa_signal_t * sig, uint32_t render_pass_id){
+    if(sig->buffer != NULL){
+        if(sig->render_pass_id != render_pass_id){
+            radspa_host_request_buffer_render(sig->buffer);
+            sig->render_pass_id = render_pass_id;
+        }
+        if(sig->buffer[1] == -32768) return sig->buffer[0];
+        return -32768;
+    }
+    return sig->value;
 }
 
 // get signal struct from a signal index
diff --git a/components/micropython/usermodule/mp_sys_bl00mbox.c b/components/micropython/usermodule/mp_sys_bl00mbox.c
index 79a1f19a308433375d33719660b2aa873d9edf25..05dc7837bf2feb21a039892343bfd55cbcdae737 100644
--- a/components/micropython/usermodule/mp_sys_bl00mbox.c
+++ b/components/micropython/usermodule/mp_sys_bl00mbox.c
@@ -68,7 +68,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_channel_get_free_obj, mp_channel_get_free);
 static mp_obj_t mp_channel_set_free(mp_obj_t index, mp_obj_t free) {
     return mp_obj_new_int(
         bl00mbox_channel_set_free(mp_obj_get_int(index), mp_obj_is_true(free)));
-    return mp_const_none;
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_channel_set_free_obj, mp_channel_set_free);
 
diff --git a/python_payload/bl00mbox/_user.py b/python_payload/bl00mbox/_user.py
index b48c5017f6dd7108b3bca91dd9b9b029bec51a90..06b76a8f7b843722d54bf06237bfc35a107ebede 100644
--- a/python_payload/bl00mbox/_user.py
+++ b/python_payload/bl00mbox/_user.py
@@ -73,11 +73,9 @@ class Signal:
         self._name = sys_bl00mbox.channel_bud_get_signal_name(
             plugin.channel_num, plugin.bud_num, signal_num
         )
-        mpx = sys_bl00mbox.channel_bud_get_signal_name_multiplex(
+        self._mpx = sys_bl00mbox.channel_bud_get_signal_name_multiplex(
             plugin.channel_num, plugin.bud_num, signal_num
         )
-        if mpx != (-1):
-            self._name += str(mpx)
         self._description = sys_bl00mbox.channel_bud_get_signal_description(
             plugin.channel_num, plugin.bud_num, signal_num
         )
@@ -96,6 +94,8 @@ class Signal:
         self._plugin._check_existence()
 
         ret = self.name
+        if self._mpx != -1:
+            ret += "[" + str(self._mpx) + "]"
         if len(self.unit):
             ret += " [" + self.unit + "]"
         ret += " [" + self.hints + "]: "
@@ -132,8 +132,11 @@ class Signal:
         if isinstance(self, SignalPitchMixin):
             ret += " / " + str(self.tone) + " semitones / " + str(self.freq) + "Hz"
 
-        if isinstance(self, SignalVolumeMixin):
-            ret += " / " + str(self.dB) + "dB / x" + str(self.mult)
+        if isinstance(self, SignalGainMixin):
+            if self.mult == 0:
+                ret += " / (mute)"
+            else:
+                ret += " / " + str(self.dB) + "dB / x" + str(self.mult)
 
         return ret
 
@@ -164,6 +167,57 @@ class Signal:
             self._plugin.channel_num, self._plugin.bud_num, self._signal_num
         )
 
+    @property
+    def _tone(self):
+        return (self.value - (32767 - 2400 * 6)) / 200
+
+    @_tone.setter
+    def _tone(self, val):
+        if isinstance(self, SignalInput):
+            if (type(val) == int) or (type(val) == float):
+                self.value = (32767 - 2400 * 6) + 200 * val
+            if type(val) == str:
+                self.value = helpers.note_name_to_sct(val)
+        else:
+            raise AttributeError("can't set output signal")
+
+    @property
+    def _freq(self):
+        tone = (self.value - (32767 - 2400 * 6)) / 200
+        return 440 * (2 ** (tone / 12))
+
+    @_freq.setter
+    def _freq(self, val):
+        if isinstance(self, SignalInput):
+            tone = 12 * math.log(val / 440, 2)
+            self.value = (32767 - 2400 * 6) + 200 * tone
+        else:
+            raise AttributeError("can't set output signal")
+
+    @property
+    def _dB(self):
+        if self.value == 0:
+            return -9999
+        return 20 * math.log((abs(self.value) / 4096), 10)
+
+    @_dB.setter
+    def _dB(self, val):
+        if isinstance(self, SignalInput):
+            self.value = int(4096 * (10 ** (val / 20)))
+        else:
+            raise AttributeError("can't set output signal")
+
+    @property
+    def _mult(self):
+        return self.value / 4096
+
+    @_mult.setter
+    def _mult(self, val):
+        if isinstance(self, SignalInput):
+            self.value = int(4096 * val)
+        else:
+            raise AttributeError("can't set output signal")
+
 
 class SignalOutput(Signal):
     @Signal.value.setter
@@ -245,70 +299,128 @@ class SignalInput(Signal):
             cons += [_makeSignal(Plugin(Channel(chan), 0, bud_num=b), s)]
         return cons
 
-
-class SignalTriggerMixin:
-    def start(self, velocity=32767):
+    def _start(self, velocity=32767):
         if self.value > 0:
-            self.value = -velocity
+            self.value = -abs(velocity)
         else:
-            self.value = velocity
+            self.value = abs(velocity)
 
-    def stop(self):
+    def _stop(self):
         self.value = 0
 
+class SignalInputTriggerMixin:
+    def start(self, velocity=32767):
+        self._start(velocity)
+
+    def stop(self):
+        self._stop()
+
 
 class SignalPitchMixin:
     @property
     def tone(self):
-        return (self.value - (32767 - 2400 * 6)) / 200
+        return self._tone
 
     @tone.setter
     def tone(self, val):
-        if (type(val) == int) or (type(val) == float):
-            self.value = (32767 - 2400 * 6) + 200 * val
-        if type(val) == str:
-            self.value = helpers.note_name_to_sct(val)
+        self._tone = val
 
     @property
     def freq(self):
-        tone = (self.value - (32767 - 2400 * 6)) / 200
-        return 440 * (2 ** (tone / 12))
+        return self._freq
 
     @freq.setter
     def freq(self, val):
-        tone = 12 * math.log(val / 440, 2)
-        self.value = (32767 - 2400 * 6) + 200 * tone
+        self._freq = val
 
 
-class SignalVolumeMixin:
+class SignalGainMixin:
     @property
     def dB(self):
-        return 20 * math.log((self.value / 4096), 10)
+        return self._dB
 
     @dB.setter
     def dB(self, val):
-        self.value = int(4096 * (10 ** (val / 20)))
+        self._dB = val
 
     @property
     def mult(self):
-        return self.value / 4096
+        return self._mult
 
     @mult.setter
     def mult(self, val):
-        self.value = int(4096 * val)
+        self._mult = val
 
 
-class SignalInputTrigger(SignalInput, SignalTriggerMixin):
+class SignalOutputTrigger(SignalOutput):
     pass
 
+class SignalOutputPitch(SignalOutput, SignalPitchMixin):
+    pass
 
-class SignalInputPitch(SignalInput, SignalPitchMixin):
+class SignalOutputGain(SignalOutput, SignalGainMixin):
+    pass
+
+class SignalInputTrigger(SignalInput, SignalInputTriggerMixin):
     pass
 
+class SignalInputPitch(SignalInput, SignalPitchMixin):
+    pass
 
-class SignalInputVolume(SignalInput, SignalVolumeMixin):
+class SignalInputGain(SignalInput, SignalGainMixin):
     pass
 
+class SignalMpxList:
+    def __init__(self):
+        self._list = []
+        self._setattr_allowed = False
+
+    def __len__(self):
+        return len(self._list)
+
+    def __getitem__(self, index):
+        return self._list[index]
+
+    def __setitem__(self, index, value):
+        try:
+            current_value = self._list[index]
+        except:
+            raise AttributeError("index does not exist")
+        if isinstance(current_value, Signal):
+            current_value.value = value
+        else:
+            raise AttributeError("signal does not exist")
+
+    def __iter__(self):
+        self._iter_index = 0
+        return self
+
+    def __next__(self):
+        self._iter_index += 1;
+        if self._iter_index >= len(self._list):
+            raise StopIteration
+        else:
+            return self._list[self._iter_index]
+
+    def add_new_signal(self, signal, index):
+        self._setattr_allowed = True
+        index = int(index)
+        if len(self._list) <= index:
+            self._list += [None] * (1 + index - len(self._list))
+        self._list[index] = signal
+        self._setattr_allowed = False
+
+    def __setattr__(self, key, value):
+        """
+        current_value = getattr(self, key, None)
+        if isinstance(current_value, Signal):
+            current_value.value = value
+            return
+        """
+        if key == "_setattr_allowed" or getattr(self, "_setattr_allowed", True):
+            super().__setattr__(key, value)
+        else:
+            raise AttributeError("signal does not exist")
 
 class SignalList:
     def __init__(self, plugin):
@@ -319,31 +431,58 @@ class SignalList:
             hints = sys_bl00mbox.channel_bud_get_signal_hints(
                 plugin.channel_num, plugin.bud_num, signal_num
             )
-            if hints & 2:
-                signal = SignalOutput(plugin, signal_num)
-                signal._hints = "output"
-            elif hints & 1:
-                if hints & 4:
+            if hints & 4:
+                if hints & 1:
                     signal = SignalInputTrigger(plugin, signal_num)
                     signal._hints = "input/trigger"
-                elif hints & 32:
+                elif hints & 2:
+                    signal = SignalOutputTrigger(plugin, signal_num)
+                    signal._hints = "output/trigger"
+            elif hints & 32:
+                if hints & 1:
                     signal = SignalInputPitch(plugin, signal_num)
                     signal._hints = "input/pitch"
-                elif hints & 8:
-                    signal = SignalInputVolume(plugin, signal_num)
-                    signal._hints = "input/volume"
-                else:
+                elif hints & 2:
+                    signal = SignalOutputPitch(plugin, signal_num)
+                    signal._hints = "output/pitch"
+            elif hints & 8:
+                if hints & 1:
+                    signal = SignalInputGain(plugin, signal_num)
+                    signal._hints = "input/gain"
+                elif hints & 2:
+                    signal = SignalOutputGain(plugin, signal_num)
+                    signal._hints = "input/gain"
+            else:
+                if hints & 1:
                     signal = SignalInput(plugin, signal_num)
                     signal._hints = "input"
+                elif hints & 2:
+                    signal = SignalOutput(plugin, signal_num)
+                    signal._hints = "output"
             self._list += [signal]
-            setattr(self, signal.name.split(" ")[0], signal)
+            name = signal.name.split(" ")[0]
+            if signal._mpx == -1:
+                setattr(self, name, signal)
+            else:
+                # <LEGACY SUPPORT>
+                setattr(self, name + str(signal._mpx), signal)
+                # </LEGACY SUPPORT>
+                current_list = getattr(self, name, None)
+                if not isinstance(current_list, SignalMpxList):
+                    setattr(self, name, SignalMpxList())
+                getattr(self, name, None).add_new_signal(signal, signal._mpx)
+        self._setattr_allowed = False
+                
 
     def __setattr__(self, key, value):
         current_value = getattr(self, key, None)
         if isinstance(current_value, Signal):
             current_value.value = value
             return
-        super().__setattr__(key, value)
+        elif getattr(self, "_setattr_allowed", True):
+            super().__setattr__(key, value)
+        else:
+            raise AttributeError("signal does not exist")
 
 
 class Plugin: