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