From 90bf0a46f1d7b15e2b418a034250ea101d04186f Mon Sep 17 00:00:00 2001
From: moon2 <moon2protonmail@protonmail.com>
Date: Mon, 7 Aug 2023 20:19:08 +0200
Subject: [PATCH] bl00mbox: plugin stuff

---
 components/bl00mbox/CMakeLists.txt            |   8 +-
 components/bl00mbox/README.md                 | 118 -------
 .../bl00mbox/bl00mbox_plugin_registry.c       |  14 +-
 .../bl00mbox/bl00mbox_radspa_requirements.c   |   2 +
 components/bl00mbox/extern/xoroshiro64star.c  |  45 +++
 components/bl00mbox/extern/xoroshiro64star.h  |   3 +
 .../include/bl00mbox_radspa_requirements.h    |   1 +
 components/bl00mbox/plugins/env_adsr.c        | 225 ++++++++++++
 components/bl00mbox/plugins/env_adsr.h        |  25 ++
 components/bl00mbox/plugins/flanger.c         | 119 +++++++
 components/bl00mbox/plugins/flanger.h         |  15 +
 components/bl00mbox/plugins/noise.c           |  36 ++
 components/bl00mbox/plugins/noise.h           |   8 +
 components/bl00mbox/plugins/osc_fm.c          | 100 ++++++
 components/bl00mbox/plugins/osc_fm.h          |  14 +
 .../bl00mbox/plugins/trad_synth/trad_synth.c  | 328 ------------------
 .../bl00mbox/plugins/trad_synth/trad_synth.h  |  43 ---
 components/bl00mbox/radspa/radspa.h           |  25 +-
 18 files changed, 628 insertions(+), 501 deletions(-)
 delete mode 100644 components/bl00mbox/README.md
 create mode 100644 components/bl00mbox/extern/xoroshiro64star.c
 create mode 100644 components/bl00mbox/extern/xoroshiro64star.h
 create mode 100644 components/bl00mbox/plugins/env_adsr.c
 create mode 100644 components/bl00mbox/plugins/env_adsr.h
 create mode 100644 components/bl00mbox/plugins/flanger.c
 create mode 100644 components/bl00mbox/plugins/flanger.h
 create mode 100644 components/bl00mbox/plugins/noise.c
 create mode 100644 components/bl00mbox/plugins/noise.h
 create mode 100644 components/bl00mbox/plugins/osc_fm.c
 create mode 100644 components/bl00mbox/plugins/osc_fm.h
 delete mode 100644 components/bl00mbox/plugins/trad_synth/trad_synth.c
 delete mode 100644 components/bl00mbox/plugins/trad_synth/trad_synth.h

diff --git a/components/bl00mbox/CMakeLists.txt b/components/bl00mbox/CMakeLists.txt
index 4dfae4a138..f4fe44122c 100644
--- a/components/bl00mbox/CMakeLists.txt
+++ b/components/bl00mbox/CMakeLists.txt
@@ -7,17 +7,21 @@ idf_component_register(
         bl00mbox_user.c
         bl00mbox_plugin_registry.c
         bl00mbox_radspa_requirements.c
-        plugins/trad_synth/trad_synth.c
+        plugins/osc_fm.c
+        plugins/env_adsr.c
         plugins/ampliverter.c
         plugins/sampler.c
         plugins/delay.c
+        plugins/flanger.c
         plugins/sequencer.c
+        plugins/noise.c
         radspa/radspa_helpers.c
+        extern/xoroshiro64star.c
     INCLUDE_DIRS
         include
-        plugins/trad_synth
         plugins
         radspa
+        extern
     REQUIRES
         st3m
 )
diff --git a/components/bl00mbox/README.md b/components/bl00mbox/README.md
deleted file mode 100644
index 03ae5d16b1..0000000000
--- a/components/bl00mbox/README.md
+++ /dev/null
@@ -1,118 +0,0 @@
-## welcome to bl00mbox!
-
-bl00mbox is a radspa plugin host that manages rendering as well as routing. it provides several high-level
-features to reduce computational load of inactive sources in complex systems.
-
-## naming conventions
-
-bl00mbox_audio_* functions are to be called only from within the audio task. There are no thread safety
-features.
-
-bl00mbox_tween_* functions are to be called only when the audio task idles. Since the audio task can block
-on them briefly they should not be run at high priority.
-
-## rendering
-The rendering engine produces samples as requested by a host task.
-
-### channels
-On the topmost layer, all plugins are assigned to a channel, representing different patches, applications and
-similar. There is a limited amount of channels as defined by BL00MBOX_CHANNELS where channel 0 is reserved
-for system sounds. The host UI provides applications with a channel number that they can use to freely
-spawn plugin instances. This allows bl00mbox to not render a channel if the application is suspended, or
-free it entirely if the application is closed while allowing for fast application switching without having
-to rebuild plugins and routing structure.
-
-The default policy for the time being is that only the last channel that has received data input as well as
-the system channel 0 are rendered, but different policies such as individual background overrides may be
-implemented.
-
-### buds
-All plugins are wrapped into buds which provide additional information, such as the assigned channel.
-
-### radspa
-Plugins are provided in the custom radspa format as defined in radspa.h. For the current scope of bl00mbox
-all signals such as audio data, pitch or events is represented as int16_t values. While this allows for high
-flexibility with signal routing, this means common signals such as note start/end are encoded. These encodings
-are visible to bl00mbox via radspa signal hints. A basic radspa plugin descriptor with name, description and
-an instance constructor function is provided to the host.
-
-The specific number of signals is dynamic and thus is not part of the descriptor. Each plugin may take one
-initialization variable (for example for numbers of channels in a polysynth or length of a delay buffer).
-Support for multiple initialization variables is planned for the future.
-
-### routing
-Routing is generally slow and is performed in-between buffer generation.
-
-### the render tree
-Each channel has its own render tree. Each node on the tree is a bud. A single bud, often a mixer, is attached
-to the tree and always rendered if the tree is active. Each bud may request signals from other buds during
-rendering, in which case that bud is rendered. For example, the root bud could be an envelope generator that
-receives audio from an oscillator child bud. If the envelope generator is in a "mute" state, the child bud
-is never evaluated, thus providing an early return.
-
-## modifying routing and parameters
-bl00mbox provides a thread safe API for routing and parameter changes.
-
-## known weaknesses
-Due to the nested evaluation of the render tree the stack size may grow large.
-
-### examples
-
-
-run these in ur repl :D!
-
-## bass line
-
-```
-import bl00mbox
-
-c = bl00mbox.Channel()
-c.volume = 10000
-osc1 = c.new_bud(420)
-env1 = c.new_bud(42)
-env1.signals.output.value = c.output
-env1.signals.input.value = osc1.signals.output
-
-osc2 = c.new_bud(420)
-env2 = c.new_bud(42)
-env2.signals.input.value = osc2.signals.output
-
-amp1 = c.new_bud(69)
-amp1.signals.input.value = env2.signals.output
-amp1.signals.output.value = osc1.signals.lin_fm
-
-env1.signals.sustain.value = 0
-env2.signals.sustain.value = 0
-env1.signals.attack.value = 10
-env2.signals.attack.value = 100
-env1.signals.decay.value = 800
-env2.signals.decay.value = 800
-
-osc1.signals.pitch.tone = -12
-osc2.signals.pitch.tone = -24
-
-osc3 = c.new_bud(420)
-osc3.signals.waveform.value = 0
-osc3.signals.pitch.tone = -100
-osc3.signals.output.value = env1.signals.trigger
-osc3.signals.output.value = env2.signals.trigger
-
-osc4 = c.new_bud(420)
-osc4.signals.waveform.value = 32767
-osc4.signals.pitch.tone = -124
-
-amp2 = c.new_bud(69)
-amp2.signals.input.value = osc4.signals.output
-amp2.signals.bias.value = 18376 - 2400 
-amp2.signals.gain.value = 300
-
-amp2.signals.output.value = osc1.signals.pitch
-
-amp3 = c.new_bud(69)
-amp3.signals.input.value = amp2.signals.output
-amp3.signals.bias.value = - 2400
-amp3.signals.gain.value = 31000
-
-amp3.signals.output.value = osc2.signals.pitch
-osc2.signals.output.value = c.output
-```
diff --git a/components/bl00mbox/bl00mbox_plugin_registry.c b/components/bl00mbox/bl00mbox_plugin_registry.c
index 66d4dd4ce8..e18ca82ce3 100644
--- a/components/bl00mbox/bl00mbox_plugin_registry.c
+++ b/components/bl00mbox/bl00mbox_plugin_registry.c
@@ -87,21 +87,25 @@ radspa_descriptor_t * bl00mbox_plugin_registry_get_id_from_index(uint32_t index)
  * removing plugins from the registry at runtime is not intended.
  */
 
