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;