diff --git a/components/bl00mbox/CMakeLists.txt b/components/bl00mbox/CMakeLists.txt
index 752296ebd7f2eb8bd092ecf20f1133e895a6618b..e70c7e06300ac88a3ebce8235bb0fe049ddfca91 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_preamp.c
         bl00mbox_plugin_registry.c
         bl00mbox_radspa_requirements.c
         radspa/standard_plugin_lib/osc.c
diff --git a/components/bl00mbox/bl00mbox_audio.c b/components/bl00mbox/bl00mbox_audio.c
index e54c15d73ba13deafaf5c5e844ba486688c81764..734e6df4f1eaf1befeeda879b165e994952a1977 100644
--- a/components/bl00mbox/bl00mbox_audio.c
+++ b/components/bl00mbox/bl00mbox_audio.c
@@ -128,16 +128,11 @@ void bl00mbox_channel_set_name(uint8_t channel_index, char * new_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;
+        memset(chan, 0, sizeof(*chan));
+        chan->gain = BL00MBOX_DEFAULT_CHANNEL_VOLUME;
         chan->is_active = true;
         chan->is_free = true;
-        chan->always_render = NULL;
-        chan->name = NULL;
-        chan->dc = 0;
+        bl00mbox_preamp_init(&chan->preamp, &chan->gain);
     }
     is_initialized = true;
 }