-#include "trad_synth.h"
+#include "osc_fm.h"
+#include "env_adsr.h"
 #include "ampliverter.h"
 #include "delay.h"
 //#include "filter.h"
-//#include "sequence_timer.h"
 #include "sequencer.h"
 #include "sampler.h"
+#include "flanger.h"
+#include "noise.h"
+
 void bl00mbox_plugin_registry_init(void){
     if(bl00mbox_plugin_registry_is_initialized) return;
-    plugin_add(&trad_osc_desc);
+    plugin_add(&osc_fm_desc);
     plugin_add(&ampliverter_desc);
-    plugin_add(&trad_env_desc);   
+    plugin_add(&env_adsr_desc);   
     plugin_add(&delay_desc);   
 //    plugin_add(&filter_desc);   
-//    plugin_add(&sequence_timer_desc);
     plugin_add(&sequencer_desc);
     plugin_add(&sampler_desc);
+    plugin_add(&flanger_desc);
+    plugin_add(&noise_desc);
 }
diff --git a/components/bl00mbox/bl00mbox_radspa_requirements.c b/components/bl00mbox/bl00mbox_radspa_requirements.c
index e24e9f6a65..dc86121be1 100644
--- a/components/bl00mbox/bl00mbox_radspa_requirements.c
+++ b/components/bl00mbox/bl00mbox_radspa_requirements.c
@@ -93,3 +93,5 @@ int16_t radspa_trigger_get(int16_t trigger_signal, int16_t * hist){
     (* hist) = trigger_signal;
     return ret;
 }
+
+int16_t radspa_random(){ return xoroshiro64star()>>16; }
diff --git a/components/bl00mbox/extern/xoroshiro64star.c b/components/bl00mbox/extern/xoroshiro64star.c
new file mode 100644
index 0000000000..507c5f5258
--- /dev/null
+++ b/components/bl00mbox/extern/xoroshiro64star.c
@@ -0,0 +1,45 @@
+/*  Written in 2016 by David Blackman and Sebastiano Vigna (vigna@acm.org)
+
+To the extent possible under law, the author has dedicated all copyright
+and related and neighboring rights to this software to the public domain
+worldwide. This software is distributed without any warranty.
+
+See <http://creativecommons.org/publicdomain/zero/1.0/>. */
+
+#include <stdint.h>
+
+/* This is xoroshiro64* 1.0, our best and fastest 32-bit small-state
+   generator for 32-bit floating-point numbers. We suggest to use its
+   upper bits for floating-point generation, as it is slightly faster than
+   xoroshiro64**. It passes all tests we are aware of except for linearity
+   tests, as the lowest six bits have low linear complexity, so if low
+   linear complexity is not considered an issue (as it is usually the
+   case) it can be used to generate 32-bit outputs, too.
+
+   We suggest to use a sign test to extract a random Boolean value, and
+   right shifts to extract subsets of bits.
+
+   The state must be seeded so that it is not everywhere zero. */
+
+
+static inline uint32_t rotl(const uint32_t x, int k) {
+	return (x << k) | (x >> (32 - k));
+}
+
+// mod1: state seed
+static uint32_t s[2] = {420, 69};
+
+//uint32_t next(void) {
+uint32_t xoroshiro64star(void) {
+    // mod2: changed name to avoid namespace collisions.
+	const uint32_t s0 = s[0];
+	uint32_t s1 = s[1];
+	const uint32_t result = s0 * 0x9E3779BB;
+
+	s1 ^= s0;
+	s[0] = rotl(s0, 26) ^ s1 ^ (s1 << 9); // a, b
+	s[1] = rotl(s1, 13); // c
+
+	return result;
+}
+
diff --git a/components/bl00mbox/extern/xoroshiro64star.h b/components/bl00mbox/extern/xoroshiro64star.h
new file mode 100644
index 0000000000..56203ff9bc
--- /dev/null
+++ b/components/bl00mbox/extern/xoroshiro64star.h
@@ -0,0 +1,3 @@
+#pragma once
+
+uint32_t xoroshiro64star(void);
diff --git a/components/bl00mbox/include/bl00mbox_radspa_requirements.h b/components/bl00mbox/include/bl00mbox_radspa_requirements.h
index 142810dd5c..78f3b13a62 100644
--- a/components/bl00mbox/include/bl00mbox_radspa_requirements.h
+++ b/components/bl00mbox/include/bl00mbox_radspa_requirements.h
@@ -3,4 +3,5 @@
 #include "bl00mbox_audio.h"
 #include "radspa.h"
 #include "radspa_helpers.h"
+#include "xoroshiro64star.h"
 
