From d890510d0ffa6d2e0ff96eaf43754bbd205f1626 Mon Sep 17 00:00:00 2001 From: Stefano D'Angelo Date: Fri, 22 Sep 2023 18:34:16 +0200 Subject: [PATCH] finalized bw_reverb + examples + fix bw_dry_wet + cosmetics --- TODO | 11 +- examples/fx_reverb/src/bw_example_fx_reverb.c | 3 +- examples/fx_reverb/vst3/Makefile | 3 + examples/fxpp_reverb/vst3/Makefile | 3 + include/bw_chorus.h | 7 +- include/bw_dry_wet.h | 15 +- include/bw_reverb.h | 952 +++++++++++++++--- 7 files changed, 805 insertions(+), 189 deletions(-) diff --git a/TODO b/TODO index c70e71e..4e54e9e 100644 --- a/TODO +++ b/TODO @@ -1,14 +1,8 @@ 1.0.0 ----- -in progress: -* add initial state (x0) to reset state of all modules -* check assumptions w.r.t. usage of math functions -* empty functions etc. to keep consistency and forward compatibility -* format declarations in a more readable way -code: +* synth(pp)_poly * IMPLEMENTATION prepocessor def??? -finally: * debugging - also check outputs different (incl bw_buf) * check all examples again * clearly specify that state is tied to a particular set of coeffs (1:N) @@ -21,7 +15,7 @@ finally: * state that mem_set() can/must be called on uninitialized state (before reset_state) build system: -* single header generation (vs modules in bwp... to think about) +* single header generation (vs modules in bwp... to think about)? post 1.0.0 ---------- @@ -77,6 +71,7 @@ code: * bw_comb: should also modulate feedback? * bw_comb: integer target delay values? * are we sure about inlining everything? +* reset style is not enough when you have feedbacks... (see reverb) build system: * make makefiles handle paths with spaces etc diff --git a/examples/fx_reverb/src/bw_example_fx_reverb.c b/examples/fx_reverb/src/bw_example_fx_reverb.c index 2c9d3ef..36b7521 100644 --- a/examples/fx_reverb/src/bw_example_fx_reverb.c +++ b/examples/fx_reverb/src/bw_example_fx_reverb.c @@ -38,7 +38,8 @@ void bw_example_fx_reverb_mem_set(bw_example_fx_reverb *instance, void *mem) { void bw_example_fx_reverb_reset(bw_example_fx_reverb *instance) { bw_reverb_reset_coeffs(&instance->reverb_coeffs); - bw_reverb_reset_state(&instance->reverb_coeffs, &instance->reverb_state); + float yl, yr; + bw_reverb_reset_state(&instance->reverb_coeffs, &instance->reverb_state, 0.f, 0.f, &yl, &yr); } void bw_example_fx_reverb_process(bw_example_fx_reverb *instance, const float** x, float** y, int n_samples) { diff --git a/examples/fx_reverb/vst3/Makefile b/examples/fx_reverb/vst3/Makefile index 20102c0..3323eb7 100644 --- a/examples/fx_reverb/vst3/Makefile +++ b/examples/fx_reverb/vst3/Makefile @@ -4,3 +4,6 @@ NAME := bw_example_fx_reverb SOURCE := bw_example_fx_reverb.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/fxpp_reverb/vst3/Makefile b/examples/fxpp_reverb/vst3/Makefile index fb2b2fa..529c282 100644 --- a/examples/fxpp_reverb/vst3/Makefile +++ b/examples/fxpp_reverb/vst3/Makefile @@ -4,3 +4,6 @@ NAME := bw_example_fxpp_reverb SOURCE := bw_example_fxpp_reverb.cpp include ${ROOT_DIR}/../../common/vst3/vst3.mk + +CXXFLAGS += -DRELEASE=1 -DNDEBUG -DBW_NO_DEBUG +#CXXFLAGS += -DDEVELOPMENT=1 -DBW_DEBUG_DEEP diff --git a/include/bw_chorus.h b/include/bw_chorus.h index 17b28b2..612980e 100644 --- a/include/bw_chorus.h +++ b/include/bw_chorus.h @@ -399,6 +399,7 @@ struct bw_chorus_state { enum bw_chorus_state_state state; uint32_t coeffs_reset_id; #endif + // Sub-components bw_comb_state comb_state; }; @@ -524,9 +525,9 @@ static inline float bw_chorus_reset_state( static inline void bw_chorus_reset_state_multi( const bw_chorus_coeffs * BW_RESTRICT coeffs, bw_chorus_state * BW_RESTRICT const * BW_RESTRICT state, - const float * x_0, - float * y_0, - size_t n_channels) { + const float * x_0, + float * y_0, + size_t n_channels) { BW_ASSERT(coeffs != NULL); BW_ASSERT_DEEP(bw_chorus_coeffs_is_valid(coeffs)); BW_ASSERT_DEEP(coeffs->state >= bw_chorus_coeffs_state_reset_coeffs); diff --git a/include/bw_dry_wet.h b/include/bw_dry_wet.h index d1aa874..1940ee7 100644 --- a/include/bw_dry_wet.h +++ b/include/bw_dry_wet.h @@ -302,7 +302,7 @@ static inline float bw_dry_wet_process1( 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)); + BW_ASSERT(bw_is_finite(y)); return y; } @@ -419,19 +419,6 @@ class DryWet { public: DryWet(); - void setSampleRate(float sampleRate); - void reset(); - void process( - const float * const *x_dry, - const float * const *x_wet, - float * const *y, - size_t nSamples); - void process( - std::array x_dry, - std::array x_wet, - std::array y, - size_t nSamples); - void setSampleRate( float sampleRate); diff --git a/include/bw_reverb.h b/include/bw_reverb.h index 2ec281b..610477b 100644 --- a/include/bw_reverb.h +++ b/include/bw_reverb.h @@ -37,6 +37,14 @@ *
    *
  • Version 1.0.0: *
      + *
    • Added initial value argument in + * bw_reverb_reset_state().
    • + *
    • Added bw_reverb_reset_state_multi() and updated C++ + * API in this regard.
    • + *
    • Now bw_reverb_reset_state() returns the initial + * output value.
    • + *
    • Added overloaded C++ reset() functions taking + * arrays as arguments.
    • *
    • Now using size_t instead of * BW_SIZE_T.
    • *
    • bw_reverb_process() and @@ -48,6 +56,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: @@ -88,56 +98,103 @@ typedef struct bw_reverb_state bw_reverb_state; * * #### bw_reverb_init() * ```>>> */ -static inline void bw_reverb_init(bw_reverb_coeffs *BW_RESTRICT coeffs); +static inline void bw_reverb_init( + bw_reverb_coeffs * BW_RESTRICT coeffs); /*! <<<``` * Initializes input parameter values in `coeffs`. * * #### bw_reverb_set_sample_rate() * ```>>> */ -static inline void bw_reverb_set_sample_rate(bw_reverb_coeffs *BW_RESTRICT coeffs, float sample_rate); +static inline void bw_reverb_set_sample_rate( + bw_reverb_coeffs * BW_RESTRICT coeffs, + float sample_rate); /*! <<<``` * Sets the `sample_rate` (Hz) value in `coeffs`. * * #### bw_reverb_mem_req() * ```>>> */ -static inline size_t bw_reverb_mem_req(const bw_reverb_coeffs *BW_RESTRICT coeffs); +static inline size_t bw_reverb_mem_req( + const bw_reverb_coeffs * BW_RESTRICT coeffs); /*! <<<``` * Returns the size, in bytes, of contiguous memory to be supplied to * `bw_reverb_mem_set()` using `coeffs`. * * #### bw_reverb_mem_set() * ```>>> */ -static inline void bw_reverb_mem_set(const bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT state, void *BW_RESTRICT mem); +static inline void bw_reverb_mem_set( + const bw_reverb_coeffs * BW_RESTRICT coeffs, + bw_reverb_state * BW_RESTRICT state, + void * BW_RESTRICT mem); /*! <<<``` - * Associates the contiguous memory block `mem` to the given `state`. + * Associates the contiguous memory block `mem` to the given `state` using + * `coeffs`. * * #### bw_reverb_reset_coeffs() * ```>>> */ -static inline void bw_reverb_reset_coeffs(bw_reverb_coeffs *BW_RESTRICT coeffs); +static inline void bw_reverb_reset_coeffs( + bw_reverb_coeffs * BW_RESTRICT coeffs); /*! <<<``` * Resets coefficients in `coeffs` to assume their target values. * * #### bw_reverb_reset_state() * ```>>> */ -static inline void bw_reverb_reset_state(const bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT state); +static inline void bw_reverb_reset_state( + const bw_reverb_coeffs * BW_RESTRICT coeffs, + bw_reverb_state * BW_RESTRICT state, + float x_l_0, + float x_r_0, + float * BW_RESTRICT y_l_0, + float * BW_RESTRICT y_r_0); /*! <<<``` - * Resets the given `state` to its initial values using the given `coeffs`. + * Resets the given `state` to its initial values using the given `coeffs` + * and the initial input values `x_l_0` (left) and `x_r_0` (right). + * + * The corresponding initial output values are put into `y_l_0` (left) and + * `y_r_0` (right). + * + * Returns the corresponding initial output value. + * + * #### bw_reverb_reset_state_multi() + * ```>>> */ +static inline void bw_reverb_reset_state_multi( + const bw_reverb_coeffs * BW_RESTRICT coeffs, + bw_reverb_state * BW_RESTRICT const * BW_RESTRICT state, + const float * x_l_0, + const float * x_r_0, + float * y_l_0, + float * y_r_0, + size_t n_channels); +/*! <<<``` + * Resets each of the `n_channels` `state`s to its initial values using the + * given `coeffs` and the corresponding initial input values in the `x_l_0` + * (left) and `x_r_0` (right) arrays. + * + * The corresponding initial output values are written into the `y_l_0` + * (left) and `y_r_0` arrays, if each is not `NULL`. * * #### bw_reverb_update_coeffs_ctrl() * ```>>> */ -static inline void bw_reverb_update_coeffs_ctrl(bw_reverb_coeffs *BW_RESTRICT coeffs); +static inline void bw_reverb_update_coeffs_ctrl( + bw_reverb_coeffs * BW_RESTRICT coeffs); /*! <<<``` * Triggers control-rate update of coefficients in `coeffs`. * * #### bw_reverb_update_coeffs_audio() * ```>>> */ -static inline void bw_reverb_update_coeffs_audio(bw_reverb_coeffs *BW_RESTRICT coeffs); +static inline void bw_reverb_update_coeffs_audio( + bw_reverb_coeffs * BW_RESTRICT coeffs); /*! <<<``` * Triggers audio-rate update of coefficients in `coeffs`. * * #### bw_reverb_process1() * ```>>> */ -static inline void bw_reverb_process1(const bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT state, float x_l, float x_r, float *y_l, float *y_r); +static inline void bw_reverb_process1( + const bw_reverb_coeffs * BW_RESTRICT coeffs, + bw_reverb_state * BW_RESTRICT state, + float x_l, + float x_r, + float * y_l, + float * y_r); /*! <<<``` * Processes one set of input samples `x_l` (left) and `x_r` (right) using * `coeffs`, while using and updating `state`. The left and right output @@ -145,16 +202,31 @@ static inline void bw_reverb_process1(const bw_reverb_coeffs *BW_RESTRICT coeffs * * #### bw_reverb_process() * ```>>> */ -static inline void bw_reverb_process(bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT state, const float *x_l, const float *x_r, float *y_l, float *y_r, size_t n_samples); +static inline void bw_reverb_process( + bw_reverb_coeffs * BW_RESTRICT coeffs, + bw_reverb_state * BW_RESTRICT state, + const float * x_l, + const float * x_r, + float * y_l, + float * y_r, + size_t n_samples); /*! <<<``` * Processes the first `n_samples` of the input buffers `x_l` (left) and * `x_r` (right) and fills the first `n_samples` of the output buffers `y_l` * (left) and `y_r` (right), while using and updating both `coeffs` and * `state` (control and audio rate). - * + * * #### bw_reverb_process_multi() * ```>>> */ -static inline void bw_reverb_process_multi(bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT const *BW_RESTRICT state, const float * const *x_l, const float * const *x_r, float * const *y_l, float * const *y_r, size_t n_channels, size_t n_samples); +static inline void bw_reverb_process_multi( + bw_reverb_coeffs * BW_RESTRICT coeffs, + bw_reverb_state * BW_RESTRICT const * BW_RESTRICT state, + const float * const * x_l, + const float * const * x_r, + float * const * y_l, + float * const * y_r, + size_t n_channels, + size_t n_samples); /*! <<<``` * Processes the first `n_samples` of the `n_channels` input buffers `x_l` * (left) and `x_r` (right) and fills the first `n_samples` of the @@ -164,7 +236,9 @@ static inline void bw_reverb_process_multi(bw_reverb_coeffs *BW_RESTRICT coeffs, * * #### bw_reverb_set_predelay() * ```>>> */ -static inline void bw_reverb_set_predelay(bw_reverb_coeffs *BW_RESTRICT coeffs, float value); +static inline void bw_reverb_set_predelay( + bw_reverb_coeffs * BW_RESTRICT coeffs, + float value); /*! <<<``` * Sets the predelay time `value` (s) in `coeffs`. * @@ -174,23 +248,33 @@ static inline void bw_reverb_set_predelay(bw_reverb_coeffs *BW_RESTRICT coeffs, * * #### bw_reverb_set_bandwidth() * ```>>> */ -static inline void bw_reverb_set_bandwidth(bw_reverb_coeffs *BW_RESTRICT coeffs, float value); +static inline void bw_reverb_set_bandwidth( + bw_reverb_coeffs * BW_RESTRICT coeffs, + float value); /*! <<<``` * Sets the input high-frequency attenuation cutoff `value` (Hz) in `coeffs`. * + * Valid range: [`20.f`, `20e3f`]. + * * Default value: `20e3f`. * * #### bw_reverb_set_damping() * ```>>> */ -static inline void bw_reverb_set_damping(bw_reverb_coeffs *BW_RESTRICT coeffs, float value); +static inline void bw_reverb_set_damping( + bw_reverb_coeffs * BW_RESTRICT coeffs, + float value); /*! <<<``` * Sets the high-frequency damping cutoff `value` (Hz) in `coeffs`. * + * Valid range: [`20.f`, `20e3f`]. + * * Default value: `20e3f`. * * #### bw_reverb_set_decay() * ```>>> */ -static inline void bw_reverb_set_decay(bw_reverb_coeffs *BW_RESTRICT coeffs, float value); +static inline void bw_reverb_set_decay( + bw_reverb_coeffs * BW_RESTRICT coeffs, + float value); /*! <<<``` * Sets the decay rate `value` in `coeffs`. * @@ -200,11 +284,43 @@ static inline void bw_reverb_set_decay(bw_reverb_coeffs *BW_RESTRICT coeffs, flo * * #### bw_reverb_set_wet() * ```>>> */ -static inline void bw_reverb_set_wet(bw_reverb_coeffs *BW_RESTRICT coeffs, float value); +static inline void bw_reverb_set_wet( + bw_reverb_coeffs * BW_RESTRICT coeffs, + float value); /*! <<<``` * Sets the output wet mixing `value` (linear gain) in `coeffs`. * + * Valid range: [`0.f`, `1.f`]. + * * Default value: `0.5f`. + * + * #### bw_reverb_coeffs_is_valid() + * ```>>> */ +static inline char bw_reverb_coeffs_is_valid( + const bw_reverb_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_reverb_coeffs`. + * + * #### bw_reverb_state_is_valid() + * ```>>> */ +static inline char bw_reverb_state_is_valid( + const bw_reverb_coeffs * BW_RESTRICT coeffs, + const bw_reverb_state * BW_RESTRICT state); +/*! <<<``` + * Tries to determine whether `state` 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. + * + * If `coeffs` is not `NULL` extra cross-checks might be performed (`state` + * is supposed to be associated to `coeffs`). + * + * `state` must at least point to a readable memory block of size greater + * than or equal to that of `bw_reverb_state`. * }}} */ #ifdef __cplusplus @@ -229,85 +345,118 @@ static inline void bw_reverb_set_wet(bw_reverb_coeffs *BW_RESTRICT coeffs, float extern "C" { #endif +#ifdef BW_DEBUG_DEEP +enum bw_reverb_coeffs_state { + bw_reverb_coeffs_state_invalid, + bw_reverb_coeffs_state_init, + bw_reverb_coeffs_state_set_sample_rate, + bw_reverb_coeffs_state_reset_coeffs +}; +#endif + +#ifdef BW_DEBUG_DEEP +enum bw_reverb_state_state { + bw_reverb_state_state_invalid, + bw_reverb_state_state_mem_set, + bw_reverb_state_state_reset_state +}; +#endif + struct bw_reverb_coeffs { +#ifdef BW_DEBUG_DEEP + uint32_t hash; + enum bw_reverb_coeffs_state state; + uint32_t reset_id; +#endif + // Sub-components - bw_delay_coeffs predelay_coeffs; - bw_lp1_coeffs bandwidth_coeffs; - bw_delay_coeffs delay_id1_coeffs; - bw_delay_coeffs delay_id2_coeffs; - bw_delay_coeffs delay_id3_coeffs; - bw_delay_coeffs delay_id4_coeffs; - bw_delay_coeffs delay_dd1_coeffs; - bw_delay_coeffs delay_dd2_coeffs; - bw_delay_coeffs delay_dd3_coeffs; - bw_delay_coeffs delay_dd4_coeffs; - bw_delay_coeffs delay_d1_coeffs; - bw_delay_coeffs delay_d2_coeffs; - bw_delay_coeffs delay_d3_coeffs; - bw_delay_coeffs delay_d4_coeffs; - bw_gain_coeffs decay_coeffs; - bw_phase_gen_coeffs phase_gen_coeffs; - bw_phase_gen_state phase_gen_state; - bw_lp1_coeffs damping_coeffs; - bw_dry_wet_coeffs dry_wet_coeffs; - bw_one_pole_coeffs smooth_coeffs; - bw_one_pole_state smooth_predelay_state; + bw_delay_coeffs predelay_coeffs; + bw_lp1_coeffs bandwidth_coeffs; + bw_delay_coeffs delay_id1_coeffs; + bw_delay_coeffs delay_id2_coeffs; + bw_delay_coeffs delay_id3_coeffs; + bw_delay_coeffs delay_id4_coeffs; + bw_delay_coeffs delay_dd1_coeffs; + bw_delay_coeffs delay_dd2_coeffs; + bw_delay_coeffs delay_dd3_coeffs; + bw_delay_coeffs delay_dd4_coeffs; + bw_delay_coeffs delay_d1_coeffs; + bw_delay_coeffs delay_d2_coeffs; + bw_delay_coeffs delay_d3_coeffs; + bw_delay_coeffs delay_d4_coeffs; + bw_gain_coeffs decay_coeffs; + bw_phase_gen_coeffs phase_gen_coeffs; + bw_phase_gen_state phase_gen_state; + bw_lp1_coeffs damping_coeffs; + bw_dry_wet_coeffs dry_wet_coeffs; + bw_one_pole_coeffs smooth_coeffs; + bw_one_pole_state smooth_predelay_state; // Coefficients - float fs; - float T; - size_t id1; - size_t id2; - size_t id3; - size_t id4; - size_t dd2; - size_t dd4; - size_t d1; - size_t d2; - size_t d3; - size_t d4; - size_t dl1; - size_t dl2; - size_t dl3; - size_t dl4; - size_t dl5; - size_t dl6; - size_t dl7; - size_t dr1; - size_t dr2; - size_t dr3; - size_t dr4; - size_t dr5; - size_t dr6; - size_t dr7; + float fs; + float T; + size_t id1; + size_t id2; + size_t id3; + size_t id4; + size_t dd2; + size_t dd4; + size_t d1; + size_t d2; + size_t d3; + size_t d4; + size_t dl1; + size_t dl2; + size_t dl3; + size_t dl4; + size_t dl5; + size_t dl6; + size_t dl7; + size_t dr1; + size_t dr2; + size_t dr3; + size_t dr4; + size_t dr5; + size_t dr6; + size_t dr7; - float s; - float diff2; + float s; + float diff2; // Parameters - float predelay; + float predelay; }; struct bw_reverb_state { - bw_delay_state predelay_state; - bw_lp1_state bandwidth_state; - bw_delay_state delay_id1_state; - bw_delay_state delay_id2_state; - bw_delay_state delay_id3_state; - bw_delay_state delay_id4_state; - bw_delay_state delay_dd1_state; - bw_delay_state delay_dd2_state; - bw_delay_state delay_dd3_state; - bw_delay_state delay_dd4_state; - bw_delay_state delay_d1_state; - bw_delay_state delay_d2_state; - bw_delay_state delay_d3_state; - bw_delay_state delay_d4_state; - bw_lp1_state damping_1_state; - bw_lp1_state damping_2_state; +#ifdef BW_DEBUG_DEEP + uint32_t hash; + enum bw_reverb_state_state state; + uint32_t coeffs_reset_id; +#endif + + // Sub-components + bw_delay_state predelay_state; + bw_lp1_state bandwidth_state; + bw_delay_state delay_id1_state; + bw_delay_state delay_id2_state; + bw_delay_state delay_id3_state; + bw_delay_state delay_id4_state; + bw_delay_state delay_dd1_state; + bw_delay_state delay_dd2_state; + bw_delay_state delay_dd3_state; + bw_delay_state delay_dd4_state; + bw_delay_state delay_d1_state; + bw_delay_state delay_d2_state; + bw_delay_state delay_d3_state; + bw_delay_state delay_d4_state; + bw_lp1_state damping_1_state; + bw_lp1_state damping_2_state; }; -static inline void bw_reverb_init(bw_reverb_coeffs *BW_RESTRICT coeffs) { +static inline void bw_reverb_init( + bw_reverb_coeffs * BW_RESTRICT coeffs) { + BW_ASSERT(coeffs != NULL); + bw_delay_init(&coeffs->predelay_coeffs, 0.1f); bw_lp1_init(&coeffs->bandwidth_coeffs); bw_delay_init(&coeffs->delay_id1_coeffs, 142.f / 29761.f); @@ -336,9 +485,24 @@ static inline void bw_reverb_init(bw_reverb_coeffs *BW_RESTRICT coeffs) { bw_one_pole_set_sticky_thresh(&coeffs->smooth_coeffs, 1e-6f); coeffs->predelay = 0.f; + +#ifdef BW_DEBUG_DEEP + coeffs->hash = bw_hash_sdbm("bw_reverb_coeffs"); + coeffs->state = bw_reverb_coeffs_state_init; + coeffs->reset_id = coeffs->hash + 1; +#endif + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state == bw_reverb_coeffs_state_init); } -static inline void bw_reverb_set_sample_rate(bw_reverb_coeffs *BW_RESTRICT coeffs, float sample_rate) { +static inline void bw_reverb_set_sample_rate( + bw_reverb_coeffs * BW_RESTRICT coeffs, + float sample_rate) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_init); + BW_ASSERT(bw_is_finite(sample_rate) && sample_rate > 0.f); + bw_delay_set_sample_rate(&coeffs->predelay_coeffs, sample_rate); bw_lp1_set_sample_rate(&coeffs->bandwidth_coeffs, sample_rate); bw_delay_set_sample_rate(&coeffs->delay_id1_coeffs, sample_rate); @@ -385,9 +549,20 @@ static inline void bw_reverb_set_sample_rate(bw_reverb_coeffs *BW_RESTRICT coeff coeffs->dr5 = (size_t)bw_roundf(coeffs->fs * (2111.f / 29761.f)); coeffs->dr6 = (size_t)bw_roundf(coeffs->fs * (335.f / 29761.f)); coeffs->dr7 = (size_t)bw_roundf(coeffs->fs * (121.f / 29761.f)); + +#ifdef BW_DEBUG_DEEP + coeffs->state = bw_reverb_coeffs_state_set_sample_rate; +#endif + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state == bw_reverb_coeffs_state_set_sample_rate); } -static inline size_t bw_reverb_mem_req(const bw_reverb_coeffs *BW_RESTRICT coeffs) { +static inline size_t bw_reverb_mem_req( + const bw_reverb_coeffs * BW_RESTRICT coeffs) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_set_sample_rate); + return bw_delay_mem_req(&coeffs->predelay_coeffs) + bw_delay_mem_req(&coeffs->delay_id1_coeffs) + bw_delay_mem_req(&coeffs->delay_id2_coeffs) @@ -403,7 +578,16 @@ static inline size_t bw_reverb_mem_req(const bw_reverb_coeffs *BW_RESTRICT coeff + bw_delay_mem_req(&coeffs->delay_d4_coeffs); } -static inline void bw_reverb_mem_set(const bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT state, void *BW_RESTRICT mem) { +static inline void bw_reverb_mem_set( + const bw_reverb_coeffs * BW_RESTRICT coeffs, + bw_reverb_state * BW_RESTRICT state, + void * BW_RESTRICT mem) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_set_sample_rate); + BW_ASSERT(state != NULL); + BW_ASSERT(mem != NULL); + char *m = (char *)mem; bw_delay_mem_set(&coeffs->predelay_coeffs, &state->predelay_state, m); m += bw_delay_mem_req(&coeffs->predelay_coeffs); @@ -430,9 +614,23 @@ static inline void bw_reverb_mem_set(const bw_reverb_coeffs *BW_RESTRICT coeffs, bw_delay_mem_set(&coeffs->delay_d3_coeffs, &state->delay_d3_state, m); m += bw_delay_mem_req(&coeffs->delay_d3_coeffs); bw_delay_mem_set(&coeffs->delay_d4_coeffs, &state->delay_d4_state, m); + +#ifdef BW_DEBUG_DEEP + state->hash = bw_hash_sdbm("bw_reverb_state"); + state->state = bw_reverb_state_state_mem_set; +#endif + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_set_sample_rate); + BW_ASSERT_DEEP(bw_reverb_state_is_valid(coeffs, state)); + BW_ASSERT_DEEP(state->state == bw_reverb_state_state_mem_set); } -static inline void bw_reverb_reset_coeffs(bw_reverb_coeffs *BW_RESTRICT coeffs) { +static inline void bw_reverb_reset_coeffs( + bw_reverb_coeffs * BW_RESTRICT coeffs) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_set_sample_rate); + bw_delay_reset_coeffs(&coeffs->predelay_coeffs); bw_lp1_reset_coeffs(&coeffs->bandwidth_coeffs); bw_delay_reset_coeffs(&coeffs->delay_id1_coeffs); @@ -449,41 +647,153 @@ static inline void bw_reverb_reset_coeffs(bw_reverb_coeffs *BW_RESTRICT coeffs) bw_delay_reset_coeffs(&coeffs->delay_d4_coeffs); bw_gain_reset_coeffs(&coeffs->decay_coeffs); bw_phase_gen_reset_coeffs(&coeffs->phase_gen_coeffs); - bw_phase_gen_reset_state(&coeffs->phase_gen_coeffs, &coeffs->phase_gen_state, 0.f); + float p, pi; + bw_phase_gen_reset_state(&coeffs->phase_gen_coeffs, &coeffs->phase_gen_state, 0.f, &p, &pi); + coeffs->s = (8.f / 29761.f) * bw_osc_sin_process1(p); bw_lp1_reset_coeffs(&coeffs->damping_coeffs); + coeffs->diff2 = bw_clipf(bw_gain_get_gain_lin(&coeffs->decay_coeffs) + 0.15f, 0.25f, 0.5f); bw_dry_wet_reset_coeffs(&coeffs->dry_wet_coeffs); - bw_reverb_set_predelay(coeffs, coeffs->predelay); // to get it rounded + coeffs->predelay = coeffs->T * bw_roundf(coeffs->fs * coeffs->predelay); // rounded bw_one_pole_reset_state(&coeffs->smooth_coeffs, &coeffs->smooth_predelay_state, coeffs->predelay); + +#ifdef BW_DEBUG_DEEP + coeffs->state = bw_reverb_coeffs_state_reset_coeffs; + coeffs->reset_id++; +#endif + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state == bw_reverb_coeffs_state_reset_coeffs); } -static inline void bw_reverb_reset_state(const bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT state) { - bw_delay_reset_state(&coeffs->predelay_coeffs, &state->predelay_state); - bw_lp1_reset_state(&coeffs->bandwidth_coeffs, &state->bandwidth_state, 0.f); - bw_delay_reset_state(&coeffs->delay_id1_coeffs, &state->delay_id1_state); - bw_delay_reset_state(&coeffs->delay_id2_coeffs, &state->delay_id2_state); - bw_delay_reset_state(&coeffs->delay_id3_coeffs, &state->delay_id3_state); - bw_delay_reset_state(&coeffs->delay_id4_coeffs, &state->delay_id4_state); - bw_delay_reset_state(&coeffs->delay_dd1_coeffs, &state->delay_dd1_state); - bw_delay_reset_state(&coeffs->delay_dd2_coeffs, &state->delay_dd2_state); - bw_delay_reset_state(&coeffs->delay_dd3_coeffs, &state->delay_dd3_state); - bw_delay_reset_state(&coeffs->delay_dd4_coeffs, &state->delay_dd4_state); - bw_delay_reset_state(&coeffs->delay_d1_coeffs, &state->delay_d1_state); - bw_delay_reset_state(&coeffs->delay_d2_coeffs, &state->delay_d2_state); - bw_delay_reset_state(&coeffs->delay_d3_coeffs, &state->delay_d3_state); - bw_delay_reset_state(&coeffs->delay_d4_coeffs, &state->delay_d4_state); - bw_lp1_reset_state(&coeffs->damping_coeffs, &state->damping_1_state, 0.f); - bw_lp1_reset_state(&coeffs->damping_coeffs, &state->damping_2_state, 0.f); +static inline void bw_reverb_reset_state( + const bw_reverb_coeffs * BW_RESTRICT coeffs, + bw_reverb_state * BW_RESTRICT state, + float x_l_0, + float x_r_0, + float * BW_RESTRICT y_l_0, + float * BW_RESTRICT y_r_0) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); + BW_ASSERT(state != NULL); + BW_ASSERT_DEEP(bw_reverb_state_is_valid(coeffs, state)); + BW_ASSERT_DEEP(state->state >= bw_reverb_state_state_mem_set); + BW_ASSERT(bw_is_finite(x_l_0)); + BW_ASSERT(bw_is_finite(x_r_0)); + BW_ASSERT(y_l_0 != NULL); + BW_ASSERT(y_r_0 != NULL); + + const float i = 0.5f * (x_l_0 + x_r_0); + const float pd = bw_delay_reset_state(&coeffs->predelay_coeffs, &state->predelay_state, i); + const float bw = bw_lp1_reset_state(&coeffs->bandwidth_coeffs, &state->bandwidth_state, pd); + + const float v1 = (1.f / (1.f + 0.75f)) * bw; + const float v2 = (1.f / (1.f + 0.625f)) * bw; + + bw_delay_reset_state(&coeffs->delay_id1_coeffs, &state->delay_id1_state, v1); + bw_delay_reset_state(&coeffs->delay_id2_coeffs, &state->delay_id2_state, v1); + bw_delay_reset_state(&coeffs->delay_id3_coeffs, &state->delay_id3_state, v2); + bw_delay_reset_state(&coeffs->delay_id4_coeffs, &state->delay_id4_state, v2); + + const float decay = bw_gain_get_gain_cur(&coeffs->decay_coeffs); + const float v3 = bw / (1.f - decay * decay); + const float v4 = decay * bw; + const float v5 = (1.f / (1.f - 0.7f)) * v3; + const float v6 = (1.f / (1.f + coeffs->diff2)) * v4; + + bw_lp1_reset_state(&coeffs->damping_coeffs, &state->damping_1_state, v3); + bw_lp1_reset_state(&coeffs->damping_coeffs, &state->damping_2_state, v3); + + bw_delay_reset_state(&coeffs->delay_d1_coeffs, &state->delay_d1_state, v3); + bw_delay_reset_state(&coeffs->delay_d2_coeffs, &state->delay_d2_state, v4); + bw_delay_reset_state(&coeffs->delay_d3_coeffs, &state->delay_d3_state, v3); + bw_delay_reset_state(&coeffs->delay_d4_coeffs, &state->delay_d4_state, v4); + + bw_delay_reset_state(&coeffs->delay_dd1_coeffs, &state->delay_dd1_state, v5); + bw_delay_reset_state(&coeffs->delay_dd2_coeffs, &state->delay_dd2_state, v6); + bw_delay_reset_state(&coeffs->delay_dd3_coeffs, &state->delay_dd3_state, v5); + bw_delay_reset_state(&coeffs->delay_dd4_coeffs, &state->delay_dd4_state, v6); + + const float y = 0.6f * (v3 - v6 - v6); + + *y_l_0 = bw_dry_wet_process1(&coeffs->dry_wet_coeffs, x_l_0, y); + *y_r_0 = bw_dry_wet_process1(&coeffs->dry_wet_coeffs, x_r_0, y); + +#ifdef BW_DEBUG_DEEP + state->state = bw_reverb_state_state_reset_state; + state->coeffs_reset_id = coeffs->reset_id; +#endif + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); + BW_ASSERT_DEEP(bw_reverb_state_is_valid(coeffs, state)); + BW_ASSERT_DEEP(state->state >= bw_reverb_state_state_reset_state); + BW_ASSERT(bw_is_finite(*y_l_0)); + BW_ASSERT(bw_is_finite(*y_r_0)); } -static inline void bw_reverb_update_coeffs_ctrl(bw_reverb_coeffs *BW_RESTRICT coeffs) { +static inline void bw_reverb_reset_state_multi( + const bw_reverb_coeffs * BW_RESTRICT coeffs, + bw_reverb_state * BW_RESTRICT const * BW_RESTRICT state, + const float * x_l_0, + const float * x_r_0, + float * y_l_0, + float * y_r_0, + size_t n_channels) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); + BW_ASSERT(state != NULL); + BW_ASSERT(x_l_0 != NULL); + BW_ASSERT(x_r_0 != NULL); + + if (y_l_0 != NULL) { + if (y_r_0 != NULL) { + for (size_t i = 0; i < n_channels; i++) + bw_reverb_reset_state(coeffs, state[i], x_l_0[i], x_r_0[i], y_l_0 + i, y_r_0 + i); + } else { + float yr; + for (size_t i = 0; i < n_channels; i++) + bw_reverb_reset_state(coeffs, state[i], x_l_0[i], x_r_0[i], y_l_0 + i, &yr); + } + } else { + if (y_r_0 != NULL) { + float yl; + for (size_t i = 0; i < n_channels; i++) + bw_reverb_reset_state(coeffs, state[i], x_l_0[i], x_r_0[i], &yl, y_r_0 + i); + } else { + float yl, yr; + for (size_t i = 0; i < n_channels; i++) + bw_reverb_reset_state(coeffs, state[i], x_l_0[i], x_r_0[i], &yl, &yr); + } + } + + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); + BW_ASSERT_DEEP(y_l_0 != NULL ? bw_has_only_finite(y_l_0, n_channels) : 1); + BW_ASSERT_DEEP(y_r_0 != NULL ? bw_has_only_finite(y_r_0, n_channels) : 1); +} + +static inline void bw_reverb_update_coeffs_ctrl( + bw_reverb_coeffs * BW_RESTRICT coeffs) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); + bw_lp1_update_coeffs_ctrl(&coeffs->bandwidth_coeffs); bw_gain_update_coeffs_ctrl(&coeffs->decay_coeffs); bw_phase_gen_update_coeffs_ctrl(&coeffs->phase_gen_coeffs); bw_dry_wet_update_coeffs_ctrl(&coeffs->dry_wet_coeffs); bw_lp1_update_coeffs_ctrl(&coeffs->damping_coeffs); + + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); } -static inline void bw_reverb_update_coeffs_audio(bw_reverb_coeffs *BW_RESTRICT coeffs) { +static inline void bw_reverb_update_coeffs_audio( + bw_reverb_coeffs * BW_RESTRICT coeffs) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); + bw_delay_update_coeffs_audio(&coeffs->predelay_coeffs); bw_lp1_update_coeffs_audio(&coeffs->bandwidth_coeffs); const float pd = bw_one_pole_process1_sticky_abs(&coeffs->smooth_coeffs, &coeffs->smooth_predelay_state, coeffs->predelay); @@ -498,9 +808,29 @@ static inline void bw_reverb_update_coeffs_audio(bw_reverb_coeffs *BW_RESTRICT c bw_lp1_update_coeffs_audio(&coeffs->damping_coeffs); coeffs->diff2 = bw_clipf(bw_gain_get_gain_lin(&coeffs->decay_coeffs) + 0.15f, 0.25f, 0.5f); bw_dry_wet_update_coeffs_audio(&coeffs->dry_wet_coeffs); + + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); } -static inline void bw_reverb_process1(const bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT state, float x_l, float x_r, float *y_l, float *y_r) { +static inline void bw_reverb_process1( + const bw_reverb_coeffs * BW_RESTRICT coeffs, + bw_reverb_state * BW_RESTRICT state, + float x_l, + float x_r, + float * y_l, + float * y_r) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); + BW_ASSERT(state != NULL); + BW_ASSERT_DEEP(bw_reverb_state_is_valid(coeffs, state)); + BW_ASSERT_DEEP(state->state >= bw_reverb_state_state_reset_state); + BW_ASSERT(bw_is_finite(x_l)); + BW_ASSERT(bw_is_finite(x_r)); + BW_ASSERT(y_l != NULL); + BW_ASSERT(y_r != NULL); + const float i = 0.5f * (x_l + x_r); const float pd = bw_delay_process1(&coeffs->predelay_coeffs, &state->predelay_state, i); const float bw = bw_lp1_process1(&coeffs->bandwidth_coeffs, &state->bandwidth_state, pd); @@ -581,43 +911,247 @@ static inline void bw_reverb_process1(const bw_reverb_coeffs *BW_RESTRICT coeffs ); *y_l = bw_dry_wet_process1(&coeffs->dry_wet_coeffs, x_l, *y_l); *y_r = bw_dry_wet_process1(&coeffs->dry_wet_coeffs, x_r, *y_r); + + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); + BW_ASSERT_DEEP(bw_reverb_state_is_valid(coeffs, state)); + BW_ASSERT_DEEP(state->state >= bw_reverb_state_state_reset_state); + BW_ASSERT(bw_is_finite(*y_l)); + BW_ASSERT(bw_is_finite(*y_r)); } -static inline void bw_reverb_process(bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT state, const float *x_l, const float *x_r, float *y_l, float *y_r, size_t n_samples) { +static inline void bw_reverb_process( + bw_reverb_coeffs * BW_RESTRICT coeffs, + bw_reverb_state * BW_RESTRICT state, + const float * x_l, + const float * x_r, + float * y_l, + float * y_r, + size_t n_samples) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); + BW_ASSERT(state != NULL); + BW_ASSERT_DEEP(bw_reverb_state_is_valid(coeffs, state)); + BW_ASSERT_DEEP(state->state >= bw_reverb_state_state_reset_state); + BW_ASSERT(x_l != NULL); + BW_ASSERT_DEEP(bw_has_only_finite(x_l, n_samples)); + BW_ASSERT(x_r != NULL); + BW_ASSERT_DEEP(bw_has_only_finite(x_r, n_samples)); + BW_ASSERT(y_l != NULL); + BW_ASSERT(y_r != NULL); + bw_reverb_update_coeffs_ctrl(coeffs); for (size_t i = 0; i < n_samples; i++) { bw_reverb_update_coeffs_audio(coeffs); bw_reverb_process1(coeffs, state, x_l[i], x_r[i], y_l + i, y_r + i); } + + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); + BW_ASSERT_DEEP(bw_reverb_state_is_valid(coeffs, state)); + BW_ASSERT_DEEP(state->state >= bw_reverb_state_state_reset_state); + BW_ASSERT_DEEP(bw_has_only_finite(y_l, n_samples)); + BW_ASSERT_DEEP(bw_has_only_finite(y_r, n_samples)); } -static inline void bw_reverb_process_multi(bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT const *BW_RESTRICT state, const float * const *x_l, const float * const *x_r, float * const *y_l, float * const *y_r, size_t n_channels, size_t n_samples) { +static inline void bw_reverb_process_multi( + bw_reverb_coeffs * BW_RESTRICT coeffs, + bw_reverb_state * BW_RESTRICT const * BW_RESTRICT state, + const float * const * x_l, + const float * const * x_r, + float * const * y_l, + float * const * y_r, + size_t n_channels, + size_t n_samples) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); + BW_ASSERT(state != NULL); + BW_ASSERT(x_l != NULL); + BW_ASSERT(x_r != NULL); + BW_ASSERT(y_l != NULL); + BW_ASSERT(y_r != NULL); + bw_reverb_update_coeffs_ctrl(coeffs); for (size_t i = 0; i < n_samples; i++) { bw_reverb_update_coeffs_audio(coeffs); for (size_t j = 0; j < n_channels; j++) bw_reverb_process1(coeffs, state[j], x_l[j][i], x_r[j][i], y_l[j] + i, y_r[j] + i); } + + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_reset_coeffs); } -static inline void bw_reverb_set_predelay(bw_reverb_coeffs *BW_RESTRICT coeffs, float value) { +static inline void bw_reverb_set_predelay( + bw_reverb_coeffs * BW_RESTRICT coeffs, + float value) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_init); + BW_ASSERT(bw_is_finite(value)); + BW_ASSERT(value >= 0.f && value <= 0.1f); + coeffs->predelay = coeffs->T * bw_roundf(coeffs->fs * value); + + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_init); } -static inline void bw_reverb_set_bandwidth(bw_reverb_coeffs *BW_RESTRICT coeffs, float value) { +static inline void bw_reverb_set_bandwidth( + bw_reverb_coeffs * BW_RESTRICT coeffs, + float value) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_init); + BW_ASSERT(bw_is_finite(value)); + BW_ASSERT(value >= 20.f && value <= 20e3f); + bw_lp1_set_cutoff(&coeffs->bandwidth_coeffs, value); + + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_init); } -static inline void bw_reverb_set_damping(bw_reverb_coeffs *BW_RESTRICT coeffs, float value) { +static inline void bw_reverb_set_damping( + bw_reverb_coeffs * BW_RESTRICT coeffs, + float value) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_init); + BW_ASSERT(bw_is_finite(value)); + BW_ASSERT(value >= 20.f && value <= 20e3f); + bw_lp1_set_cutoff(&coeffs->damping_coeffs, value); + + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_init); } -static inline void bw_reverb_set_decay(bw_reverb_coeffs *BW_RESTRICT coeffs, float value) { +static inline void bw_reverb_set_decay( + bw_reverb_coeffs * BW_RESTRICT coeffs, + float value) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_init); + BW_ASSERT(bw_is_finite(value)); + BW_ASSERT(value >= 0.f && value < 1.f); + bw_gain_set_gain_lin(&coeffs->decay_coeffs, value); + + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_init); } -static inline void bw_reverb_set_wet(bw_reverb_coeffs *BW_RESTRICT coeffs, float value) { +static inline void bw_reverb_set_wet( + bw_reverb_coeffs * BW_RESTRICT coeffs, + float value) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_init); + BW_ASSERT(bw_is_finite(value)); + BW_ASSERT(value >= 0.f && value <= 1.f); + bw_dry_wet_set_wet(&coeffs->dry_wet_coeffs, value); + + BW_ASSERT_DEEP(bw_reverb_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_reverb_coeffs_state_init); +} + +static inline char bw_reverb_coeffs_is_valid( + const bw_reverb_coeffs * BW_RESTRICT coeffs) { + BW_ASSERT(coeffs != NULL); + +#ifdef BW_DEBUG_DEEP + if (coeffs->hash != bw_hash_sdbm("bw_reverb_coeffs")) + return 0; + if (coeffs->state < bw_reverb_coeffs_state_init || coeffs->state > bw_reverb_coeffs_state_reset_coeffs) + return 0; +#endif + + if (!bw_is_finite(coeffs->predelay) || coeffs->predelay < 0.f || coeffs->predelay > 0.1f) + return 0; + + if (!bw_one_pole_coeffs_is_valid(&coeffs->smooth_coeffs)) + return 0; + if (!bw_phase_gen_coeffs_is_valid(&coeffs->phase_gen_coeffs)) + return 0; + +#ifdef BW_DEBUG_DEEP + if (coeffs->state >= bw_reverb_coeffs_state_set_sample_rate) { + if (!bw_is_finite(coeffs->fs) || coeffs->fs <= 0.f) + return 0; + if (!bw_is_finite(coeffs->T) || coeffs->T <= 0.f) + return 0; + } + + if (coeffs->state >= bw_reverb_coeffs_state_reset_coeffs) { + if (!bw_is_finite(coeffs->s) || coeffs->s < -8.f / 29761.f || coeffs->s > 8.f / 29761.f) + return 0; + if (!bw_is_finite(coeffs->diff2) || coeffs->diff2 < 0.25f || coeffs->s > 0.5f) + return 0; + + if (!bw_one_pole_state_is_valid(&coeffs->smooth_coeffs, &coeffs->smooth_predelay_state)) + return 0; + if (!bw_phase_gen_state_is_valid(&coeffs->phase_gen_coeffs, &coeffs->phase_gen_state)) + return 0; + } +#endif + + return bw_delay_coeffs_is_valid(&coeffs->predelay_coeffs) + && bw_lp1_coeffs_is_valid(&coeffs->bandwidth_coeffs) + && bw_delay_coeffs_is_valid(&coeffs->delay_id1_coeffs) + && bw_delay_coeffs_is_valid(&coeffs->delay_id2_coeffs) + && bw_delay_coeffs_is_valid(&coeffs->delay_id3_coeffs) + && bw_delay_coeffs_is_valid(&coeffs->delay_id4_coeffs) + && bw_delay_coeffs_is_valid(&coeffs->delay_dd1_coeffs) + && bw_delay_coeffs_is_valid(&coeffs->delay_dd2_coeffs) + && bw_delay_coeffs_is_valid(&coeffs->delay_dd3_coeffs) + && bw_delay_coeffs_is_valid(&coeffs->delay_dd4_coeffs) + && bw_delay_coeffs_is_valid(&coeffs->delay_d1_coeffs) + && bw_delay_coeffs_is_valid(&coeffs->delay_d2_coeffs) + && bw_delay_coeffs_is_valid(&coeffs->delay_d3_coeffs) + && bw_delay_coeffs_is_valid(&coeffs->delay_d4_coeffs) + && bw_lp1_coeffs_is_valid(&coeffs->damping_coeffs) + && bw_dry_wet_coeffs_is_valid(&coeffs->dry_wet_coeffs); +} + +static inline char bw_reverb_state_is_valid( + const bw_reverb_coeffs * BW_RESTRICT coeffs, + const bw_reverb_state * BW_RESTRICT state) { + BW_ASSERT(state != NULL); + +#ifdef BW_DEBUG_DEEP + if (state->hash != bw_hash_sdbm("bw_reverb_state")) + return 0; + if (state->state < bw_reverb_state_state_mem_set || state->state > bw_reverb_state_state_reset_state) + return 0; + + if (state->state >= bw_reverb_state_state_reset_state) { + if (coeffs != NULL && coeffs->reset_id != state->coeffs_reset_id) + return 0; + + if (!bw_lp1_state_is_valid(coeffs ? &coeffs->bandwidth_coeffs : NULL, &state->bandwidth_state) + || !bw_lp1_state_is_valid(coeffs ? &coeffs->damping_coeffs : NULL, &state->damping_1_state) + || !bw_lp1_state_is_valid(coeffs ? &coeffs->damping_coeffs : NULL, &state->damping_2_state)) + return 0; + } +#endif + + return bw_delay_state_is_valid(coeffs ? &coeffs->predelay_coeffs : NULL, &state->predelay_state) + && bw_delay_state_is_valid(coeffs ? &coeffs->delay_id1_coeffs : NULL, &state->delay_id1_state) + && bw_delay_state_is_valid(coeffs ? &coeffs->delay_id2_coeffs : NULL, &state->delay_id2_state) + && bw_delay_state_is_valid(coeffs ? &coeffs->delay_id3_coeffs : NULL, &state->delay_id3_state) + && bw_delay_state_is_valid(coeffs ? &coeffs->delay_id4_coeffs : NULL, &state->delay_id4_state) + && bw_delay_state_is_valid(coeffs ? &coeffs->delay_dd1_coeffs : NULL, &state->delay_dd1_state) + && bw_delay_state_is_valid(coeffs ? &coeffs->delay_dd2_coeffs : NULL, &state->delay_dd2_state) + && bw_delay_state_is_valid(coeffs ? &coeffs->delay_dd3_coeffs : NULL, &state->delay_dd3_state) + && bw_delay_state_is_valid(coeffs ? &coeffs->delay_dd4_coeffs : NULL, &state->delay_dd4_state) + && bw_delay_state_is_valid(coeffs ? &coeffs->delay_d1_coeffs : NULL, &state->delay_d1_state) + && bw_delay_state_is_valid(coeffs ? &coeffs->delay_d2_coeffs : NULL, &state->delay_d2_state) + && bw_delay_state_is_valid(coeffs ? &coeffs->delay_d3_coeffs : NULL, &state->delay_d3_state) + && bw_delay_state_is_valid(coeffs ? &coeffs->delay_d4_coeffs : NULL, &state->delay_d4_state); } #ifdef __cplusplus @@ -636,28 +1170,64 @@ template class Reverb { public: Reverb(); + ~Reverb(); - void setSampleRate(float sampleRate); - void reset(); - void process( - const float * const *x_l, - const float * const *x_r, - float * const *y_l, - float * const *y_r, - size_t nSamples); - void process( - std::array x_l, - std::array x_r, - std::array y_l, - std::array y_r, - size_t nSamples); + void setSampleRate( + float sampleRate); - void setPredelay(float value); - void setBandwidth(float value); - void setDamping(float value); - void setDecay(float value); - void setWet(float value); + void reset( + float xL0 = 0.f, + float xR0 = 0.f, + float * BW_RESTRICT yL0 = nullptr, + float * BW_RESTRICT yR0 = nullptr); + + void reset( + float xL0, + float xR0, + std::array * BW_RESTRICT yL0, + std::array * BW_RESTRICT yR0); + + void reset( + const float * xL0, + const float * xR0, + float * yL0 = nullptr, + float * yR0 = nullptr); + + void reset( + std::array xL0, + std::array xR0, + std::array * BW_RESTRICT yL0 = nullptr, + std::array * BW_RESTRICT yR0 = nullptr); + + void process( + const float * const * xL, + const float * const * xR, + float * const * yL, + float * const * yR, + size_t nSamples); + + void process( + std::array xL, + std::array xR, + std::array yL, + std::array yR, + size_t nSamples); + + void setPredelay( + float value); + + void setBandwidth( + float value); + + void setDamping( + float value); + + void setDecay( + float value); + + void setWet( + float value); /*! <<<... * } * ``` @@ -669,10 +1239,10 @@ public: * change at any time in future versions. Please, do not use it directly. */ private: - bw_reverb_coeffs coeffs; - bw_reverb_state states[N_CHANNELS]; - bw_reverb_state *BW_RESTRICT statesP[N_CHANNELS]; - void *BW_RESTRICT mem; + bw_reverb_coeffs coeffs; + bw_reverb_state states[N_CHANNELS]; + bw_reverb_state * BW_RESTRICT statesP[N_CHANNELS]; + void * BW_RESTRICT mem; }; template @@ -690,7 +1260,8 @@ inline Reverb::~Reverb() { } template -inline void Reverb::setSampleRate(float sampleRate) { +inline void Reverb::setSampleRate( + float sampleRate) { bw_reverb_set_sample_rate(&coeffs, sampleRate); size_t req = bw_reverb_mem_req(&coeffs); if (mem != nullptr) @@ -702,54 +1273,109 @@ inline void Reverb::setSampleRate(float sampleRate) { } template -inline void Reverb::reset() { +inline void Reverb::reset( + float xL0, + float xR0, + float * BW_RESTRICT yL0, + float * BW_RESTRICT yR0) { bw_reverb_reset_coeffs(&coeffs); - for (size_t i = 0; i < N_CHANNELS; i++) - bw_reverb_reset_state(&coeffs, states + i); + if (yL0 != nullptr) { + if (yR0 != nullptr) { + for (size_t i = 0; i < N_CHANNELS; i++) + bw_reverb_reset_state(&coeffs, states + i, xL0, xR0, yL0 + i, yR0 + i); + } else { + float yr; + for (size_t i = 0; i < N_CHANNELS; i++) + bw_reverb_reset_state(&coeffs, states + i, xL0, xR0, yL0 + i, &yr); + } + } else { + if (yR0 != nullptr) { + float yl; + for (size_t i = 0; i < N_CHANNELS; i++) + bw_reverb_reset_state(&coeffs, states + i, xL0, xR0, &yl, yR0 + i); + } else { + float yl, yr; + for (size_t i = 0; i < N_CHANNELS; i++) + bw_reverb_reset_state(&coeffs, states + i, xL0, xR0, &yl, &yr); + } + } +} + +template +inline void Reverb::reset( + float xL0, + float xR0, + std::array * BW_RESTRICT yL0, + std::array * BW_RESTRICT yR0) { + reset(xL0, xR0, yL0 != nullptr ? yL0->data() : nullptr, yR0 != nullptr ? yR0->data() : nullptr); +} + +template +inline void Reverb::reset( + const float * xL0, + const float * xR0, + float * yL0, + float * yR0) { + bw_reverb_reset_coeffs(&coeffs); + bw_reverb_reset_state_multi(&coeffs, statesP, xL0, xR0, yL0, yR0, N_CHANNELS); +} + +template +inline void Reverb::reset( + std::array xL0, + std::array xR0, + std::array * BW_RESTRICT yL0, + std::array * BW_RESTRICT yR0) { + reset(xL0.data(), xR0.data(), yL0 != nullptr ? yL0->data() : nullptr, yR0 != nullptr ? yR0->data() : nullptr); } template inline void Reverb::process( - const float * const *x_l, - const float * const *x_r, - float * const *y_l, - float * const *y_r, - size_t nSamples) { - bw_reverb_process_multi(&coeffs, statesP, x_l, x_r, y_l, y_r, N_CHANNELS, nSamples); + const float * const * xL, + const float * const * xR, + float * const * yL, + float * const * yR, + size_t nSamples) { + bw_reverb_process_multi(&coeffs, statesP, xL, xR, yL, yR, N_CHANNELS, nSamples); } template inline void Reverb::process( - std::array x_l, - std::array x_r, - std::array y_l, - std::array y_r, - size_t nSamples) { - process(x_l.data(), x_r.data(), y_l.data(), y_r.data(), nSamples); + std::array xL, + std::array xR, + std::array yL, + std::array yR, + size_t nSamples) { + process(xL.data(), xR.data(), yL.data(), yR.data(), nSamples); } template -inline void Reverb::setPredelay(float value) { +inline void Reverb::setPredelay( + float value) { bw_reverb_set_predelay(&coeffs, value); } template -inline void Reverb::setBandwidth(float value) { +inline void Reverb::setBandwidth( + float value) { bw_reverb_set_bandwidth(&coeffs, value); } template -inline void Reverb::setDamping(float value) { +inline void Reverb::setDamping( + float value) { bw_reverb_set_damping(&coeffs, value); } template -inline void Reverb::setDecay(float value) { +inline void Reverb::setDecay( + float value) { bw_reverb_set_decay(&coeffs, value); } template -inline void Reverb::setWet(float value) { +inline void Reverb::setWet( + float value) { bw_reverb_set_wet(&coeffs, value); }