@@ -157,44 +152,43 @@ void bl00mbox_channel_disable(uint8_t chan){
 void bl00mbox_channel_set_compute_mean_square(uint8_t chan, bool compute){
     if(chan >= (BL00MBOX_CHANNELS)) return;
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
-    ch->compute_mean_square = compute;
-    if(!compute) ch->mean_square = 0;
+    ch->preamp.compute_mean_square = compute;
 }
 
 bool bl00mbox_channel_get_compute_mean_square(uint8_t chan){
     if(chan >= (BL00MBOX_CHANNELS)) return 0;
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
-    return ch->compute_mean_square;
+    return ch->preamp.compute_mean_square;
 }
 
 uint32_t bl00mbox_channel_get_mean_square(uint8_t chan){
     if(chan >= (BL00MBOX_CHANNELS)) return 0;
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
-    return ch->mean_square;
+    return ch->preamp.mean_square;
 }
 
-void bl00mbox_channel_set_sys_gain(uint8_t chan, int16_t volume){
+void bl00mbox_channel_set_sys_gain(uint8_t chan, int32_t gain){
     if(chan >= (BL00MBOX_CHANNELS)) return;
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
-    ch->sys_gain = volume;
+    ch->preamp.gain = gain;
 }
 
-int16_t bl00mbox_channel_get_sys_gain(uint8_t chan){
+int32_t bl00mbox_channel_get_sys_gain(uint8_t chan){
     if(chan >= (BL00MBOX_CHANNELS)) return 0;
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
-    return ch->sys_gain;
+    return ch->preamp.gain;
 }
 
-void bl00mbox_channel_set_volume(uint8_t chan, uint16_t volume){
+void bl00mbox_channel_set_volume(uint8_t chan, int32_t gain){
     if(chan >= (BL00MBOX_CHANNELS)) return;
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
-    ch->volume = volume < 32767 ? volume : 32767;
+    ch->gain = gain;
 }
 
-int16_t bl00mbox_channel_get_volume(uint8_t chan){
+int32_t bl00mbox_channel_get_volume(uint8_t chan){
     if(chan >= (BL00MBOX_CHANNELS)) return 0;
     bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
-    return ch->volume;
+    return ch->gain;
 }
 
 void bl00mbox_audio_bud_render(bl00mbox_bud_t * bud){
@@ -208,16 +202,16 @@ 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){
+static bool bl00mbox_audio_channel_render(bl00mbox_channel_t * chan, int32_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;
 
-    int32_t vol = radspa_mult_shift(chan->volume, chan->sys_gain);
+    bool will_render = bl00mbox_preamp_will_render(&chan->preamp);
 
     // early exit when no sources or muted:
-    if((root == NULL) || (!chan->is_active) || (!vol)){
+    if((root == NULL) || (!chan->is_active) || (!will_render)){
         return false;
     }
 
@@ -258,37 +252,7 @@ static bool bl00mbox_audio_channel_render(bl00mbox_channel_t * chan, int16_t * o
         }
         root = root->next;
     }
-
-    for(uint16_t i = 0; i < full_buffer_len; i++){
-        // flip around for rounding towards zero/mulsh boost
-        bool invert = chan->dc < 0;
-        if(invert) chan->dc = -chan->dc;
-        chan->dc = ((uint64_t) chan->dc * (((1<<12) - 1)<<20)) >> 32;
-        if(invert) chan->dc = -chan->dc;
-
-        chan->dc += acc[i];
-
-        acc[i] -= (chan->dc >> 12);
-    }
-    if(adding){
-        for(uint16_t i = 0; i < full_buffer_len; i++){
-            out[i] = radspa_add_sat(radspa_gain(acc[i], vol), out[i]);
-        }
-    } else {
-        for(uint16_t i = 0; i < full_buffer_len; i++){
-            out[i] = radspa_gain(acc[i], vol);
-        }
-    }
-    if(chan->compute_mean_square){
-        for(uint16_t i = 0; i < full_buffer_len; i++){
-            int32_t sq = acc[i];
-            sq = (sq * sq) - chan->mean_square;
-            // always round down with negative sq so that decay always works.
-            // bitshift instead of div does that for us nicely.
-            // cannot underflow as ((-a) >> 11) can never be less than -a.
-            chan->mean_square += sq >> 11;
-        }
-    }
+    bl00mbox_preamp_render(&chan->preamp, acc, out, full_buffer_len, adding);
     return true;
 }
 
@@ -303,7 +267,7 @@ bool _bl00mbox_audio_render(int16_t * rx, int16_t * tx, uint16_t len){
     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];
+    int32_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;
@@ -323,8 +287,9 @@ bool _bl00mbox_audio_render(int16_t * rx, int16_t * tx, uint16_t len){
     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];
+        int16_t clipped = (acc[i] > 32767) ? 32767 : ((acc[i] < -32768) ? -32768 : acc[i]);
+        tx[2*i] = clipped;
+        tx[2*i+1] = clipped;
     }
     return true;
 }
diff --git a/components/bl00mbox/bl00mbox_preamp.c b/components/bl00mbox/bl00mbox_preamp.c
new file mode 100644
index 0000000000000000000000000000000000000000..208f7ad52843b0319c5883500f13af47ae768fe8
--- /dev/null
+++ b/components/bl00mbox/bl00mbox_preamp.c
@@ -0,0 +1,77 @@
+#include "bl00mbox_preamp.h"
+
+void bl00mbox_preamp_init(bl00mbox_preamp_t * preamp, int32_t * ext_gain){
+    memset(preamp, 0, sizeof(* preamp));
+    preamp->gain = 32768;
+    preamp->ext_gain = ext_gain;
+}
+
+bool bl00mbox_preamp_will_render(bl00mbox_preamp_t * preamp){
+    return(!preamp->mute && preamp->gain && (!preamp->ext_gain || (*preamp->ext_gain)));
+}
+
+static inline int32_t apply_gain(int32_t input, int32_t gain){
+     // unity gain at 1<<22, max gain <60dB
+     return (((int64_t) (input << 10)) * gain) >> 32;
+}
+
+bool bl00mbox_preamp_render(bl00mbox_preamp_t * preamp, int32_t * input, int32_t * output,
+                            uint16_t len, bool adding){
+    if(!bl00mbox_preamp_will_render(preamp)) return false;
+    int32_t gain = preamp->gain;
+    if(preamp->ext_gain){
+        gain *=* preamp->ext_gain;
+        gain >>= 8;
+    } else {
+        gain <<= 7;
+    }
+
+    if(preamp->remove_dc){
+        for(uint16_t i = 0; i < len; i++){
+            // flip around for rounding towards zero/mulsh boost
+            bool invert = preamp->dc < 0;
+            if(invert) preamp->dc = -preamp->dc;
+            preamp->dc = ((uint64_t) preamp->dc * (((1<<12) - 1)<<20)) >> 32;
+            if(invert) preamp->dc = -preamp->dc;
+
+            preamp->dc += input[i];
+
+            input[i] -= (preamp->dc >> 12);
+        }
+    }
+
+    if(preamp->compute_mean_square){
+        for(uint16_t i = 0; i < len; i++){
+            int32_t sq = input[i];
+            sq = (sq * sq) - preamp->mean_square;
+            // always round down with negative sq so that decay always works.
+            // bitshift instead of div does that for us nicely.
+            // cannot underflow as ((-a) >> 11) can never be less than -a.
+            preamp->mean_square += sq >> 11;
+        }
+    } else {
+        preamp->mean_square = 0;
+    }
+    
+    if(gain != 1L<<22){
+        if(adding){
+            for(uint16_t i = 0; i < len; i++){
+                output[i] += apply_gain(input[i], gain);
+            }
+        } else {
+            for(uint16_t i = 0; i < len; i++){
+                output[i] = apply_gain(input[i], gain);
+            }
+        }
+    } else {
+        if(adding){
+            for(uint16_t i = 0; i < len; i++){
+                output[i] += input[i];
+            }
+        } else {
+            memcpy(output, input, len*sizeof(int32_t));
+        }
+    }
+    return true;
+}
+
diff --git a/components/bl00mbox/include/bl00mbox_audio.h b/components/bl00mbox/include/bl00mbox_audio.h
index 18cd250e1fb4aa7fda9ec37f90fc4f92ba1dc98e..dba7161471b3d9e1b022625400e8142d65843432 100644
--- a/components/bl00mbox/include/bl00mbox_audio.h
+++ b/components/bl00mbox/include/bl00mbox_audio.h
@@ -14,6 +14,7 @@
 #include <string.h>
 #include "radspa.h"
 #include "radspa_helpers.h"
+#include "bl00mbox_preamp.h"
 
 struct _bl00mbox_bud_t;
 struct _bl00mbox_connection_source_t;
@@ -64,12 +65,10 @@ typedef struct _bl00mbox_channel_root_t{
 typedef struct{
     bool is_active; // rendering can be skipped if false
     bool is_free;
-    bool compute_mean_square;
-    uint32_t mean_square;
     char * name;
-    int32_t volume;
-    int32_t sys_gain;
-    int32_t dc;
+    int32_t gain;
+    bl00mbox_preamp_t preamp;
+
     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
@@ -82,10 +81,10 @@ 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_volume(uint8_t chan, int32_t volume);
+int32_t bl00mbox_channel_get_volume(uint8_t chan);
+void bl00mbox_channel_set_sys_gain(uint8_t chan, int32_t volume);
+int32_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);
diff --git a/components/bl00mbox/include/bl00mbox_preamp.h b/components/bl00mbox/include/bl00mbox_preamp.h
new file mode 100644
index 0000000000000000000000000000000000000000..6a7a65d5d5fd6971d04172b6fa434e0cfeab1932
--- /dev/null
+++ b/components/bl00mbox/include/bl00mbox_preamp.h
@@ -0,0 +1,25 @@
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+typedef struct {
+    bool mute;
+    // unity gain at 1<<15
+    int32_t gain;
+    // if NULL this is ignored and only gain is applied, else unity at 1<<15
+    int32_t * ext_gain;
+
+    // whether or not to remove dc from the signal and its data storage
+    bool remove_dc;
+    int32_t dc;
+    
+    // whether or not to compute rms of the signal and its data storage
+    bool compute_mean_square;
+    uint32_t mean_square;
+} bl00mbox_preamp_t;
+
+void bl00mbox_preamp_init(bl00mbox_preamp_t * preamp, int32_t * ext_gain);
+bool bl00mbox_preamp_will_render(bl00mbox_preamp_t * preamp);
+bool bl00mbox_preamp_render(bl00mbox_preamp_t * preamp, int32_t * input, int32_t * output,
+                            uint16_t len, bool adding);
diff --git a/components/bl00mbox/micropython/bl00mbox/_user.py b/components/bl00mbox/micropython/bl00mbox/_user.py
index 698be81259fabacbcabe7b8c271c31e3daa3b3f0..7be3f588882a91a010dfff285713c61531f1ceac 100644
--- a/components/bl00mbox/micropython/bl00mbox/_user.py
+++ b/components/bl00mbox/micropython/bl00mbox/_user.py
@@ -707,7 +707,6 @@ class Channel:
             value = 0
         else:
             value = int(32768 * (10 ** (value / 20)))
-            value = min(32767, max(0, value))
         sys_bl00mbox.channel_set_volume(self.channel_num, value)
 
     @property
@@ -780,7 +779,7 @@ class SysChannel(Channel):
             return -math.inf
         else:
             try:
-                return 20 * math.log(abs(ret) / 4096, 10)
+                return 20 * math.log(abs(ret) / 32768, 10)
             except:
                 return -math.inf
 
@@ -789,8 +788,7 @@ class SysChannel(Channel):
         if value == -math.inf:
             value = 0
         else:
-            value = int(4096 * (10 ** (value / 20)))
-            value = min(32767, max(0, value))
+            value = int(32768 * (10 ** (value / 20)))
         sys_bl00mbox.channel_set_sys_gain(self.channel_num, value)
 
     @property
@@ -800,8 +798,8 @@ class SysChannel(Channel):
             if ret == 0:
                 return -math.inf
             else:
-                mult = abs(sys_bl00mbox.channel_get_volume(self.channel_num)) / 32768
-                mult *= abs(sys_bl00mbox.channel_get_sys_gain(self.channel_num)) / 4096
+                mult = sys_bl00mbox.channel_get_volume(self.channel_num) / 32768
+                mult *= sys_bl00mbox.channel_get_sys_gain(self.channel_num) / 32768
                 if mult == 0:
                     return -math.inf
                 ret *= mult * mult