From 6d77af6319d0b25ae3ff3bbb4fad97e62956bd17 Mon Sep 17 00:00:00 2001 From: Stefano D'Angelo Date: Thu, 6 Apr 2023 12:03:39 +0200 Subject: [PATCH] fix modulation bug in bw_comb + bw_chorus tentative --- examples/fx_chorus/daisy-seed/Makefile | 7 + .../fx_chorus/daisy-seed/config_daisy_seed.h | 39 +++ examples/fx_chorus/src/bw_example_fx_chorus.c | 71 +++++ examples/fx_chorus/src/bw_example_fx_chorus.h | 62 ++++ examples/fx_chorus/src/config.h | 93 ++++++ examples/fx_chorus/vst3/Makefile | 6 + examples/fx_chorus/vst3/config_vst3.h | 36 +++ examples/fx_chorus/web/Makefile | 5 + examples/fx_chorus/web/config.js | 58 ++++ include/bw_chorus.h | 281 ++++++++++++++++++ include/bw_comb.h | 14 +- 11 files changed, 665 insertions(+), 7 deletions(-) create mode 100644 examples/fx_chorus/daisy-seed/Makefile create mode 100644 examples/fx_chorus/daisy-seed/config_daisy_seed.h create mode 100644 examples/fx_chorus/src/bw_example_fx_chorus.c create mode 100644 examples/fx_chorus/src/bw_example_fx_chorus.h create mode 100644 examples/fx_chorus/src/config.h create mode 100644 examples/fx_chorus/vst3/Makefile create mode 100644 examples/fx_chorus/vst3/config_vst3.h create mode 100644 examples/fx_chorus/web/Makefile create mode 100644 examples/fx_chorus/web/config.js create mode 100644 include/bw_chorus.h diff --git a/examples/fx_chorus/daisy-seed/Makefile b/examples/fx_chorus/daisy-seed/Makefile new file mode 100644 index 0000000..04db8c7 --- /dev/null +++ b/examples/fx_chorus/daisy-seed/Makefile @@ -0,0 +1,7 @@ +ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) + +TARGET = bw_example_fx_chorus + +C_SOURCES += ${ROOT_DIR}/../src/bw_example_fx_chorus.c + +include ${ROOT_DIR}/../../common/daisy-seed/daisy-seed.mk diff --git a/examples/fx_chorus/daisy-seed/config_daisy_seed.h b/examples/fx_chorus/daisy-seed/config_daisy_seed.h new file mode 100644 index 0000000..0e5825d --- /dev/null +++ b/examples/fx_chorus/daisy-seed/config_daisy_seed.h @@ -0,0 +1,39 @@ +/* + * Brickworks + * + * Copyright (C) 2023 Orastron Srl unipersonale + * + * Brickworks is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * Brickworks is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Brickworks. If not, see . + * + * File authors: Stefano D'Angelo + */ + +#ifndef _CONFIG_DAISY_SEED_H +#define _CONFIG_DAISY_SEED_H + +struct config_pin { + int param_index; + int pin; +}; + +#define NUM_PINS 5 + +static struct config_pin config_pins[NUM_PINS] = { + { 0, 15 }, + { 1, 16 }, + { 2, 17 }, + { 3, 18 }, + { 4, 19 }, +}; + +#endif diff --git a/examples/fx_chorus/src/bw_example_fx_chorus.c b/examples/fx_chorus/src/bw_example_fx_chorus.c new file mode 100644 index 0000000..6080c0e --- /dev/null +++ b/examples/fx_chorus/src/bw_example_fx_chorus.c @@ -0,0 +1,71 @@ +/* + * Brickworks + * + * Copyright (C) 2023 Orastron Srl unipersonale + * + * Brickworks is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * Brickworks is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Brickworks. If not, see . + * + * File author: Stefano D'Angelo + */ + +#include "bw_example_fx_chorus.h" + +void bw_example_fx_chorus_init(bw_example_fx_chorus *instance) { + bw_chorus_init(&instance->chorus_coeffs, 0.1f); +} + +void bw_example_fx_chorus_set_sample_rate(bw_example_fx_chorus *instance, float sample_rate) { + bw_chorus_set_sample_rate(&instance->chorus_coeffs, sample_rate); +} + +BW_SIZE_T bw_example_fx_chorus_mem_req(bw_example_fx_chorus *instance) { + return bw_chorus_mem_req(&instance->chorus_coeffs); +} + +void bw_example_fx_chorus_mem_set(bw_example_fx_chorus *instance, void *mem) { + bw_chorus_mem_set(&instance->chorus_state, mem); +} + +void bw_example_fx_chorus_reset(bw_example_fx_chorus *instance) { + bw_chorus_reset_coeffs(&instance->chorus_coeffs); + bw_chorus_reset_state(&instance->chorus_coeffs, &instance->chorus_state); +} + +void bw_example_fx_chorus_process(bw_example_fx_chorus *instance, const float** x, float** y, int n_samples) { + bw_chorus_process(&instance->chorus_coeffs, &instance->chorus_state, x[0], y[0], n_samples); +} + +void bw_example_fx_chorus_set_parameter(bw_example_fx_chorus *instance, int index, float value) { + instance->params[index] = value; + switch (index) { + case p_rate: + bw_chorus_set_rate(&instance->chorus_coeffs, value); + break; + case p_delay: + bw_chorus_set_delay(&instance->chorus_coeffs, 0.1f * value); + break; + case p_amount: + bw_chorus_set_amount(&instance->chorus_coeffs, 0.05f * value); + break; + case p_input: + bw_chorus_set_coeff_x(&instance->chorus_coeffs, value); + break; + case p_mod: + bw_chorus_set_coeff_mod(&instance->chorus_coeffs, value); + break; + } +} + +float bw_example_fx_chorus_get_parameter(bw_example_fx_chorus *instance, int index) { + return instance->params[index]; +} diff --git a/examples/fx_chorus/src/bw_example_fx_chorus.h b/examples/fx_chorus/src/bw_example_fx_chorus.h new file mode 100644 index 0000000..19aaf9f --- /dev/null +++ b/examples/fx_chorus/src/bw_example_fx_chorus.h @@ -0,0 +1,62 @@ +/* + * Brickworks + * + * Copyright (C) 2023 Orastron Srl unipersonale + * + * Brickworks is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * Brickworks is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Brickworks. If not, see . + * + * File author: Stefano D'Angelo + */ + +#ifndef _BW_EXAMPLE_FX_CHORUS_H +#define _BW_EXAMPLE_FX_CHORUS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +enum { + p_rate, + p_delay, + p_amount, + p_input, + p_mod, + p_n +}; + +struct _bw_example_fx_chorus { + // Sub-components + bw_chorus_coeffs chorus_coeffs; + bw_chorus_state chorus_state; + + // Parameters + float params[p_n]; +}; +typedef struct _bw_example_fx_chorus bw_example_fx_chorus; + +void bw_example_fx_chorus_init(bw_example_fx_chorus *instance); +void bw_example_fx_chorus_set_sample_rate(bw_example_fx_chorus *instance, float sample_rate); +BW_SIZE_T bw_example_fx_chorus_mem_req(bw_example_fx_chorus *instance); +void bw_example_fx_chorus_mem_set(bw_example_fx_chorus *instance, void *mem); +void bw_example_fx_chorus_reset(bw_example_fx_chorus *instance); +void bw_example_fx_chorus_process(bw_example_fx_chorus *instance, const float** x, float** y, int n_samples); +void bw_example_fx_chorus_set_parameter(bw_example_fx_chorus *instance, int index, float value); +float bw_example_fx_chorus_get_parameter(bw_example_fx_chorus *instance, int index); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/examples/fx_chorus/src/config.h b/examples/fx_chorus/src/config.h new file mode 100644 index 0000000..434537d --- /dev/null +++ b/examples/fx_chorus/src/config.h @@ -0,0 +1,93 @@ +/* + * Brickworks + * + * Copyright (C) 2023 Orastron Srl unipersonale + * + * Brickworks is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * Brickworks is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Brickworks. If not, see . + * + * File authors: Stefano D'Angelo + */ + +#ifndef _CONFIG_H +#define _CONFIG_H + +// Definitions + +#define IO_MONO 1 +#define IO_STEREO (1<<1) + +struct config_io_bus { + const char *name; + char out; + char aux; + char cv; + char configs; +}; + +struct config_parameter { + const char *name; + const char *shortName; + const char *units; + char out; + char bypass; + int steps; + float defaultValueUnmapped; +}; + +// Data + +#define COMPANY_NAME "Orastron" +#define COMPANY_WEBSITE "https://www.orastron.com/" +#define COMPANY_MAILTO "mailto:info@orastron.com" + +#define PLUGIN_NAME "bw_example_fx_chorus" +#define PLUGIN_VERSION "0.4.0" + +#define NUM_BUSES_IN 1 +#define NUM_BUSES_OUT 1 +#define NUM_CHANNELS_IN 1 +#define NUM_CHANNELS_OUT 1 + +static struct config_io_bus config_buses_in[NUM_BUSES_IN] = { + { "Audio in", 0, 0, 0, IO_MONO } +}; + +static struct config_io_bus config_buses_out[NUM_BUSES_OUT] = { + { "Audio out", 1, 0, 0, IO_MONO } +}; + +#define NUM_PARAMETERS 5 + +static struct config_parameter config_parameters[NUM_PARAMETERS] = { + { "Modulation rate", "Rate", "", 0, 0, 0, 0.5f }, + { "Delay", "Delay", "s", 0, 0, 0, 0.5f }, + { "Modulation amount", "Mod amount", "", 0, 0, 0, 0.5f }, + { "Input", "Input", "", 0, 0, 0, 0.5f }, + { "Modulated branch", "Mod branch", "", 0, 0, 0, 0.5f }, +}; + +// Internal API + +#include "bw_example_fx_chorus.h" + +#define P_TYPE bw_example_fx_chorus +#define P_INIT bw_example_fx_chorus_init +#define P_SET_SAMPLE_RATE bw_example_fx_chorus_set_sample_rate +#define P_MEM_REQ bw_example_fx_chorus_mem_req +#define P_MEM_SET bw_example_fx_chorus_mem_set +#define P_RESET bw_example_fx_chorus_reset +#define P_PROCESS bw_example_fx_chorus_process +#define P_SET_PARAMETER bw_example_fx_chorus_set_parameter +#define P_GET_PARAMETER bw_example_fx_chorus_get_parameter + +#endif diff --git a/examples/fx_chorus/vst3/Makefile b/examples/fx_chorus/vst3/Makefile new file mode 100644 index 0000000..b412ced --- /dev/null +++ b/examples/fx_chorus/vst3/Makefile @@ -0,0 +1,6 @@ +ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) + +NAME := bw_example_fx_chorus +SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_chorus.c + +include ${ROOT_DIR}/../../common/vst3/vst3.mk diff --git a/examples/fx_chorus/vst3/config_vst3.h b/examples/fx_chorus/vst3/config_vst3.h new file mode 100644 index 0000000..9d5ef90 --- /dev/null +++ b/examples/fx_chorus/vst3/config_vst3.h @@ -0,0 +1,36 @@ +/* + * Brickworks + * + * Copyright (C) 2023 Orastron Srl unipersonale + * + * Brickworks is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * Brickworks is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Brickworks. If not, see . + * + * File authors: Stefano D'Angelo, Paolo Marrone + */ + +#ifndef _VST3_CONFIG_H +#define _VST3_CONFIG_H + +#define PLUGIN_SUBCATEGORY "Fx|Modulation" + +#define PLUGIN_GUID_1 0x0715578d +#define PLUGIN_GUID_2 0x81f14d84 +#define PLUGIN_GUID_3 0xb5cb3f8c +#define PLUGIN_GUID_4 0x2c86b83e + +#define CTRL_GUID_1 0x5bf9980b +#define CTRL_GUID_2 0x4f564a04 +#define CTRL_GUID_3 0xbac8690c +#define CTRL_GUID_4 0x14712352 + +#endif diff --git a/examples/fx_chorus/web/Makefile b/examples/fx_chorus/web/Makefile new file mode 100644 index 0000000..5963de8 --- /dev/null +++ b/examples/fx_chorus/web/Makefile @@ -0,0 +1,5 @@ +ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_chorus.c +NEEDS_MEMSET := yes + +include ${ROOT_DIR}/../../common/web/web.mk diff --git a/examples/fx_chorus/web/config.js b/examples/fx_chorus/web/config.js new file mode 100644 index 0000000..861dc56 --- /dev/null +++ b/examples/fx_chorus/web/config.js @@ -0,0 +1,58 @@ +/* + * Brickworks + * + * Copyright (C) 2023 Orastron Srl unipersonale + * + * Brickworks is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * Brickworks is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Brickworks. If not, see . + * + * File author: Stefano D'Angelo + */ + +var buses = [ + { + stereo: false, + output: false + }, + { + stereo: false, + output: true + } +]; + +var parameters = [ + { + name: "Modulation rate", + output: false, + defaultValue: 0.5 + }, + { + name: "Delay", + output: false, + defaultValue: 0.5 + }, + { + name: "Modulation amount", + output: false, + defaultValue: 0.5 + }, + { + name: "Input", + output: false, + defaultValue: 0.5 + }, + { + name: "Modulated branch", + output: false, + defaultValue: 0.5 + } +]; diff --git a/include/bw_chorus.h b/include/bw_chorus.h new file mode 100644 index 0000000..225e66b --- /dev/null +++ b/include/bw_chorus.h @@ -0,0 +1,281 @@ +/* + * Brickworks + * + * Copyright (C) 2022, 2023 Orastron Srl unipersonale + * + * Brickworks is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * Brickworks is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Brickworks. If not, see . + * + * File author: Stefano D'Angelo + */ + +/*! + * module_type {{{ dsp }}} + * version {{{ 0.4.0 }}} + * requires {{{ + * bw_buf bw_config bw_comb bw_common bw_delay bw_gain bw_math bw_one_pole + * bw_osc_sin bw_phase_gen + * }}} + * description {{{ + * Chorus / vibrato with variable speed and amount. + * + * It outputs a mix of the dry input signal with itself going through a + * modulated delay. + * }}} + * changelog {{{ + *
    + *
  • Version 0.4.0: + *
  • First release.
  • + *
