Skip to content
Snippets Groups Projects
Commit 940d38f8 authored by moon2's avatar moon2 :speech_balloon:
Browse files

bl00mbox callbacks/weak references/backend rewrite

parent c3388838
No related branches found
No related tags found
No related merge requests found
Pipeline #13120 passed
Showing
with 1648 additions and 2267 deletions
......@@ -8,6 +8,7 @@ idf_component_register(
bl00mbox_os.c
bl00mbox_plugin_registry.c
bl00mbox_radspa_requirements.c
bl00mbox_containers.c
radspa/standard_plugin_lib/osc.c
radspa/standard_plugin_lib/osc_fm.c
radspa/standard_plugin_lib/env_adsr.c
......
//SPDX-License-Identifier: CC0-1.0
#include "bl00mbox_audio.h"
#include "bl00mbox_ll.h"
#include "bl00mbox_user.h"
#include "bl00mbox_os.h"
#include <assert.h>
static bool is_initialized = false;
static uint16_t full_buffer_len;
static uint32_t render_pass_id;
int16_t * bl00mbox_line_in_interlaced = NULL;
static int32_t free_chan_index = 0; // increments
static bl00mbox_ll_t * all_chans = NULL;
static bl00mbox_ll_t * background_mute_override_chans = NULL;
// grab user_lock for r/w
static bl00mbox_set_t * all_chans = NULL;
// grab user_lock for r/w
static bl00mbox_set_t * background_mute_override_chans = NULL;
// grab user_lock for r/w
static bl00mbox_channel_t * foreground_chan = NULL;
static bl00mbox_lock_t render_lock = NULL;
static bl00mbox_lock_t user_lock = NULL;
static bl00mbox_channel_t * cached_chan = NULL;
// grab both user_lock and active_chans_lock for writing.
// for user-side reading grab user_lock.
// render task reads with active_chans_lock.
static bl00mbox_channel_array_t * active_chans = 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;
}
static bl00mbox_lock_t active_chans_lock = NULL;
static bl00mbox_channel_t * cached_chan = NULL;
bool bl00mbox_get_channel_exists(int32_t channel_index){
return (bool) bl00mbox_get_channel(channel_index);
static void update_active_chans(){
// must be called after changing foreground_chan or background_mute_override_chans
// while _still_ holding user_lock but not active_chans_lock
size_t num_chans = background_mute_override_chans->len;
if(foreground_chan) num_chans++;
bl00mbox_channel_array_t * new_active_chans = malloc(sizeof(bl00mbox_channel_array_t)
+ num_chans * sizeof(bl00mbox_channel_t *));
if(new_active_chans){
size_t index = 0;
if(foreground_chan){
new_active_chans->channels[index] = foreground_chan;
index++;
}
bl00mbox_set_iter_t iter;
bl00mbox_set_iter_start(&iter, background_mute_override_chans);
bl00mbox_channel_t * chan;
while((chan = bl00mbox_set_iter_next(&iter))){
new_active_chans->channels[index] = chan;
index++;
}
new_active_chans->len = num_chans;
} else {
bl00mbox_log_error("out of memory");
}
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;
#ifdef BL00MBOX_DEBUG
if(new_active_chans) bl00mbox_log_error("active chans: %d", (int) new_active_chans->len);
#endif
bl00mbox_channel_array_t * previous_active_chans;
bl00mbox_take_lock(&active_chans_lock);
previous_active_chans = active_chans;
active_chans = new_active_chans;
bl00mbox_give_lock(&active_chans_lock);
free(previous_active_chans);
}
bl00mbox_channel_array_t * bl00mbox_collect_channels(bool active){
bl00mbox_channel_array_t * ret = NULL;
bl00mbox_take_lock(&user_lock);
if(active){
if(active_chans){
size_t ret_size = sizeof(bl00mbox_channel_array_t);
ret_size += active_chans->len * sizeof(bl00mbox_channel_t *);
ret = malloc(ret_size);
if(ret) memcpy(ret, active_chans, ret_size);
} else {
ret = malloc(sizeof(bl00mbox_channel_array_t));
if(ret) ret->len = 0;
}
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;
} else {
ret = bl00mbox_set_to_array(all_chans);
}
bl00mbox_channel_t * chan = chll->content;
return chan->index;
bl00mbox_give_lock(&user_lock);
if(!ret) bl00mbox_log_error("out of memory");
return ret;
}
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;
void bl00mbox_channel_set_background_mute_override(bl00mbox_channel_t * chan, bool enable){
bl00mbox_take_lock(&user_lock);
chan->background_mute_override = enable;
bool update;
if(enable){
bl00mbox_ll_prepend(&background_mute_override_chans, ch, &render_lock);
update = bl00mbox_set_add(background_mute_override_chans, chan);
} else {
bl00mbox_ll_pop(&background_mute_override_chans, ch, &render_lock);
update = bl00mbox_set_remove(background_mute_override_chans, chan);
}
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);
if(update) update_active_chans();
bl00mbox_give_lock(&user_lock);
}
int32_t bl00mbox_channel_get_foreground_index(){
if(foreground_chan) return foreground_chan->index;
return -1;
bool bl00mbox_channel_get_foreground(bl00mbox_channel_t * chan){
return foreground_chan == chan;
}
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);
}
void bl00mbox_channel_set_foreground(bl00mbox_channel_t * chan, bool enable){
if(bl00mbox_channel_get_foreground(chan) == enable) return;
bl00mbox_take_lock(&user_lock);
foreground_chan = enable ? chan : NULL;
update_active_chans();
bl00mbox_give_lock(&user_lock);
}
void bl00mbox_channel_event(int32_t index){
void bl00mbox_channel_event(bl00mbox_channel_t * chan){
#ifdef BL00MBOX_AUTO_FOREGROUNDING
bl00mbox_channel_set_foreground_index(index);
bl00mbox_channel_set_foreground(chan, true);
#endif
}
static bl00mbox_channel_t * _bl00mbox_channel_create(){
if(free_chan_index < 0) return NULL;
bl00mbox_channel_t * bl00mbox_channel_create(){
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;
}
if(chan){
chan->volume = BL00MBOX_DEFAULT_CHANNEL_VOLUME;
chan->sys_gain = 4096;
chan->is_active = true;
chan->index = free_chan_index;
free_chan_index += 1;
if(!bl00mbox_create_lock(&chan->render_lock)){
free(chan);
chan = NULL;
} else {
bl00mbox_create_lock(&chan->render_lock);
bl00mbox_take_lock(&render_lock);
bl00mbox_take_lock(&user_lock);
if(!bl00mbox_set_add_skip_unique_check(all_chans, chan)){
free(chan);
chan = NULL;
} else {
foreground_chan = chan;
bl00mbox_give_lock(&render_lock);
return chan;
update_active_chans();
}
bl00mbox_give_lock(&user_lock);
}
}
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_delete(bl00mbox_channel_t * chan){
if(!chan) return;
void bl00mbox_channel_clear(bl00mbox_channel_t * chan){
bl00mbox_set_iter_t iter;
bl00mbox_set_iter_start(&iter, &chan->plugins);
bl00mbox_plugin_t * plugin;
while((plugin = bl00mbox_set_iter_next(&iter))){
bl00mbox_plugin_destroy(plugin);
}
chan->plugin_id = 0;
}
void bl00mbox_channel_destroy(bl00mbox_channel_t * chan){
bl00mbox_channel_clear(chan);
bl00mbox_take_lock(&user_lock);
if(cached_chan == chan) cached_chan = NULL;
bool is_active = false;
if(foreground_chan == chan){
bl00mbox_take_lock(&render_lock);
foreground_chan = NULL;
bl00mbox_give_lock(&render_lock);
is_active = true;
}
if(bl00mbox_set_remove(background_mute_override_chans, chan)){
is_active = true;
}
bl00mbox_ll_pop(&background_mute_override_chans, chan, &render_lock);
bl00mbox_ll_pop(&all_chans, chan, NULL);
bl00mbox_channel_clear(chan->index);
if(is_active) update_active_chans();
bl00mbox_set_remove(all_chans, chan);
bl00mbox_give_lock(&user_lock);
#ifdef BL00MBOX_DEBUG
if(chan->connections.len) bl00mbox_log_error("connections nonempty");
if(chan->roots.len) bl00mbox_log_error("roots nonempty");
if(chan->always_render.len) bl00mbox_log_error("always render nonempty");
if(chan->plugins.len) bl00mbox_log_error("plugins nonempty");
bl00mbox_wait();
#endif
// 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.
......@@ -146,165 +183,74 @@ void bl00mbox_channel_delete(bl00mbox_channel_t * chan){
// 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->render_plugins);
free(chan->render_buffers);
free(chan->name);
free(chan);
}
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(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;
}
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(int32_t channel_index){
bl00mbox_channel_t * chan = bl00mbox_get_channel(channel_index);
if(!chan) return NULL;
return chan->name;
}
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(int32_t chan){
bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
if(!ch) return;
ch->is_active = true;
}
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(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(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(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(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(int32_t chan){
bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
if(!ch) return 0;
return ch->sys_gain;
}
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(int32_t chan){
bl00mbox_channel_t * ch = bl00mbox_get_channel(chan);
if(!ch) return 0;
return ch->volume;
}
void bl00mbox_audio_bud_render(bl00mbox_bud_t * bud){
if(bud->render_pass_id == render_pass_id) return;
void bl00mbox_audio_plugin_render(bl00mbox_plugin_t * plugin){
if(plugin->render_pass_id == render_pass_id) return;
#ifdef BL00MBOX_LOOPS_ENABLE
if(bud->is_being_rendered) return;
if(plugin->is_being_rendered) return;
#endif
bud->is_being_rendered = true;
bud->plugin->render(bud->plugin, full_buffer_len, render_pass_id);
bud->render_pass_id = render_pass_id;
bud->is_being_rendered = false;
plugin->is_being_rendered = true;
plugin->rugin->render(plugin->rugin, full_buffer_len, render_pass_id);
plugin->render_pass_id = render_pass_id;
plugin->is_being_rendered = 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;
while(always != NULL){
bl00mbox_audio_bud_render(always->bud);
always = always->next;
}
bl00mbox_channel_root_t * root = chan->root_list;
int32_t vol = radspa_mult_shift(chan->volume, chan->sys_gain);
if(!vol) return false; // don't render if muted
// early exit when no sources or muted:
if((root == NULL) || (!vol)){
return false;
// render these even if nothing is plugged into mixer
if(chan->render_plugins){
for(size_t i = 0; i < chan->render_plugins->len; i++){
bl00mbox_audio_plugin_render(chan->render_plugins->elems[i]);
}
}
if(!(chan->render_buffers && chan->render_buffers->len)) return false;
int32_t acc[full_buffer_len];
bool acc_init = false;
while(root != NULL){
bl00mbox_audio_bud_render(root->con->source_bud);
if(root->con->buffer[1] == -32768){
if(!acc_init){
for(uint16_t i = 0; i < full_buffer_len; i++){
acc[i] = root->con->buffer[0];
// first one non-adding
int16_t * buffer = chan->render_buffers->elems[0];
if(buffer[1] == -32768){
for(size_t i = 0; i < full_buffer_len; i++){
acc[i] = buffer[0];
}
acc_init = true;
} else if(root->con->buffer[0]){
for(uint16_t i = 0; i < full_buffer_len; i++){
acc[i] += root->con->buffer[0];
} else {
for(size_t i = 0; i < full_buffer_len; i++){
acc[i] = buffer[i];
}
}
} else {
if(!acc_init){
for(uint16_t i = 0; i < full_buffer_len; i++){
acc[i] = root->con->buffer[i];
// rest adding
for(size_t i = 1; i < chan->render_buffers->len; i++){
buffer = chan->render_buffers->elems[i];
if(buffer[1] == -32768){
if(buffer[0]){
for(size_t i = 0; i < full_buffer_len; i++){
acc[i] += buffer[0];
}
acc_init = true;
} else {
for(uint16_t i = 0; i < full_buffer_len; i++){
acc[i] += root->con->buffer[i];
}
} else {
for(size_t i = 0; i < full_buffer_len; i++){
acc[i] += buffer[i];
}
}
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;
int invert = chan->dc < 0 ? -1 : 1;
chan->dc = chan->dc * invert;
chan->dc = ((uint64_t) chan->dc * (((1<<12) - 1)<<20)) >> 32;
if(invert) chan->dc = -chan->dc;
chan->dc = chan->dc * invert;
chan->dc += acc[i];
......@@ -319,7 +265,7 @@ static bool _bl00mbox_audio_channel_render(bl00mbox_channel_t * chan, int16_t *
out[i] = radspa_gain(acc[i], vol);
}
}
if(chan->compute_mean_square){
if(chan->compute_rms){
for(uint16_t i = 0; i < full_buffer_len; i++){
int32_t sq = acc[i];
sq = (sq * sq) - chan->mean_square;
......@@ -333,52 +279,51 @@ static bool _bl00mbox_audio_channel_render(bl00mbox_channel_t * chan, int16_t *
}
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);
// null it out if nothing was rendered
chan->mean_square *= ret;
return ret;
}
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
void bl00mbox_audio_render(int16_t * rx, int16_t * tx, uint16_t len){
full_buffer_len = len/2;
bl00mbox_line_in_interlaced = rx;
int16_t acc[full_buffer_len];
bool acc_init = false;
bl00mbox_take_lock(&render_lock);
// 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;
bl00mbox_take_lock(&active_chans_lock);
render_pass_id++;
if(active_chans){
for(size_t i = 0; i < active_chans->len; i++){
acc_init = bl00mbox_audio_channel_render(active_chans->channels[i], acc, acc_init) || acc_init;
}
}
bl00mbox_give_lock(&active_chans_lock);
bl00mbox_give_lock(&render_lock);
if(!acc_init) return false;
if(acc_init){
for(uint16_t i = 0; i < full_buffer_len; i++){
tx[2*i] = acc[i];
tx[2*i+1] = acc[i];
}
return true;
} else {
memset(tx, 0, len * sizeof(int16_t));
}
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;
assert(bl00mbox_create_lock(&active_chans_lock));
// micropython objects are generally not thread safe, so for most of the user API we need
// not care about locking after they've been created. however, bl00mbox Channel objects may
// be created by different threads (i.e., thread API), so these sets must be fully thread safe.
assert(bl00mbox_create_lock(&user_lock));
all_chans = calloc(1, sizeof(bl00mbox_set_t));
assert(all_chans);
background_mute_override_chans = calloc(1, sizeof(bl00mbox_set_t));
assert(background_mute_override_chans);
}
#include "bl00mbox_containers.h"
static bool equals(void * some, void * other, bl00mbox_set_key_t key){
if(!key) return some == other;
return key(some, other);
}
static bool set_add_inner(bl00mbox_set_t * set, void * content){
bl00mbox_ll_t * ll = malloc(sizeof(bl00mbox_ll_t));
if(!ll) return false;
ll->content = content;
ll->next = set->start;
set->start = ll;
set->len++;
return true;
}
bool bl00mbox_set_contains(bl00mbox_set_t * set, void * content){
if(!content) return false; // NULL pointers can't be in set
if(!set->start) return false;
bl00mbox_ll_t * seek = set->start;
while(seek){
if(equals(seek->content, content, set->key)) break;
seek = seek->next;
}
return seek;
}
bool bl00mbox_set_add_skip_unique_check(bl00mbox_set_t * set, void * content){
if(!content) return false; // don't allow for NULL pointers in set
#ifdef BL00MBOX_DEBUG
if(bl00mbox_set_contains(set, content)){
bl00mbox_log_error("set corrupted");
return false;
}
#endif
return set_add_inner(set, content);
}
bool bl00mbox_set_add(bl00mbox_set_t * set, void * content){
if(bl00mbox_set_contains(set, content)) return false;
return set_add_inner(set, content);
}
bool bl00mbox_set_remove(bl00mbox_set_t * set, void * content){
if(!content) return false;
bl00mbox_ll_t * seek = set->start;
bl00mbox_ll_t * prev = NULL;
while(seek){
if(equals(seek->content, content, set->key)) break;
prev = seek;
seek = seek->next;
}
if(seek){
bl00mbox_ll_t ** target = prev ? &(prev->next) : &(set->start);
(* target) = seek->next;
set->len--;
}
free(seek);
return true;
}
void bl00mbox_set_iter_start(bl00mbox_set_iter_t * iter, bl00mbox_set_t * set){
iter->next = set->start;
}
void * bl00mbox_set_iter_next(bl00mbox_set_iter_t * iter){
if(!iter->next) return NULL;
void * ret = iter->next->content;
iter->next = iter->next->next;
return ret;
}
bl00mbox_array_t * bl00mbox_set_to_array(bl00mbox_set_t * set){
bl00mbox_array_t * ret = malloc(sizeof(bl00mbox_array_t) + set->len * sizeof(void *));
if(!ret) return NULL;
ret->len = set->len;
bl00mbox_set_iter_t iter;
bl00mbox_set_iter_start(&iter, set);
void * content;
size_t index = 0;
while((content = bl00mbox_set_iter_next(&iter))){
ret->elems[index++] = content;
}
return ret;
}
#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); }
bool bl00mbox_create_lock(bl00mbox_lock_t * lock){
assert(*lock == NULL);
* lock = xSemaphoreCreateMutex();
return(*lock != NULL);
}
void bl00mbox_delete_lock(bl00mbox_lock_t * lock){ vSemaphoreDelete(* lock); }
void bl00mbox_take_lock(bl00mbox_lock_t * lock){ xSemaphoreTake(* lock, portMAX_DELAY); }
void bl00mbox_give_lock(bl00mbox_lock_t * lock){ xSemaphoreGive(* lock); }
#ifdef BL00MBOX_DEBUG
void bl00mbox_wait() { vTaskDelay(10); }
#endif
#endif
#if defined(BL00MBOX_ESPIDF)
......
......@@ -2,8 +2,8 @@
#include "bl00mbox_radspa_requirements.h"
bool radspa_host_request_buffer_render(int16_t * buf){
bl00mbox_bud_t * bud = ((bl00mbox_connection_t *) buf)->source_bud;
bl00mbox_audio_bud_render(bud);
bl00mbox_plugin_t * plugin = ((bl00mbox_connection_t *) buf)->source.plugin;
bl00mbox_audio_plugin_render(plugin);
return 1;
}
......
This diff is collapsed.
#pragma once
#define BL00MBOX_FLOW3R
//#define BL00MBOX_DEBUG
#ifdef BL00MBOX_FLOW3R
//#include "flow3r_bsp.h"
//#define BL00MBOX_MAX_BUFFER_LEN FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE
......@@ -10,4 +12,18 @@
#define BL00MBOX_LOOPS_ENABLE
#define BL00MBOX_FREERTOS
#define BL00MBOX_ESPIDF
// note: this option relies on implementation details of the garbage
// collector, specifically the fact that it is stop-the-world and
// collects all unreachable objects it can find before it gives control
// back to micropython. changing it from stop-the-world to anything
// else is to our knowledge not possible without rewriting all mutators,
// so we consider this aspect future proof. also we see no reason to
// not collect all of them since sweeping is very fast, unless it is
// outsourced to a separate sweeping task, in which case this option
// may result in use-after-free, but we don't really see that happening
// anytime soon.
// if you want to use this option in such an environment however,
// setting a ChannelCore's callback to None before dropping all
// references to it solves the issue.
#define BL00MBOX_CALLBACKS_FUNSAFE
#endif
......@@ -2,6 +2,7 @@
#pragma once
#include "bl00mbox_config.h"
#include "bl00mbox_os.h"
#include "bl00mbox_containers.h"
#include <stdio.h>
#include <math.h>
......@@ -9,98 +10,98 @@
#include "radspa.h"
#include "radspa_helpers.h"
struct _bl00mbox_bud_t;
struct _bl00mbox_plugin_t;
struct _bl00mbox_connection_source_t;
struct _bl00mbox_channel_root_t;
struct _bl00mbox_channel_t;
extern int16_t * bl00mbox_line_in_interlaced;
typedef struct _bl00mbox_bud_t{
radspa_t * plugin; // plugin
// pointer is unique identifier, no memcpy of this!
typedef struct _bl00mbox_plugin_t{
radspa_t * rugin; // radspa plugin
char * name;
uint64_t index; // unique index number for bud
uint32_t id; // unique number in channel to for UI purposes
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
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
} bl00mbox_bud_t;
typedef struct _bl00mbox_bud_list_t{
struct _bl00mbox_bud_t * bud;
struct _bl00mbox_bud_list_t * next;
} bl00mbox_bud_list_t;
typedef struct _bl00mbox_connection_subscriber_t{
uint8_t type; // 0: standard signal input, 1: output mixer
int32_t channel;
uint64_t bud_index;
uint32_t signal_index;
struct _bl00mbox_connection_subscriber_t * next;
} bl00mbox_connection_subscriber_t;
struct _bl00mbox_channel_t * channel; // channel that owns the plugin
void * parent;
struct _bl00mbox_plugin_t ** parent_self_ref;
} bl00mbox_plugin_t;
// pointer is NOT unique identifier, memcpy allowed
typedef struct {
bl00mbox_plugin_t * plugin;
radspa_signal_t * rignal;
int index;
} bl00mbox_signal_t;
typedef struct _bl00mbox_connection_t{ //child of bl00mbox_ll_t
int16_t buffer[BL00MBOX_MAX_BUFFER_LEN]; // MUST stay on top of struct bc type casting!
struct _bl00mbox_bud_t * source_bud;
uint32_t signal_index; // signal of source_bud that renders to buffer
struct _bl00mbox_connection_subscriber_t * subs;
int32_t channel;
struct _bl00mbox_connection_t * chan_next; //for linked list in bl00mbox_channel_t;
bl00mbox_signal_t source;
bl00mbox_set_t subscribers; // content: bl00mbox_signal_t;
bool connected_to_mixer; // legacy thing, don't wanna sentinel subsribers
} bl00mbox_connection_t;
typedef struct _bl00mbox_channel_root_t{
struct _bl00mbox_connection_t * con;
struct _bl00mbox_channel_root_t * next;
} bl00mbox_channel_root_t;
typedef struct{
int32_t index;
bl00mbox_lock_t render_lock;
bool is_active; // rendering can be skipped if false
bool compute_mean_square;
uint32_t mean_square;
// pointer is unique identifier, no memcpy of this!
typedef struct _bl00mbox_channel_t{
char * name;
int32_t volume;
bool background_mute_override;
// secondary gain that the channel user is supposed to leave untouched so that the OS
// can change gain too, for example for a system mixer. we still want to apply both in the
// same pass, so we keep it here.
int32_t sys_gain;
// whether to keep track of the rms volume of the channel. adds a bit of cpu load, best
// keep it off if not used.
bool compute_rms;
// we are storing the un-rooted value. this means that if you want the value in decibels
// you have to only do the log operation and can skip the root as you can express roots
// as divisions in the log domain which is much cheaper.
uint32_t mean_square;
// average output used for DC blocking.
int32_t dc;
struct _bl00mbox_channel_root_t * root_list; // list of all roots associated with channels
uint32_t render_pass_id; // may be used by host to determine whether recomputation is necessary
struct _bl00mbox_bud_t * buds; // linked list with all channel buds
struct _bl00mbox_bud_list_t * always_render; // linked list of buds that should always render
struct _bl00mbox_connection_t * connections; // linked list with all channel connections
uint32_t plugin_id; // used to give each plugin in channel a unique identifier
// we render all of these and add them up. it's a legacy thing from when we had a output mixer
// but that kinda mixes poorly (heh) with our general API.
bl00mbox_set_t roots; // content: bl00mbox_connection_t
bl00mbox_set_t connections; // content: bl00mbox_connection_t
bl00mbox_set_t always_render; // content: bl00mbox_plugin_t
bl00mbox_set_t plugins; // content: bl00mbox_plugin_t
bl00mbox_lock_t render_lock;
// take .render_lock before changing these
bl00mbox_array_t * render_plugins;
bl00mbox_array_t * render_buffers;
void * parent;
struct _bl00mbox_channel_t ** parent_self_ref;
} bl00mbox_channel_t;
typedef struct {
size_t len;
bl00mbox_channel_t * channels[];
} bl00mbox_channel_array_t;
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);
bl00mbox_channel_t * bl00mbox_channel_create();
void bl00mbox_audio_plugin_render(bl00mbox_plugin_t * plugin);
bool bl00mbox_channel_get_foreground(bl00mbox_channel_t * chan);
void bl00mbox_channel_set_foreground(bl00mbox_channel_t * chan, bool enable);
void bl00mbox_channel_set_background_mute_override(bl00mbox_channel_t * chan, bool enable);
void bl00mbox_channel_clear(bl00mbox_channel_t * chan);
void bl00mbox_channel_destroy(bl00mbox_channel_t * chan);
void bl00mbox_channel_event(bl00mbox_channel_t * channel);
bl00mbox_channel_array_t * bl00mbox_collect_channels(bool active);
#pragma once
#include "bl00mbox_os.h"
typedef struct _bl00mbox_ll_t {
struct _bl00mbox_ll_t * next;
void * content;
} bl00mbox_ll_t;
typedef struct {
size_t len;
void * elems[];
} bl00mbox_array_t;
typedef bool (* bl00mbox_set_key_t)(void *, void *);
// initialize all zeroed out except for content type
typedef struct {
bl00mbox_ll_t * start;
bl00mbox_set_key_t key;
size_t len;
} bl00mbox_set_t;
typedef struct {
bl00mbox_ll_t * next;
}bl00mbox_set_iter_t;
bool bl00mbox_set_contains(bl00mbox_set_t * set, void * content);
bool bl00mbox_set_add_skip_unique_check(bl00mbox_set_t * set, void * content);
bool bl00mbox_set_add(bl00mbox_set_t * set, void * content);
bool bl00mbox_set_remove(bl00mbox_set_t * set, void * content);
// removing from set during iteration is safe, adding is NOT
void bl00mbox_set_iter_start(bl00mbox_set_iter_t * iter, bl00mbox_set_t * set);
void * bl00mbox_set_iter_next(bl00mbox_set_iter_t * iter);
bl00mbox_array_t * bl00mbox_set_to_array(bl00mbox_set_t * set);
#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;
}
#pragma once
#include "bl00mbox_config.h"
#include <stdbool.h>
typedef enum {
BL00MBOX_ERROR_OK = false,
BL00MBOX_ERROR_OOM,
BL00MBOX_ERROR_INVALID_CONNECTION,
BL00MBOX_ERROR_INVALID_IDENTIFIER,
} bl00mbox_error_t;
#ifdef BL00MBOX_FREERTOS
#include "freertos/FreeRTOS.h"
......@@ -7,16 +15,25 @@
typedef SemaphoreHandle_t bl00mbox_lock_t;
#endif
void bl00mbox_create_lock(bl00mbox_lock_t * lock);
bool 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_DEBUG
void bl00mbox_wait();
#endif
#ifdef BL00MBOX_ESPIDF
#include "esp_log.h"
#define bl00mbox_log_error(txt, ...) ESP_LOGE("bl00mbox", txt, ##__VA_ARGS__);
#ifdef BL00MBOX_DEBUG
#define bl00mbox_log_info(txt, ...) ESP_LOGE("bl00mbox", txt, ##__VA_ARGS__);
#else
#define bl00mbox_log_info(txt, ...) ESP_LOGI("bl00mbox", txt, ##__VA_ARGS__);
#endif
#else
void bl00mbox_log_error(char * txt, ...);
void bl00mbox_log_info(char * txt, ...);
#endif
......@@ -8,54 +8,32 @@
#include "bl00mbox_plugin_registry.h"
#include "bl00mbox_audio.h"
#include "bl00mbox_os.h"
#include "bl00mbox_containers.h"
#include <stdint.h>
#include "bl00mbox_audio.h"
#include "radspa_helpers.h"
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(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(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(int32_t channel, uint64_t bud_index,
uint16_t signal_index, uint8_t pos);
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(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);
// lazy 2nd error channel: pointer return types resulting in NULL means OOM
uint16_t bl00mbox_channel_plugins_num(bl00mbox_channel_t * chan);
uint16_t bl00mbox_channel_conns_num(bl00mbox_channel_t * chan);
uint16_t bl00mbox_channel_mixer_num(bl00mbox_channel_t * chan);
bl00mbox_array_t * bl00mbox_channel_collect_plugins(bl00mbox_channel_t * chan);
bl00mbox_array_t * bl00mbox_channel_collect_connections_mx(bl00mbox_channel_t * chan);
bl00mbox_plugin_t * bl00mbox_plugin_create(bl00mbox_channel_t * chan, uint32_t id, uint32_t init_var);
void bl00mbox_plugin_destroy(bl00mbox_plugin_t * plugin);
void bl00mbox_plugin_set_always_render(bl00mbox_plugin_t * plugin, bool value);
bl00mbox_error_t bl00mbox_signal_set_value(bl00mbox_signal_t * signal, int value);
int16_t bl00mbox_signal_get_value(bl00mbox_signal_t * signal);
bl00mbox_error_t bl00mbox_signal_connect(bl00mbox_signal_t * some, bl00mbox_signal_t * other);
bl00mbox_error_t bl00mbox_signal_connect_mx(bl00mbox_signal_t * some);
void bl00mbox_signal_disconnect(bl00mbox_signal_t * signal);
bl00mbox_array_t * bl00mbox_signal_collect_connections(bl00mbox_signal_t * signal);
bl00mbox_connection_t * bl00mbox_connection_from_signal(bl00mbox_signal_t * signal);
bool bl00mbox_signal_is_input(bl00mbox_signal_t * signal);
bool bl00mbox_signal_is_output(bl00mbox_signal_t * signal);
......@@ -7,30 +7,48 @@ import cpython.wave as wave
class _Patch:
def __init__(self, chan):
self.plugins = _PatchPluginList()
self.signals = _PatchSignalList()
self._channel = chan
# old style
self.plugins = _PatchPluginList()
# new style
self._plugins = []
def new(self, *args, **kwargs):
plugin = self._channel.new(*args, **kwargs)
self._plugins.append(plugin)
return plugin
def __repr__(self):
ret = "[patch] " + type(self).__name__
ret += "\n [signals:] " + "\n ".join(repr(self.signals).split("\n"))
ret += "\n [plugins:] " + "\n ".join(repr(self.plugins).split("\n"))
# old style
plugin_repr = repr(self.plugins)
# new style
for plugin in self._plugins:
plugin_repr += "\n" + repr(plugin)
ret += "\n [plugins:] " + "\n ".join(plugin_repr.split("\n"))
return ret
def delete(self):
# new style
plugins = self._plugins[:]
# old style
for plugin_name in self.plugins.__dict__:
if not plugin_name.startswith("_"):
plugin = self.plugins.__dict__[plugin_name]
if type(plugin) == list:
for p in plugin:
p.delete()
else:
plugin_list = self.plugins.__dict__[plugin_name]
if (type(plugin_list)) != list:
plugin_list = [plugin_list]
plugins += plugin_list
for plugin in set(plugins):
plugin.delete()
class _PatchItemList:
def __init__(self):
self._items = []
# workaround
def __iter__(self):
return iter(self._items)
......@@ -44,11 +62,10 @@ class _PatchItemList:
return ret
def __setattr__(self, key, value):
# old style
current_value = getattr(self, key, None)
if current_value is None and not key.startswith("_"):
self._items.append(key)
super().__setattr__(key, value)
......@@ -57,7 +74,7 @@ class _PatchSignalList(_PatchItemList):
current_value = getattr(self, key, None)
if isinstance(current_value, bl00mbox.Signal):
current_value.value = value
return
else:
super().__setattr__(key, value)
......
......@@ -51,80 +51,43 @@ _fill()
class _Plugin:
def __init__(self, channel, plugin_id, bud_num=None, init_var=0):
self._channel_num = channel.channel_num
if bud_num == None:
self._plugin_id = plugin_id
self._bud_num = sys_bl00mbox.channel_new_bud(
self.channel_num, self.plugin_id, init_var
_core_keys = (
"always_render",
"delete",
"init_var",
"table_len",
"name",
"plugin_id",
)
if self._bud_num == None:
raise bl00mbox.Bl00mboxError("plugin init failed")
def __setattr__(self, key, value):
if key in self._core_keys:
setattr(self._core, key, value)
else:
self._bud_num = bud_num
self._check_existence()
self._plugin_id = sys_bl00mbox.channel_bud_get_plugin_id(
self.channel_num, self.bud_num
)
self._name = sys_bl00mbox.channel_bud_get_name(self.channel_num, self.bud_num)
super().__setattr__(key, value)
def __getattr__(self, key):
return getattr(self._core, key)
def __init__(self, channel, core=None, plugin_id=None, init_var=0):
if core:
self._core = core
elif plugin_id is not None:
self._core = sys_bl00mbox.PluginCore(channel._core, plugin_id, init_var)
else:
raise ValueError("must supply core or plugin id")
self._signals = bl00mbox.SignalList(self)
self._channel = channel
def __repr__(self):
self._check_existence()
ret = "[plugin " + str(self.bud_num) + "] " + self.name
ret = f"[plugin {self._core.id}] {self.name}"
for sig in self.signals._list:
ret += "\n " + "\n ".join(sig._no_desc().split("\n"))
return ret
def __del__(self):
self._check_existence()
sys_bl00mbox.channel_delete_bud(self.channel_num, self.bud_num)
def _check_existence(self):
if not sys_bl00mbox.channel_bud_exists(self.channel_num, self.bud_num):
raise bl00mbox.Bl00mboxError("plugin has been deleted")
@property
def always_render(self):
self._check_existence()
return sys_bl00mbox.channel_bud_get_always_render(
self.channel_num, self.bud_num
)
@always_render.setter
def always_render(self, value):
self._check_existence()
sys_bl00mbox.channel_bud_set_always_render(
self.channel_num, self.bud_num, value
)
def delete(self):
self._check_existence()
sys_bl00mbox.channel_delete_bud(self.channel_num, self.bud_num)
@property
def init_var(self):
return sys_bl00mbox.channel_bud_get_init_var(self.channel_num, self.bud_num)
@property
def table_len(self):
return sys_bl00mbox.channel_bud_get_table_len(self.channel_num, self.bud_num)
@property
def channel_num(self):
return self._channel_num
@property
def plugin_id(self):
return self._plugin_id
@property
def name(self):
return self._name
@property
def bud_num(self):
return self._bud_num
# will fail if plugin was deleted
_ = self._core.plugin_id
@property
def signals(self):
......@@ -132,33 +95,22 @@ class _Plugin:
@property
def table(self):
ret = []
for x in range(
sys_bl00mbox.channel_bud_get_table_len(self.channel_num, self.bud_num)
):
ret += [
sys_bl00mbox.channel_bud_get_table_value(
self.channel_num, self.bud_num, x
)
]
_table = self.table_int16_array
ret = [None] * self.table_len
for x in range(self.table_len):
ret[x] = _table[x]
return ret
@table.setter
def table(self, stuff):
max_len = sys_bl00mbox.channel_bud_get_table_len(self.channel_num, self.bud_num)
if len(stuff) > max_len:
stuff = stuff[:max_len]
for x, y in enumerate(stuff):
sys_bl00mbox.channel_bud_set_table_value(
self.channel_num, self.bud_num, x, y
)
def table(self, data):
_table = self.table_int16_array
for x in range(min(self.table_len, len(data))):
_table[x] = data[x]
@property
def table_pointer(self):
pointer = sys_bl00mbox.channel_bud_get_table_pointer(
self.channel_num, self.bud_num
)
max_len = sys_bl00mbox.channel_bud_get_table_len(self.channel_num, self.bud_num)
pointer = self._core.table_pointer
max_len = self._core.table_len
return (pointer, max_len)
@property
......@@ -192,22 +144,20 @@ class _Plugin:
_plugin_subclasses = {}
def _make_new_plugin(channel, plugin_id, bud_num, *args, **kwargs):
if bud_num is not None:
plugin_id = sys_bl00mbox.channel_bud_get_plugin_id(channel.channel_num, bud_num)
def _make_plugin(channel, core, plugin_id, *args, **kwargs):
if plugin_id in _plugin_subclasses:
return _plugin_subclasses[plugin_id](
channel, plugin_id, bud_num, *args, **kwargs
)
return _plugin_subclasses[plugin_id](channel, core, plugin_id, *args, **kwargs)
else:
init_var = kwargs.get("init_var", None)
if init_var is None and len(args) == 1:
init_var = args[0]
try:
init_var = int(init_var)
except:
return _Plugin(channel, plugin_id, bud_num)
return _Plugin(channel, plugin_id, bud_num, init_var)
init_var = 0
if not core:
_init_var = kwargs.get("init_var", None)
if _init_var is None:
if len(args) == 1:
init_var = int(args[0])
else:
init_var = _init_var
return _Plugin(channel, core, plugin_id, init_var)
def _plugin_set_subclass(plugin_id):
......@@ -229,19 +179,19 @@ class _Sampler(_Plugin):
_STATUS = 10
_BUFFER = 11
def __init__(self, channel, plugin_id, bud_num, init_var=1000):
def __init__(self, channel, core, plugin_id, init_var=1000):
self._filename = ""
if bud_num is not None:
super().__init__(channel, plugin_id, bud_num=bud_num)
if core is not None:
super().__init__(channel, core, None)
self._memory_len = self.init_var
elif type(init_var) is str:
with wave.open(init_var, "r") as f:
self._memory_len = f.getnframes()
super().__init__(channel, plugin_id, init_var=self._memory_len)
super().__init__(channel, None, plugin_id, init_var=self._memory_len)
self.load(init_var)
else:
self._memory_len = int(48 * init_var)
super().__init__(channel, plugin_id, init_var=self._memory_len)
super().__init__(channel, None, plugin_id, init_var=self._memory_len)
def __repr__(self):
ret = super().__repr__()
......@@ -532,14 +482,14 @@ class _Distortion(_Plugin):
@_plugin_set_subclass(172)
class _PolySqueeze(_Plugin):
def __init__(self, channel, plugin_id, bud_num, num_outputs=3, num_inputs=10):
if bud_num is None:
def __init__(self, channel, core, plugin_id, num_outputs=3, num_inputs=10):
if core is None:
outs = max(min(num_outputs, 16), 1)
ins = max(min(num_inputs, 32), num_outputs)
init_var = outs + (ins * 256)
super().__init__(channel, plugin_id, init_var=init_var)
super().__init__(channel, core, plugin_id, init_var=init_var)
else:
super().__init__(channel, plugin_id, bud_num=bud_num)
super().__init__(channel, core, None)
@_plugin_set_subclass(420)
......@@ -548,6 +498,35 @@ class _Osc(_Plugin):
def wave(self):
return tuple([self.table_int8_array[i] for i in range(64)])
@property
def speed(self):
val = self.signals.speed.value
if val < -10922:
return "lfo"
elif val < 10922:
return "auto"
else:
return "audio"
@speed.setter
def speed(self, val):
if val == "lfo":
self.signals.speed.switch.LFO = True
elif val == "auto":
self.signals.speed.switch.AUTO = True
elif val == "audio":
self.signals.speed.switch.AUTIO = True
else:
raise ValueError('speed must be "lfo", "auto" or "audio"')
@property
def antialiasing(self):
return bool(self.table_int8_array[64])
@antialiasing.setter
def antialiasing(self, val):
self.table_int8_array[64] = bool(val)
def __repr__(self):
ret = super().__repr__()
wave = self.wave
......@@ -580,18 +559,18 @@ class _Osc(_Plugin):
@_plugin_set_subclass(56709)
class _Sequencer(_Plugin):
def __init__(self, channel, plugin_id, bud_num, num_tracks=4, num_steps=16):
if bud_num is None:
def __init__(self, channel, core, plugin_id, num_tracks=4, num_steps=16):
if core is None:
self.num_steps = num_steps % 256
self.num_tracks = num_tracks % 256
init_var = (self.num_steps * 256) + (self.num_tracks)
super().__init__(channel, plugin_id, init_var=init_var)
super().__init__(channel, core, plugin_id, init_var=init_var)
tracktable = [-32767] + ([0] * self.num_steps)
self.table = tracktable * self.num_tracks
else:
super().__init__(channel, plugin_id, bud_num=bud_num)
super().__init__(channel, core, None)
self.num_tracks = self.init_var % 256
self.num_steps = (self.init_var // 256) % 256
......
This diff is collapsed.
This diff is collapsed.
......@@ -235,7 +235,7 @@ static inline int16_t fake_sine(int16_t tri){
// performance. we see some low hanging fruits but we can't justify spending any more time
// on this until flow3r 1.3 is done.
static inline int16_t saw(int32_t saw, osc_data_t * data){
static inline int16_t saw(int16_t saw, osc_data_t * data){
int16_t saw_sgn = saw > 0 ? 1 : -1;
int16_t saw_abs = saw * saw_sgn;
if(saw_abs > data->blep_coeffs[0]){
......@@ -262,7 +262,8 @@ static inline int16_t square(int16_t saw, osc_data_t * data){
}
static inline void get_blep_coeffs(osc_data_t * data, int32_t incr){
if(incr == data->incr_prev) return;
// if antialiasing is false we externally write INT16_MAX to blep_coeffs[0]
if(incr == data->incr_prev || (!data->antialiasing)) return;
int32_t incr_norm = ((int64_t) incr * 65535) >> 32; // max 65534
incr_norm = incr_norm > 0 ? incr_norm : -incr_norm;
data->blep_coeffs[0] = 32767 - incr_norm;
......@@ -286,7 +287,7 @@ static inline int16_t apply_morph(osc_data_t * data, uint32_t counter){
}
static inline int16_t apply_waveform(osc_data_t * data, int16_t input, int32_t incr){
int32_t a, b;
int16_t a, b;
if(data->waveform_coeffs[0] < (32767-10922)){
b = triangle(input);
a = fake_sine(b);
......@@ -353,6 +354,8 @@ static inline int16_t apply_waveform(osc_data_t * data, int16_t input, int32_t i
radspa_signal_set_value_noclip(sync_out_sig, i, data->counter > (1UL<<31) ? 32767 : -32767); \
}
#define ANTIALIASING_INDEX 64
void osc_run(radspa_t * osc, uint16_t num_samples, uint32_t render_pass_id){
osc_data_t * data = osc->plugin_data;
int8_t * table = (int8_t * ) osc->plugin_table;
......@@ -379,6 +382,9 @@ void osc_run(radspa_t * osc, uint16_t num_samples, uint32_t render_pass_id){
bool out_const = out_sig->buffer == NULL;
bool sync_out_const = sync_out_sig->buffer == NULL;
data->antialiasing = table[ANTIALIASING_INDEX];
if(!data->antialiasing) data->blep_coeffs[0] = INT16_MAX;
bool lfo = speed < -10922; // manual setting
lfo = lfo || (out_const && sync_out_const); // unlikely, host should ideally prevent that case
......@@ -401,6 +407,7 @@ void osc_run(radspa_t * osc, uint16_t num_samples, uint32_t render_pass_id){
// trigger signal? would result in "auto-humanize" when attached to any other consumer
// but that's fine we think.
radspa_signal_set_const_value(sync_out_sig, data->counter > (1UL<<31) ? 32767 : -32767);
// max index 63
table[(data->counter<<1)>>(32-6)] = out >> 8;
}
......@@ -428,7 +435,9 @@ void osc_run(radspa_t * osc, uint16_t num_samples, uint32_t render_pass_id){
#pragma GCC pop_options
radspa_t * osc_create(uint32_t init_var){
radspa_t * osc = radspa_standard_plugin_create(&osc_desc, OSC_NUM_SIGNALS, sizeof(osc_data_t), 32);
radspa_t * osc = radspa_standard_plugin_create(&osc_desc, OSC_NUM_SIGNALS, sizeof(osc_data_t), 33);
int8_t * table = (int8_t * ) osc->plugin_table;
table[ANTIALIASING_INDEX] = true;
osc->render = osc_run;
radspa_signal_set(osc, OSC_OUT, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0);
radspa_signal_set(osc, OSC_PITCH, "pitch", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_SCT, RADSPA_SIGNAL_VAL_SCT_A440);
......
......@@ -16,6 +16,7 @@ typedef struct {
int32_t morph_coeffs[3];
int16_t morph_gate_prev;
bool morph_no_pwm_prev;
bool antialiasing;
} osc_data_t;
extern radspa_descriptor_t osc_desc;
......
......@@ -9,6 +9,8 @@ import sys_display
import sys_kernel
import captouch
import bl00mbox
class Responder(ABCBase):
"""
......@@ -164,3 +166,7 @@ class Reactor:
# Share!
if ftop.run(delta):
print(ftop.report)
# Background!
for callback in bl00mbox.Sys.collect_active_callbacks():
callback(hr, delta)
......@@ -213,10 +213,6 @@ def run_main() -> None:
except Exception as e:
log.error(f"Failed to set hostname {e}")
for chan in bl00mbox.all_channels():
chan.clear()
chan.free = True
bundles = BundleManager()
bundles.update()
application_settings.synchronize_apps(bundles.bundles.values())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment