From b8758686b8285dd368a90222baccb2e864c5c08f Mon Sep 17 00:00:00 2001
From: moon2 <moon2protonmail@protonmail.com>
Date: Thu, 9 May 2024 05:48:40 +0200
Subject: [PATCH] bl00mbox channel system upgrade

channels are now dynamically allocated and can be garbage
collected. note that channels do NOT live on the mp heap
so their memory footprint won't trigger a gc, we rely on
pyst3m just doing that on its own every now and then.
---
 components/bl00mbox/CMakeLists.txt            |   2 +
 components/bl00mbox/bl00mbox.c                |   2 +-
 components/bl00mbox/bl00mbox_audio.c          | 329 ++++++++++--------
 components/bl00mbox/bl00mbox_os.c             |  36 ++
 components/bl00mbox/bl00mbox_user.c           | 143 ++++----
 components/bl00mbox/config/bl00mbox_config.h  |  13 +
 components/bl00mbox/include/bl00mbox_audio.h  |  77 ++--
 components/bl00mbox/include/bl00mbox_ll.h     |  54 +++
 components/bl00mbox/include/bl00mbox_os.h     |  22 ++
 components/bl00mbox/include/bl00mbox_user.h   |  84 ++---
 .../bl00mbox/micropython/bl00mbox/_plugins.py |   3 +-
 .../bl00mbox/micropython/bl00mbox/_user.py    |  85 +++--
 .../bl00mbox/micropython/mp_sys_bl00mbox.c    |  78 ++++-
 .../standard_plugin_lib/range_shifter.c       |   2 +-
 .../standard_plugin_lib/slew_rate_limiter.c   |   2 -
 15 files changed, 609 insertions(+), 323 deletions(-)
 create mode 100644 components/bl00mbox/bl00mbox_os.c
 create mode 100644 components/bl00mbox/config/bl00mbox_config.h
 create mode 100644 components/bl00mbox/include/bl00mbox_ll.h
 create mode 100644 components/bl00mbox/include/bl00mbox_os.h