+ * + * + * }}} + */ + +#ifndef _BW_CHORUS_H +#define _BW_CHORUS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*! api {{{ + * #### bw_chorus_coeffs + * ```>>> */ +typedef struct _bw_chorus_coeffs bw_chorus_coeffs; +/*! <<<``` + * Coefficients and related. + * + * ### bw_chorus_state + * ```>>> */ +typedef struct _bw_chorus_state bw_chorus_state; +/*! <<<``` + * Internal state and related. + * + * #### bw_chorus_init() + * ```>>> */ +static inline void bw_chorus_init(bw_chorus_coeffs *BW_RESTRICT coeffs, float max_delay); +/*! <<<``` + * Initializes input parameter values in `coeffs` using `max_delay` (s) as + * the maximum delay time. + * + * #### bw_chorus_set_sample_rate() + * ```>>> */ +static inline void bw_chorus_set_sample_rate(bw_chorus_coeffs *BW_RESTRICT coeffs, float sample_rate); +/*! <<<``` + * Sets the `sample_rate` (Hz) value in `coeffs`. + * + * ### bw_chorus_mem_req() + * ```>>> */ +static inline BW_SIZE_T bw_chorus_mem_req(bw_chorus_coeffs *BW_RESTRICT coeffs); +/*! <<<``` + * Returns the size, in bytes, of contiguous memory to be supplied to + * `bw_chorus_mem_set()` using `coeffs`. + * + * ### bw_chorus_mem_set() + * ```>>> */ +static inline void bw_chorus_mem_set(bw_chorus_state *BW_RESTRICT state, void *mem); +/*! <<<``` + * Associates the contiguous memory block `mem` to the given `state`. + * + * #### bw_chorus_reset_coeffs() + * ```>>> */ +static inline void bw_chorus_reset_coeffs(bw_chorus_coeffs *BW_RESTRICT coeffs); +/*! <<<``` + * Resets coefficients in `coeffs` to assume their target values. + * + * #### bw_chorus_reset_state() + * ```>>> */ +static inline void bw_chorus_reset_state(const bw_chorus_coeffs *BW_RESTRICT coeffs, bw_chorus_state *BW_RESTRICT state); +/*! <<<``` + * Resets the given `state` to its initial values using the given `coeffs`. + * + * #### bw_chorus_update_coeffs_ctrl() + * ```>>> */ +static inline void bw_chorus_update_coeffs_ctrl(bw_chorus_coeffs *BW_RESTRICT coeffs); +/*! <<<``` + * Triggers control-rate update of coefficients in `coeffs`. + * + * #### bw_chorus_update_coeffs_audio() + * ```>>> */ +static inline void bw_chorus_update_coeffs_audio(bw_chorus_coeffs *BW_RESTRICT coeffs); +/*! <<<``` + * Triggers audio-rate update of coefficients in `coeffs`. + * + * #### bw_chorus_process1() + * ```>>> */ +static inline float bw_chorus_process1(const bw_chorus_coeffs *BW_RESTRICT coeffs, bw_chorus_state *BW_RESTRICT state, float x); +/*! <<<``` + * Processes one input sample `x` using `coeffs`, while using and updating + * `state`. Returns the corresponding output sample. + * + * #### bw_chorus_process() + * ```>>> */ +static inline void bw_chorus_process(bw_chorus_coeffs *BW_RESTRICT coeffs, bw_chorus_state *BW_RESTRICT state, const float *x, float *y, int n_samples); +/*! <<<``` + * Processes the first `n_samples` of the input buffer `x` and fills the + * first `n_samples` of the output buffer `y`, while using and updating both + * `coeffs` and `state` (control and audio rate). + * + * #### bw_chorus_set_rate() + * ```>>> */ +static inline void bw_chorus_set_rate(bw_chorus_coeffs *BW_RESTRICT coeffs, float value); +/*! <<<``` + * Sets the modulation rate `value` (Hz) in `coeffs`. + * + * Default value: `1.f`. + * + * #### bw_chorus_set_delay() + * ```>>> */ +static inline void bw_chorus_set_delay(bw_chorus_coeffs *BW_RESTRICT coeffs, float value); +/*! <<<``` + * Sets the center delay `value` (s) in `coeffs`. + * + * Default value: `0.f`. + * + * #### bw_chorus_set_amount() + * ```>>> */ +static inline void bw_chorus_set_amount(bw_chorus_coeffs *BW_RESTRICT coeffs, float value); +/*! <<<``` + * Sets the modulation amount (semi-amplitude) `value` (s) in `coeffs`. + * + * Default value: `0.f`. + * + * #### bw_chorus_set_coeff_x() + * ```>>> */ +static inline void bw_chorus_set_coeff_x(bw_chorus_coeffs *BW_RESTRICT coeffs, float value); +/*! <<<``` + * Sets the dry input coefficient `value` in `coeffs`. + * + * Default value: `0.f`. + * + * #### bw_chorus_set_coeff_mod() + * ```>>> */ +static inline void bw_chorus_set_coeff_mod(bw_chorus_coeffs *BW_RESTRICT coeffs, float value); +/*! <<<``` + * Sets the modulated branch coefficient `value` in `coeffs`. + * + * Default value: `1.f`. + * }}} */ + +/*** Implementation ***/ + +/* WARNING: This part of the file is not part of the public API. Its content may + * change at any time in future versions. Please, do not use it directly. */ + +#include +#include +#include + +struct _bw_chorus_coeffs { + // Sub-components + bw_phase_gen_coeffs phase_gen_coeffs; + bw_phase_gen_state phase_gen_state; + bw_comb_coeffs comb_coeffs; + + // Coefficients + float mod; + + // Parameters + float delay; + float amount; +}; + +struct _bw_chorus_state { + // Sub-components + bw_comb_state comb_state; +}; + +static inline void bw_chorus_init(bw_chorus_coeffs *BW_RESTRICT coeffs, float max_delay) { + bw_phase_gen_init(&coeffs->phase_gen_coeffs); + bw_comb_init(&coeffs->comb_coeffs, max_delay); + bw_comb_set_coeff_blend(&coeffs->comb_coeffs, 0.f); + bw_comb_set_coeff_ff(&coeffs->comb_coeffs, 1.f); + coeffs->delay = 0.f; + coeffs->amount = 0.f; +} + +static inline void bw_chorus_set_sample_rate(bw_chorus_coeffs *BW_RESTRICT coeffs, float sample_rate) { + bw_phase_gen_set_sample_rate(&coeffs->phase_gen_coeffs, sample_rate); + bw_comb_set_sample_rate(&coeffs->comb_coeffs, sample_rate); +} + +static inline BW_SIZE_T bw_chorus_mem_req(bw_chorus_coeffs *BW_RESTRICT coeffs) { + return bw_comb_mem_req(&coeffs->comb_coeffs); +} + +static inline void bw_chorus_mem_set(bw_chorus_state *BW_RESTRICT state, void *mem) { + bw_comb_mem_set(&state->comb_state, mem); +} + +static inline void bw_chorus_reset_coeffs(bw_chorus_coeffs *BW_RESTRICT coeffs) { + bw_phase_gen_reset_coeffs(&coeffs->phase_gen_coeffs); + bw_phase_gen_reset_state(&coeffs->phase_gen_coeffs, &coeffs->phase_gen_state, 0.f); + bw_comb_reset_coeffs(&coeffs->comb_coeffs); +} + +static inline void bw_chorus_reset_state(const bw_chorus_coeffs *BW_RESTRICT coeffs, bw_chorus_state *BW_RESTRICT state) { + bw_comb_reset_state(&coeffs->comb_coeffs, &state->comb_state); +} + +static inline void bw_chorus_update_coeffs_ctrl(bw_chorus_coeffs *BW_RESTRICT coeffs) { + bw_phase_gen_update_coeffs_ctrl(&coeffs->phase_gen_coeffs); + bw_comb_update_coeffs_ctrl(&coeffs->comb_coeffs); +} + +static inline void bw_chorus_update_coeffs_audio(bw_chorus_coeffs *BW_RESTRICT coeffs) { + bw_phase_gen_update_coeffs_audio(&coeffs->phase_gen_coeffs); + float p, pi; + bw_phase_gen_process1(&coeffs->phase_gen_coeffs, &coeffs->phase_gen_state, &p, &pi); + coeffs->mod = coeffs->delay + coeffs->amount * bw_osc_sin_process1(p); + bw_comb_update_coeffs_audio(&coeffs->comb_coeffs); +} + +static inline float bw_chorus_process1(const bw_chorus_coeffs *BW_RESTRICT coeffs, bw_chorus_state *BW_RESTRICT state, float x) { + return bw_comb_process1(&coeffs->comb_coeffs, &state->comb_state, x, coeffs->mod); +} + +static inline void bw_chorus_process(bw_chorus_coeffs *BW_RESTRICT coeffs, bw_chorus_state *BW_RESTRICT state, const float *x, float *y, int n_samples) { + bw_chorus_update_coeffs_ctrl(coeffs); + for (int i = 0; i < n_samples; i++) { + bw_chorus_update_coeffs_audio(coeffs); + y[i] = bw_chorus_process1(coeffs, state, x[i]); + } +} + +static inline void bw_chorus_set_rate(bw_chorus_coeffs *BW_RESTRICT coeffs, float value) { + bw_phase_gen_set_frequency(&coeffs->phase_gen_coeffs, value); +} + +static inline void bw_chorus_set_delay(bw_chorus_coeffs *BW_RESTRICT coeffs, float value) { + coeffs->delay = value; +} + +static inline void bw_chorus_set_amount(bw_chorus_coeffs *BW_RESTRICT coeffs, float value) { + coeffs->amount = value; +} + +static inline void bw_chorus_set_coeff_x(bw_chorus_coeffs *BW_RESTRICT coeffs, float value) { + bw_comb_set_coeff_blend(&coeffs->comb_coeffs, value); +} + +static inline void bw_chorus_set_coeff_mod(bw_chorus_coeffs *BW_RESTRICT coeffs, float value) { + bw_comb_set_coeff_ff(&coeffs->comb_coeffs, value); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/bw_comb.h b/include/bw_comb.h index 10bd1bd..be80bb5 100644 --- a/include/bw_comb.h +++ b/include/bw_comb.h @@ -85,7 +85,7 @@ static inline void bw_comb_set_sample_rate(bw_comb_coeffs *BW_RESTRICT coeffs, f static inline BW_SIZE_T bw_comb_mem_req(bw_comb_coeffs *BW_RESTRICT coeffs); /*! <<<``` * Returns the size, in bytes, of contiguous memory to be supplied to - * `bw_delay_mem_set()` using `coeffs`. + * `bw_comb_mem_set()` using `coeffs`. * * ### bw_comb_mem_set() * ```>>> */ @@ -250,8 +250,8 @@ static inline void bw_comb_reset_coeffs(bw_comb_coeffs *BW_RESTRICT coeffs) { bw_gain_reset_coeffs(&coeffs->blend_coeffs); bw_gain_reset_coeffs(&coeffs->ff_coeffs); bw_gain_reset_coeffs(&coeffs->fb_coeffs); - bw_one_pole_reset_state(&coeffs->smooth_coeffs, &coeffs->smooth_delay_ff_state, coeffs->fs * coeffs->delay_ff); - bw_one_pole_reset_state(&coeffs->smooth_coeffs, &coeffs->smooth_delay_fb_state, coeffs->fs * coeffs->delay_fb); + bw_one_pole_reset_state(&coeffs->smooth_coeffs, &coeffs->smooth_delay_ff_state, coeffs->delay_ff); + bw_one_pole_reset_state(&coeffs->smooth_coeffs, &coeffs->smooth_delay_fb_state, coeffs->delay_fb); } static inline void bw_comb_reset_state(const bw_comb_coeffs *BW_RESTRICT coeffs, bw_comb_state *BW_RESTRICT state) { @@ -268,10 +268,10 @@ static inline void bw_comb_update_coeffs_audio(bw_comb_coeffs *BW_RESTRICT coeff bw_gain_update_coeffs_audio(&coeffs->blend_coeffs); bw_gain_update_coeffs_audio(&coeffs->ff_coeffs); bw_gain_update_coeffs_audio(&coeffs->fb_coeffs); - bw_one_pole_process1(&coeffs->smooth_coeffs, &coeffs->smooth_delay_ff_state, coeffs->fs * coeffs->delay_ff); - bw_one_pole_process1(&coeffs->smooth_coeffs, &coeffs->smooth_delay_fb_state, coeffs->fs * coeffs->delay_fb); + bw_one_pole_process1(&coeffs->smooth_coeffs, &coeffs->smooth_delay_ff_state, coeffs->delay_ff); + bw_one_pole_process1(&coeffs->smooth_coeffs, &coeffs->smooth_delay_fb_state, coeffs->delay_fb); const BW_SIZE_T len = bw_delay_get_length(&coeffs->delay_coeffs); - const float dfb = bw_maxf(bw_one_pole_get_y_z1(&coeffs->smooth_delay_fb_state), 1.f) - 1.f; + const float dfb = bw_maxf(coeffs->fs * bw_one_pole_get_y_z1(&coeffs->smooth_delay_fb_state), 1.f) - 1.f; float dfbif, dfbf; bw_intfracf(dfb, &dfbif, &dfbf); BW_SIZE_T dfbi = (BW_SIZE_T)dfbif; @@ -285,7 +285,7 @@ static inline void bw_comb_update_coeffs_audio(bw_comb_coeffs *BW_RESTRICT coeff static inline float bw_comb_process1(const bw_comb_coeffs *BW_RESTRICT coeffs, bw_comb_state *BW_RESTRICT state, float x, float x_mod) { const BW_SIZE_T len = bw_delay_get_length(&coeffs->delay_coeffs); - const float dff = bw_maxf(bw_one_pole_get_y_z1(&coeffs->smooth_delay_ff_state) + x_mod, 0.f); + const float dff = bw_maxf(coeffs->fs * (bw_one_pole_get_y_z1(&coeffs->smooth_delay_ff_state) + x_mod), 0.f); float dffif, dfff; bw_intfracf(dff, &dffif, &dfff); BW_SIZE_T dffi = (BW_SIZE_T)dffif;