From dc93bd675b99f28a2644f6b5f4db2918824c4949 Mon Sep 17 00:00:00 2001 From: Stefano D'Angelo Date: Mon, 18 Sep 2023 17:04:11 +0200 Subject: [PATCH] finalized bw_{ring_mod,osc_sin}, bw_dry_wet (untested) + new fx(pp)_ring_mod + cosmetics --- examples/fx_ring_mod/android/Makefile | 6 + examples/fx_ring_mod/daisy-seed/Makefile | 7 + .../daisy-seed/config_daisy_seed.h | 36 +++ examples/fx_ring_mod/ios/Makefile | 6 + .../fx_ring_mod/src/bw_example_fx_ring_mod.c | 62 ++++ .../fx_ring_mod/src/bw_example_fx_ring_mod.h | 61 ++++ examples/fx_ring_mod/src/config.h | 88 ++++++ examples/fx_ring_mod/src/config.js | 43 +++ examples/fx_ring_mod/vst3/Makefile | 9 + examples/fx_ring_mod/vst3/config_vst3.h | 36 +++ examples/fx_ring_mod/web/Makefile | 4 + examples/fxpp_ring_mod/android/Makefile | 6 + examples/fxpp_ring_mod/daisy-seed/Makefile | 7 + .../daisy-seed/config_daisy_seed.h | 36 +++ examples/fxpp_ring_mod/ios/Makefile | 6 + .../src/bw_example_fxpp_ring_mod.cpp | 59 ++++ .../src/bw_example_fxpp_ring_mod.h | 58 ++++ examples/fxpp_ring_mod/src/config.h | 88 ++++++ examples/fxpp_ring_mod/src/config.js | 43 +++ examples/fxpp_ring_mod/vst3/Makefile | 9 + examples/fxpp_ring_mod/vst3/config_vst3.h | 36 +++ include/bw_dry_wet.h | 290 +++++++++++++++--- include/bw_osc_pulse.h | 2 +- include/bw_osc_saw.h | 2 +- include/bw_osc_sin.h | 71 +++-- include/bw_osc_tri.h | 2 +- include/bw_ring_mod.h | 280 ++++++++++++++--- 27 files changed, 1248 insertions(+), 105 deletions(-) create mode 100644 examples/fx_ring_mod/android/Makefile create mode 100644 examples/fx_ring_mod/daisy-seed/Makefile create mode 100644 examples/fx_ring_mod/daisy-seed/config_daisy_seed.h create mode 100644 examples/fx_ring_mod/ios/Makefile create mode 100644 examples/fx_ring_mod/src/bw_example_fx_ring_mod.c create mode 100644 examples/fx_ring_mod/src/bw_example_fx_ring_mod.h create mode 100644 examples/fx_ring_mod/src/config.h create mode 100644 examples/fx_ring_mod/src/config.js create mode 100644 examples/fx_ring_mod/vst3/Makefile create mode 100644 examples/fx_ring_mod/vst3/config_vst3.h create mode 100644 examples/fx_ring_mod/web/Makefile create mode 100644 examples/fxpp_ring_mod/android/Makefile create mode 100644 examples/fxpp_ring_mod/daisy-seed/Makefile create mode 100644 examples/fxpp_ring_mod/daisy-seed/config_daisy_seed.h create mode 100644 examples/fxpp_ring_mod/ios/Makefile create mode 100644 examples/fxpp_ring_mod/src/bw_example_fxpp_ring_mod.cpp create mode 100644 examples/fxpp_ring_mod/src/bw_example_fxpp_ring_mod.h create mode 100644 examples/fxpp_ring_mod/src/config.h create mode 100644 examples/fxpp_ring_mod/src/config.js create mode 100644 examples/fxpp_ring_mod/vst3/Makefile create mode 100644 examples/fxpp_ring_mod/vst3/config_vst3.h 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); }