diff --git a/components/bl00mbox/plugins/env_adsr.c b/components/bl00mbox/plugins/env_adsr.c
new file mode 100644
index 0000000000..d4c04942cd
--- /dev/null
+++ b/components/bl00mbox/plugins/env_adsr.c
@@ -0,0 +1,225 @@
+#include "env_adsr.h"
+
+radspa_descriptor_t env_adsr_desc = {
+    .name = "env_adsr",
+    .id = 42,
+    .description = "simple ADSR envelope",
+    .create_plugin_instance = env_adsr_create,
+    .destroy_plugin_instance = radspa_standard_plugin_destroy
+};
+
+#define ENV_ADSR_NUM_SIGNALS 9
+#define ENV_ADSR_OUTPUT 0
+#define ENV_ADSR_PHASE 1
+#define ENV_ADSR_INPUT 2
+#define ENV_ADSR_TRIGGER 3
+#define ENV_ADSR_ATTACK 4
+#define ENV_ADSR_DECAY 5
+#define ENV_ADSR_SUSTAIN 6
+#define ENV_ADSR_RELEASE 7
+#define ENV_ADSR_GATE 8
+
+#define ENV_ADSR_PHASE_OFF 0
+#define ENV_ADSR_PHASE_ATTACK 1
+#define ENV_ADSR_PHASE_DECAY 2
+#define ENV_ADSR_PHASE_SUSTAIN 3
+#define ENV_ADSR_PHASE_RELEASE 4
+
+radspa_t * env_adsr_create(uint32_t init_var){
+    radspa_t * env_adsr = radspa_standard_plugin_create(&env_adsr_desc, ENV_ADSR_NUM_SIGNALS, sizeof(env_adsr_data_t),0);
+    env_adsr->render = env_adsr_run;
+    radspa_signal_set(env_adsr, ENV_ADSR_OUTPUT, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0);
+    radspa_signal_set(env_adsr, ENV_ADSR_PHASE, "phase", RADSPA_SIGNAL_HINT_OUTPUT, 0);
+    radspa_signal_set(env_adsr, ENV_ADSR_INPUT, "input", RADSPA_SIGNAL_HINT_INPUT, 32767);
+    radspa_signal_set(env_adsr, ENV_ADSR_TRIGGER, "trigger", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_TRIGGER, 0);
+    radspa_signal_set(env_adsr, ENV_ADSR_ATTACK, "attack", RADSPA_SIGNAL_HINT_INPUT, 100);
+    radspa_signal_set(env_adsr, ENV_ADSR_DECAY, "decay", RADSPA_SIGNAL_HINT_INPUT, 250);
+    radspa_signal_set(env_adsr, ENV_ADSR_SUSTAIN, "sustain", RADSPA_SIGNAL_HINT_INPUT, 16000);
+    radspa_signal_set(env_adsr, ENV_ADSR_RELEASE, "release", RADSPA_SIGNAL_HINT_INPUT, 50);
+    radspa_signal_set(env_adsr, ENV_ADSR_GATE, "gate", RADSPA_SIGNAL_HINT_INPUT,0);
+    radspa_signal_get_by_index(env_adsr, ENV_ADSR_ATTACK)->unit = "ms";
+    radspa_signal_get_by_index(env_adsr, ENV_ADSR_DECAY)->unit = "ms";
+    radspa_signal_get_by_index(env_adsr, ENV_ADSR_SUSTAIN)->unit = "ms";
+
+    env_adsr_data_t * data = env_adsr->plugin_data;
+    data->trigger_prev = 0;
+    data->env_phase = ENV_ADSR_PHASE_OFF;
+
+    return env_adsr;
+}
+
+static int16_t env_adsr_run_single(env_adsr_data_t * env){
+    uint32_t tmp;
+    switch(env->env_phase){
+        case ENV_ADSR_PHASE_OFF:
+            env->env_counter = 0;;
+            break;
+        case ENV_ADSR_PHASE_ATTACK:
+            tmp = env->env_counter + env->attack;
+            if(tmp < env->env_counter){ // overflow
+                tmp = ~((uint32_t) 0); // max out
+                env->env_phase = ENV_ADSR_PHASE_DECAY;
+            }
+            env->env_counter = tmp;
+            break;
+        case ENV_ADSR_PHASE_DECAY:
+            tmp = env->env_counter - env->decay;
+            if(tmp > env->env_counter){ // underflow
+                tmp = 0; //bottom out
+            }
+            env->env_counter = tmp;
+
+            if(env->env_counter <= env->sustain){
+                env->env_counter = env->sustain;
+                env->env_phase = ENV_ADSR_PHASE_SUSTAIN;
+            } else if(env->env_counter < env->gate){
+                env->env_counter = 0;
+                env->env_phase = ENV_ADSR_PHASE_OFF;
+            }
+            break;
+        case ENV_ADSR_PHASE_SUSTAIN:
+            if(env->sustain == 0) env->env_phase = ENV_ADSR_PHASE_OFF;
+            env->env_counter = env->sustain;
+            break;
+        case ENV_ADSR_PHASE_RELEASE:
+            tmp = env->env_counter - env->release;
+            if(tmp > env->env_counter){ // underflow
+                tmp = 0; //bottom out
+                env->env_phase = ENV_ADSR_PHASE_OFF;
+            }
+            env->env_counter = tmp;
+            /*
+            if(env->env_counter < env->gate){
+                env->env_counter = 0;
+                env->env_phase = ENV_ADSR_PHASE_OFF;
+            }
+            */
+            break;
+    }
+    return env->env_counter >> 17;
+}
+
+#define SAMPLE_RATE_SORRY 48000
+#define ENV_ADSR_UNDERSAMPLING 5
+
+
+static inline uint32_t env_adsr_time_ms_to_val_rise(uint16_t time_ms, uint32_t val){
+    if(!time_ms) return UINT32_MAX;
+    uint32_t div = time_ms * ((SAMPLE_RATE_SORRY)/1000);
+    return val/div;
+}
+
+static inline uint32_t uint32_sat_leftshift(uint32_t input, uint16_t left){
+    if(!left) return input; // nothing to do
+    if(input >> (32-left)) return UINT32_MAX; // sat
+    return input << left;
+}
+
+
+void env_adsr_run(radspa_t * env_adsr, uint16_t num_samples, uint32_t render_pass_id){
+    env_adsr_data_t * plugin_data = env_adsr->plugin_data;
+    radspa_signal_t * output_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_OUTPUT);
+    radspa_signal_t * phase_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_PHASE);
+    if((output_sig->buffer == NULL) && (phase_sig->buffer == NULL)) return;
+    radspa_signal_t * trigger_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_TRIGGER);
+    radspa_signal_t * input_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_INPUT);
+    radspa_signal_t * attack_sig = NULL;
+    radspa_signal_t * decay_sig = NULL;
+    radspa_signal_t * sustain_sig = NULL;
+    radspa_signal_t * release_sig = NULL;
+    radspa_signal_t * gate_sig = NULL;
+
+    int16_t ret = output_sig->value;
+
+    for(uint16_t i = 0; i < num_samples; i++){
+        static int16_t env = 0;
+
+        int16_t trigger = trigger_sig->get_value(trigger_sig, i, num_samples, render_pass_id);
+        int16_t vel = radspa_trigger_get(trigger, &(plugin_data->trigger_prev));
+
+        if(vel < 0){ // stop
+            if(plugin_data->env_phase != ENV_ADSR_PHASE_OFF){
+                plugin_data->env_phase = ENV_ADSR_PHASE_RELEASE;
+                plugin_data->release_init_val = plugin_data->env_counter;
+            }
+        } else if(vel > 0 ){ // start
+            plugin_data->env_phase = ENV_ADSR_PHASE_ATTACK;
+            plugin_data->velocity = ((uint32_t) vel) << 17;
+        }
+
+        if(!(i%(1<<ENV_ADSR_UNDERSAMPLING))){
+            uint16_t time_ms;
+            uint32_t sus;
+            switch(plugin_data->env_phase){
+                case ENV_ADSR_PHASE_OFF:
+                    break;
+                case ENV_ADSR_PHASE_ATTACK:
+                    if(attack_sig == NULL){
+                        attack_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_ATTACK);
+                    }
+                    time_ms = attack_sig->get_value(attack_sig, i, num_samples, render_pass_id);
+                    if(time_ms != plugin_data->attack_prev_ms){
+                        plugin_data->attack = uint32_sat_leftshift(env_adsr_time_ms_to_val_rise(time_ms, UINT32_MAX), ENV_ADSR_UNDERSAMPLING);
+                        plugin_data->attack_prev_ms = time_ms;
+                    }
+                    break;
+                case ENV_ADSR_PHASE_DECAY:
+                    if(sustain_sig == NULL){
+                        sustain_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_SUSTAIN);
+                    }
+                    sus = sustain_sig->get_value(sustain_sig, i, num_samples, render_pass_id);
+                    plugin_data->sustain = sus<<17;
+
+                    if(gate_sig == NULL){
+                        gate_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_GATE);
+                    }
+                    sus = gate_sig->get_value(gate_sig, i, num_samples, render_pass_id);
+                    plugin_data->gate = sus<<17;
+
+                    if(decay_sig == NULL){
+                        decay_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_DECAY);
+                    }
+                    time_ms = decay_sig->get_value(decay_sig, i, num_samples, render_pass_id);
+                    if(time_ms != plugin_data->decay_prev_ms){
+                        plugin_data->decay = uint32_sat_leftshift(env_adsr_time_ms_to_val_rise(time_ms, UINT32_MAX-plugin_data->sustain), ENV_ADSR_UNDERSAMPLING);
+                        plugin_data->decay_prev_ms = time_ms;
+                    }
+                    break;
+                case ENV_ADSR_PHASE_SUSTAIN:
+                    if(sustain_sig == NULL){
+                        sustain_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_SUSTAIN);
+                    }
+                    sus = sustain_sig->get_value(sustain_sig, i, num_samples, render_pass_id);
+                    plugin_data->sustain = sus<<17;
+                    break;
+                case ENV_ADSR_PHASE_RELEASE:
+                    if(gate_sig == NULL){
+                        gate_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_GATE);
+                    }
+                    sus = gate_sig->get_value(gate_sig, i, num_samples, render_pass_id);
+                    plugin_data->gate = sus<<17;
+
+                    if(release_sig == NULL){
+                        release_sig = radspa_signal_get_by_index(env_adsr, ENV_ADSR_RELEASE);
+                    }
+                    time_ms = release_sig->get_value(release_sig, i, num_samples, render_pass_id);
+                    if(time_ms != plugin_data->release_prev_ms){
+                        plugin_data->release = uint32_sat_leftshift(env_adsr_time_ms_to_val_rise(time_ms, plugin_data->release_init_val), ENV_ADSR_UNDERSAMPLING);
+                        plugin_data->release_prev_ms = time_ms;
+                    }
+                    break;
+            }
+            env = env_adsr_run_single(plugin_data);
+        }
+        if(env){
+            int16_t input = input_sig->get_value(input_sig, i, num_samples, render_pass_id);
+            ret = radspa_mult_shift(env, input);
+        } else {
+            ret = 0;
+        }
+        if(phase_sig->buffer != NULL) (phase_sig->buffer)[i] = plugin_data->env_phase;
+        if(output_sig->buffer != NULL) (output_sig->buffer)[i] = ret;
+    }
+    phase_sig->value = plugin_data->env_phase;
+    output_sig->value = ret;
+}
diff --git a/components/bl00mbox/plugins/env_adsr.h b/components/bl00mbox/plugins/env_adsr.h
new file mode 100644
index 0000000000..c2d9534514
--- /dev/null
+++ b/components/bl00mbox/plugins/env_adsr.h
@@ -0,0 +1,25 @@
+#pragma once
+#include "radspa.h"
+#include "radspa_helpers.h"
+
+typedef struct {
+    uint32_t    env_counter;
+    uint32_t    attack;
+    uint32_t    decay;
+    uint32_t    sustain;
+    uint32_t    release;
+    uint32_t    release_init_val;
+    uint16_t    attack_prev_ms;
+    uint16_t    decay_prev_ms;
+    uint16_t    release_prev_ms;
+    uint32_t    gate;
+    uint32_t    velocity;
+    uint8_t     env_phase;
+    uint8_t     skip_hold;
+    int16_t     trigger_prev;
+} env_adsr_data_t;
+
+extern radspa_descriptor_t env_adsr_desc;
+radspa_t * env_adsr_create(uint32_t init_var);
+void env_adsr_run(radspa_t * osc, uint16_t num_samples, uint32_t render_pass_id);
+
diff --git a/components/bl00mbox/plugins/flanger.c b/components/bl00mbox/plugins/flanger.c
new file mode 100644
index 0000000000..085ff80b62
--- /dev/null
+++ b/components/bl00mbox/plugins/flanger.c
@@ -0,0 +1,119 @@
+#include "flanger.h"
+
+radspa_t * flanger_create(uint32_t init_var);
+radspa_descriptor_t flanger_desc = {
+    .name = "flanger",
+    .id = 123,
+    .description = "flanger with subsample interpolation and negative mix/resonance capability.\
+                    does not come with lfo.",
+    .create_plugin_instance = flanger_create,
+    .destroy_plugin_instance = radspa_standard_plugin_destroy
+};
+
+#define FLANGER_BUFFER_SIZE 4800
+#define FIXED_POINT_DIGITS 4
+#define VARIABLE_NAME ((FLANGER_BUFFER_SIZE)<<(FIXED_POINT_DIGITS))
+
+#define FLANGER_NUM_SIGNALS 6
+#define FLANGER_OUTPUT 0
+#define FLANGER_INPUT 1
+#define FLANGER_MANUAL 2
+#define FLANGER_RESONANCE 3
+#define FLANGER_LEVEL 4
+#define FLANGER_MIX 5
+
+static int16_t fixed_point_list_access(int32_t * buf, uint32_t fp_index, uint32_t buf_len){
+    uint32_t index = (fp_index) >> (FIXED_POINT_DIGITS);
+    while(index >= buf_len) index -= buf_len;
+    uint32_t next_index = index + 1;
+    while(next_index >= buf_len) next_index -= buf_len;
+
+    uint32_t subindex = (fp_index) & ((1<<(FIXED_POINT_DIGITS)) - 1);
+    int32_t ret =  buf[index] * ((1<<(FIXED_POINT_DIGITS)) - subindex);
+    ret += (buf[next_index]) * subindex;
+    ret = ret >> (FIXED_POINT_DIGITS);
+    return radspa_clip(ret);
+}
+
+void flanger_run(radspa_t * flanger, uint16_t num_samples, uint32_t render_pass_id){
+    radspa_signal_t * output_sig = radspa_signal_get_by_index(flanger, FLANGER_OUTPUT);
+    if(output_sig->buffer == NULL) return;
+    flanger_data_t * data = flanger->plugin_data;
+    int32_t * buf = flanger->plugin_table;
+    radspa_signal_t * input_sig = radspa_signal_get_by_index(flanger, FLANGER_INPUT);
+    radspa_signal_t * manual_sig = radspa_signal_get_by_index(flanger, FLANGER_MANUAL);
+    radspa_signal_t * reso_sig = radspa_signal_get_by_index(flanger, FLANGER_RESONANCE);
+    radspa_signal_t * level_sig = radspa_signal_get_by_index(flanger, FLANGER_LEVEL);
+    radspa_signal_t * mix_sig = radspa_signal_get_by_index(flanger, FLANGER_MIX);
+
+    static int16_t ret = 0;
+
+    for(uint16_t i = 0; i < num_samples; i++){
+        int32_t manual = manual_sig->get_value(manual_sig, i, num_samples, render_pass_id);
+        if(manual != data->manual_prev){
+            // index propto 1/radspa_sct_to_rel_freq(manual) -> signflip faster
+            int32_t invert = ((2400*(FIXED_POINT_DIGITS)) - 7572) - manual;
+            uint32_t rho = radspa_sct_to_rel_freq(radspa_clip(invert), 0);
+            if(rho > VARIABLE_NAME) rho = VARIABLE_NAME;
+            data->read_head_offset = rho;
+        }
+        data->manual_prev = manual;
+
+        data->write_head_position++;
+        while(data->write_head_position >= FLANGER_BUFFER_SIZE) data->write_head_position -= FLANGER_BUFFER_SIZE;
+        data->read_head_position = ((data->write_head_position)<<(FIXED_POINT_DIGITS));
+        data->read_head_position -= data->read_head_offset;
+        while(data->read_head_position < 0) data->read_head_position += VARIABLE_NAME; //underflow
+
+        int32_t dry = input_sig->get_value(input_sig, i, num_samples, render_pass_id);
+        int16_t reso = reso_sig->get_value(reso_sig, i, num_samples, render_pass_id);
+        int16_t level = level_sig->get_value(level_sig, i, num_samples, render_pass_id);
+        int16_t mix = mix_sig->get_value(mix_sig, i, num_samples, render_pass_id);
+
+        buf[data->write_head_position] = dry;
+        int32_t wet = fixed_point_list_access(buf, data->read_head_position, FLANGER_BUFFER_SIZE);
+        buf[data->write_head_position] += radspa_mult_shift(wet, reso);
+        
+        int16_t dry_vol = (mix>0) ? (32767-mix) : (32767+mix); //always pos polarity
+
+        ret = radspa_add_sat(radspa_mult_shift(dry, dry_vol), radspa_mult_shift(radspa_clip(wet), mix));
+        ret = radspa_clip(radspa_mult_shift(ret, level));
+        (output_sig->buffer)[i] = ret;
+    }
+    output_sig->value = ret;
+}
+
+radspa_t * flanger_create(uint32_t init_var){
+    radspa_t * flanger = radspa_standard_plugin_create(&flanger_desc, FLANGER_NUM_SIGNALS, sizeof(flanger_data_t),
+                                                            2 * FLANGER_BUFFER_SIZE);
+    if(flanger == NULL) return NULL;
+    flanger_data_t * plugin_data = flanger->plugin_data;
+    plugin_data->manual_prev = 40000;
+    flanger->render = flanger_run;
+    radspa_signal_set(flanger, FLANGER_OUTPUT, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0);
+    radspa_signal_set(flanger, FLANGER_INPUT, "input", RADSPA_SIGNAL_HINT_INPUT, 0);
+    radspa_signal_set(flanger, FLANGER_MANUAL, "manual", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_SCT, 18367);
+    radspa_signal_set(flanger, FLANGER_RESONANCE, "resonance", RADSPA_SIGNAL_HINT_INPUT, 16000);
+    radspa_signal_set(flanger, FLANGER_LEVEL, "level", RADSPA_SIGNAL_HINT_INPUT, 16000);
+    radspa_signal_set(flanger, FLANGER_MIX, "mix", RADSPA_SIGNAL_HINT_INPUT, 1<<14);
+    return flanger;
+}
+
+#undef FLANGER_DLY_GAIN
+#undef FLANGER_BUFFER_SIZE
+#undef FLANGER_NUM_SIGNALS
+#undef FLANGER_OUTPUT
+#undef FLANGER_INPUT
+#undef FLANGER_MANUAL
+#undef FLANGER_RESONANCE
+#undef FLANGER_LEVEL
+#undef FLANGER_MIX
+#undef FIXED_POINT_DIGITS
+
+/* delay_time = 1/freq
+ * delay_samples = delay_time * SAMPLE_RATE
+ * freq(sct) = pow(2, (sct + 2708)/2400))
+ * freq = sct_to_rel_freq(sct) * SAMPLE_RATE / UINT32_MAX
+ * delay_samples = UINT32_MAX / sct_to_rel_freq(sct)
+ * delay_samples = sct_to_rel_freq(-7572-sct + 2400 * FIXED_POINT_DIGITS)
+ */
diff --git a/components/bl00mbox/plugins/flanger.h b/components/bl00mbox/plugins/flanger.h
new file mode 100644
index 0000000000..62be7e19cb
--- /dev/null
+++ b/components/bl00mbox/plugins/flanger.h
@@ -0,0 +1,15 @@
+#pragma once
+#include <radspa.h>
+#include <radspa_helpers.h>
+
+typedef struct {
+    uint32_t write_head_position;
+    int32_t read_head_position;
+    int32_t read_head_offset;
+    int32_t manual_prev;
+} flanger_data_t;
+
+extern radspa_descriptor_t flanger_desc;
+radspa_t * flanger_create(uint32_t init_var);
+void flanger_run(radspa_t * osc, uint16_t num_samples, uint32_t render_pass_id);
+
diff --git a/components/bl00mbox/plugins/noise.c b/components/bl00mbox/plugins/noise.c
new file mode 100644
index 0000000000..3c0cb2bed7
--- /dev/null
+++ b/components/bl00mbox/plugins/noise.c
@@ -0,0 +1,36 @@
+#include "noise.h"
+
+radspa_t * noise_create(uint32_t init_var);
+radspa_descriptor_t noise_desc = {
+    .name = "noise",
+    .id = 0,
+    .description = "random data",
+    .create_plugin_instance = noise_create,
+    .destroy_plugin_instance = radspa_standard_plugin_destroy
+};
+
+#define NOISE_NUM_SIGNALS 1
+#define NOISE_OUTPUT 0
+
+void noise_run(radspa_t * noise, uint16_t num_samples, uint32_t render_pass_id){
+    radspa_signal_t * output_sig = radspa_signal_get_by_index(noise, NOISE_OUTPUT);
+    if(output_sig->buffer == NULL) return;
+
+    static int16_t ret = 0;
+    for(uint16_t i = 0; i < num_samples; i++){
+        (output_sig->buffer)[i] = radspa_random();
+
+    }
+    output_sig->value = ret;
+}
+
+radspa_t * noise_create(uint32_t init_var){
+    radspa_t * noise = radspa_standard_plugin_create(&noise_desc, NOISE_NUM_SIGNALS, sizeof(char), 0);
+    if(noise == NULL) return NULL;
+    noise->render = noise_run;
+    radspa_signal_set(noise, NOISE_OUTPUT, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0);
+    return noise;
+}
+
+#undef NOISE_NUM_SIGNALS
+#undef NOISE_OUTPUT
diff --git a/components/bl00mbox/plugins/noise.h b/components/bl00mbox/plugins/noise.h
new file mode 100644
index 0000000000..709f71bd98
--- /dev/null
+++ b/components/bl00mbox/plugins/noise.h
@@ -0,0 +1,8 @@
+#pragma once
+#include <radspa.h>
+#include <radspa_helpers.h>
+
+extern radspa_descriptor_t noise_desc;
+radspa_t * noise_create(uint32_t init_var);
+void noise_run(radspa_t * osc, uint16_t num_samples, uint32_t render_pass_id);
+
diff --git a/components/bl00mbox/plugins/osc_fm.c b/components/bl00mbox/plugins/osc_fm.c
new file mode 100644
index 0000000000..ed525eec33
--- /dev/null
+++ b/components/bl00mbox/plugins/osc_fm.c
@@ -0,0 +1,100 @@
+#include "osc_fm.h"
+
+static inline int16_t waveshaper(int16_t saw, int16_t shape);
+
+radspa_descriptor_t osc_fm_desc = {
+    .name = "osc_fm",
+    .id = 420,
+    .description = "simple audio band oscillator with classic waveforms and linear fm input",
+    .create_plugin_instance = osc_fm_create,
+    .destroy_plugin_instance = radspa_standard_plugin_destroy
+};
+
+#define OSC_FM_NUM_SIGNALS 4
+#define OSC_FM_OUTPUT 0
+#define OSC_FM_PITCH 1
+#define OSC_FM_WAVEFORM 2
+#define OSC_FM_LIN_FM 3
+
+radspa_t * osc_fm_create(uint32_t init_var){
+    radspa_t * osc_fm = radspa_standard_plugin_create(&osc_fm_desc, OSC_FM_NUM_SIGNALS, sizeof(osc_fm_data_t), 0);
+    osc_fm->render = osc_fm_run;
+    radspa_signal_set(osc_fm, OSC_FM_OUTPUT, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0);
+    radspa_signal_set(osc_fm, OSC_FM_PITCH, "pitch", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_SCT, 18367);
+    radspa_signal_set(osc_fm, OSC_FM_WAVEFORM, "waveform", RADSPA_SIGNAL_HINT_INPUT, -16000);
+    radspa_signal_set(osc_fm, OSC_FM_LIN_FM, "lin_fm", RADSPA_SIGNAL_HINT_INPUT, 0);
+    return osc_fm;
+}
+
+void osc_fm_run(radspa_t * osc_fm, uint16_t num_samples, uint32_t render_pass_id){
+    osc_fm_data_t * plugin_data = osc_fm->plugin_data;
+    radspa_signal_t * output_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_OUTPUT);
+    radspa_signal_t * pitch_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_PITCH);
+    radspa_signal_t * waveform_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_WAVEFORM);
+    radspa_signal_t * lin_fm_sig = radspa_signal_get_by_index(osc_fm, OSC_FM_LIN_FM);
+    if(output_sig->buffer == NULL) return;
+
+    int16_t ret = 0;
+    for(uint16_t i = 0; i < num_samples; i++){
+        int16_t pitch = pitch_sig->get_value(pitch_sig, i, num_samples, render_pass_id);
+        int16_t wave = waveform_sig->get_value(waveform_sig, i, num_samples, render_pass_id);
+        int32_t lin_fm = lin_fm_sig->get_value(lin_fm_sig, i, num_samples, render_pass_id);
+
+        if(pitch != plugin_data->prev_pitch){
+            plugin_data->incr = radspa_sct_to_rel_freq(pitch, 0);
+            plugin_data->prev_pitch = pitch;
+        }
+        plugin_data->counter += plugin_data->incr;
+        if(lin_fm){
+            plugin_data->counter += lin_fm * (plugin_data->incr >> 15);
+        }
+
+        int32_t tmp = (plugin_data->counter) >> 17;
+        tmp = (tmp*2) - 32767;
+        ret = waveshaper(tmp, wave);
+        (output_sig->buffer)[i] = ret;
+    }
+    output_sig->value = ret;
+}
+
+static inline int16_t triangle(int16_t saw){
+    int32_t tmp = saw;
+    tmp += 16384;
+    if(tmp > 32767) tmp -= 65535;
+    if(tmp > 0) tmp = -tmp;
+    tmp = (2 * tmp) + 32767;
+    return tmp;
+}
+
+inline int16_t waveshaper(int16_t saw, int16_t shape){
+    int32_t tmp = saw;
+    uint8_t sh = ((uint16_t) shape) >> 14;
+    sh = (sh + 2)%4;
+    switch(sh){
+        case 0: //sine
+            tmp = triangle(tmp);
+            if(tmp > 0){
+                tmp = 32767 - tmp;
+                tmp = (tmp*tmp)>>15;
+                tmp = 32767. - tmp;
+            } else {
+                tmp = 32767 + tmp;
+                tmp = (tmp*tmp)>>15;
+                tmp = tmp - 32767.;
+            }
+            break;
+        case 1: //tri
+            tmp = triangle(tmp);
+            break;
+        case 2: //square:
+            if(tmp > 0){
+                tmp = 32767;
+            } else {
+                tmp = -32767;
+            }
+            break;
+        default: //saw
+            break;
+    }
+    return tmp;
+}
diff --git a/components/bl00mbox/plugins/osc_fm.h b/components/bl00mbox/plugins/osc_fm.h
new file mode 100644
index 0000000000..e51113a785
--- /dev/null
+++ b/components/bl00mbox/plugins/osc_fm.h
@@ -0,0 +1,14 @@
+#pragma once
+#include "radspa.h"
+#include "radspa_helpers.h"
+
+typedef struct {
+    uint32_t counter;
+    int16_t prev_pitch;
+    int32_t incr;
+} osc_fm_data_t;
+
+extern radspa_descriptor_t osc_fm_desc;
+radspa_t * osc_fm_create(uint32_t init_var);
+void osc_fm_run(radspa_t * osc, uint16_t num_samples, uint32_t render_pass_id);
+
diff --git a/components/bl00mbox/plugins/trad_synth/trad_synth.c b/components/bl00mbox/plugins/trad_synth/trad_synth.c
deleted file mode 100644
index aa1032bf85..0000000000
--- a/components/bl00mbox/plugins/trad_synth/trad_synth.c
+++ /dev/null
@@ -1,328 +0,0 @@
-#include "trad_synth.h"
-
-// plugin descriptions in trad_synth.h
-
-static inline int16_t waveshaper(int16_t saw, int16_t shape);
-
-// plugin: trad_osc
-radspa_descriptor_t trad_osc_desc = {
-    .name = "osc_fm",
-    .id = 420,
-    .description = "simple audio band oscillator with classic waveforms",
-    .create_plugin_instance = trad_osc_create,
-    .destroy_plugin_instance = radspa_standard_plugin_destroy
-};
-
-#define TRAD_OSC_NUM_SIGNALS 4
-#define TRAD_OSC_OUTPUT 0
-#define TRAD_OSC_PITCH 1
-#define TRAD_OSC_WAVEFORM 2
-#define TRAD_OSC_LIN_FM 3
-
-radspa_t * trad_osc_create(uint32_t init_var){
-    radspa_t * trad_osc = radspa_standard_plugin_create(&trad_osc_desc, TRAD_OSC_NUM_SIGNALS, sizeof(trad_osc_data_t), 0);
-    trad_osc->render = trad_osc_run;
-    radspa_signal_set(trad_osc, TRAD_OSC_OUTPUT, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0);
-    radspa_signal_set(trad_osc, TRAD_OSC_PITCH, "pitch", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_SCT, 18367);
-    radspa_signal_set(trad_osc, TRAD_OSC_WAVEFORM, "waveform", RADSPA_SIGNAL_HINT_INPUT, -16000);
-    radspa_signal_set(trad_osc, TRAD_OSC_LIN_FM, "lin_fm", RADSPA_SIGNAL_HINT_INPUT, 0);
-    return trad_osc;
-}
-
-void trad_osc_run(radspa_t * trad_osc, uint16_t num_samples, uint32_t render_pass_id){
-    trad_osc_data_t * plugin_data = trad_osc->plugin_data;
-    radspa_signal_t * output_sig = radspa_signal_get_by_index(trad_osc, TRAD_OSC_OUTPUT);
-    radspa_signal_t * pitch_sig = radspa_signal_get_by_index(trad_osc, TRAD_OSC_PITCH);
-    radspa_signal_t * waveform_sig = radspa_signal_get_by_index(trad_osc, TRAD_OSC_WAVEFORM);
-    radspa_signal_t * lin_fm_sig = radspa_signal_get_by_index(trad_osc, TRAD_OSC_LIN_FM);
-    if(output_sig->buffer == NULL) return;
-
-    int16_t ret = 0;
-    for(uint16_t i = 0; i < num_samples; i++){
-        int16_t pitch = pitch_sig->get_value(pitch_sig, i, num_samples, render_pass_id);
-        int16_t wave = waveform_sig->get_value(waveform_sig, i, num_samples, render_pass_id);
-        int32_t lin_fm = lin_fm_sig->get_value(lin_fm_sig, i, num_samples, render_pass_id);
-
-        if(pitch != plugin_data->prev_pitch){
-            plugin_data->incr = radspa_sct_to_rel_freq(pitch, 0);
-            plugin_data->prev_pitch = pitch;
-        }
-        plugin_data->counter += plugin_data->incr;
-        if(lin_fm){
-            plugin_data->counter += lin_fm * (plugin_data->incr >> 15);
-        }
-
-        int32_t tmp = (plugin_data->counter) >> 17;
-        tmp = (tmp*2) - 32767;
-        ret = waveshaper(tmp, wave);
-        (output_sig->buffer)[i] = ret;
-    }
-    output_sig->value = ret;
-}
-
-static inline int16_t triangle(int16_t saw){
-    int32_t tmp = saw;
-    tmp += 16384;
-    if(tmp > 32767) tmp -= 65535;
-    if(tmp > 0) tmp = -tmp;
-    tmp = (2 * tmp) + 32767;
-    return tmp;
-}
-
-inline int16_t waveshaper(int16_t saw, int16_t shape){
-    int32_t tmp = saw;
-    uint8_t sh = ((uint16_t) shape) >> 14;
-    sh = (sh + 2)%4;
-    switch(sh){
-        case 0: //sine
-            tmp = triangle(tmp);
-            if(tmp > 0){
-                tmp = 32767 - tmp;
-                tmp = (tmp*tmp)>>15;
-                tmp = 32767. - tmp;
-            } else {
-                tmp = 32767 + tmp;
-                tmp = (tmp*tmp)>>15;
-                tmp = tmp - 32767.;
-            }
-            break;
-        case 1: //tri
-            tmp = triangle(tmp);
-            break;
-        case 2: //square:
-            if(tmp > 0){
-                tmp = 32767;
-            } else {
-                tmp = -32767;
-            }
-            break;
-        default: //saw
-            break;
-    }
-    return tmp;
-}
-
-// plugin: trad_env
-radspa_descriptor_t trad_env_desc = {
-    .name = "env_adsr",
-    .id = 42,
-    .description = "simple ADSR envelope",
-    .create_plugin_instance = trad_env_create,
-    .destroy_plugin_instance = radspa_standard_plugin_destroy
-};
-
-#define TRAD_ENV_NUM_SIGNALS 9
-#define TRAD_ENV_OUTPUT 0
-#define TRAD_ENV_PHASE 1
-#define TRAD_ENV_INPUT 2
-#define TRAD_ENV_TRIGGER 3
-#define TRAD_ENV_ATTACK 4
-#define TRAD_ENV_DECAY 5
-#define TRAD_ENV_SUSTAIN 6
-#define TRAD_ENV_RELEASE 7
-#define TRAD_ENV_GATE 8
-
-#define TRAD_ENV_PHASE_OFF 0
-#define TRAD_ENV_PHASE_ATTACK 1
-#define TRAD_ENV_PHASE_DECAY 2
-#define TRAD_ENV_PHASE_SUSTAIN 3
-#define TRAD_ENV_PHASE_RELEASE 4
-
-radspa_t * trad_env_create(uint32_t init_var){
-    radspa_t * trad_env = radspa_standard_plugin_create(&trad_env_desc, TRAD_ENV_NUM_SIGNALS, sizeof(trad_env_data_t),0);
-    trad_env->render = trad_env_run;
-    radspa_signal_set(trad_env, TRAD_ENV_OUTPUT, "output", RADSPA_SIGNAL_HINT_OUTPUT, 0);
-    radspa_signal_set(trad_env, TRAD_ENV_PHASE, "phase", RADSPA_SIGNAL_HINT_OUTPUT, 0);
-    radspa_signal_set(trad_env, TRAD_ENV_INPUT, "input", RADSPA_SIGNAL_HINT_INPUT, 32767);
-    radspa_signal_set(trad_env, TRAD_ENV_TRIGGER, "trigger", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_TRIGGER, 0);
-    radspa_signal_set(trad_env, TRAD_ENV_ATTACK, "attack", RADSPA_SIGNAL_HINT_INPUT, 100);
-    radspa_signal_set(trad_env, TRAD_ENV_DECAY, "decay", RADSPA_SIGNAL_HINT_INPUT, 250);
-    radspa_signal_set(trad_env, TRAD_ENV_SUSTAIN, "sustain", RADSPA_SIGNAL_HINT_INPUT, 16000);
-    radspa_signal_set(trad_env, TRAD_ENV_RELEASE, "release", RADSPA_SIGNAL_HINT_INPUT, 50);
-    radspa_signal_set(trad_env, TRAD_ENV_GATE, "gate", RADSPA_SIGNAL_HINT_INPUT,0);
-    radspa_signal_get_by_index(trad_env, TRAD_ENV_ATTACK)->unit = "ms";
-    radspa_signal_get_by_index(trad_env, TRAD_ENV_DECAY)->unit = "ms";
-    radspa_signal_get_by_index(trad_env, TRAD_ENV_SUSTAIN)->unit = "ms";
-
-    trad_env_data_t * data = trad_env->plugin_data;
-    data->trigger_prev = 0;
-    data->env_phase = TRAD_ENV_PHASE_OFF;
-
-    return trad_env;
-}
-
-static int16_t trad_env_run_single(trad_env_data_t * env){
-    uint32_t tmp;
-    switch(env->env_phase){
-        case TRAD_ENV_PHASE_OFF:
-            env->env_counter = 0;;
-            break;
-        case TRAD_ENV_PHASE_ATTACK:
-            tmp = env->env_counter + env->attack;
-            if(tmp < env->env_counter){ // overflow
-                tmp = ~((uint32_t) 0); // max out
-                env->env_phase = TRAD_ENV_PHASE_DECAY;
-            }
-            env->env_counter = tmp;
-            break;
-        case TRAD_ENV_PHASE_DECAY:
-            tmp = env->env_counter - env->decay;
-            if(tmp > env->env_counter){ // underflow
-                tmp = 0; //bottom out
-            }
-            env->env_counter = tmp;
-
-            if(env->env_counter <= env->sustain){
-                env->env_counter = env->sustain;
-                env->env_phase = TRAD_ENV_PHASE_SUSTAIN;
-            } else if(env->env_counter < env->gate){
-                env->env_counter = 0;
-                env->env_phase = TRAD_ENV_PHASE_OFF;
-            }
-            break;
-        case TRAD_ENV_PHASE_SUSTAIN:
-            if(env->sustain == 0) env->env_phase = TRAD_ENV_PHASE_OFF;
-            env->env_counter = env->sustain;
-            break;
-        case TRAD_ENV_PHASE_RELEASE:
-            tmp = env->env_counter - env->release;
-            if(tmp > env->env_counter){ // underflow
-                tmp = 0; //bottom out
-                env->env_phase = TRAD_ENV_PHASE_OFF;
-            }
-            env->env_counter = tmp;
-            /*
-            if(env->env_counter < env->gate){
-                env->env_counter = 0;
-                env->env_phase = TRAD_ENV_PHASE_OFF;
-            }
-            */
-            break;
-    }
-    return env->env_counter >> 17;
-}
-
-#define SAMPLE_RATE_SORRY 48000
-#define TRAD_ENV_UNDERSAMPLING 5
-
-
-static inline uint32_t trad_env_time_ms_to_val_rise(uint16_t time_ms, uint32_t val){
-    if(!time_ms) return UINT32_MAX;
-    uint32_t div = time_ms * ((SAMPLE_RATE_SORRY)/1000);
-    return val/div;
-}
-
-static inline uint32_t uint32_sat_leftshift(uint32_t input, uint16_t left){
-    if(!left) return input; // nothing to do
-    if(input >> (32-left)) return UINT32_MAX; // sat
-    return input << left;
-}
-
-
-void trad_env_run(radspa_t * trad_env, uint16_t num_samples, uint32_t render_pass_id){
-    trad_env_data_t * plugin_data = trad_env->plugin_data;
-    radspa_signal_t * output_sig = radspa_signal_get_by_index(trad_env, TRAD_ENV_OUTPUT);
-    radspa_signal_t * phase_sig = radspa_signal_get_by_index(trad_env, TRAD_ENV_PHASE);
-    if((output_sig->buffer == NULL) && (phase_sig->buffer == NULL)) return;
-    radspa_signal_t * trigger_sig = radspa_signal_get_by_index(trad_env, TRAD_ENV_TRIGGER);
-    radspa_signal_t * input_sig = radspa_signal_get_by_index(trad_env, TRAD_ENV_INPUT);
-    radspa_signal_t * attack_sig = NULL;
-    radspa_signal_t * decay_sig = NULL;
-    radspa_signal_t * sustain_sig = NULL;
-    radspa_signal_t * release_sig = NULL;
-    radspa_signal_t * gate_sig = NULL;
-
-    int16_t ret = output_sig->value;
-
-    for(uint16_t i = 0; i < num_samples; i++){
-        static int16_t env = 0;
-
-        int16_t trigger = trigger_sig->get_value(trigger_sig, i, num_samples, render_pass_id);
-        int16_t vel = radspa_trigger_get(trigger, &(plugin_data->trigger_prev));
-
-        if(vel < 0){ // stop
-            if(plugin_data->env_phase != TRAD_ENV_PHASE_OFF){
-                plugin_data->env_phase = TRAD_ENV_PHASE_RELEASE;
-                plugin_data->release_init_val = plugin_data->env_counter;
-            }
-        } else if(vel > 0 ){ // start
-            plugin_data->env_phase = TRAD_ENV_PHASE_ATTACK;
-            plugin_data->velocity = ((uint32_t) vel) << 17;
-        }
-
-        if(!(i%(1<<TRAD_ENV_UNDERSAMPLING))){
-            uint16_t time_ms;
-            uint32_t sus;
-            switch(plugin_data->env_phase){
-                case TRAD_ENV_PHASE_OFF:
-                    break;
-                case TRAD_ENV_PHASE_ATTACK:
-                    if(attack_sig == NULL){
-                        attack_sig = radspa_signal_get_by_index(trad_env, TRAD_ENV_ATTACK);
-                    }
-                    time_ms = attack_sig->get_value(attack_sig, i, num_samples, render_pass_id);
-                    if(time_ms != plugin_data->attack_prev_ms){
-                        plugin_data->attack = uint32_sat_leftshift(trad_env_time_ms_to_val_rise(time_ms, UINT32_MAX), TRAD_ENV_UNDERSAMPLING);
-                        plugin_data->attack_prev_ms = time_ms;
-                    }
-                    break;
-                case TRAD_ENV_PHASE_DECAY:
-                    if(sustain_sig == NULL){
-                        sustain_sig = radspa_signal_get_by_index(trad_env, TRAD_ENV_SUSTAIN);
-                    }
-                    sus = sustain_sig->get_value(sustain_sig, i, num_samples, render_pass_id);
-                    plugin_data->sustain = sus<<17;
-
-                    if(gate_sig == NULL){
-                        gate_sig = radspa_signal_get_by_index(trad_env, TRAD_ENV_GATE);
-                    }
-                    sus = gate_sig->get_value(gate_sig, i, num_samples, render_pass_id);
-                    plugin_data->gate = sus<<17;
-
-                    if(decay_sig == NULL){
-                        decay_sig = radspa_signal_get_by_index(trad_env, TRAD_ENV_DECAY);
-                    }
-                    time_ms = decay_sig->get_value(decay_sig, i, num_samples, render_pass_id);
-                    if(time_ms != plugin_data->decay_prev_ms){
-                        plugin_data->decay = uint32_sat_leftshift(trad_env_time_ms_to_val_rise(time_ms, UINT32_MAX-plugin_data->sustain), TRAD_ENV_UNDERSAMPLING);
-                        plugin_data->decay_prev_ms = time_ms;
-                    }
-                    break;
-                case TRAD_ENV_PHASE_SUSTAIN:
-                    if(sustain_sig == NULL){
-                        sustain_sig = radspa_signal_get_by_index(trad_env, TRAD_ENV_SUSTAIN);
-                    }
-                    sus = sustain_sig->get_value(sustain_sig, i, num_samples, render_pass_id);
-                    plugin_data->sustain = sus<<17;
-                    break;
-                case TRAD_ENV_PHASE_RELEASE:
-                    if(gate_sig == NULL){
-                        gate_sig = radspa_signal_get_by_index(trad_env, TRAD_ENV_GATE);
-                    }
-                    sus = gate_sig->get_value(gate_sig, i, num_samples, render_pass_id);
-                    plugin_data->gate = sus<<17;
-
-                    if(release_sig == NULL){
-                        release_sig = radspa_signal_get_by_index(trad_env, TRAD_ENV_RELEASE);
-                    }
-                    time_ms = release_sig->get_value(release_sig, i, num_samples, render_pass_id);
-                    if(time_ms != plugin_data->release_prev_ms){
-                        plugin_data->release = uint32_sat_leftshift(trad_env_time_ms_to_val_rise(time_ms, plugin_data->release_init_val), TRAD_ENV_UNDERSAMPLING);
-                        plugin_data->release_prev_ms = time_ms;
-                    }
-                    break;
-            }
-            env = trad_env_run_single(plugin_data);
-        }
-        if(env){
-            int16_t input = input_sig->get_value(input_sig, i, num_samples, render_pass_id);
-            ret = radspa_mult_shift(env, input);
-        } else {
-            ret = 0;
-        }
-        if(phase_sig->buffer != NULL) (phase_sig->buffer)[i] = plugin_data->env_phase;
-        if(output_sig->buffer != NULL) (output_sig->buffer)[i] = ret;
-    }
-    phase_sig->value = plugin_data->env_phase;
-    output_sig->value = ret;
-}
diff --git a/components/bl00mbox/plugins/trad_synth/trad_synth.h b/components/bl00mbox/plugins/trad_synth/trad_synth.h
deleted file mode 100644
index e186cc7d35..0000000000
--- a/components/bl00mbox/plugins/trad_synth/trad_synth.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#pragma once
-#include <math.h>
-#include "radspa.h"
-#include "radspa_helpers.h"
-
-/* provides traditional synthesizer functionality distributed over several plugins.
- */
-
-/* plugin: trad_osc
- * oscillator that can generate sine, square, saw and triangle waves in the audio band. uses trad_wave.
- */
-
-typedef struct {
-    uint32_t counter;
-    int16_t prev_pitch;
-    int32_t incr;
-} trad_osc_data_t;
-
-extern radspa_descriptor_t trad_osc_desc;
-radspa_t * trad_osc_create(uint32_t init_var);
-void trad_osc_run(radspa_t * osc, uint16_t num_samples, uint32_t render_pass_id);
-
-typedef struct {
-    uint32_t    env_counter;
-    uint32_t    attack;
-    uint32_t    decay;
-    uint32_t    sustain;
-    uint32_t    release;
-    uint32_t    release_init_val;
-    uint16_t    attack_prev_ms;
-    uint16_t    decay_prev_ms;
-    uint16_t    release_prev_ms;
-    uint32_t    gate;
-    uint32_t    velocity;
-    uint8_t     env_phase;
-    uint8_t     skip_hold;
-    int16_t     trigger_prev;
-} trad_env_data_t;
-
-extern radspa_descriptor_t trad_env_desc;
-radspa_t * trad_env_create(uint32_t init_var);
-void trad_env_run(radspa_t * osc, uint16_t num_samples, uint32_t render_pass_id);
-
diff --git a/components/bl00mbox/radspa/radspa.h b/components/bl00mbox/radspa/radspa.h
index 8093a71d98..5ad964d53b 100644
--- a/components/bl00mbox/radspa/radspa.h
+++ b/components/bl00mbox/radspa/radspa.h
@@ -1,10 +1,20 @@
 //SPDX-License-Identifier: CC0-1.0
 
