diff --git a/examples/fx_ring_mod/android/Makefile b/examples/fx_ring_mod/android/Makefile
new file mode 100644
index 0000000..6b0defe
--- /dev/null
+++ b/examples/fx_ring_mod/android/Makefile
@@ -0,0 +1,6 @@
+ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
+
+NAME := bw_example_fx_ring_mod
+SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_ring_mod.c
+
+include ${ROOT_DIR}/../../common/android/android.mk
diff --git a/examples/fx_ring_mod/daisy-seed/Makefile b/examples/fx_ring_mod/daisy-seed/Makefile
new file mode 100644
index 0000000..bddfb18
--- /dev/null
+++ b/examples/fx_ring_mod/daisy-seed/Makefile
@@ -0,0 +1,7 @@
+ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
+
+TARGET = bw_example_fx_ring_mod
+
+C_SOURCES += ${ROOT_DIR}/../src/bw_example_fx_ring_mod.c
+
+include ${ROOT_DIR}/../../common/daisy-seed/daisy-seed.mk
diff --git a/examples/fx_ring_mod/daisy-seed/config_daisy_seed.h b/examples/fx_ring_mod/daisy-seed/config_daisy_seed.h
new file mode 100644
index 0000000..c56c264
--- /dev/null
+++ b/examples/fx_ring_mod/daisy-seed/config_daisy_seed.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
+ */
+
+#ifndef _CONFIG_DAISY_SEED_H
+#define _CONFIG_DAISY_SEED_H
+
+struct config_pin {
+ int param_index;
+ int pin;
+};
+
+#define NUM_PINS 2
+
+static struct config_pin config_pins[NUM_PINS] = {
+ { 0, 15 },
+ { 1, 16 }
+};
+
+#endif
diff --git a/examples/fx_ring_mod/ios/Makefile b/examples/fx_ring_mod/ios/Makefile
new file mode 100644
index 0000000..b3ed2ec
--- /dev/null
+++ b/examples/fx_ring_mod/ios/Makefile
@@ -0,0 +1,6 @@
+ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
+
+NAME := bw_example_fx_ring_mod
+SOURCE := ${NAME}.c
+
+include ${ROOT_DIR}/../../common/ios/ios.mk
diff --git a/examples/fx_ring_mod/src/bw_example_fx_ring_mod.c b/examples/fx_ring_mod/src/bw_example_fx_ring_mod.c
new file mode 100644
index 0000000..faff936
--- /dev/null
+++ b/examples/fx_ring_mod/src/bw_example_fx_ring_mod.c
@@ -0,0 +1,62 @@
+/*
+ * 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
+ */
+
+#include "bw_example_fx_ring_mod.h"
+
+#include
+
+void bw_example_fx_ring_mod_init(bw_example_fx_ring_mod *instance) {
+ bw_phase_gen_init(&instance->phase_gen_coeffs);
+ bw_ring_mod_init(&instance->ring_mod_coeffs);
+}
+
+void bw_example_fx_ring_mod_set_sample_rate(bw_example_fx_ring_mod *instance, float sample_rate) {
+ bw_phase_gen_set_sample_rate(&instance->phase_gen_coeffs, sample_rate);
+ bw_ring_mod_set_sample_rate(&instance->ring_mod_coeffs, sample_rate);
+}
+
+void bw_example_fx_ring_mod_reset(bw_example_fx_ring_mod *instance) {
+ bw_phase_gen_reset_coeffs(&instance->phase_gen_coeffs);
+ float v, v_inc;
+ bw_phase_gen_reset_state(&instance->phase_gen_coeffs, &instance->phase_gen_state, 0.f, &v, &v_inc);
+ bw_ring_mod_reset_coeffs(&instance->ring_mod_coeffs);
+}
+
+void bw_example_fx_ring_mod_process(bw_example_fx_ring_mod *instance, const float** x, float** y, int n_samples) {
+ bw_phase_gen_process(&instance->phase_gen_coeffs, &instance->phase_gen_state, NULL, y[0], NULL, n_samples);
+ bw_osc_sin_process(y[0], y[0], n_samples);
+ bw_ring_mod_process(&instance->ring_mod_coeffs, x[0], y[0], y[0], n_samples);
+}
+
+void bw_example_fx_ring_mod_set_parameter(bw_example_fx_ring_mod *instance, int index, float value) {
+ instance->params[index] = value;
+ switch (index) {
+ case p_freq:
+ bw_phase_gen_set_frequency(&instance->phase_gen_coeffs, 100.f + (1000.f - 100.f) * value * value * value);
+ break;
+ case p_amount:
+ bw_ring_mod_set_amount(&instance->ring_mod_coeffs, -1.f + 2.f * value);
+ break;
+ }
+}
+
+float bw_example_fx_ring_mod_get_parameter(bw_example_fx_ring_mod *instance, int index) {
+ return instance->params[index];
+}
diff --git a/examples/fx_ring_mod/src/bw_example_fx_ring_mod.h b/examples/fx_ring_mod/src/bw_example_fx_ring_mod.h
new file mode 100644
index 0000000..542e465
--- /dev/null
+++ b/examples/fx_ring_mod/src/bw_example_fx_ring_mod.h
@@ -0,0 +1,61 @@
+/*
+ * 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_RING_MOD_H
+#define _BW_EXAMPLE_FX_RING_MOD_H
+
+#include "platform.h"
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ p_freq,
+ p_amount,
+ p_n
+};
+
+struct _bw_example_fx_ring_mod {
+ // Sub-components
+ bw_phase_gen_coeffs phase_gen_coeffs;
+ bw_phase_gen_state phase_gen_state;
+ bw_ring_mod_coeffs ring_mod_coeffs;
+
+ // Parameters
+ float params[p_n];
+};
+typedef struct _bw_example_fx_ring_mod bw_example_fx_ring_mod;
+
+void bw_example_fx_ring_mod_init(bw_example_fx_ring_mod *instance);
+void bw_example_fx_ring_mod_set_sample_rate(bw_example_fx_ring_mod *instance, float sample_rate);
+void bw_example_fx_ring_mod_reset(bw_example_fx_ring_mod *instance);
+void bw_example_fx_ring_mod_process(bw_example_fx_ring_mod *instance, const float** x, float** y, int n_samples);
+void bw_example_fx_ring_mod_set_parameter(bw_example_fx_ring_mod *instance, int index, float value);
+float bw_example_fx_ring_mod_get_parameter(bw_example_fx_ring_mod *instance, int index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/examples/fx_ring_mod/src/config.h b/examples/fx_ring_mod/src/config.h
new file mode 100644
index 0000000..a9de353
--- /dev/null
+++ b/examples/fx_ring_mod/src/config.h
@@ -0,0 +1,88 @@
+/*
+ * 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 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_ring_mod"
+#define PLUGIN_VERSION "1.0.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 2
+
+static struct config_parameter config_parameters[NUM_PARAMETERS] = {
+ { "Frequency", "Freq", "Hz", 0, 0, 0, 0.5f },
+ { "Amount", "Amount", "", 0, 0, 0, 1.0f },
+};
+
+// Internal API
+
+#include "bw_example_fx_ring_mod.h"
+
+#define P_TYPE bw_example_fx_ring_mod
+#define P_INIT bw_example_fx_ring_mod_init
+#define P_SET_SAMPLE_RATE bw_example_fx_ring_mod_set_sample_rate
+#define P_RESET bw_example_fx_ring_mod_reset
+#define P_PROCESS bw_example_fx_ring_mod_process
+#define P_SET_PARAMETER bw_example_fx_ring_mod_set_parameter
+#define P_GET_PARAMETER bw_example_fx_ring_mod_get_parameter
+
+#endif
diff --git a/examples/fx_ring_mod/src/config.js b/examples/fx_ring_mod/src/config.js
new file mode 100644
index 0000000..a47b8e7
--- /dev/null
+++ b/examples/fx_ring_mod/src/config.js
@@ -0,0 +1,43 @@
+/*
+ * 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: "Frequency",
+ output: false,
+ defaultValue: 0.5
+ },
+ {
+ name: "Amount",
+ output: false,
+ defaultValue: 1.0
+ }
+];
diff --git a/examples/fx_ring_mod/vst3/Makefile b/examples/fx_ring_mod/vst3/Makefile
new file mode 100644
index 0000000..9485c17
--- /dev/null
+++ b/examples/fx_ring_mod/vst3/Makefile
@@ -0,0 +1,9 @@
+ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
+
+NAME := bw_example_fx_ring_mod
+SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_ring_mod.c
+
+include ${ROOT_DIR}/../../common/vst3/vst3.mk
+
+CXXFLAGS += -DRELEASE=1 -DNDEBUG -DBW_NO_DEBUG
+#CXXFLAGS += -DDEVELOPMENT=1 -DBW_DEBUG_DEEP
diff --git a/examples/fx_ring_mod/vst3/config_vst3.h b/examples/fx_ring_mod/vst3/config_vst3.h
new file mode 100644
index 0000000..b222d04
--- /dev/null
+++ b/examples/fx_ring_mod/vst3/config_vst3.h
@@ -0,0 +1,36 @@
+/*
+ * Brickworks
+ *
+ * Copyright (C) 2022 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"
+
+#define PLUGIN_GUID_1 0x2680121d
+#define PLUGIN_GUID_2 0x4e0b47fa
+#define PLUGIN_GUID_3 0xb213e6e7
+#define PLUGIN_GUID_4 0xd13cde60
+
+#define CTRL_GUID_1 0xc747bf7a
+#define CTRL_GUID_2 0x90ac41d0
+#define CTRL_GUID_3 0xb53d3df9
+#define CTRL_GUID_4 0x2c81a74d
+
+#endif
diff --git a/examples/fx_ring_mod/web/Makefile b/examples/fx_ring_mod/web/Makefile
new file mode 100644
index 0000000..302c580
--- /dev/null
+++ b/examples/fx_ring_mod/web/Makefile
@@ -0,0 +1,4 @@
+ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
+SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_ring_mod.c
+
+include ${ROOT_DIR}/../../common/web/web.mk
diff --git a/examples/fxpp_ring_mod/android/Makefile b/examples/fxpp_ring_mod/android/Makefile
new file mode 100644
index 0000000..334f877
--- /dev/null
+++ b/examples/fxpp_ring_mod/android/Makefile
@@ -0,0 +1,6 @@
+ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
+
+NAME := bw_example_fxpp_ring_mod
+SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fxpp_ring_mod.cpp
+
+include ${ROOT_DIR}/../../common/android/android.mk
diff --git a/examples/fxpp_ring_mod/daisy-seed/Makefile b/examples/fxpp_ring_mod/daisy-seed/Makefile
new file mode 100644
index 0000000..1aba51c
--- /dev/null
+++ b/examples/fxpp_ring_mod/daisy-seed/Makefile
@@ -0,0 +1,7 @@
+ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
+
+TARGET = bw_example_fxpp_ring_mod
+
+CPP_SOURCES_EXTRA = ${ROOT_DIR}/../src/bw_example_fxpp_ring_mod.cpp
+
+include ${ROOT_DIR}/../../common/daisy-seed/daisy-seed.mk
diff --git a/examples/fxpp_ring_mod/daisy-seed/config_daisy_seed.h b/examples/fxpp_ring_mod/daisy-seed/config_daisy_seed.h
new file mode 100644
index 0000000..c56c264
--- /dev/null
+++ b/examples/fxpp_ring_mod/daisy-seed/config_daisy_seed.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
+ */
+
+#ifndef _CONFIG_DAISY_SEED_H
+#define _CONFIG_DAISY_SEED_H
+
+struct config_pin {
+ int param_index;
+ int pin;
+};
+
+#define NUM_PINS 2
+
+static struct config_pin config_pins[NUM_PINS] = {
+ { 0, 15 },
+ { 1, 16 }
+};
+
+#endif
diff --git a/examples/fxpp_ring_mod/ios/Makefile b/examples/fxpp_ring_mod/ios/Makefile
new file mode 100644
index 0000000..fd7d079
--- /dev/null
+++ b/examples/fxpp_ring_mod/ios/Makefile
@@ -0,0 +1,6 @@
+ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
+
+NAME := bw_example_fxpp_ring_mod
+SOURCE := ${NAME}.cpp
+
+include ${ROOT_DIR}/../../common/ios/ios.mk
diff --git a/examples/fxpp_ring_mod/src/bw_example_fxpp_ring_mod.cpp b/examples/fxpp_ring_mod/src/bw_example_fxpp_ring_mod.cpp
new file mode 100644
index 0000000..b38886d
--- /dev/null
+++ b/examples/fxpp_ring_mod/src/bw_example_fxpp_ring_mod.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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_fxpp_ring_mod.h"
+
+#include
+
+void bw_example_fxpp_ring_mod_init(bw_example_fxpp_ring_mod *instance) {
+ (void)instance;
+}
+
+void bw_example_fxpp_ring_mod_set_sample_rate(bw_example_fxpp_ring_mod *instance, float sample_rate) {
+ instance->phaseGen.setSampleRate(sample_rate);
+ instance->ringMod.setSampleRate(sample_rate);
+}
+
+void bw_example_fxpp_ring_mod_reset(bw_example_fxpp_ring_mod *instance) {
+ instance->phaseGen.reset();
+ instance->ringMod.reset();
+}
+
+void bw_example_fxpp_ring_mod_process(bw_example_fxpp_ring_mod *instance, const float** x, float** y, int n_samples) {
+ instance->phaseGen.process({}, {y[0]}, {}, n_samples);
+ oscSinProcess<1>({y[0]}, {y[0]}, n_samples);
+ instance->ringMod.process({x[0]}, {y[0]}, {y[0]}, n_samples);
+}
+
+void bw_example_fxpp_ring_mod_set_parameter(bw_example_fxpp_ring_mod *instance, int index, float value) {
+ instance->params[index] = value;
+ switch (index) {
+ case p_freq:
+ instance->phaseGen.setFrequency(100.f + (1000.f - 100.f) * value * value * value);
+ break;
+ case p_amount:
+ instance->ringMod.setAmount(-1.f + 2.f * value);
+ break;
+ }
+}
+
+float bw_example_fxpp_ring_mod_get_parameter(bw_example_fxpp_ring_mod *instance, int index) {
+ return instance->params[index];
+}
diff --git a/examples/fxpp_ring_mod/src/bw_example_fxpp_ring_mod.h b/examples/fxpp_ring_mod/src/bw_example_fxpp_ring_mod.h
new file mode 100644
index 0000000..899e4f3
--- /dev/null
+++ b/examples/fxpp_ring_mod/src/bw_example_fxpp_ring_mod.h
@@ -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
+ */
+
+#ifndef _BW_EXAMPLE_FXPP_WAH_H
+#define _BW_EXAMPLE_FXPP_WAH_H
+
+#include "platform.h"
+
+#include
+#include
+
+using namespace Brickworks;
+
+extern "C" {
+
+enum {
+ p_freq,
+ p_amount,
+ p_n
+};
+
+struct _bw_example_fxpp_ring_mod {
+ // Sub-components
+ PhaseGen<1> phaseGen;
+ RingMod<1> ringMod;
+
+ // Parameters
+ float params[p_n];
+};
+typedef struct _bw_example_fxpp_ring_mod bw_example_fxpp_ring_mod;
+
+void bw_example_fxpp_ring_mod_init(bw_example_fxpp_ring_mod *instance);
+void bw_example_fxpp_ring_mod_set_sample_rate(bw_example_fxpp_ring_mod *instance, float sample_rate);
+void bw_example_fxpp_ring_mod_reset(bw_example_fxpp_ring_mod *instance);
+void bw_example_fxpp_ring_mod_process(bw_example_fxpp_ring_mod *instance, const float** x, float** y, int n_samples);
+void bw_example_fxpp_ring_mod_set_parameter(bw_example_fxpp_ring_mod *instance, int index, float value);
+float bw_example_fxpp_ring_mod_get_parameter(bw_example_fxpp_ring_mod *instance, int index);
+
+}
+
+#endif
diff --git a/examples/fxpp_ring_mod/src/config.h b/examples/fxpp_ring_mod/src/config.h
new file mode 100644
index 0000000..81163f4
--- /dev/null
+++ b/examples/fxpp_ring_mod/src/config.h
@@ -0,0 +1,88 @@
+/*
+ * 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_fxpp_ring_mod"
+#define PLUGIN_VERSION "1.0.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 2
+
+static struct config_parameter config_parameters[NUM_PARAMETERS] = {
+ { "Frequency", "Freq", "Hz", 0, 0, 0, 0.5f },
+ { "Amount", "Amount", "", 0, 0, 0, 1.0f },
+};
+
+// Internal API
+
+#include "bw_example_fxpp_ring_mod.h"
+
+#define P_TYPE bw_example_fxpp_ring_mod
+#define P_INIT bw_example_fxpp_ring_mod_init
+#define P_SET_SAMPLE_RATE bw_example_fxpp_ring_mod_set_sample_rate
+#define P_RESET bw_example_fxpp_ring_mod_reset
+#define P_PROCESS bw_example_fxpp_ring_mod_process
+#define P_SET_PARAMETER bw_example_fxpp_ring_mod_set_parameter
+#define P_GET_PARAMETER bw_example_fxpp_ring_mod_get_parameter
+
+#endif
diff --git a/examples/fxpp_ring_mod/src/config.js b/examples/fxpp_ring_mod/src/config.js
new file mode 100644
index 0000000..a47b8e7
--- /dev/null
+++ b/examples/fxpp_ring_mod/src/config.js
@@ -0,0 +1,43 @@
+/*
+ * 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: "Frequency",
+ output: false,
+ defaultValue: 0.5
+ },
+ {
+ name: "Amount",
+ output: false,
+ defaultValue: 1.0
+ }
+];
diff --git a/examples/fxpp_ring_mod/vst3/Makefile b/examples/fxpp_ring_mod/vst3/Makefile
new file mode 100644
index 0000000..872f4ea
--- /dev/null
+++ b/examples/fxpp_ring_mod/vst3/Makefile
@@ -0,0 +1,9 @@
+ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
+
+NAME := bw_example_fxpp_ring_mod
+SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fxpp_ring_mod.cpp
+
+include ${ROOT_DIR}/../../common/vst3/vst3.mk
+
+CXXFLAGS += -DRELEASE=1 -DNDEBUG -DBW_NO_DEBUG
+#CXXFLAGS += -DDEVELOPMENT=1 -DBW_DEBUG_DEEP
diff --git a/examples/fxpp_ring_mod/vst3/config_vst3.h b/examples/fxpp_ring_mod/vst3/config_vst3.h
new file mode 100644
index 0000000..34f4c8b
--- /dev/null
+++ b/examples/fxpp_ring_mod/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"
+
+#define PLUGIN_GUID_1 0xf4737471
+#define PLUGIN_GUID_2 0xb94c4c95
+#define PLUGIN_GUID_3 0x8f4091ce
+#define PLUGIN_GUID_4 0x5da09fa8
+
+#define CTRL_GUID_1 0x1e8f7e86
+#define CTRL_GUID_2 0xc29447c1
+#define CTRL_GUID_3 0x91f175ce
+#define CTRL_GUID_4 0x220b5635
+
+#endif
diff --git a/include/bw_dry_wet.h b/include/bw_dry_wet.h
index 601a280..d1aa874 100644
--- a/include/bw_dry_wet.h
+++ b/include/bw_dry_wet.h
@@ -39,6 +39,8 @@
* Added overloaded C++ process()
function taking
* C-style arrays as arguments.
* Removed usage of reserved identifiers.
+ * Clearly specified parameter validity ranges.
+ * Added debugging code.
*
*
* Version 0.6.0:
@@ -73,44 +75,58 @@ typedef struct bw_dry_wet_coeffs bw_dry_wet_coeffs;
*
* #### bw_dry_wet_init()
* ```>>> */
-static inline void bw_dry_wet_init(bw_dry_wet_coeffs *BW_RESTRICT coeffs);
+static inline void bw_dry_wet_init(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs);
/*! <<<```
* Initializes input parameter values in `coeffs`.
*
* #### bw_dry_wet_set_sample_rate()
* ```>>> */
-static inline void bw_dry_wet_set_sample_rate(bw_dry_wet_coeffs *BW_RESTRICT coeffs, float sample_rate);
+static inline void bw_dry_wet_set_sample_rate(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs,
+ float sample_rate);
/*! <<<```
* Sets the `sample_rate` (Hz) value in `coeffs`.
*
* #### bw_dry_wet_reset_coeffs()
* ```>>> */
-static inline void bw_dry_wet_reset_coeffs(bw_dry_wet_coeffs *BW_RESTRICT coeffs);
+static inline void bw_dry_wet_reset_coeffs(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs);
/*! <<<```
* Resets coefficients in `coeffs` to assume their target values.
*
* #### bw_dry_wet_update_coeffs_ctrl()
* ```>>> */
-static inline void bw_dry_wet_update_coeffs_ctrl(bw_dry_wet_coeffs *BW_RESTRICT coeffs);
+static inline void bw_dry_wet_update_coeffs_ctrl(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs);
/*! <<<```
* Triggers control-rate update of coefficients in `coeffs`.
*
* #### bw_dry_wet_update_coeffs_audio()
* ```>>> */
-static inline void bw_dry_wet_update_coeffs_audio(bw_dry_wet_coeffs *BW_RESTRICT coeffs);
+static inline void bw_dry_wet_update_coeffs_audio(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs);
/*! <<<```
* Triggers audio-rate update of coefficients in `coeffs`.
*
* #### bw_dry_wet_process1()
* ```>>> */
-static inline float bw_dry_wet_process1(const bw_dry_wet_coeffs *BW_RESTRICT coeffs, float x_dry, float x_wet);
+static inline float bw_dry_wet_process1(
+ const bw_dry_wet_coeffs * BW_RESTRICT coeffs,
+ float x_dry,
+ float x_wet);
/*! <<<```
* Processes one dry input sample `x_dry` and one wet input sample `x_wet`
* using `coeffs` and returns the corresponding output sample.
*
* #### bw_dry_wet_process()
* ```>>> */
-static inline void bw_dry_wet_process(bw_dry_wet_coeffs *BW_RESTRICT coeffs, const float *x_dry, const float *x_wet, float *y, size_t n_samples);
+static inline void bw_dry_wet_process(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs,
+ const float * x_dry,
+ const float * x_wet,
+ float * y,
+ size_t n_samples);
/*! <<<```
* Processes the first `n_samples` of the dry input buffer `x_dry` and of the
* wet input buffer `x_wet` and fills the first `n_samples` of the output
@@ -118,7 +134,13 @@ static inline void bw_dry_wet_process(bw_dry_wet_coeffs *BW_RESTRICT coeffs, con
*
* #### bw_dry_wet_process_multi()
* ```>>> */
-static inline void bw_dry_wet_process_multi(bw_dry_wet_coeffs *BW_RESTRICT coeffs, const float * const *x_dry, const float * const *x_wet, float * const *y, size_t n_channels, size_t n_samples);
+static inline void bw_dry_wet_process_multi(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs,
+ const float * const * x_dry,
+ const float * const * x_wet,
+ float * const * y,
+ size_t n_channels,
+ size_t n_samples);
/*! <<<```
* Processes the first `n_samples` of the `n_channels` dry input buffers
* `x_dry` and of the `n_channels` wet input buffers `x_wet`, and fills the
@@ -127,19 +149,39 @@ static inline void bw_dry_wet_process_multi(bw_dry_wet_coeffs *BW_RESTRICT coeff
*
* #### bw_dry_wet_set_wet()
* ```>>> */
-static inline void bw_dry_wet_set_wet(bw_dry_wet_coeffs *BW_RESTRICT coeffs, float value);
+static inline void bw_dry_wet_set_wet(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs,
+ float value);
/*! <<<```
* Sets the wet gain parameter to the given `value` (linear gain) in `coeffs`.
*
+ * Valid range: [`0.f`, `1.f`].
+ *
* Default value: `1.f`.
*
* #### bw_dry_wet_set_smooth_tau()
* ```>>> */
-static inline void bw_dry_wet_set_smooth_tau(bw_dry_wet_coeffs *BW_RESTRICT coeffs, float value);
+static inline void bw_dry_wet_set_smooth_tau(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs,
+ float value);
/*! <<<```
* Sets the smoothing time constant `value` (s) in `coeffs`.
*
+ * `value` must be non-negative.
+ *
* Default value: `0.05f`.
+ *
+ * #### bw_dry_wet_coeffs_is_valid()
+ * ```>>> */
+static inline char bw_dry_wet_coeffs_is_valid(
+ const bw_dry_wet_coeffs * BW_RESTRICT coeffs);
+/*! <<<```
+ * Tries to determine whether `coeffs` is valid and returns non-`0` if it
+ * seems to be the case and `0` if it is certainly not. False positives are
+ * possible, false negatives are not.
+ *
+ * `coeffs` must at least point to a readable memory block of size greater
+ * than or equal to that of `bw_dry_wet_coeffs`.
* }}} */
#ifdef __cplusplus
@@ -157,58 +199,207 @@ static inline void bw_dry_wet_set_smooth_tau(bw_dry_wet_coeffs *BW_RESTRICT coef
extern "C" {
#endif
+#ifdef BW_DEBUG_DEEP
+enum bw_dry_wet_coeffs_state {
+ bw_dry_wet_coeffs_state_invalid,
+ bw_dry_wet_coeffs_state_init,
+ bw_dry_wet_coeffs_state_set_sample_rate,
+ bw_dry_wet_coeffs_state_reset_coeffs
+};
+#endif
+
struct bw_dry_wet_coeffs {
+#ifdef BW_DEBUG_DEEP
+ uint32_t hash;
+ enum bw_dry_wet_coeffs_state state;
+#endif
+
// Sub-components
- bw_gain_coeffs gain_coeffs;
+ bw_gain_coeffs gain_coeffs;
};
-static inline void bw_dry_wet_init(bw_dry_wet_coeffs *BW_RESTRICT coeffs) {
+static inline void bw_dry_wet_init(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+
bw_gain_init(&coeffs->gain_coeffs);
+
+#ifdef BW_DEBUG_DEEP
+ coeffs->hash = bw_hash_sdbm("bw_dry_wet_coeffs");
+ coeffs->state = bw_dry_wet_coeffs_state_init;
+#endif
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state == bw_dry_wet_coeffs_state_init);
}
-static inline void bw_dry_wet_set_sample_rate(bw_dry_wet_coeffs *BW_RESTRICT coeffs, float sample_rate) {
+static inline void bw_dry_wet_set_sample_rate(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs,
+ float sample_rate) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_init);
+ BW_ASSERT(bw_is_finite(sample_rate) && sample_rate > 0.f);
+
bw_gain_set_sample_rate(&coeffs->gain_coeffs, sample_rate);
+
+#ifdef BW_DEBUG_DEEP
+ coeffs->state = bw_dry_wet_coeffs_state_set_sample_rate;
+#endif
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state == bw_dry_wet_coeffs_state_set_sample_rate);
}
-static inline void bw_dry_wet_reset_coeffs(bw_dry_wet_coeffs *BW_RESTRICT coeffs) {
+static inline void bw_dry_wet_reset_coeffs(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_set_sample_rate);
+
bw_gain_reset_coeffs(&coeffs->gain_coeffs);
+
+#ifdef BW_DEBUG_DEEP
+ coeffs->state = bw_dry_wet_coeffs_state_reset_coeffs;
+#endif
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state == bw_dry_wet_coeffs_state_reset_coeffs);
}
-static inline void bw_dry_wet_update_coeffs_ctrl(bw_dry_wet_coeffs *BW_RESTRICT coeffs) {
+static inline void bw_dry_wet_update_coeffs_ctrl(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_reset_coeffs);
+
bw_gain_update_coeffs_ctrl(&coeffs->gain_coeffs);
+
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_reset_coeffs);
}
-static inline void bw_dry_wet_update_coeffs_audio(bw_dry_wet_coeffs *BW_RESTRICT coeffs) {
+static inline void bw_dry_wet_update_coeffs_audio(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_reset_coeffs);
+
bw_gain_update_coeffs_audio(&coeffs->gain_coeffs);
+
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_reset_coeffs);
}
-static inline float bw_dry_wet_process1(const bw_dry_wet_coeffs *BW_RESTRICT coeffs, float x_dry, float x_wet) {
- return bw_gain_process1(&coeffs->gain_coeffs, x_wet - x_dry) + x_dry;
+static inline float bw_dry_wet_process1(
+ const bw_dry_wet_coeffs * BW_RESTRICT coeffs,
+ float x_dry,
+ float x_wet) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_reset_coeffs);
+ BW_ASSERT(bw_is_finite(x_dry));
+ BW_ASSERT(bw_is_finite(x_wet));
+
+ const float y = bw_gain_process1(&coeffs->gain_coeffs, x_wet - x_dry) + x_dry;
+
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_reset_coeffs);
+ BW_ASSERT(bw_is_finite(*y));
+
+ return y;
}
-static inline void bw_dry_wet_process(bw_dry_wet_coeffs *BW_RESTRICT coeffs, const float *x_dry, const float *x_wet, float *y, size_t n_samples) {
+static inline void bw_dry_wet_process(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs,
+ const float * x_dry,
+ const float * x_wet,
+ float * y,
+ size_t n_samples) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_reset_coeffs);
+ BW_ASSERT(x_dry != NULL);
+ BW_ASSERT_DEEP(bw_has_only_finite(x_dry, n_samples));
+ BW_ASSERT(x_wet != NULL);
+ BW_ASSERT_DEEP(bw_has_only_finite(x_wet, n_samples));
+ BW_ASSERT(y != NULL);
+
bw_dry_wet_update_coeffs_ctrl(coeffs);
for (size_t i = 0; i < n_samples; i++) {
bw_dry_wet_update_coeffs_audio(coeffs);
y[i] = bw_dry_wet_process1(coeffs, x_dry[i], x_wet[i]);
}
+
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_reset_coeffs);
+ BW_ASSERT_DEEP(bw_has_only_finite(y, n_samples));
}
-static inline void bw_dry_wet_process_multi(bw_dry_wet_coeffs *BW_RESTRICT coeffs, const float * const *x_dry, const float * const *x_wet, float * const *y, size_t n_channels, size_t n_samples) {
+static inline void bw_dry_wet_process_multi(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs,
+ const float * const * x_dry,
+ const float * const * x_wet,
+ float * const * y,
+ size_t n_channels,
+ size_t n_samples) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_reset_coeffs);
+ BW_ASSERT(x_dry != NULL);
+ BW_ASSERT(x_wet != NULL);
+ BW_ASSERT(y != NULL);
+
bw_dry_wet_update_coeffs_ctrl(coeffs);
for (size_t i = 0; i < n_samples; i++) {
bw_dry_wet_update_coeffs_audio(coeffs);
for (size_t j = 0; j < n_channels; j++)
y[j][i] = bw_dry_wet_process1(coeffs, x_dry[j][i], x_wet[j][i]);
}
+
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_reset_coeffs);
}
-static inline void bw_dry_wet_set_wet(bw_dry_wet_coeffs *BW_RESTRICT coeffs, float value) {
+static inline void bw_dry_wet_set_wet(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs,
+ float value) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_init);
+ BW_ASSERT(bw_is_finite(value));
+ BW_ASSERT(value >= 0.f && value <= 1.f);
+
bw_gain_set_gain_lin(&coeffs->gain_coeffs, value);
+
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_init);
}
-static inline void bw_dry_wet_set_smooth_tau(bw_dry_wet_coeffs *BW_RESTRICT coeffs, float value) {
+static inline void bw_dry_wet_set_smooth_tau(
+ bw_dry_wet_coeffs * BW_RESTRICT coeffs,
+ float value) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_init);
+ BW_ASSERT(bw_is_finite(value));
+ BW_ASSERT(value >= 0.f);
+
bw_gain_set_smooth_tau(&coeffs->gain_coeffs, value);
+
+ BW_ASSERT_DEEP(bw_dry_wet_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_dry_wet_coeffs_state_init);
+}
+
+static inline char bw_dry_wet_coeffs_is_valid(
+ const bw_dry_wet_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+
+#ifdef BW_DEBUG_DEEP
+ if (coeffs->hash != bw_hash_sdbm("bw_dry_wet_coeffs"))
+ return 0;
+ if (coeffs->state < bw_dry_wet_coeffs_state_init || coeffs->state > bw_dry_wet_coeffs_state_reset_coeffs)
+ return 0;
+#endif
+
+ return bw_gain_coeffs_is_valid(&coeffs->gain_coeffs);
}
#ifdef __cplusplus
@@ -241,8 +432,28 @@ public:
std::array y,
size_t nSamples);
- void setWet(float value);
- void setSmoothTau(float value);
+ void setSampleRate(
+ float sampleRate);
+
+ void reset();
+
+ void process(
+ const float * const * xDry,
+ const float * const * xWet,
+ float * const * y,
+ size_t nSamples);
+
+ void process(
+ std::array xDry,
+ std::array xWet,
+ std::array y,
+ size_t nSamples);
+
+ void setWet(
+ float value);
+
+ void setSmoothTau(
+ float value);
/*! <<<...
* }
* ```
@@ -254,7 +465,7 @@ public:
* change at any time in future versions. Please, do not use it directly. */
private:
- bw_dry_wet_coeffs coeffs;
+ bw_dry_wet_coeffs coeffs;
};
template
@@ -263,7 +474,8 @@ inline DryWet::DryWet() {
}
template
-inline void DryWet::setSampleRate(float sampleRate) {
+inline void DryWet::setSampleRate(
+ float sampleRate) {
bw_dry_wet_set_sample_rate(&coeffs, sampleRate);
}
@@ -274,29 +486,31 @@ inline void DryWet::reset() {
template
inline void DryWet::process(
- const float * const *x_dry,
- const float * const *x_wet,
- float * const *y,
- size_t nSamples) {
- bw_dry_wet_process_multi(&coeffs, x_dry, x_wet, y, N_CHANNELS, nSamples);
+ const float * const * xDry,
+ const float * const * xWet,
+ float * const * y,
+ size_t nSamples) {
+ bw_dry_wet_process_multi(&coeffs, xDry, xWet, y, N_CHANNELS, nSamples);
}
template
inline void DryWet::process(
- std::array x_dry,
- std::array x_wet,
- std::array y,
- size_t nSamples) {
- process(x_dry.data(), x_wet.data(), y.data(), nSamples);
+ std::array xDry,
+ std::array xWet,
+ std::array y,
+ size_t nSamples) {
+ process(xDry.data(), xWet.data(), y.data(), nSamples);
}
template
-inline void DryWet::setWet(float value) {
+inline void DryWet::setWet(
+ float value) {
bw_dry_wet_set_wet(&coeffs, value);
}
template
-inline void DryWet::setSmoothTau(float value) {
+inline void DryWet::setSmoothTau(
+ float value) {
bw_dry_wet_set_smooth_tau(&coeffs, value);
}
diff --git a/include/bw_osc_pulse.h b/include/bw_osc_pulse.h
index 98ae0b9..8cdd928 100644
--- a/include/bw_osc_pulse.h
+++ b/include/bw_osc_pulse.h
@@ -131,7 +131,7 @@ static inline void bw_osc_pulse_update_coeffs_audio(bw_osc_pulse_coeffs *BW_REST
static inline float bw_osc_pulse_process1(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, float x);
static inline float bw_osc_pulse_process1_antialias(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, float x, float x_phase_inc);
/*! <<<```
- * These function process one input sample `x`, indicating the normalized
+ * These function process one input sample `x`, representing the normalized
* phase, using `coeffs`. They return the corresponding output sample.
*
* In particular:
diff --git a/include/bw_osc_saw.h b/include/bw_osc_saw.h
index dd27b35..ef28e5a 100644
--- a/include/bw_osc_saw.h
+++ b/include/bw_osc_saw.h
@@ -106,7 +106,7 @@ static inline void bw_osc_saw_init(bw_osc_saw_coeffs *BW_RESTRICT coeffs);
static inline float bw_osc_saw_process1(const bw_osc_saw_coeffs *BW_RESTRICT coeffs, float x);
static inline float bw_osc_saw_process1_antialias(const bw_osc_saw_coeffs *BW_RESTRICT coeffs, float x, float x_phase_inc);
/*! <<<```
- * These function process one input sample `x`, indicating the normalized
+ * These function process one input sample `x`, representing the normalized
* phase, using `coeffs`. They return the corresponding output sample.
*
* In particular:
diff --git a/include/bw_osc_sin.h b/include/bw_osc_sin.h
index f139872..30b58a7 100644
--- a/include/bw_osc_sin.h
+++ b/include/bw_osc_sin.h
@@ -41,6 +41,7 @@
* Added overloaded C++ oscSinProcess()
function taking
* C-style arrays as arguments.
* Removed usage of reserved identifiers.
+ * Added debugging code.
*
*
* Version 0.6.0:
@@ -80,15 +81,18 @@ extern "C" {
/*! api {{{
* #### bw_osc_sin_process1()
* ```>>> */
-static inline float bw_osc_sin_process1(float x);
+static inline float bw_osc_sin_process1(
+ float x);
/*! <<<```
- * Processes one input sample `x`, indicating the normalized phase, and
- * returns the corresponding output
- * sample.
+ * Processes one input sample `x`, representing the normalized phase, and
+ * returns the corresponding output sample.
*
* #### bw_osc_sin_process()
* ```>>> */
-static inline void bw_osc_sin_process(const float *x, float *y, size_t n_samples);
+static inline void bw_osc_sin_process(
+ const float * x,
+ float * y,
+ size_t n_samples);
/*! <<<```
* Processes the first `n_samples` of the input buffer `x`, containing the
* normalized phase signal, and fills the first `n_samples` of the output
@@ -96,7 +100,11 @@ static inline void bw_osc_sin_process(const float *x, float *y, size_t n_samples
*
* #### bw_osc_sin_process_multi()
* ```>>> */
-static inline void bw_osc_sin_process_multi(const float * const *x, float * const *y, size_t n_channels, size_t n_samples);
+static inline void bw_osc_sin_process_multi(
+ const float * const * x,
+ float * const * y,
+ size_t n_channels,
+ size_t n_samples);
/*! <<<```
* Processes the first `n_samples` of the `n_channels` input buffers `x`,
* containing the normalized phase signals, and fills the first `n_samples`
@@ -118,16 +126,39 @@ static inline void bw_osc_sin_process_multi(const float * const *x, float * cons
extern "C" {
#endif
-static inline float bw_osc_sin_process1(float x) {
- return bw_sin2pif(x);
+static inline float bw_osc_sin_process1(
+ float x) {
+ BW_ASSERT(bw_is_finite(x));
+
+ const float y = bw_sin2pif(x);
+
+ BW_ASSERT(bw_is_finite(y));
+
+ return y;
}
-static inline void bw_osc_sin_process(const float *x, float *y, size_t n_samples) {
+static inline void bw_osc_sin_process(
+ const float * x,
+ float * y,
+ size_t n_samples) {
+ BW_ASSERT(x != NULL);
+ BW_ASSERT_DEEP(bw_has_only_finite(x, n_samples));
+ BW_ASSERT(y != NULL);
+
for (size_t i = 0; i < n_samples; i++)
y[i] = bw_osc_sin_process1(x[i]);
+
+ BW_ASSERT_DEEP(bw_has_only_finite(y, n_samples));
}
-static inline void bw_osc_sin_process_multi(const float * const *x, float * const *y, size_t n_channels, size_t n_samples) {
+static inline void bw_osc_sin_process_multi(
+ const float * const * x,
+ float * const * y,
+ size_t n_channels,
+ size_t n_samples) {
+ BW_ASSERT(x != NULL);
+ BW_ASSERT(y != NULL);
+
for (size_t i = 0; i < n_channels; i++)
bw_osc_sin_process(x[i], y[i], n_samples);
}
@@ -146,15 +177,15 @@ namespace Brickworks {
* ```>>> */
template
void oscSinProcess(
- const float * const *x,
- float * const *y,
- size_t nSamples);
+ const float * const * x,
+ float * const * y,
+ size_t nSamples);
template
void oscSinProcess(
std::array x,
- std::array y,
- size_t nSamples);
+ std::array y,
+ size_t nSamples);
/*! <<<```
* }}} */
@@ -165,17 +196,17 @@ void oscSinProcess(
template
inline void oscSinProcess(
- const float * const *x,
- float * const *y,
- size_t nSamples) {
+ const float * const * x,
+ float * const * y,
+ size_t nSamples) {
bw_osc_sin_process_multi(x, y, N_CHANNELS, nSamples);
}
template
inline void oscSinProcess(
std::array x,
- std::array y,
- size_t nSamples) {
+ std::array y,
+ size_t nSamples) {
oscSinProcess(x.data(), y.data(), nSamples);
}
diff --git a/include/bw_osc_tri.h b/include/bw_osc_tri.h
index ac96a2a..7f1ea0b 100644
--- a/include/bw_osc_tri.h
+++ b/include/bw_osc_tri.h
@@ -133,7 +133,7 @@ static inline void bw_osc_tri_update_coeffs_audio(bw_osc_tri_coeffs *BW_RESTRICT
static inline float bw_osc_tri_process1(const bw_osc_tri_coeffs *BW_RESTRICT coeffs, float x);
static inline float bw_osc_tri_process1_antialias(const bw_osc_tri_coeffs *BW_RESTRICT coeffs, float x, float x_phase_inc);
/*! <<<```
- * These function process one input sample `x`, indicating the normalized
+ * These function process one input sample `x`, representing the normalized
* phase, using `coeffs`. They return the corresponding output sample.
*
* In particular:
diff --git a/include/bw_ring_mod.h b/include/bw_ring_mod.h
index 96b1f18..83c2552 100644
--- a/include/bw_ring_mod.h
+++ b/include/bw_ring_mod.h
@@ -39,6 +39,9 @@
* Added overloaded C++ process()
function taking
* C-style arrays as arguments.
* Removed usage of reserved identifiers.
+ * Fixed inverted-polarity modulation (for real this time).
+ * Clearly specified parameter validity ranges.
+ * Added debugging code.
*
*
* Version 0.6.0:
@@ -81,44 +84,58 @@ typedef struct bw_ring_mod_coeffs bw_ring_mod_coeffs;
*
* #### bw_ring_mod_init()
* ```>>> */
-static inline void bw_ring_mod_init(bw_ring_mod_coeffs *BW_RESTRICT coeffs);
+static inline void bw_ring_mod_init(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs);
/*! <<<```
* Initializes input parameter values in `coeffs`.
*
* #### bw_ring_mod_set_sample_rate()
* ```>>> */
-static inline void bw_ring_mod_set_sample_rate(bw_ring_mod_coeffs *BW_RESTRICT coeffs, float sample_rate);
+static inline void bw_ring_mod_set_sample_rate(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs,
+ float sample_rate);
/*! <<<```
* Sets the `sample_rate` (Hz) value in `coeffs`.
*
* #### bw_ring_mod_reset_coeffs()
* ```>>> */
-static inline void bw_ring_mod_reset_coeffs(bw_ring_mod_coeffs *BW_RESTRICT coeffs);
+static inline void bw_ring_mod_reset_coeffs(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs);
/*! <<<```
* Resets coefficients in `coeffs` to assume their target values.
*
* #### bw_ring_mod_update_coeffs_ctrl()
* ```>>> */
-static inline void bw_ring_mod_update_coeffs_ctrl(bw_ring_mod_coeffs *BW_RESTRICT coeffs);
+static inline void bw_ring_mod_update_coeffs_ctrl(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs);
/*! <<<```
* Triggers control-rate update of coefficients in `coeffs`.
*
* #### bw_ring_mod_update_coeffs_audio()
* ```>>> */
-static inline void bw_ring_mod_update_coeffs_audio(bw_ring_mod_coeffs *BW_RESTRICT coeffs);
+static inline void bw_ring_mod_update_coeffs_audio(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs);
/*! <<<```
* Triggers audio-rate update of coefficients in `coeffs`.
*
* #### bw_ring_mod_process1()
* ```>>> */
-static inline float bw_ring_mod_process1(const bw_ring_mod_coeffs *BW_RESTRICT coeffs, float x_mod, float x_car);
+static inline float bw_ring_mod_process1(
+ const bw_ring_mod_coeffs * BW_RESTRICT coeffs,
+ float x_mod,
+ float x_car);
/*! <<<```
* Processes one modulation input sample `x_mod` and one carrier input sample
* `x_car` using `coeffs` and returns the corresponding output sample.
*
* #### bw_ring_mod_process()
* ```>>> */
-static inline void bw_ring_mod_process(bw_ring_mod_coeffs *BW_RESTRICT coeffs, const float *x_mod, const float *x_car, float *y, size_t n_samples);
+static inline void bw_ring_mod_process(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs,
+ const float * x_mod,
+ const float * x_car,
+ float * y,
+ size_t n_samples);
/*! <<<```
* Processes the first `n_samples` of the modulation input buffer `x_mod` and
* of the carrier input buffer `x_car` and fills the first `n_samples` of the
@@ -127,7 +144,13 @@ static inline void bw_ring_mod_process(bw_ring_mod_coeffs *BW_RESTRICT coeffs, c
*
* #### bw_ring_mod_process_multi()
* ```>>> */
-static inline void bw_ring_mod_process_multi(bw_ring_mod_coeffs *BW_RESTRICT coeffs, const float * const *x_mod, const float * const *x_car, float * const *y, size_t n_channels, size_t n_samples);
+static inline void bw_ring_mod_process_multi(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs,
+ const float * const * x_mod,
+ const float * const * x_car,
+ float * const * y,
+ size_t n_channels,
+ size_t n_samples);
/*! <<<```
* Processes the first `n_samples` of the `n_channels` modulation input
* buffers `x_mod` and of the `n_channels` carrier input buffers `x_car`, and
@@ -136,13 +159,30 @@ static inline void bw_ring_mod_process_multi(bw_ring_mod_coeffs *BW_RESTRICT coe
*
* #### bw_ring_mod_set_amount()
* ```>>> */
-static inline void bw_ring_mod_set_amount(bw_ring_mod_coeffs *BW_RESTRICT coeffs, float value);
+static inline void bw_ring_mod_set_amount(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs,
+ float value);
/*! <<<```
* Sets the modulation amount parameter to the given `value` (`0.f` = no
* modulation, `1.f` = full modulation, `-1.f` = full modulation with
* inverted polarity) in `coeffs`.
*
+ * Valid range: [`-1.f` (full modulation with inverted polarity),
+ * `1.f` (full modulation)].
+ *
* Default value: `1.f`.
+ *
+ * #### bw_ring_mod_coeffs_is_valid()
+ * ```>>> */
+static inline char bw_ring_mod_coeffs_is_valid(
+ const bw_ring_mod_coeffs * BW_RESTRICT coeffs);
+/*! <<<```
+ * Tries to determine whether `coeffs` is valid and returns non-`0` if it
+ * seems to be the case and `0` if it is certainly not. False positives are
+ * possible, false negatives are not.
+ *
+ * `coeffs` must at least point to a readable memory block of size greater
+ * than or equal to that of `bw_ring_mod_coeffs`.
* }}} */
#ifdef __cplusplus
@@ -161,60 +201,206 @@ static inline void bw_ring_mod_set_amount(bw_ring_mod_coeffs *BW_RESTRICT coeffs
extern "C" {
#endif
+#ifdef BW_DEBUG_DEEP
+enum bw_ring_mod_coeffs_state {
+ bw_ring_mod_coeffs_state_invalid,
+ bw_ring_mod_coeffs_state_init,
+ bw_ring_mod_coeffs_state_set_sample_rate,
+ bw_ring_mod_coeffs_state_reset_coeffs
+};
+#endif
+
struct bw_ring_mod_coeffs {
+#ifdef BW_DEBUG_DEEP
+ uint32_t hash;
+ enum bw_ring_mod_coeffs_state state;
+#endif
+
// Sub-components
- bw_one_pole_coeffs smooth_coeffs;
- bw_one_pole_state smooth_state;
+ bw_one_pole_coeffs smooth_coeffs;
+ bw_one_pole_state smooth_state;
// Parameters
- float mod_amount;
+ float mod_amount;
};
-static inline void bw_ring_mod_init(bw_ring_mod_coeffs *BW_RESTRICT coeffs) {
+static inline void bw_ring_mod_init(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+
bw_one_pole_init(&coeffs->smooth_coeffs);
bw_one_pole_set_tau(&coeffs->smooth_coeffs, 0.05f);
coeffs->mod_amount = 1.f;
+
+#ifdef BW_DEBUG_DEEP
+ coeffs->hash = bw_hash_sdbm("bw_ring_mod_coeffs");
+ coeffs->state = bw_ring_mod_coeffs_state_init;
+#endif
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state == bw_ring_mod_coeffs_state_init);
}
-static inline void bw_ring_mod_set_sample_rate(bw_ring_mod_coeffs *BW_RESTRICT coeffs, float sample_rate) {
+static inline void bw_ring_mod_set_sample_rate(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs,
+ float sample_rate) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_init);
+ BW_ASSERT(bw_is_finite(sample_rate) && sample_rate > 0.f);
+
bw_one_pole_set_sample_rate(&coeffs->smooth_coeffs, sample_rate);
bw_one_pole_reset_coeffs(&coeffs->smooth_coeffs);
+
+#ifdef BW_DEBUG_DEEP
+ coeffs->state = bw_ring_mod_coeffs_state_set_sample_rate;
+#endif
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state == bw_ring_mod_coeffs_state_set_sample_rate);
}
-static inline void bw_ring_mod_reset_coeffs(bw_ring_mod_coeffs *BW_RESTRICT coeffs) {
+static inline void bw_ring_mod_reset_coeffs(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_set_sample_rate);
+
bw_one_pole_reset_state(&coeffs->smooth_coeffs, &coeffs->smooth_state, coeffs->mod_amount);
+
+#ifdef BW_DEBUG_DEEP
+ coeffs->state = bw_ring_mod_coeffs_state_reset_coeffs;
+#endif
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state == bw_ring_mod_coeffs_state_reset_coeffs);
}
-static inline void bw_ring_mod_update_coeffs_ctrl(bw_ring_mod_coeffs *BW_RESTRICT coeffs) {
+static inline void bw_ring_mod_update_coeffs_ctrl(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_reset_coeffs);
+
(void)coeffs;
}
-static inline void bw_ring_mod_update_coeffs_audio(bw_ring_mod_coeffs *BW_RESTRICT coeffs) {
+static inline void bw_ring_mod_update_coeffs_audio(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_reset_coeffs);
+
bw_one_pole_process1(&coeffs->smooth_coeffs, &coeffs->smooth_state, coeffs->mod_amount);
+
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_reset_coeffs);
}
-static inline float bw_ring_mod_process1(const bw_ring_mod_coeffs *BW_RESTRICT coeffs, float x_mod, float x_car) {
+static inline float bw_ring_mod_process1(
+ const bw_ring_mod_coeffs * BW_RESTRICT coeffs,
+ float x_mod,
+ float x_car) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_reset_coeffs);
+ BW_ASSERT(bw_is_finite(x_mod));
+ BW_ASSERT(bw_is_finite(x_car));
+
const float k = bw_one_pole_get_y_z1(&coeffs->smooth_state);
- return k * x_car * x_mod + bw_absf(1.f - k) * x_mod;
+ const float y = (k * x_car - bw_absf(k)) * x_mod + x_mod;
+
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_reset_coeffs);
+ BW_ASSERT(bw_is_finite(y));
+
+ return y;
}
-static inline void bw_ring_mod_process(bw_ring_mod_coeffs *BW_RESTRICT coeffs, const float *x_mod, const float *x_car, float *y, size_t n_samples) {
+static inline void bw_ring_mod_process(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs,
+ const float * x_mod,
+ const float * x_car,
+ float * y,
+ size_t n_samples) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_reset_coeffs);
+ BW_ASSERT(x_mod != NULL);
+ BW_ASSERT_DEEP(bw_has_only_finite(x_mod, n_samples));
+ BW_ASSERT(x_car != NULL);
+ BW_ASSERT_DEEP(bw_has_only_finite(x_car, n_samples));
+ BW_ASSERT(y != NULL);
+
for (size_t i = 0; i < n_samples; i++) {
bw_ring_mod_update_coeffs_audio(coeffs);
y[i] = bw_ring_mod_process1(coeffs, x_mod[i], x_car[i]);
}
+
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_reset_coeffs);
+ BW_ASSERT_DEEP(bw_has_only_finite(y, n_samples));
}
-static inline void bw_ring_mod_process_multi(bw_ring_mod_coeffs *BW_RESTRICT coeffs, const float * const *x_mod, const float * const *x_car, float * const *y, size_t n_channels, size_t n_samples) {
+static inline void bw_ring_mod_process_multi(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs,
+ const float * const * x_mod,
+ const float * const * x_car,
+ float * const * y,
+ size_t n_channels,
+ size_t n_samples) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_reset_coeffs);
+ BW_ASSERT(x_mod != NULL);
+ BW_ASSERT(x_car != NULL);
+ BW_ASSERT(y != NULL);
+
for (size_t i = 0; i < n_samples; i++) {
bw_ring_mod_update_coeffs_audio(coeffs);
for (size_t j = 0; j < n_channels; j++)
y[j][i] = bw_ring_mod_process1(coeffs, x_mod[j][i], x_car[j][i]);
}
+
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_reset_coeffs);
}
-static inline void bw_ring_mod_set_amount(bw_ring_mod_coeffs *BW_RESTRICT coeffs, float value) {
+static inline void bw_ring_mod_set_amount(
+ bw_ring_mod_coeffs * BW_RESTRICT coeffs,
+ float value) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_init);
+ BW_ASSERT(bw_is_finite(value));
+ BW_ASSERT(value >= -1.f && value <= 1.f);
+
coeffs->mod_amount = value;
+
+ BW_ASSERT_DEEP(bw_ring_mod_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_ring_mod_coeffs_state_init);
+}
+
+static inline char bw_ring_mod_coeffs_is_valid(
+ const bw_ring_mod_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+
+#ifdef BW_DEBUG_DEEP
+ if (coeffs->hash != bw_hash_sdbm("bw_ring_mod_coeffs"))
+ return 0;
+ if (coeffs->state < bw_ring_mod_coeffs_state_init || coeffs->state > bw_ring_mod_coeffs_state_reset_coeffs)
+ return 0;
+#endif
+
+ if (!bw_is_finite(coeffs->mod_amount) || coeffs->mod_amount < -1.f || coeffs->mod_amount > 1.f)
+ return 0;
+
+ if (!bw_one_pole_coeffs_is_valid(&coeffs->smooth_coeffs))
+ return 0;
+
+#ifdef BW_DEBUG_DEEP
+ if (coeffs->state >= bw_ring_mod_coeffs_state_reset_coeffs && !bw_one_pole_state_is_valid(&coeffs->smooth_coeffs, &coeffs->smooth_state))
+ return 0;
+#endif
+
+ return 1;
}
#ifdef __cplusplus
@@ -234,18 +420,22 @@ class RingMod {
public:
RingMod();
- void setSampleRate(float sampleRate);
+ void setSampleRate(
+ float sampleRate);
+
void reset();
+
void process(
- const float * const *x_mod,
- const float * const *x_car,
- float * const *y,
- size_t nSamples);
+ const float * const * xMod,
+ const float * const * xCar,
+ float * const * y,
+ size_t nSamples);
+
void process(
- std::array x_mod,
- std::array x_car,
- std::array y,
- size_t nSamples);
+ std::array xMod,
+ std::array xCar,
+ std::array y,
+ size_t nSamples);
void setAmount(float value);
/*! <<<...
@@ -259,7 +449,7 @@ public:
* change at any time in future versions. Please, do not use it directly. */
private:
- bw_ring_mod_coeffs coeffs;
+ bw_ring_mod_coeffs coeffs;
};
template
@@ -268,7 +458,8 @@ inline RingMod::RingMod() {
}
template
-inline void RingMod::setSampleRate(float sampleRate) {
+inline void RingMod::setSampleRate(
+ float sampleRate) {
bw_ring_mod_set_sample_rate(&coeffs, sampleRate);
}
@@ -279,24 +470,25 @@ inline void RingMod::reset() {
template
inline void RingMod::process(
- const float * const *x_mod,
- const float * const *x_car,
- float * const *y,
- size_t nSamples) {
- bw_ring_mod_process_multi(&coeffs, x_mod, x_car, y, N_CHANNELS, nSamples);
+ const float * const * xMod,
+ const float * const * xCar,
+ float * const * y,
+ size_t nSamples) {
+ bw_ring_mod_process_multi(&coeffs, xMod, xCar, y, N_CHANNELS, nSamples);
}
template
inline void RingMod::process(
- std::array x_mod,
- std::array x_car,
- std::array y,
- size_t nSamples) {
- process(x_mod.data(), x_car.data(), y.data(), nSamples);
+ std::array xMod,
+ std::array xCar,
+ std::array y,
+ size_t nSamples) {
+ process(xMod.data(), xCar.data(), y.data(), nSamples);
}
template
-inline void RingMod::setAmount(float value) {
+inline void RingMod::setAmount(
+ float value) {
bw_ring_mod_set_amount(&coeffs, value);
}