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(&liverter_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