-// Version 0.1.0
+// Please do not define a new version number. If want to distribute a modified version of
+// this file, kindly append "-modified" to the version string below so it is not mistaken
+// for an official release.
+
+// Version 0.1.0+
 
 /* Realtime Audio Developer's Simple Plugin Api
  *
  * Written from scratch but largely inspired by faint memories of the excellent ladspa.h
+ *
+ * Plugins may only include this file and the corresponding "radspa_helpers.h" with
+ * the same version string. Specifically, do not include <math.h> no matter how tempting
+ * it may be - it's a notoriously slow library on most architectures and has no place
+ * in realtime audio synthesis.
+ *
  * For a simple plugin implementation example check ampliverter.c/.h :D
  */
 
@@ -28,11 +38,14 @@
 
 // signal hints
 
-#define RADSPA_SIGNAL_HINT_INPUT 1
-#define RADSPA_SIGNAL_HINT_OUTPUT 2
-#define RADSPA_SIGNAL_HINT_TRIGGER 4
+#define RADSPA_SIGNAL_HINT_INPUT (1<<0)
+#define RADSPA_SIGNAL_HINT_OUTPUT (1<<1)
+#define RADSPA_SIGNAL_HINT_TRIGGER (1<<2)
+#define RADSPA_SIGNAL_HINT_VOL (1<<3)
+#define RADSPA_SIGNAL_HINT_SCT (1<<5)
 
-#define RADSPA_SIGNAL_HINT_SCT 32
+#define RADSPA_SIGNAL_VAL_SCT_A440 (INT16_MAX - 6*2400)
+#define RADSPA_SIGNAL_VAL_UNITY_GAIN (1<<11)
 
 struct _radspa_descriptor_t;
 struct _radspa_signal_t;
@@ -107,3 +120,5 @@ extern int16_t radspa_mult_shift(int32_t a, int32_t b);
 extern int16_t radspa_trigger_start(int16_t velocity, int16_t * hist);
 extern int16_t radspa_trigger_stop(int16_t * hist);
 extern int16_t radspa_trigger_get(int16_t trigger_signal, int16_t * hist);
+
+extern int16_t radspa_random();
-- 
GitLab