diff --git a/components/bl00mbox/CMakeLists.txt b/components/bl00mbox/CMakeLists.txt
index ecdabe8423..851a55b400 100644
--- a/components/bl00mbox/CMakeLists.txt
+++ b/components/bl00mbox/CMakeLists.txt
@@ -5,6 +5,7 @@ idf_component_register(
         bl00mbox.c
         bl00mbox_audio.c
         bl00mbox_user.c
+        bl00mbox_os.c
         bl00mbox_plugin_registry.c
         bl00mbox_radspa_requirements.c
         radspa/standard_plugin_lib/osc.c
@@ -36,4 +37,5 @@ idf_component_register(
         radspa
         radspa/standard_plugin_lib
         extern
+        config
 )
diff --git a/components/bl00mbox/bl00mbox.c b/components/bl00mbox/bl00mbox.c
index ad55b04417..cd601f717e 100644
--- a/components/bl00mbox/bl00mbox.c
+++ b/components/bl00mbox/bl00mbox.c
@@ -5,5 +5,5 @@
 
 void bl00mbox_init(){
     bl00mbox_plugin_registry_init();
-    bl00mbox_channels_init();
+    bl00mbox_audio_init();
 }
diff --git a/components/bl00mbox/bl00mbox_audio.c b/components/bl00mbox/bl00mbox_audio.c
index 763110736a..ae8a3e2cf8 100644
--- a/components/bl00mbox/bl00mbox_audio.c
+++ b/components/bl00mbox/bl00mbox_audio.c
@@ -1,199 +1,241 @@
 //SPDX-License-Identifier: CC0-1.0
 #include "bl00mbox_audio.h"
+#include "bl00mbox_ll.h"
+#include "bl00mbox_user.h"
+#include "bl00mbox_os.h"
 
 static bool is_initialized = false;
-static bool bl00mbox_audio_run = true;
-void bl00mbox_audio_enable(){ bl00mbox_audio_run = true; }
-void bl00mbox_audio_disable(){ bl00mbox_audio_run = false; }
 static uint16_t full_buffer_len;
 
 static uint32_t render_pass_id;
 
 int16_t * bl00mbox_line_in_interlaced = NULL;
 
-// fixed-length list of channels
-static bl00mbox_channel_t channels[BL00MBOX_CHANNELS];
-static int8_t last_chan_event = 0;
-// foregrounded channel is always rendered
-// note: regardless of settings the system channel 0 is always rendered
-static uint8_t bl00mbox_channel_foreground = 0;
-// channels may request being active while not being in foreground
-static bool bl00mbox_channel_background_mute_override[BL00MBOX_CHANNELS] = {false,};
-
-bool bl00mbox_channel_set_background_mute_override(uint8_t channel_index, bool enable){
-#ifdef BL00MBOX_BACKGROUND_MUTE_OVERRIDE_ENABLE
-    if(channel_index >= BL00MBOX_CHANNELS) return false;
-    bl00mbox_channel_background_mute_override[channel_index] = enable;
-    return true;
-#else
-    return false;
-#endif
+static int32_t free_chan_index = 0; // increments
+static bl00mbox_ll_t * all_chans = NULL;
+static bl00mbox_ll_t * background_mute_override_chans = NULL;
+static bl00mbox_channel_t * foreground_chan = NULL;
+static bl00mbox_lock_t render_lock = NULL;
+
+static bl00mbox_channel_t * cached_chan = NULL;
+
+bl00mbox_channel_t * bl00mbox_get_channel(int32_t channel_index){
+    if(channel_index < 0) return NULL;
+    if(cached_chan && (cached_chan->index == channel_index)){
+        return cached_chan;
+    }
+    bl00mbox_ll_t * chll = all_chans;
+    while(chll){
+        bl00mbox_channel_t * chan = chll->content;
+        if(chan->index == channel_index){
+            cached_chan = chan;
+            return chan;
+        }
+        chll = chll->next;
+    }
+    return NULL;
 }
 
-bool bl00mbox_channel_get_background_mute_override(uint8_t channel_index){
-    if(channel_index >= BL00MBOX_CHANNELS) return false;
-    return bl00mbox_channel_background_mute_override[channel_index];
+bool bl00mbox_get_channel_exists(int32_t channel_index){
+    return (bool) bl00mbox_get_channel(channel_index);
 }
 
-static void ** ptr_to_be_set_by_audio_task = NULL;
-static void * ptr_to_be_set_by_audio_task_target = NULL;
-// TODO: multicore thread safety on this boi
-static volatile bool ptr_set_request_pending = false;
-static uint64_t bl00mbox_audio_waitfor_timeout = 0ULL;
+int32_t bl00mbox_get_channel_index_positional_all_chans(int32_t position){
+    bl00mbox_ll_t * chll = all_chans;
+    if(!chll) return -1;
+    while(position){
+        position--;
+        chll = chll->next;
+        if(!chll) return -1;
+    }
+    bl00mbox_channel_t * chan = chll->content;
+    return chan->index;
+}
 
-bool bl00mbox_audio_waitfor_pointer_change(void ** ptr, void * new_val){
-    /// takes pointer to pointer that is to be set null
-    if(!is_initialized) return false;
-    ptr_to_be_set_by_audio_task = ptr;
-    ptr_to_be_set_by_audio_task_target = new_val;
-    ptr_set_request_pending = true;
-
-    volatile uint64_t timeout = 0; // cute
-    while(ptr_set_request_pending){
-        timeout++;
-        // TODO: nop
-        if(bl00mbox_audio_waitfor_timeout && (timeout = bl00mbox_audio_waitfor_timeout)){
-            return false;
-        }
+int32_t bl00mbox_get_channel_index_positional_background_mute_override_chans(int32_t position){
+    bl00mbox_ll_t * chll = background_mute_override_chans;
+    if(!chll) return -1;
+    while(position){
+        position--;
+        chll = chll->next;
+        if(!chll) return -1;
+    }
+    bl00mbox_channel_t * chan = chll->content;
+    return chan->index;
+}
+
+bool bl00mbox_channel_set_background_mute_override(int32_t channel_index, bool enable){
+    bl00mbox_channel_t * ch = bl00mbox_get_channel(channel_index);
+    if(!ch) return false;
+    if(enable){
+        bl00mbox_ll_prepend(&background_mute_override_chans, ch, &render_lock);
+    } else {
+        bl00mbox_ll_pop(&background_mute_override_chans, ch, &render_lock);
     }
     return true;
 }
 
-bool bl00mbox_audio_do_pointer_change(){
-    if(ptr_set_request_pending){
-        (* ptr_to_be_set_by_audio_task) = ptr_to_be_set_by_audio_task_target;
-        ptr_to_be_set_by_audio_task_target = NULL;
-        ptr_to_be_set_by_audio_task = NULL;
-        ptr_set_request_pending = false;
-        return true;
+bool bl00mbox_channel_get_background_mute_override(int32_t channel_index){
+    bl00mbox_channel_t * ch = bl00mbox_get_channel(channel_index);
+    if(!ch) return false;
+    return bl00mbox_ll_contains(&background_mute_override_chans, ch);
+}
+
+int32_t bl00mbox_channel_get_foreground_index(){
+    if(foreground_chan) return foreground_chan->index;
+    return -1;
+}
+
+void bl00mbox_channel_set_foreground_index(int32_t channel_index){
+    bl00mbox_channel_t * chan = bl00mbox_get_channel(channel_index);
+    if(!chan) return;
+    if(foreground_chan != chan){
+        bl00mbox_take_lock(&render_lock);
+        foreground_chan = chan;
+        bl00mbox_give_lock(&render_lock);
     }
-    return false;
 }
 
-void bl00mbox_channel_event(uint8_t chan){
+void bl00mbox_channel_event(int32_t index){
 #ifdef BL00MBOX_AUTO_FOREGROUNDING
-    last_chan_event = chan;
+    bl00mbox_channel_set_foreground_index(index);
 #endif
 }
 
-bl00mbox_channel_t * bl00mbox_get_channel(uint8_t channel_index){
-    if(channel_index >= BL00MBOX_CHANNELS) return NULL;
-    return &(channels[channel_index]);
+static bl00mbox_channel_t * _bl00mbox_channel_create(){
+    if(free_chan_index < 0) return NULL;
+    bl00mbox_channel_t * chan = calloc(1, sizeof(bl00mbox_channel_t));
+    if(!chan) return NULL;
+    if(!bl00mbox_ll_prepend(&all_chans, chan, NULL)){
+        free(chan);
+        return NULL;
+    }
+    chan->volume = BL00MBOX_DEFAULT_CHANNEL_VOLUME;
+    chan->sys_gain = 4096;
+    chan->is_active = true;
+    chan->index = free_chan_index;
+    free_chan_index += 1; 
+
+    bl00mbox_create_lock(&chan->render_lock);
+    bl00mbox_take_lock(&render_lock);
+    foreground_chan = chan;
+    bl00mbox_give_lock(&render_lock);
+    return chan;
 }
-
-uint8_t bl00mbox_channel_get_foreground_index(){
-    return bl00mbox_channel_foreground;
+bl00mbox_channel_t * bl00mbox_channel_create(){
+    bl00mbox_channel_t * chan = _bl00mbox_channel_create();
+    if(!chan) bl00mbox_log_error("channel allocation failed");
+    return chan;
 }
 
-void bl00mbox_channel_set_foreground_index(uint8_t channel_index){
-    if(channel_index >= BL00MBOX_CHANNELS) return;
-    last_chan_event = channel_index;
+void  bl00mbox_channel_delete(bl00mbox_channel_t * chan){
+    if(!chan) return;
+    if(cached_chan == chan) cached_chan = NULL;
+    if(foreground_chan == chan){
+        bl00mbox_take_lock(&render_lock);
+        foreground_chan = NULL;
+        bl00mbox_give_lock(&render_lock);
+    }
+    bl00mbox_ll_pop(&background_mute_override_chans, chan, &render_lock);
+    bl00mbox_ll_pop(&all_chans, chan, NULL);
+    bl00mbox_channel_clear(chan->index);
+    // be really sure that nobody else holds the lock. the renderer
+    // doesn't at this point, but if there's multiple tasks running
+    // clients there may be collisions.
+    // since the client api is generally not thread safe at this point
+    // it's okay, we can add the feature easily by just wrapping _all_
+    // client api in a lock at some point in the future.
+    bl00mbox_delete_lock(&chan->render_lock);
+    free(chan->name);
+    free(chan);
 }
 
-bool bl00mbox_channel_get_free(uint8_t channel_index){
-    if(channel_index >= BL00MBOX_CHANNELS) return false;
-    return bl00mbox_get_channel(channel_index)->is_free;
+bool bl00mbox_channel_get_free(int32_t channel_index){
+    // TODO: deprecate
+    bl00mbox_channel_t * chan = bl00mbox_get_channel(channel_index);
+    return !chan;
 }
 
-bool bl00mbox_channel_set_free(uint8_t channel_index, bool free){
-    if(channel_index >= BL00MBOX_CHANNELS) return false;
-    bl00mbox_get_channel(channel_index)->is_free = free;
+bool bl00mbox_channel_set_free(int32_t channel_index, bool set_free){
+    // TODO: deprecate
+    bl00mbox_channel_t * chan = bl00mbox_get_channel(channel_index);
+    if(!chan) return false;
+    if(set_free) bl00mbox_channel_delete(chan);
     return true;
 }
 
-uint8_t bl00mbox_channel_get_free_index(){
-    uint8_t ret = 1; // never return system channel
-    for(; ret < BL00MBOX_CHANNELS; ret++){
-        if(bl00mbox_get_channel(ret)->is_free){
-            bl00mbox_get_channel(ret)->is_free = false;
-            break;
-        }
-    }
-    last_chan_event = ret;
-    return ret;
+int32_t bl00mbox_channel_get_free_index(){
+    bl00mbox_channel_t * chan = bl00mbox_channel_create();
+    if(chan) return chan->index;
+    return -1;
 }
 
-char * bl00mbox_channel_get_name(uint8_t channel_index){
-    if(channel_index >= BL00MBOX_CHANNELS) return NULL;
-    return bl00mbox_get_channel(channel_index)->name;
-}
 
-void bl00mbox_channel_set_name(uint8_t channel_index, char * new_name){
-    if(channel_index >= BL00MBOX_CHANNELS) return;
-    bl00mbox_channel_t * chan =  bl00mbox_get_channel(channel_index);
-    if(chan->name != NULL) free(chan->name);
-    chan->name = strdup(new_name);
+char * bl00mbox_channel_get_name(int32_t channel_index){
+    bl00mbox_channel_t * chan = bl00mbox_get_channel(channel_index);
+    if(!chan) return NULL;
+    return chan->name;
 }
 
-void bl00mbox_channels_init(){
-    for(uint8_t i = 0; i < BL00MBOX_CHANNELS; i++){
-        bl00mbox_channel_t * chan = bl00mbox_get_channel(i);
-        chan->volume = BL00MBOX_DEFAULT_CHANNEL_VOLUME;
-        chan->sys_gain = 4096;
-        chan->root_list = NULL;
-        chan->buds = NULL;
-        chan->connections = NULL;
-        chan->is_active = true;
-        chan->is_free = true;
-        chan->always_render = NULL;
-        chan->name = NULL;
-        chan->dc = 0;
-    }
-    is_initialized = true;
+void bl00mbox_channel_set_name(int32_t channel_index, char * new_name){
+    bl00mbox_channel_t * ch =  bl00mbox_get_channel(channel_index);
+    if(!ch) return;
+    if(ch->name != NULL) free(ch->name);
+    ch->name = strdup(new_name);
 }
 
-void bl00mbox_channel_enable(uint8_t chan){
-    if(chan >= (BL00MBOX_CHANNELS)) return;
+void bl00mbox_channel_enable(int32_t chan){
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
+    if(!ch) return;
     ch->is_active = true;
 }
 
-void bl00mbox_channel_disable(uint8_t chan){
-    if(chan >= (BL00MBOX_CHANNELS)) return;
+void bl00mbox_channel_disable(int32_t chan){
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
+    if(!ch) return;
     ch->is_active = false;
 }
 
-void bl00mbox_channel_set_compute_mean_square(uint8_t chan, bool compute){
-    if(chan >= (BL00MBOX_CHANNELS)) return;
+void bl00mbox_channel_set_compute_mean_square(int32_t chan, bool compute){
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
+    if(!ch) return;
     ch->compute_mean_square = compute;
     if(!compute) ch->mean_square = 0;
 }
 
-bool bl00mbox_channel_get_compute_mean_square(uint8_t chan){
-    if(chan >= (BL00MBOX_CHANNELS)) return 0;
+bool bl00mbox_channel_get_compute_mean_square(int32_t chan){
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
+    if(!ch) return 0;
     return ch->compute_mean_square;
 }
 
-uint32_t bl00mbox_channel_get_mean_square(uint8_t chan){
-    if(chan >= (BL00MBOX_CHANNELS)) return 0;
+uint32_t bl00mbox_channel_get_mean_square(int32_t chan){
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
+    if(!ch) return 0;
     return ch->mean_square;
 }
 
-void bl00mbox_channel_set_sys_gain(uint8_t chan, int16_t volume){
-    if(chan >= (BL00MBOX_CHANNELS)) return;
+void bl00mbox_channel_set_sys_gain(int32_t chan, int16_t volume){
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
+    if(!ch) return;
     ch->sys_gain = volume;
 }
 
-int16_t bl00mbox_channel_get_sys_gain(uint8_t chan){
-    if(chan >= (BL00MBOX_CHANNELS)) return 0;
+int16_t bl00mbox_channel_get_sys_gain(int32_t chan){
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
+    if(!ch) return 0;
     return ch->sys_gain;
 }
 
-void bl00mbox_channel_set_volume(uint8_t chan, uint16_t volume){
-    if(chan >= (BL00MBOX_CHANNELS)) return;
+void bl00mbox_channel_set_volume(int32_t chan, uint16_t volume){
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
+    if(!ch) return;
     ch->volume = volume < 32767 ? volume : 32767;
 }
 
-int16_t bl00mbox_channel_get_volume(uint8_t chan){
-    if(chan >= (BL00MBOX_CHANNELS)) return 0;
+int16_t bl00mbox_channel_get_volume(int32_t chan){
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
+    if(!ch) return 0;
     return ch->volume;
 }
 
@@ -208,8 +250,7 @@ void bl00mbox_audio_bud_render(bl00mbox_bud_t * bud){
     bud->is_being_rendered = false;
 }
 
-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;
+static bool _bl00mbox_audio_channel_render(bl00mbox_channel_t * chan, int16_t * out, bool adding){
     chan->render_pass_id = render_pass_id;
 
     bl00mbox_bud_list_t * always = chan->always_render;
@@ -223,7 +264,7 @@ static bool bl00mbox_audio_channel_render(bl00mbox_channel_t * chan, int16_t * o
     int32_t vol = radspa_mult_shift(chan->volume, chan->sys_gain);
 
     // early exit when no sources or muted:
-    if((root == NULL) || (!chan->is_active) || (!vol)){
+    if((root == NULL) || (!vol)){
         return false;
     }
 
@@ -291,33 +332,36 @@ static bool bl00mbox_audio_channel_render(bl00mbox_channel_t * chan, int16_t * o
     return true;
 }
 
-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;
+static bool bl00mbox_audio_channel_render(bl00mbox_channel_t * chan, int16_t * out, bool adding){
+    if(!chan) return false;
+    if(!chan->is_active) return false;
+    if(render_pass_id == chan->render_pass_id) return false;
+    bl00mbox_take_lock(&chan->render_lock);
+    bool ret = _bl00mbox_audio_channel_render(chan, out, adding);
+    bl00mbox_give_lock(&chan->render_lock);
+    return ret;
+}
 
-    if(!bl00mbox_audio_run) return false;
+static bool _bl00mbox_audio_render(int16_t * rx, int16_t * tx, uint16_t len){
+    if(!is_initialized) 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
-    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
-    acc_init = bl00mbox_audio_channel_render(&(channels[bl00mbox_channel_foreground]), acc, acc_init) || acc_init;
+    bl00mbox_take_lock(&render_lock);
 
-    // 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]){
-            acc_init = bl00mbox_audio_channel_render(&(channels[i]), acc, acc_init) || acc_init;
-        }
+    // re-rendering channels is ok, if render_pass_id didn't change it will just exit
+    acc_init = bl00mbox_audio_channel_render(foreground_chan, acc, acc_init) || acc_init;
+    bl00mbox_ll_t * chll = background_mute_override_chans;
+    while(chll){
+        acc_init = bl00mbox_audio_channel_render(chll->content, acc, acc_init) || acc_init;
+        chll = chll->next;
     }
-#endif
+
+    bl00mbox_give_lock(&render_lock);
 
     if(!acc_init) return false;
 
@@ -331,3 +375,10 @@ bool _bl00mbox_audio_render(int16_t * rx, int16_t * tx, uint16_t len){
 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));
 }
+
+void bl00mbox_audio_init(){
+    if(render_lock) abort();
+    bl00mbox_create_lock(&render_lock);
+    if(!render_lock) abort();
+    is_initialized = true;
+}
diff --git a/components/bl00mbox/bl00mbox_os.c b/components/bl00mbox/bl00mbox_os.c
new file mode 100644
index 0000000000..9f7807012f
--- /dev/null
+++ b/components/bl00mbox/bl00mbox_os.c
@@ -0,0 +1,36 @@
+#include "bl00mbox_os.h"
+
+#ifdef BL00MBOX_FREERTOS
+void bl00mbox_create_lock(bl00mbox_lock_t * lock){ * lock = xSemaphoreCreateMutex(); }
+void bl00mbox_delete_lock(bl00mbox_lock_t * lock){ if(lock) vSemaphoreDelete(* lock); }
+void bl00mbox_take_lock(bl00mbox_lock_t * lock){ if(lock) xSemaphoreTake(* lock, portMAX_DELAY); }
+void bl00mbox_give_lock(bl00mbox_lock_t * lock){ if(lock) xSemaphoreGive(* lock); }
+#endif
+
+#if defined(BL00MBOX_ESPIDF)
+// macros in header
+#elif defined(BL00MBOX_PRINTF)
+#include <stdarg.h>
+#include <string.h>
+static void _bl00mbox_log(char * pre, char * txt, va_list args){
+    int len = strlen(pre) + strlen(txt) + 2;
+    char msg[len];
+    snprintf(msg, len, "%s%s\n", pre, txt);
+    vprintf(msg, args);
+}
+void bl00mbox_log_error(char * txt, ...){
+    va_list args;
+    va_start(args, txt);
+    _bl00mbox_log("bl00mbox error: ", txt, args);
+    va_end(args);
+};
+void bl00mbox_log_info(char * txt, ...){
+    va_list args;
+    va_start(args, txt);
+    _bl00mbox_log("bl00mbox info: ", txt, args);
+    va_end(args);
+};
+#else
+void bl00mbox_log_error(char * txt, ...){};
+void bl00mbox_log_info(char * txt, ...){};
+#endif
diff --git a/components/bl00mbox/bl00mbox_user.c b/components/bl00mbox/bl00mbox_user.c
index e828b4f4a9..3d81973a09 100644
--- a/components/bl00mbox/bl00mbox_user.c
+++ b/components/bl00mbox/bl00mbox_user.c
@@ -7,9 +7,9 @@ radspa_signal_t * bl00mbox_signal_get_by_index(radspa_t * plugin, uint16_t signa
 }
 
 static uint64_t bl00mbox_bud_index = 1;
-bl00mbox_bud_t * bl00mbox_channel_get_bud_by_index(uint8_t channel, uint32_t index);
+bl00mbox_bud_t * bl00mbox_channel_get_bud_by_index(int32_t channel, uint32_t index);
 
-uint16_t bl00mbox_channel_buds_num(uint8_t channel){
+uint16_t bl00mbox_channel_buds_num(int32_t channel){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     uint16_t ret = 0;
     if(chan->buds != NULL){
@@ -23,7 +23,7 @@ uint16_t bl00mbox_channel_buds_num(uint8_t channel){
     return ret;
 }
 
-uint64_t bl00mbox_channel_get_bud_by_list_pos(uint8_t channel, uint32_t pos){
+uint64_t bl00mbox_channel_get_bud_by_list_pos(int32_t channel, uint32_t pos){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     uint16_t ret = 0;
     if(chan->buds != NULL){
@@ -39,7 +39,7 @@ uint64_t bl00mbox_channel_get_bud_by_list_pos(uint8_t channel, uint32_t pos){
     return 0;
 }
 
-uint16_t bl00mbox_channel_conns_num(uint8_t channel){
+uint16_t bl00mbox_channel_conns_num(int32_t channel){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     uint16_t ret = 0;
     if(chan->connections != NULL){
@@ -53,7 +53,7 @@ uint16_t bl00mbox_channel_conns_num(uint8_t channel){
     return ret;
 }
 
-uint16_t bl00mbox_channel_mixer_num(uint8_t channel){
+uint16_t bl00mbox_channel_mixer_num(int32_t channel){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     uint16_t ret = 0;
     if(chan->root_list != NULL){
@@ -67,7 +67,7 @@ uint16_t bl00mbox_channel_mixer_num(uint8_t channel){
     return ret;
 }
 
-uint64_t bl00mbox_channel_get_bud_by_mixer_list_pos(uint8_t channel, uint32_t pos){
+uint64_t bl00mbox_channel_get_bud_by_mixer_list_pos(int32_t channel, uint32_t pos){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     uint16_t ret = 0;
     if(chan->buds != NULL){
@@ -83,7 +83,7 @@ uint64_t bl00mbox_channel_get_bud_by_mixer_list_pos(uint8_t channel, uint32_t po
     return 0;
 }
 
-uint32_t bl00mbox_channel_get_signal_by_mixer_list_pos(uint8_t channel, uint32_t pos){
+uint32_t bl00mbox_channel_get_signal_by_mixer_list_pos(int32_t channel, uint32_t pos){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     uint16_t ret = 0;
     if(chan->buds != NULL){
@@ -99,7 +99,7 @@ uint32_t bl00mbox_channel_get_signal_by_mixer_list_pos(uint8_t channel, uint32_t
     return 0;
 }
 
-uint16_t bl00mbox_channel_subscriber_num(uint8_t channel, uint64_t bud_index, uint16_t signal_index){
+uint16_t bl00mbox_channel_subscriber_num(int32_t channel, uint64_t bud_index, uint16_t signal_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return 0;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -121,7 +121,7 @@ uint16_t bl00mbox_channel_subscriber_num(uint8_t channel, uint64_t bud_index, ui
     return ret;
 }
 
-uint64_t bl00mbox_channel_get_bud_by_subscriber_list_pos(uint8_t channel, uint64_t bud_index,
+uint64_t bl00mbox_channel_get_bud_by_subscriber_list_pos(int32_t channel, uint64_t bud_index,
                 uint16_t signal_index, uint8_t pos){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return 0;
@@ -146,7 +146,7 @@ uint64_t bl00mbox_channel_get_bud_by_subscriber_list_pos(uint8_t channel, uint64
     return 0;
 }
 
-int32_t bl00mbox_channel_get_signal_by_subscriber_list_pos(uint8_t channel, uint64_t bud_index,
+int32_t bl00mbox_channel_get_signal_by_subscriber_list_pos(int32_t channel, uint64_t bud_index,
                 uint16_t signal_index, uint8_t pos){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return 0;
@@ -171,7 +171,7 @@ int32_t bl00mbox_channel_get_signal_by_subscriber_list_pos(uint8_t channel, uint
     return 0;
 }
 
-uint64_t bl00mbox_channel_get_source_bud(uint8_t channel, uint64_t bud_index, uint16_t signal_index){
+uint64_t bl00mbox_channel_get_source_bud(int32_t channel, uint64_t bud_index, uint16_t signal_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return 0;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -183,7 +183,7 @@ uint64_t bl00mbox_channel_get_source_bud(uint8_t channel, uint64_t bud_index, ui
     return conn->source_bud->index;
 }
 
-uint16_t bl00mbox_channel_get_source_signal(uint8_t channel, uint64_t bud_index, uint16_t signal_index){
+uint16_t bl00mbox_channel_get_source_signal(int32_t channel, uint64_t bud_index, uint16_t signal_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return 0;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -195,7 +195,7 @@ uint16_t bl00mbox_channel_get_source_signal(uint8_t channel, uint64_t bud_index,
     return conn->signal_index;
 }
 
-static bl00mbox_connection_t * create_connection(uint8_t channel){
+static bl00mbox_connection_t * create_connection(int32_t channel){
     bl00mbox_connection_t * ret = malloc(sizeof(bl00mbox_connection_t));
     if(ret == NULL) return NULL;
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
@@ -217,18 +217,20 @@ static bl00mbox_connection_t * create_connection(uint8_t channel){
 
 static bool weak_delete_connection(bl00mbox_connection_t * conn){
     if(conn->subs != NULL) return false;
+    bl00mbox_channel_t * chan = bl00mbox_get_channel(conn->channel);
 
     // nullify source bud connection;
     bl00mbox_bud_t * bud = conn->source_bud;
     if(bud != NULL){
         radspa_signal_t * tx = bl00mbox_signal_get_by_index(bud->plugin, conn->signal_index);
         if(tx != NULL){
-            bl00mbox_audio_waitfor_pointer_change(&(tx->buffer), NULL);
+            bl00mbox_take_lock(&chan->render_lock);
+            tx->buffer = NULL;
+            bl00mbox_give_lock(&chan->render_lock);
         }
     }
 
     // pop from channel list
-    bl00mbox_channel_t * chan = bl00mbox_get_channel(conn->channel);
     if(chan->connections != NULL){
         if(chan->connections != conn){
             bl00mbox_connection_t * prev = chan->connections;
@@ -238,9 +240,15 @@ static bool weak_delete_connection(bl00mbox_connection_t * conn){
                     break;
                 }
             }
-            if(prev->chan_next != NULL) bl00mbox_audio_waitfor_pointer_change(&(prev->chan_next), conn->chan_next);
+            if(prev->chan_next != NULL){
+                bl00mbox_take_lock(&chan->render_lock);
+                prev->chan_next = conn->chan_next;
+                bl00mbox_give_lock(&chan->render_lock);
+            }
         } else {
-            bl00mbox_audio_waitfor_pointer_change(&(chan->connections), conn->chan_next);
+            bl00mbox_take_lock(&chan->render_lock);
+            chan->connections = conn->chan_next;
+            bl00mbox_give_lock(&chan->render_lock);
         }
     }
 
@@ -248,7 +256,7 @@ static bool weak_delete_connection(bl00mbox_connection_t * conn){
     return true;
 }
 
-bl00mbox_bud_t * bl00mbox_channel_get_bud_by_index(uint8_t channel, uint32_t index){
+bl00mbox_bud_t * bl00mbox_channel_get_bud_by_index(int32_t channel, uint32_t index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return NULL;
     if(chan->buds == NULL) return NULL;
@@ -261,7 +269,7 @@ bl00mbox_bud_t * bl00mbox_channel_get_bud_by_index(uint8_t channel, uint32_t ind
     return bud;
 }
 
-bl00mbox_bud_t * bl00mbox_channel_new_bud(uint8_t channel, uint32_t id, uint32_t init_var){
+bl00mbox_bud_t * bl00mbox_channel_new_bud(int32_t channel, uint32_t id, uint32_t init_var){
     /// creates a new bud instance of the plugin with descriptor id "id" and the initialization variable
     /// "init_var" and appends it to the plugin list of the corresponding channel. returns pointer to
     /// the bud if successfull, else NULL.
@@ -296,7 +304,7 @@ bl00mbox_bud_t * bl00mbox_channel_new_bud(uint8_t channel, uint32_t id, uint32_t
     return bud;
 }
 
-bool bl00mbox_channel_delete_bud(uint8_t channel, uint32_t bud_index){
+bool bl00mbox_channel_delete_bud(int32_t channel, uint32_t bud_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -324,11 +332,13 @@ bool bl00mbox_channel_delete_bud(uint8_t channel, uint32_t bud_index){
             seek = seek->chan_next;
         }
         if(seek != NULL){
+            bl00mbox_take_lock(&chan->render_lock);
             if(prev != NULL){
-                bl00mbox_audio_waitfor_pointer_change(&(prev->chan_next), seek->chan_next);
+                prev->chan_next = seek->chan_next;
             } else {
-                bl00mbox_audio_waitfor_pointer_change(&(chan->buds), seek->chan_next);
+                chan->buds = seek->chan_next;
             }
+            bl00mbox_give_lock(&chan->render_lock);
             free_later = true;
         }
     }
@@ -338,7 +348,7 @@ bool bl00mbox_channel_delete_bud(uint8_t channel, uint32_t bud_index){
     return true;
 }
 
-bool bl00mbox_channel_clear(uint8_t channel){
+bool bl00mbox_channel_clear(int32_t channel){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = chan->buds;
@@ -350,7 +360,7 @@ bool bl00mbox_channel_clear(uint8_t channel){
     return true;
 }
 
-bool bl00mbox_channel_connect_signal_to_output_mixer(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index){
+bool bl00mbox_channel_connect_signal_to_output_mixer(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -418,7 +428,7 @@ bool bl00mbox_channel_connect_signal_to_output_mixer(uint8_t channel, uint32_t b
     return true;
 }
 
-bool bl00mbox_channel_disconnect_signal_from_output_mixer(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index){
+bool bl00mbox_channel_disconnect_signal_from_output_mixer(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index){
     //TODO
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
@@ -442,11 +452,13 @@ bool bl00mbox_channel_disconnect_signal_from_output_mixer(uint8_t channel, uint3
     }
     if(rt == NULL) return false; // root doesn't exist
 
+    bl00mbox_take_lock(&chan->render_lock);
     if(rt_prev == NULL){
-        bl00mbox_audio_waitfor_pointer_change(&(chan->root_list), rt->next);
+        chan->root_list = rt->next;
     } else {
-        bl00mbox_audio_waitfor_pointer_change(&(rt_prev->next), rt->next);
+        rt_prev->next = rt->next;
     }
+    bl00mbox_give_lock(&chan->render_lock);
     free(rt);
 
     if(conn->subs != NULL){
@@ -460,11 +472,13 @@ bool bl00mbox_channel_disconnect_signal_from_output_mixer(uint8_t channel, uint3
             seek = seek->next;
         }
         if(seek != NULL){
+            bl00mbox_take_lock(&chan->render_lock);
             if(prev != NULL){
-                bl00mbox_audio_waitfor_pointer_change(&(prev->next), seek->next);
+                prev->next = seek->next;
             } else {
-                bl00mbox_audio_waitfor_pointer_change(&(conn->subs), seek->next);
+                conn->subs = seek->next;
             }
+            bl00mbox_give_lock(&chan->render_lock);
             free(seek);
         }
     }
@@ -474,7 +488,8 @@ bool bl00mbox_channel_disconnect_signal_from_output_mixer(uint8_t channel, uint3
     return true;
 }
 
-bool bl00mbox_channel_disconnect_signal_rx(uint8_t channel, uint32_t bud_rx_index, uint32_t bud_rx_signal_index){
+bool bl00mbox_channel_disconnect_signal_rx(int32_t channel, uint32_t bud_rx_index, uint32_t bud_rx_signal_index){
+    bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     bl00mbox_bud_t * bud_rx = bl00mbox_channel_get_bud_by_index(channel, bud_rx_index);
     if(bud_rx == NULL) return false; // bud index doesn't exist
 
@@ -491,7 +506,9 @@ bool bl00mbox_channel_disconnect_signal_rx(uint8_t channel, uint32_t bud_rx_inde
     radspa_signal_t * tx = bl00mbox_signal_get_by_index(bud_tx->plugin, conn->signal_index);
     if(tx == NULL) return false; // signal index doesn't exist
 
-    bl00mbox_audio_waitfor_pointer_change(&(rx->buffer), NULL);
+    bl00mbox_take_lock(&chan->render_lock);
+    rx->buffer = NULL;
+    bl00mbox_give_lock(&chan->render_lock);
 
     if(conn->subs != NULL){
         bl00mbox_connection_subscriber_t * seek = conn->subs;
@@ -506,11 +523,13 @@ bool bl00mbox_channel_disconnect_signal_rx(uint8_t channel, uint32_t bud_rx_inde
             seek = seek->next;
         }
         if(seek != NULL){
+            bl00mbox_take_lock(&chan->render_lock);
             if(prev != NULL){
-                bl00mbox_audio_waitfor_pointer_change(&(prev->next), seek->next);
+                prev->next = seek->next;
             } else {
-                bl00mbox_audio_waitfor_pointer_change(&(conn->subs), seek->next);
+                conn->subs = seek->next;
             }
+            bl00mbox_give_lock(&chan->render_lock);
             free(seek);
         }
     }
@@ -520,7 +539,7 @@ bool bl00mbox_channel_disconnect_signal_rx(uint8_t channel, uint32_t bud_rx_inde
     return true;
 }
 
-bool bl00mbox_channel_disconnect_signal_tx(uint8_t channel, uint32_t bud_tx_index, uint32_t bud_tx_signal_index){
+bool bl00mbox_channel_disconnect_signal_tx(int32_t channel, uint32_t bud_tx_index, uint32_t bud_tx_signal_index){
     bl00mbox_bud_t * bud_tx = bl00mbox_channel_get_bud_by_index(channel, bud_tx_index);
     if(bud_tx == NULL) return false; // bud index doesn't exist
 
@@ -546,7 +565,7 @@ bool bl00mbox_channel_disconnect_signal_tx(uint8_t channel, uint32_t bud_tx_inde
     return true;
 }
 
-bool bl00mbox_channel_disconnect_signal(uint8_t channel, uint32_t bud_index, uint32_t signal_index){
+bool bl00mbox_channel_disconnect_signal(int32_t channel, uint32_t bud_index, uint32_t signal_index){
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
     if(bud == NULL) return false; // bud index doesn't exist
     radspa_signal_t * sig = bl00mbox_signal_get_by_index(bud->plugin, signal_index);
@@ -560,7 +579,7 @@ bool bl00mbox_channel_disconnect_signal(uint8_t channel, uint32_t bud_index, uin
     return false;
 }
 
-bool bl00mbox_channel_connect_signal(uint8_t channel, uint32_t bud_rx_index, uint32_t bud_rx_signal_index,
+bool bl00mbox_channel_connect_signal(int32_t channel, uint32_t bud_rx_index, uint32_t bud_rx_signal_index,
                                                uint32_t bud_tx_index, uint32_t bud_tx_signal_index){
     bl00mbox_bud_t * bud_rx = bl00mbox_channel_get_bud_by_index(channel, bud_rx_index);
     bl00mbox_bud_t * bud_tx = bl00mbox_channel_get_bud_by_index(channel, bud_tx_index);
@@ -612,7 +631,7 @@ bool bl00mbox_channel_connect_signal(uint8_t channel, uint32_t bud_rx_index, uin
     return true;
 }
 
-bool bl00mbox_channel_bud_exists(uint8_t channel, uint32_t bud_index){
+bool bl00mbox_channel_bud_exists(int32_t channel, uint32_t bud_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -623,7 +642,7 @@ bool bl00mbox_channel_bud_exists(uint8_t channel, uint32_t bud_index){
     }
 }
 
-char * bl00mbox_channel_bud_get_name(uint8_t channel, uint32_t bud_index){
+char * bl00mbox_channel_bud_get_name(int32_t channel, uint32_t bud_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -631,7 +650,7 @@ char * bl00mbox_channel_bud_get_name(uint8_t channel, uint32_t bud_index){
     return bud->plugin->descriptor->name;
 }
 
-char * bl00mbox_channel_bud_get_description(uint8_t channel, uint32_t bud_index){
+char * bl00mbox_channel_bud_get_description(int32_t channel, uint32_t bud_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -639,7 +658,7 @@ char * bl00mbox_channel_bud_get_description(uint8_t channel, uint32_t bud_index)
     return bud->plugin->descriptor->description;
 }
 
-uint32_t bl00mbox_channel_bud_get_plugin_id(uint8_t channel, uint32_t bud_index){
+uint32_t bl00mbox_channel_bud_get_plugin_id(int32_t channel, uint32_t bud_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -647,7 +666,7 @@ uint32_t bl00mbox_channel_bud_get_plugin_id(uint8_t channel, uint32_t bud_index)
     return bud->plugin->descriptor->id;
 }
 
-uint32_t bl00mbox_channel_bud_get_init_var(uint8_t channel, uint32_t bud_index){
+uint32_t bl00mbox_channel_bud_get_init_var(int32_t channel, uint32_t bud_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -655,7 +674,7 @@ uint32_t bl00mbox_channel_bud_get_init_var(uint8_t channel, uint32_t bud_index){
     return bud->init_var;
 }
 
-uint16_t bl00mbox_channel_bud_get_num_signals(uint8_t channel, uint32_t bud_index){
+uint16_t bl00mbox_channel_bud_get_num_signals(int32_t channel, uint32_t bud_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -663,7 +682,7 @@ uint16_t bl00mbox_channel_bud_get_num_signals(uint8_t channel, uint32_t bud_inde
     return bud->plugin->len_signals;
 }
 
-char * bl00mbox_channel_bud_get_signal_name(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index){
+char * bl00mbox_channel_bud_get_signal_name(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -673,7 +692,7 @@ char * bl00mbox_channel_bud_get_signal_name(uint8_t channel, uint32_t bud_index,
     return sig->name;
 }
 
-int8_t bl00mbox_channel_bud_get_signal_name_multiplex(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index){
+int8_t bl00mbox_channel_bud_get_signal_name_multiplex(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -683,7 +702,7 @@ int8_t bl00mbox_channel_bud_get_signal_name_multiplex(uint8_t channel, uint32_t
     return sig->name_multiplex;
 }
 
-char * bl00mbox_channel_bud_get_signal_description(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index){
+char * bl00mbox_channel_bud_get_signal_description(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -693,7 +712,7 @@ char * bl00mbox_channel_bud_get_signal_description(uint8_t channel, uint32_t bud
     return sig->description;
 }
 
-char * bl00mbox_channel_bud_get_signal_unit(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index){
+char * bl00mbox_channel_bud_get_signal_unit(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -703,7 +722,7 @@ char * bl00mbox_channel_bud_get_signal_unit(uint8_t channel, uint32_t bud_index,
     return sig->unit;
 }
 
-bool bl00mbox_channel_bud_get_always_render(uint8_t channel, uint32_t bud_index){
+bool bl00mbox_channel_bud_get_always_render(int32_t channel, uint32_t bud_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -711,7 +730,7 @@ bool bl00mbox_channel_bud_get_always_render(uint8_t channel, uint32_t bud_index)
     return bud->always_render;
 }
 
-bool bl00mbox_channel_bud_set_always_render(uint8_t channel, uint32_t bud_index, bool value){
+bool bl00mbox_channel_bud_set_always_render(int32_t channel, uint32_t bud_index, bool value){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -723,13 +742,17 @@ bool bl00mbox_channel_bud_set_always_render(uint8_t channel, uint32_t bud_index,
         new->bud = bud;
         new->next = NULL;
         if(chan->always_render == NULL){
-            bl00mbox_audio_waitfor_pointer_change(&(chan->always_render), new);
+            bl00mbox_take_lock(&chan->render_lock);
+            chan->always_render = new;
+            bl00mbox_give_lock(&chan->render_lock);
         } else{
             bl00mbox_bud_list_t * end = chan->always_render;
             while(end->next != NULL){
                 end = end->next;
             }
-            bl00mbox_audio_waitfor_pointer_change(&(end->next), new);
+            bl00mbox_take_lock(&chan->render_lock);
+            end->next = new;
+            bl00mbox_give_lock(&chan->render_lock);
         }
     } else {
         if(chan->always_render != NULL){
@@ -743,11 +766,13 @@ bool bl00mbox_channel_bud_set_always_render(uint8_t channel, uint32_t bud_index,
                 seek = seek->next;
             }
             if(seek != NULL){
+                bl00mbox_take_lock(&chan->render_lock);
                 if(prev != NULL){
-                    bl00mbox_audio_waitfor_pointer_change(&(prev->next), seek->next);
+                    prev->next = seek->next;
                 } else {
-                    bl00mbox_audio_waitfor_pointer_change(&(chan->always_render), seek->next);
+                    chan->always_render = seek->next;
                 }
+                bl00mbox_give_lock(&chan->render_lock);
                 free(seek);
             }
         }
@@ -758,7 +783,7 @@ bool bl00mbox_channel_bud_set_always_render(uint8_t channel, uint32_t bud_index,
     return true;
 }
 
-bool bl00mbox_channel_bud_set_signal_value(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index, int16_t value){
+bool bl00mbox_channel_bud_set_signal_value(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index, int16_t value){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -776,7 +801,7 @@ bool bl00mbox_channel_bud_set_signal_value(uint8_t channel, uint32_t bud_index,
     return true;
 }
 
-int16_t bl00mbox_channel_bud_get_signal_value(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index){
+int16_t bl00mbox_channel_bud_get_signal_value(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return -32768;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -792,7 +817,7 @@ int16_t bl00mbox_channel_bud_get_signal_value(uint8_t channel, uint32_t bud_inde
     }
 }
 
-uint32_t bl00mbox_channel_bud_get_signal_hints(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index){
+uint32_t bl00mbox_channel_bud_get_signal_hints(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -803,7 +828,7 @@ uint32_t bl00mbox_channel_bud_get_signal_hints(uint8_t channel, uint32_t bud_ind
     return sig->hints;
 }
 
-bool bl00mbox_channel_bud_set_table_value(uint8_t channel, uint32_t bud_index, uint32_t table_index, int16_t value){
+bool bl00mbox_channel_bud_set_table_value(int32_t channel, uint32_t bud_index, uint32_t table_index, int16_t value){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -816,7 +841,7 @@ bool bl00mbox_channel_bud_set_table_value(uint8_t channel, uint32_t bud_index, u
     return true;
 }
 
-int16_t bl00mbox_channel_bud_get_table_value(uint8_t channel, uint32_t bud_index, uint32_t table_index){
+int16_t bl00mbox_channel_bud_get_table_value(int32_t channel, uint32_t bud_index, uint32_t table_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -827,7 +852,7 @@ int16_t bl00mbox_channel_bud_get_table_value(uint8_t channel, uint32_t bud_index
     return bud->plugin->plugin_table[table_index];
 }
 
-int16_t * bl00mbox_channel_bud_get_table_pointer(uint8_t channel, uint32_t bud_index){
+int16_t * bl00mbox_channel_bud_get_table_pointer(int32_t channel, uint32_t bud_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return false;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
@@ -836,7 +861,7 @@ int16_t * bl00mbox_channel_bud_get_table_pointer(uint8_t channel, uint32_t bud_i
     return bud->plugin->plugin_table;
 }
 
-uint32_t bl00mbox_channel_bud_get_table_len(uint8_t channel, uint32_t bud_index){
+uint32_t bl00mbox_channel_bud_get_table_len(int32_t channel, uint32_t bud_index){
     bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
     if(chan == NULL) return 0;
     bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
diff --git a/components/bl00mbox/config/bl00mbox_config.h b/components/bl00mbox/config/bl00mbox_config.h
new file mode 100644
index 0000000000..38171d8dc5
--- /dev/null
+++ b/components/bl00mbox/config/bl00mbox_config.h
@@ -0,0 +1,13 @@
+#pragma once
+#define BL00MBOX_FLOW3R
+
+#ifdef BL00MBOX_FLOW3R
+//#include "flow3r_bsp.h"
+//#define BL00MBOX_MAX_BUFFER_LEN FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE
+#define BL00MBOX_MAX_BUFFER_LEN 64
+#define BL00MBOX_DEFAULT_CHANNEL_VOLUME 8192
+#define BL00MBOX_AUTO_FOREGROUNDING
+#define BL00MBOX_LOOPS_ENABLE
+#define BL00MBOX_FREERTOS
+#define BL00MBOX_ESPIDF
+#endif
diff --git a/components/bl00mbox/include/bl00mbox_audio.h b/components/bl00mbox/include/bl00mbox_audio.h
index 18cd250e1f..0a4490c54c 100644
--- a/components/bl00mbox/include/bl00mbox_audio.h
+++ b/components/bl00mbox/include/bl00mbox_audio.h
@@ -1,13 +1,7 @@
 //SPDX-License-Identifier: CC0-1.0
 #pragma once
-
-//TODO: move this to kconfig someday?
-#define BL00MBOX_MAX_BUFFER_LEN 128
-#define BL00MBOX_DEFAULT_CHANNEL_VOLUME 8000
-#define BL00MBOX_CHANNELS 32
-#define BL00MBOX_BACKGROUND_MUTE_OVERRIDE_ENABLE
-#define BL00MBOX_AUTO_FOREGROUNDING
-#define BL00MBOX_LOOPS_ENABLE
+#include "bl00mbox_config.h"
+#include "bl00mbox_os.h"
 
 #include <stdio.h>
 #include <math.h>
@@ -28,7 +22,7 @@ typedef struct _bl00mbox_bud_t{
     uint64_t index; // unique index number for bud
     uint32_t render_pass_id; // may be used by host to determine whether recomputation is necessary
     uint32_t init_var; // init var that was used for plugin creation
-    uint8_t channel; // index of channel that owns the plugin
+    int32_t channel; // index of channel that owns the plugin
     volatile bool is_being_rendered; // true if rendering the plugin is in progress, else false.
     bool always_render;
     struct _bl00mbox_bud_t * chan_next; //for linked list in bl00mbox_channel_t
@@ -41,7 +35,7 @@ typedef struct _bl00mbox_bud_list_t{
 
 typedef struct _bl00mbox_connection_subscriber_t{
     uint8_t type; // 0: standard signal input, 1: output mixer
-    uint8_t channel;
+    int32_t channel;
     uint64_t bud_index;
     uint32_t signal_index;
     struct _bl00mbox_connection_subscriber_t * next;
@@ -52,7 +46,7 @@ typedef struct _bl00mbox_connection_t{ //child of bl00mbox_ll_t
     struct _bl00mbox_bud_t * source_bud;
     uint32_t signal_index; // signal of source_bud that renders to buffer
     struct _bl00mbox_connection_subscriber_t * subs;
-    uint8_t channel;
+    int32_t channel;
     struct _bl00mbox_connection_t * chan_next; //for linked list in bl00mbox_channel_t;
 } bl00mbox_connection_t;
 
@@ -62,8 +56,9 @@ typedef struct _bl00mbox_channel_root_t{
 } bl00mbox_channel_root_t;
 
 typedef struct{
+    int32_t index;
+    bl00mbox_lock_t render_lock;
     bool is_active; // rendering can be skipped if false
-    bool is_free;
     bool compute_mean_square;
     uint32_t mean_square;
     char * name;
@@ -77,31 +72,35 @@ typedef struct{
     struct _bl00mbox_connection_t * connections; // linked list with all channel connections
 } bl00mbox_channel_t;
 
-bl00mbox_channel_t * bl00mbox_get_channel(uint8_t chan);
-void bl00mbox_channel_enable(uint8_t chan);
-
-void bl00mbox_channel_enable(uint8_t chan);
-void bl00mbox_channel_disable(uint8_t chan);
-void bl00mbox_channel_set_volume(uint8_t chan, uint16_t volume);
-int16_t bl00mbox_channel_get_volume(uint8_t chan);
-void bl00mbox_channel_set_sys_gain(uint8_t chan, int16_t volume);
-int16_t bl00mbox_channel_get_sys_gain(uint8_t chan);
-void bl00mbox_channel_set_compute_mean_square(uint8_t chan, bool compute);
-bool bl00mbox_channel_get_compute_mean_square(uint8_t chan);
-uint32_t bl00mbox_channel_get_mean_square(uint8_t chan);
-void bl00mbox_channel_event(uint8_t chan);
-uint8_t bl00mbox_channel_get_free_index();
-void bl00mbox_channels_init();
-uint8_t bl00mbox_channel_get_foreground_index();
-void bl00mbox_channel_set_foreground_index(uint8_t channel_index);
-bool bl00mbox_channel_get_free(uint8_t channel_index);
-bool bl00mbox_channel_set_free(uint8_t channel_index, bool free);
-
-bool bl00mbox_channel_get_background_mute_override(uint8_t channel_index);
-bool bl00mbox_channel_set_background_mute_override(uint8_t channel_index, bool enable);
-
-char * bl00mbox_channel_get_name(uint8_t channel_index);
-void bl00mbox_channel_set_name(uint8_t channel_index, char * new_name);
-
-bool bl00mbox_audio_waitfor_pointer_change(void ** ptr, void * new_val);
+void bl00mbox_audio_init();
+
+bl00mbox_channel_t * bl00mbox_get_channel(int32_t chan);
+void bl00mbox_channel_enable(int32_t chan);
+
+void bl00mbox_channel_enable(int32_t chan);
+void bl00mbox_channel_disable(int32_t chan);
+void bl00mbox_channel_set_volume(int32_t chan, uint16_t volume);
+int16_t bl00mbox_channel_get_volume(int32_t chan);
+void bl00mbox_channel_set_sys_gain(int32_t chan, int16_t volume);
+int16_t bl00mbox_channel_get_sys_gain(int32_t chan);
+void bl00mbox_channel_set_compute_mean_square(int32_t chan, bool compute);
+bool bl00mbox_channel_get_compute_mean_square(int32_t chan);
+uint32_t bl00mbox_channel_get_mean_square(int32_t chan);
+void bl00mbox_channel_event(int32_t chan);
+bool bl00mbox_channel_get_free(int32_t channel_index);
+bool bl00mbox_channel_set_free(int32_t channel_index, bool free);
+
+bool bl00mbox_channel_get_background_mute_override(int32_t channel_index);
+bool bl00mbox_channel_set_background_mute_override(int32_t channel_index, bool enable);
+
+char * bl00mbox_channel_get_name(int32_t channel_index);
+void bl00mbox_channel_set_name(int32_t channel_index, char * new_name);
+
 void bl00mbox_audio_bud_render(bl00mbox_bud_t * bud);
+
+bool bl00mbox_get_channel_exists(int32_t channel_index);
+int32_t bl00mbox_channel_get_free_index();
+int32_t bl00mbox_get_channel_index_positional_all_chans(int32_t position);
+int32_t bl00mbox_get_channel_index_positional_background_mute_override_chans(int32_t position);
+int32_t bl00mbox_channel_get_foreground_index();
+void bl00mbox_channel_set_foreground_index(int32_t channel_index);
diff --git a/components/bl00mbox/include/bl00mbox_ll.h b/components/bl00mbox/include/bl00mbox_ll.h
new file mode 100644
index 0000000000..0d3a315805
--- /dev/null
+++ b/components/bl00mbox/include/bl00mbox_ll.h
@@ -0,0 +1,54 @@
+#pragma once
+#include "bl00mbox_os.h"
+
+typedef struct _bl00mbox_ll_t {
+    struct _bl00mbox_ll_t * next;
+    void * content;
+} bl00mbox_ll_t;
+
+bool bl00mbox_ll_contains(bl00mbox_ll_t ** startref, void * content){
+    if(!startref) return false; // list doesn't exist
+    bl00mbox_ll_t * seek = * startref;
+    if(!seek) return false; // list is empty
+    while(seek){
+        if(seek->content == content) break;
+        seek = seek->next;
+    }
+    return seek;
+}
+
+bool bl00mbox_ll_prepend(bl00mbox_ll_t ** startref, void * content, bl00mbox_lock_t * write_lock){
+    if(!startref) return false;
+    if(bl00mbox_ll_contains(startref, content)) return false;
+    bl00mbox_ll_t * ll = malloc(sizeof(bl00mbox_ll_t));
+    if(!ll) return false;
+    ll->content = content;
+    ll->next = *startref;
+    bl00mbox_take_lock(write_lock);
+    *startref = ll;
+    bl00mbox_give_lock(write_lock);
+    return true;
+}
+
+bool bl00mbox_ll_pop(bl00mbox_ll_t ** startref, void * content, bl00mbox_lock_t * write_lock){
+    if(!startref) return false; // list doesn't exist
+    bl00mbox_ll_t * seek = * startref;
+    if(!seek) return false; // list is empty
+    bl00mbox_ll_t * prev = NULL;
+    while(seek){
+        if(seek->content == content) break;
+        prev = seek;
+        seek = seek->next;
+    }
+    if(!seek) return false; // not found
+    bl00mbox_take_lock(write_lock);
+    if(prev){ // normal
+        prev->next = seek->next;
+    } else { // first element
+        * startref = seek->next;
+    }
+    bl00mbox_give_lock(write_lock);
+    free(seek);
+    return true;
+}
+
diff --git a/components/bl00mbox/include/bl00mbox_os.h b/components/bl00mbox/include/bl00mbox_os.h
new file mode 100644
index 0000000000..fbb422fa42
--- /dev/null
+++ b/components/bl00mbox/include/bl00mbox_os.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "bl00mbox_config.h"
+
+#ifdef BL00MBOX_FREERTOS
+#include "freertos/FreeRTOS.h"
+#include "freertos/semphr.h"
+typedef SemaphoreHandle_t bl00mbox_lock_t;
+#endif
+
+void bl00mbox_create_lock(bl00mbox_lock_t * lock);
+void bl00mbox_delete_lock(bl00mbox_lock_t * lock);
+void bl00mbox_take_lock(bl00mbox_lock_t * lock);
+void bl00mbox_give_lock(bl00mbox_lock_t * lock);
+
+#ifdef BL00MBOX_ESPIDF
+#include "esp_log.h"
+#define bl00mbox_log_error(txt, ...) ESP_LOGE("bl00mbox", txt, ##__VA_ARGS__);
+#define bl00mbox_log_info(txt, ...) ESP_LOGI("bl00mbox", txt, ##__VA_ARGS__);
+#else
+void bl00mbox_log_error(char * txt, ...);
+void bl00mbox_log_info(char * txt, ...);
+#endif
diff --git a/components/bl00mbox/include/bl00mbox_user.h b/components/bl00mbox/include/bl00mbox_user.h
index e4f2cb8bc0..58bdd59f9b 100644
--- a/components/bl00mbox/include/bl00mbox_user.h
+++ b/components/bl00mbox/include/bl00mbox_user.h
@@ -7,53 +7,55 @@
 
 #include "bl00mbox_plugin_registry.h"
 #include "bl00mbox_audio.h"
+#include "bl00mbox_os.h"
+
 #include <stdint.h>
 #include "bl00mbox_audio.h"
 #include "radspa_helpers.h"
 
-uint16_t bl00mbox_channel_buds_num(uint8_t channel);
-uint64_t bl00mbox_channel_get_bud_by_list_pos(uint8_t channel, uint32_t pos);
-uint16_t bl00mbox_channel_conns_num(uint8_t channel);
-uint16_t bl00mbox_channel_mixer_num(uint8_t channel);
-uint64_t bl00mbox_channel_get_bud_by_mixer_list_pos(uint8_t channel, uint32_t pos);
-uint32_t bl00mbox_channel_get_signal_by_mixer_list_pos(uint8_t channel, uint32_t pos);
-bool bl00mbox_channel_clear(uint8_t channel);
+uint16_t bl00mbox_channel_buds_num(int32_t channel);
+uint64_t bl00mbox_channel_get_bud_by_list_pos(int32_t channel, uint32_t pos);
+uint16_t bl00mbox_channel_conns_num(int32_t channel);
+uint16_t bl00mbox_channel_mixer_num(int32_t channel);
+uint64_t bl00mbox_channel_get_bud_by_mixer_list_pos(int32_t channel, uint32_t pos);
+uint32_t bl00mbox_channel_get_signal_by_mixer_list_pos(int32_t channel, uint32_t pos);
+bool bl00mbox_channel_clear(int32_t channel);
 
-bool bl00mbox_channel_connect_signal_to_output_mixer(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index);
-bool bl00mbox_channel_connect_signal(uint8_t channel, uint32_t bud_rx_index, uint32_t bud_rx_signal_index,
+bool bl00mbox_channel_connect_signal_to_output_mixer(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index);
+bool bl00mbox_channel_connect_signal(int32_t channel, uint32_t bud_rx_index, uint32_t bud_rx_signal_index,
                                                uint32_t bud_tx_index, uint32_t bud_tx_signal_index);
-bool bl00mbox_channel_disconnect_signal_rx(uint8_t channel, uint32_t bud_rx_index, uint32_t bud_rx_signal_index);
-bool bl00mbox_channel_disconnect_signal_tx(uint8_t channel, uint32_t bud_tx_index, uint32_t bud_tx_signal_index);
-bool bl00mbox_channel_disconnect_signal(uint8_t channel, uint32_t bud_tx_index, uint32_t bud_tx_signal_index);
-bool bl00mbox_channel_disconnect_signal_from_output_mixer(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index);
-
-bl00mbox_bud_t * bl00mbox_channel_new_bud(uint8_t channel, uint32_t id, uint32_t init_var);
-bool bl00mbox_channel_delete_bud(uint8_t channel, uint32_t bud_index);
-bool bl00mbox_channel_bud_exists(uint8_t channel, uint32_t bud_index);
-char * bl00mbox_channel_bud_get_name(uint8_t channel, uint32_t bud_index);
-char * bl00mbox_channel_bud_get_description(uint8_t channel, uint32_t bud_index);
-uint32_t bl00mbox_channel_bud_get_plugin_id(uint8_t channel, uint32_t bud_index);
-uint32_t bl00mbox_channel_bud_get_init_var(uint8_t channel, uint32_t bud_index);
-uint16_t bl00mbox_channel_bud_get_num_signals(uint8_t channel, uint32_t bud_index);
-
-char * bl00mbox_channel_bud_get_signal_name(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index);
-int8_t bl00mbox_channel_bud_get_signal_name_multiplex(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index);
-char * bl00mbox_channel_bud_get_signal_description(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index);
-char * bl00mbox_channel_bud_get_signal_unit(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index);
-bool bl00mbox_channel_bud_get_always_render(uint8_t channel, uint32_t bud_index);
-bool bl00mbox_channel_bud_set_always_render(uint8_t channel, uint32_t bud_index, bool value);
-bool bl00mbox_channel_bud_set_signal_value(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index, int16_t value);
-int16_t bl00mbox_channel_bud_get_signal_value(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index);
-uint32_t bl00mbox_channel_bud_get_signal_hints(uint8_t channel, uint32_t bud_index, uint32_t bud_signal_index);
-uint16_t bl00mbox_channel_subscriber_num(uint8_t channel, uint64_t bud_index, uint16_t signal_index);
-uint64_t bl00mbox_channel_get_bud_by_subscriber_list_pos(uint8_t channel, uint64_t bud_index,
+bool bl00mbox_channel_disconnect_signal_rx(int32_t channel, uint32_t bud_rx_index, uint32_t bud_rx_signal_index);
+bool bl00mbox_channel_disconnect_signal_tx(int32_t channel, uint32_t bud_tx_index, uint32_t bud_tx_signal_index);
+bool bl00mbox_channel_disconnect_signal(int32_t channel, uint32_t bud_tx_index, uint32_t bud_tx_signal_index);
+bool bl00mbox_channel_disconnect_signal_from_output_mixer(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index);
+
+bl00mbox_bud_t * bl00mbox_channel_new_bud(int32_t channel, uint32_t id, uint32_t init_var);
+bool bl00mbox_channel_delete_bud(int32_t channel, uint32_t bud_index);
+bool bl00mbox_channel_bud_exists(int32_t channel, uint32_t bud_index);
+char * bl00mbox_channel_bud_get_name(int32_t channel, uint32_t bud_index);
+char * bl00mbox_channel_bud_get_description(int32_t channel, uint32_t bud_index);
+uint32_t bl00mbox_channel_bud_get_plugin_id(int32_t channel, uint32_t bud_index);
+uint32_t bl00mbox_channel_bud_get_init_var(int32_t channel, uint32_t bud_index);
+uint16_t bl00mbox_channel_bud_get_num_signals(int32_t channel, uint32_t bud_index);
+
+char * bl00mbox_channel_bud_get_signal_name(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index);
+int8_t bl00mbox_channel_bud_get_signal_name_multiplex(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index);
+char * bl00mbox_channel_bud_get_signal_description(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index);
+char * bl00mbox_channel_bud_get_signal_unit(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index);
+bool bl00mbox_channel_bud_get_always_render(int32_t channel, uint32_t bud_index);
+bool bl00mbox_channel_bud_set_always_render(int32_t channel, uint32_t bud_index, bool value);
+bool bl00mbox_channel_bud_set_signal_value(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index, int16_t value);
+int16_t bl00mbox_channel_bud_get_signal_value(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index);
+uint32_t bl00mbox_channel_bud_get_signal_hints(int32_t channel, uint32_t bud_index, uint32_t bud_signal_index);
+uint16_t bl00mbox_channel_subscriber_num(int32_t channel, uint64_t bud_index, uint16_t signal_index);
+uint64_t bl00mbox_channel_get_bud_by_subscriber_list_pos(int32_t channel, uint64_t bud_index,
                 uint16_t signal_index, uint8_t pos);
-int32_t bl00mbox_channel_get_signal_by_subscriber_list_pos(uint8_t channel, uint64_t bud_index,
+int32_t bl00mbox_channel_get_signal_by_subscriber_list_pos(int32_t channel, uint64_t bud_index,
                 uint16_t signal_index, uint8_t pos);
-uint64_t bl00mbox_channel_get_source_bud(uint8_t channel, uint64_t bud_index, uint16_t signal_index);
-uint16_t bl00mbox_channel_get_source_signal(uint8_t channel, uint64_t bud_index, uint16_t signal_index);
+uint64_t bl00mbox_channel_get_source_bud(int32_t channel, uint64_t bud_index, uint16_t signal_index);
+uint16_t bl00mbox_channel_get_source_signal(int32_t channel, uint64_t bud_index, uint16_t signal_index);
 
-bool bl00mbox_channel_bud_set_table_value(uint8_t channel, uint32_t bud_index, uint32_t table_index, int16_t value);
-int16_t bl00mbox_channel_bud_get_table_value(uint8_t channel, uint32_t bud_index, uint32_t table_index);
-uint32_t bl00mbox_channel_bud_get_table_len(uint8_t channel, uint32_t bud_index);
-int16_t * bl00mbox_channel_bud_get_table_pointer(uint8_t channel, uint32_t bud_index);
+bool bl00mbox_channel_bud_set_table_value(int32_t channel, uint32_t bud_index, uint32_t table_index, int16_t value);
+int16_t bl00mbox_channel_bud_get_table_value(int32_t channel, uint32_t bud_index, uint32_t table_index);
+uint32_t bl00mbox_channel_bud_get_table_len(int32_t channel, uint32_t bud_index);
+int16_t * bl00mbox_channel_bud_get_table_pointer(int32_t channel, uint32_t bud_index);
diff --git a/components/bl00mbox/micropython/bl00mbox/_plugins.py b/components/bl00mbox/micropython/bl00mbox/_plugins.py
index 2d3bbffb4b..0c0d6f3cd4 100644
--- a/components/bl00mbox/micropython/bl00mbox/_plugins.py
+++ b/components/bl00mbox/micropython/bl00mbox/_plugins.py
@@ -688,14 +688,15 @@ class _Sequencer(_Plugin):
         num_tracks = min(len(beat["tracks"]), self.num_tracks)
         [self.load_track_pattern(beat["tracks"][i], i) for i in range(num_tracks)]
 
+
 @_plugin_set_subclass(38)
 class _TriggerMerge(_Plugin):
     @property
     def block_stop(self):
         table = self.table_int16_array
         return bool(table[0])
+
     @block_stop.setter
     def block_stop(self, val):
         table = self.table_int16_array
         table[0] = 1 if val else 0
-
diff --git a/components/bl00mbox/micropython/bl00mbox/_user.py b/components/bl00mbox/micropython/bl00mbox/_user.py
index 698be81259..56ca658ebd 100644
--- a/components/bl00mbox/micropython/bl00mbox/_user.py
+++ b/components/bl00mbox/micropython/bl00mbox/_user.py
@@ -307,11 +307,11 @@ class SignalOutput(Signal):
             if (s >= 0) and (b > 0):
                 cons += [
                     _makeSignal(
-                        bl00mbox._plugins._make_new_plugin(Channel(chan), 0, b), s
+                        bl00mbox._plugins._make_new_plugin(SysChannel(chan), 0, b), s
                     )
                 ]
             elif s == -1:
-                cons += [ChannelMixer(Channel(chan))]
+                cons += [ChannelMixer(SysChannel(chan))]
         return cons
 
 
@@ -358,7 +358,9 @@ class SignalInput(Signal):
         s = sys_bl00mbox.channel_get_source_signal(chan, bud, sig)
         if (s >= 0) and (b > 0):
             cons += [
-                _makeSignal(bl00mbox._plugins._make_new_plugin(Channel(chan), 0, b), s)
+                _makeSignal(
+                    bl00mbox._plugins._make_new_plugin(SysChannel(chan), 0, b), s
+                )
             ]
         return cons
 
@@ -572,17 +574,14 @@ class Channel:
         elif type(name) == str:
             self._channel_num = sys_bl00mbox.channel_get_free_index()
             self.name = name
-        elif type(name) == int:
-            num = int(name)
-            if num < sys_bl00mbox.NUM_CHANNELS and num >= 0:
-                self._channel_num = num
-            else:
-                self._channel_num = sys_bl00mbox.NUM_CHANNELS - 1  # garbage channel
         else:
-            self._channel_num = sys_bl00mbox.NUM_CHANNELS - 1  # garbage channel
+            raise TypeError("must be None or str")
+        if self._channel_num < 0:
+            raise Bl00mboxError("Channel could not be initialized")
         global _channel_init_callback
         if _channel_init_callback is not None:
             _channel_init_callback(self)
+        self._tripwire = sys_bl00mbox.channel_wire(self._channel_num)
 
     def __repr__(self):
         ret = "[channel " + str(self.channel_num)
@@ -596,8 +595,9 @@ class Channel:
         ret += "\n  gain: " + str(self.gain_dB) + "dB"
         b = self.num_plugins
         ret += "\n  plugins: " + str(b)
-        if len(self.plugins) != b:
-            ret += " (desync" + str(len(self.plugins)) + ")"
+        # actual_len = len(list(self.plugins))
+        # if actual_len != b:
+        #     ret += " (desync" + actual_len + ")"
         ret += "\n  " + "\n  ".join(repr(self.mixer).split("\n"))
         return ret
 
@@ -610,10 +610,6 @@ class Channel:
 
     @property
     def name(self):
-        if self._channel_num == 0:
-            return "system"
-        if self._channel_num == 31:
-            return "garbage"
         name = sys_bl00mbox.channel_get_name(self.channel_num)
         if name is None:
             return ""
@@ -641,19 +637,6 @@ class Channel:
         else:
             raise TypeError("not a plugin")
 
-    @staticmethod
-    def print_overview():
-        ret = []
-        for i in range(sys_bl00mbox.NUM_CHANNELS):
-            c = Channel(i)
-            if c.free:
-                continue
-            ret += [repr(c)]
-        if len(ret) != 0:
-            print("\n".join(ret))
-        else:
-            print("no active channels")
-
     def print_plugins(self):
         for plugin in self.plugins:
             print(repr(plugin))
@@ -670,11 +653,9 @@ class Channel:
 
     @property
     def plugins(self):
-        ret = []
         for i in range(sys_bl00mbox.channel_buds_num(self.channel_num)):
             b = sys_bl00mbox.channel_get_bud_by_list_pos(self.channel_num, i)
-            ret += [bl00mbox._plugins._make_new_plugin(self, 0, b)]
-        return ret
+            yield [bl00mbox._plugins._make_new_plugin(self, 0, b)]
 
     @property
     def channel_num(self):
@@ -763,15 +744,19 @@ class Channel:
         if val:
             sys_bl00mbox.channel_set_foreground(self.channel_num)
         elif sys_bl00mbox.channel_get_foreground() == self.channel_num:
-            sys_bl00mbox.channel_set_foreground(0)
+            sys_bl00mbox.channel_set_foreground(-1)
 
 
 class SysChannel(Channel):
-    def __init__(self, index=0):
-        if index < sys_bl00mbox.NUM_CHANNELS and index >= 0:
-            self._channel_num = index
+    def __init__(self, identifier):
+        if isinstance(identifier, int):
+            if not sys_bl00mbox.channel_get_exists(identifier):
+                raise Bl00mboxError(f"channel index {identifier} not found")
+            self._channel_num = identifier
+        elif isinstance(identifier, Channel):
+            self._channel_num = identifier.channel_num
         else:
-            raise Bl00mboxError(f"channel index {index} not found")
+            raise Bl00mboxError("improper identifier")
 
     @property
     def sys_gain_dB(self):
@@ -808,3 +793,29 @@ class SysChannel(Channel):
                 return 10 * math.log(ret, 10) + _rms_base
         else:
             return None
+
+
+def all_channels():
+    index = 0
+    while True:
+        ret = sys_bl00mbox.channel_get_index_positional_all_chans(index)
+        if ret < 0:
+            break
+        yield SysChannel(ret)
+        index += 1
+
+
+def active_channels():
+    index = 0
+    fore = sys_bl00mbox.channel_get_foreground()
+    if fore >= 0:
+        yield SysChannel(fore)
+    while True:
+        ret = sys_bl00mbox.channel_get_index_positional_background_mute_override_chans(
+            index
+        )
+        if ret < 0:
+            break
+        if ret != fore:
+            yield SysChannel(ret)
+        index += 1
diff --git a/components/bl00mbox/micropython/mp_sys_bl00mbox.c b/components/bl00mbox/micropython/mp_sys_bl00mbox.c
index 7324036ef0..e5e85360ac 100644
--- a/components/bl00mbox/micropython/mp_sys_bl00mbox.c
+++ b/components/bl00mbox/micropython/mp_sys_bl00mbox.c
@@ -5,10 +5,15 @@
 #include "py/runtime.h"
 
 #include "bl00mbox.h"
+#include "bl00mbox_os.h"
 #include "bl00mbox_plugin_registry.h"
 #include "bl00mbox_user.h"
 #include "radspa.h"
 
+#if !MICROPY_ENABLE_FINALISER
+#error "bl00mbox needs finaliser"
+#endif
+
 // ========================
 //    PLUGIN OPERATIONS
 // ========================
@@ -55,6 +60,66 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_plugin_registry_num_plugins_obj,
 //    CHANNEL OPERATIONS
 // ========================
 
+STATIC const mp_obj_type_t channel_wire_type;
+
+typedef struct _channel_wire_obj_t {
+    mp_obj_base_t base;
+    int32_t index;
+} channel_wire_obj_t;
+
+STATIC mp_obj_t channel_wire_make_new(const mp_obj_type_t *type, size_t n_args,
+                                      size_t n_kw, const mp_obj_t *args) {
+    mp_arg_check_num(n_args, n_kw, 1, 1, true);
+    channel_wire_obj_t *self = m_new_obj_with_finaliser(channel_wire_obj_t);
+    self->base.type = &channel_wire_type;
+    self->index = mp_obj_get_int(args[0]);
+    bl00mbox_log_info("making channel %d", (int)self->index);
+    return MP_OBJ_FROM_PTR(self);
+}
+
+mp_obj_t mp_channel_wire_trip(mp_obj_t self_in) {
+    channel_wire_obj_t *self = MP_OBJ_TO_PTR(self_in);
+    bl00mbox_log_info("freeing channel %d", (int)self->index);
+    bl00mbox_channel_clear(self->index);
+    bl00mbox_channel_set_free(self->index, true);
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_channel_wire_trip_obj, mp_channel_wire_trip);
+
+STATIC const mp_rom_map_elem_t channel_wire_locals_dict_table[] = {
+    { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_channel_wire_trip_obj) },
+};
+STATIC MP_DEFINE_CONST_DICT(channel_wire_locals_dict,
+                            channel_wire_locals_dict_table);
+
+STATIC MP_DEFINE_CONST_OBJ_TYPE(channel_wire_type, MP_QSTR_channel_wire,
+                                MP_TYPE_FLAG_NONE, make_new,
+                                channel_wire_make_new, locals_dict,
+                                &channel_wire_locals_dict);
+
+static mp_obj_t mp_channel_get_exists(mp_obj_t pos) {
+    return mp_obj_new_bool(bl00mbox_get_channel_exists(mp_obj_get_int(pos)));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_channel_get_exists_obj,
+                                 mp_channel_get_exists);
+
+static mp_obj_t mp_channel_get_index_positional_all_chans(mp_obj_t pos) {
+    return mp_obj_new_int(
+        bl00mbox_get_channel_index_positional_all_chans(mp_obj_get_int(pos)));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_channel_get_index_positional_all_chans_obj,
+                                 mp_channel_get_index_positional_all_chans);
+
+static mp_obj_t mp_channel_get_index_positional_background_mute_override_chans(
+    mp_obj_t pos) {
+    return mp_obj_new_int(
+        bl00mbox_get_channel_index_positional_background_mute_override_chans(
+            mp_obj_get_int(pos)));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(
+    mp_channel_get_index_positional_background_mute_override_chans_obj,
+    mp_channel_get_index_positional_background_mute_override_chans);
+
 STATIC mp_obj_t mp_channel_clear(mp_obj_t index) {
     return mp_obj_new_bool(bl00mbox_channel_clear(mp_obj_get_int(index)));
 }
@@ -533,7 +598,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(
     mp_channel_disconnect_signal_from_output_mixer_obj,
     mp_channel_disconnect_signal_from_output_mixer);
 
-STATIC const mp_map_elem_t bl00mbox_globals_table[] = {
+STATIC const mp_rom_map_elem_t bl00mbox_globals_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR___name__),
       MP_OBJ_NEW_QSTR(MP_QSTR_sys_bl00mbox) },
 
@@ -548,6 +613,15 @@ STATIC const mp_map_elem_t bl00mbox_globals_table[] = {
       MP_ROM_PTR(&mp_plugin_index_get_description_obj) },
 
     // CHANNEL OPERATIONS
+    { MP_OBJ_NEW_QSTR(MP_QSTR_channel_wire), (mp_obj_t)&channel_wire_type },
+    { MP_ROM_QSTR(MP_QSTR_channel_get_exists),
+      MP_ROM_PTR(&mp_channel_get_exists_obj) },
+    { MP_ROM_QSTR(MP_QSTR_channel_get_index_positional_all_chans),
+      MP_ROM_PTR(&mp_channel_get_index_positional_all_chans_obj) },
+    { MP_ROM_QSTR(
+          MP_QSTR_channel_get_index_positional_background_mute_override_chans),
+      MP_ROM_PTR(
+          &mp_channel_get_index_positional_background_mute_override_chans_obj) },
     { MP_ROM_QSTR(MP_QSTR_channel_get_free),
       MP_ROM_PTR(&mp_channel_get_free_obj) },
     { MP_ROM_QSTR(MP_QSTR_channel_set_free),
@@ -668,14 +742,12 @@ STATIC const mp_map_elem_t bl00mbox_globals_table[] = {
       MP_ROM_PTR(&mp_channel_disconnect_signal_from_output_mixer_obj) },
 
     // CONSTANTS
-    { MP_ROM_QSTR(MP_QSTR_NUM_CHANNELS), MP_ROM_INT(BL00MBOX_CHANNELS) },
     { MP_ROM_QSTR(MP_QSTR_RADSPA_SIGNAL_HINT_SCT),
       MP_ROM_INT(RADSPA_SIGNAL_HINT_SCT) },
     { MP_ROM_QSTR(MP_QSTR_RADSPA_SIGNAL_HINT_GAIN),
       MP_ROM_INT(RADSPA_SIGNAL_HINT_GAIN) },
     { MP_ROM_QSTR(MP_QSTR_RADSPA_SIGNAL_HINT_TRIGGER),
       MP_ROM_INT(RADSPA_SIGNAL_HINT_TRIGGER) },
-    { MP_ROM_QSTR(MP_QSTR_BL00MBOX_CHANNELS), MP_ROM_INT(BL00MBOX_CHANNELS) },
 };
 
 STATIC MP_DEFINE_CONST_DICT(mp_module_bl00mbox_globals, bl00mbox_globals_table);
diff --git a/components/bl00mbox/radspa/standard_plugin_lib/range_shifter.c b/components/bl00mbox/radspa/standard_plugin_lib/range_shifter.c
index ad2c0202c7..a0dd0e6b57 100644
--- a/components/bl00mbox/radspa/standard_plugin_lib/range_shifter.c
+++ b/components/bl00mbox/radspa/standard_plugin_lib/range_shifter.c
@@ -63,7 +63,7 @@ void range_shifter_run(radspa_t * range_shifter, uint16_t num_samples, uint32_t
     }
     bool input_const = true;
     if(speed > -10922){
-        bool input_const = input != RADSPA_SIGNAL_NONCONST;
+        input_const = input != RADSPA_SIGNAL_NONCONST;
     }
 
     int32_t output_span;
diff --git a/components/bl00mbox/radspa/standard_plugin_lib/slew_rate_limiter.c b/components/bl00mbox/radspa/standard_plugin_lib/slew_rate_limiter.c
index 6341e42850..e2c8ec7a4b 100644
--- a/components/bl00mbox/radspa/standard_plugin_lib/slew_rate_limiter.c
+++ b/components/bl00mbox/radspa/standard_plugin_lib/slew_rate_limiter.c
@@ -1,7 +1,5 @@
 #include "slew_rate_limiter.h"
 
-static inline int16_t waveshaper(int16_t saw, int16_t shape);
-
 radspa_descriptor_t slew_rate_limiter_desc = {
     .name = "slew_rate_limiter",
     .id = 23,
-- 
GitLab