diff --git a/examples/fx_balance/src/bw_example_fx_balance.c b/examples/fx_balance/src/bw_example_fx_balance.c index 1d6ca4d..43745f6 100644 --- a/examples/fx_balance/src/bw_example_fx_balance.c +++ b/examples/fx_balance/src/bw_example_fx_balance.c @@ -33,8 +33,8 @@ void bw_example_fx_balance_set_sample_rate(bw_example_fx_balance *instance, floa void bw_example_fx_balance_reset(bw_example_fx_balance *instance) { bw_balance_reset_coeffs(&instance->balance_coeffs); bw_ppm_reset_coeffs(&instance->ppm_coeffs); - bw_ppm_reset_state(&instance->ppm_coeffs, &instance->ppm_l_state); - bw_ppm_reset_state(&instance->ppm_coeffs, &instance->ppm_r_state); + bw_ppm_reset_state(&instance->ppm_coeffs, &instance->ppm_l_state, 0.f); + bw_ppm_reset_state(&instance->ppm_coeffs, &instance->ppm_r_state, 0.f); } void bw_example_fx_balance_process(bw_example_fx_balance *instance, const float** x, float** y, int n_samples) { diff --git a/examples/fx_balance/vst3/Makefile b/examples/fx_balance/vst3/Makefile index 9c3a672..414340d 100644 --- a/examples/fx_balance/vst3/Makefile +++ b/examples/fx_balance/vst3/Makefile @@ -4,3 +4,6 @@ NAME := bw_example_fx_balance SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fx_balance.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_balance/vst3/Makefile b/examples/fxpp_balance/vst3/Makefile index f5b4c31..94b7933 100644 --- a/examples/fxpp_balance/vst3/Makefile +++ b/examples/fxpp_balance/vst3/Makefile @@ -4,3 +4,6 @@ NAME := bw_example_fxpp_balance SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_fxpp_balance.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_balance.h b/include/bw_balance.h index 5639792..1cee3c0 100644 --- a/include/bw_balance.h +++ b/include/bw_balance.h @@ -38,6 +38,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: @@ -81,37 +83,48 @@ typedef struct bw_balance_coeffs bw_balance_coeffs; * * #### bw_balance_init() * ```>>> */ -static inline void bw_balance_init(bw_balance_coeffs *BW_RESTRICT coeffs); +static inline void bw_balance_init( + bw_balance_coeffs * BW_RESTRICT coeffs); /*! <<<``` * Initializes input parameter values in `coeffs`. * * #### bw_balance_set_sample_rate() * ```>>> */ -static inline void bw_balance_set_sample_rate(bw_balance_coeffs *BW_RESTRICT coeffs, float sample_rate); +static inline void bw_balance_set_sample_rate( + bw_balance_coeffs * BW_RESTRICT coeffs, + float sample_rate); /*! <<<``` * Sets the `sample_rate` (Hz) value in `coeffs`. * * #### bw_balance_reset_coeffs() * ```>>> */ -static inline void bw_balance_reset_coeffs(bw_balance_coeffs *BW_RESTRICT coeffs); +static inline void bw_balance_reset_coeffs( + bw_balance_coeffs * BW_RESTRICT coeffs); /*! <<<``` * Resets coefficients in `coeffs` to assume their target values. * * #### bw_balance_update_coeffs_ctrl() * ```>>> */ -static inline void bw_balance_update_coeffs_ctrl(bw_balance_coeffs *BW_RESTRICT coeffs); +static inline void bw_balance_update_coeffs_ctrl( + bw_balance_coeffs * BW_RESTRICT coeffs); /*! <<<``` * Triggers control-rate update of coefficients in `coeffs`. * * #### bw_balance_update_coeffs_audio() * ```>>> */ -static inline void bw_balance_update_coeffs_audio(bw_balance_coeffs *BW_RESTRICT coeffs); +static inline void bw_balance_update_coeffs_audio( + bw_balance_coeffs * BW_RESTRICT coeffs); /*! <<<``` * Triggers audio-rate update of coefficients in `coeffs`. * * #### bw_balance_process1() * ```>>> */ -static inline void bw_balance_process1(const bw_balance_coeffs *BW_RESTRICT coeffs, float x_l, float x_r, float *BW_RESTRICT y_l, float *BW_RESTRICT y_r); +static inline void bw_balance_process1( + const bw_balance_coeffs * BW_RESTRICT coeffs, + float x_l, + float x_r, + float * BW_RESTRICT y_l, + float * BW_RESTRICT y_r); /*! <<<``` * Processes one set of input samples `x_l` (left) and `x_r` (right) using * `coeffs`. The left and right output samples are put into `y_l` (left) and @@ -119,7 +132,13 @@ static inline void bw_balance_process1(const bw_balance_coeffs *BW_RESTRICT coef * * #### bw_balance_process() * ```>>> */ -static inline void bw_balance_process(bw_balance_coeffs *BW_RESTRICT coeffs, const float *x_l, const float *x_r, float *y_l, float *y_r, size_t n_samples); +static inline void bw_balance_process( + bw_balance_coeffs * BW_RESTRICT coeffs, + 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` @@ -128,7 +147,14 @@ static inline void bw_balance_process(bw_balance_coeffs *BW_RESTRICT coeffs, con * * #### bw_balance_process_multi() * ```>>> */ -static inline void bw_balance_process_multi(bw_balance_coeffs *BW_RESTRICT coeffs, 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_balance_process_multi( + bw_balance_coeffs * BW_RESTRICT coeffs, + 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 @@ -137,12 +163,28 @@ static inline void bw_balance_process_multi(bw_balance_coeffs *BW_RESTRICT coeff * * #### bw_balance_set_balance() * ```>>> */ -static inline void bw_balance_set_balance(bw_balance_coeffs *BW_RESTRICT coeffs, float value); +static inline void bw_balance_set_balance( + bw_balance_coeffs * BW_RESTRICT coeffs, + float value); /*! <<<``` * Sets the balance `value`, where `-1.f` corresponds to hard left balance, * `0.f` to center balance, and `1.f` to hard right balance. * + * Valid range: [`-1.f` (hard left balance), `1.f` (hard right balance)]. + * * Default value: `0.f`. + * + * #### bw_balance_coeffs_is_valid() + * ```>>> */ +static inline char bw_balance_coeffs_is_valid( + const bw_balance_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_balance_coeffs`. * }}} */ #ifdef __cplusplus @@ -161,28 +203,67 @@ static inline void bw_balance_set_balance(bw_balance_coeffs *BW_RESTRICT coeffs, extern "C" { #endif +#ifdef BW_DEBUG_DEEP +enum bw_balance_coeffs_state { + bw_balance_coeffs_state_invalid, + bw_balance_coeffs_state_init, + bw_balance_coeffs_state_set_sample_rate, + bw_balance_coeffs_state_reset_coeffs +}; +#endif + struct bw_balance_coeffs { +#ifdef BW_DEBUG_DEEP + uint32_t hash; + enum bw_balance_coeffs_state state; +#endif + // Sub-components - bw_gain_coeffs l_coeffs; - bw_gain_coeffs r_coeffs; + bw_gain_coeffs l_coeffs; + bw_gain_coeffs r_coeffs; // Parameters - float balance; - float balance_prev; + float balance; + float balance_prev; }; -static inline void bw_balance_init(bw_balance_coeffs *BW_RESTRICT coeffs) { +static inline void bw_balance_init( + bw_balance_coeffs * BW_RESTRICT coeffs) { + BW_ASSERT(coeffs != NULL); + bw_gain_init(&coeffs->l_coeffs); bw_gain_init(&coeffs->r_coeffs); coeffs->balance = 0.f; + +#ifdef BW_DEBUG_DEEP + coeffs->hash = bw_hash_sdbm("bw_balance_coeffs"); + coeffs->state = bw_balance_coeffs_state_init; +#endif + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state == bw_balance_coeffs_state_init); } -static inline void bw_balance_set_sample_rate(bw_balance_coeffs *BW_RESTRICT coeffs, float sample_rate) { +static inline void bw_balance_set_sample_rate( + bw_balance_coeffs * BW_RESTRICT coeffs, + float sample_rate) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_init); + BW_ASSERT(bw_is_finite(sample_rate) && sample_rate > 0.f); + bw_gain_set_sample_rate(&coeffs->l_coeffs, sample_rate); bw_gain_set_sample_rate(&coeffs->r_coeffs, sample_rate); + +#ifdef BW_DEBUG_DEEP + coeffs->state = bw_balance_coeffs_state_set_sample_rate; +#endif + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state == bw_balance_coeffs_state_set_sample_rate); } -static inline void bw_balance_do_update_coeffs(bw_balance_coeffs *BW_RESTRICT coeffs, char force) { +static inline void bw_balance_do_update_coeffs( + bw_balance_coeffs * BW_RESTRICT coeffs, + char force) { if (force || coeffs->balance != coeffs->balance_prev) { bw_gain_set_gain_lin(&coeffs->l_coeffs, bw_minf(1.f - coeffs->balance, 1.f)); bw_gain_set_gain_lin(&coeffs->r_coeffs, bw_minf(1.f + coeffs->balance, 1.f)); @@ -190,47 +271,164 @@ static inline void bw_balance_do_update_coeffs(bw_balance_coeffs *BW_RESTRICT co } } -static inline void bw_balance_reset_coeffs(bw_balance_coeffs *BW_RESTRICT coeffs) { +static inline void bw_balance_reset_coeffs( + bw_balance_coeffs * BW_RESTRICT coeffs) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_set_sample_rate); + bw_balance_do_update_coeffs(coeffs, 1); bw_gain_reset_coeffs(&coeffs->l_coeffs); bw_gain_reset_coeffs(&coeffs->r_coeffs); + +#ifdef BW_DEBUG_DEEP + coeffs->state = bw_balance_coeffs_state_reset_coeffs; +#endif + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state == bw_balance_coeffs_state_reset_coeffs); } -static inline void bw_balance_update_coeffs_ctrl(bw_balance_coeffs *BW_RESTRICT coeffs) { +static inline void bw_balance_update_coeffs_ctrl( + bw_balance_coeffs * BW_RESTRICT coeffs) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_reset_coeffs); + bw_balance_do_update_coeffs(coeffs, 0); bw_gain_update_coeffs_ctrl(&coeffs->l_coeffs); bw_gain_update_coeffs_ctrl(&coeffs->r_coeffs); + + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_reset_coeffs); } -static inline void bw_balance_update_coeffs_audio(bw_balance_coeffs *BW_RESTRICT coeffs) { +static inline void bw_balance_update_coeffs_audio( + bw_balance_coeffs * BW_RESTRICT coeffs) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_reset_coeffs); + bw_gain_update_coeffs_audio(&coeffs->l_coeffs); bw_gain_update_coeffs_audio(&coeffs->r_coeffs); + + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_reset_coeffs); } -static inline void bw_balance_process1(const bw_balance_coeffs *BW_RESTRICT coeffs, float x_l, float x_r, float *BW_RESTRICT y_l, float *BW_RESTRICT y_r) { +static inline void bw_balance_process1( + const bw_balance_coeffs * BW_RESTRICT coeffs, + float x_l, + float x_r, + float * BW_RESTRICT y_l, + float * BW_RESTRICT y_r) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_reset_coeffs); + BW_ASSERT(bw_is_finite(x_l)); + BW_ASSERT(bw_is_finite(x_r)); + *y_l = bw_gain_process1(&coeffs->l_coeffs, x_l); *y_r = bw_gain_process1(&coeffs->r_coeffs, x_r); + + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_reset_coeffs); + BW_ASSERT(bw_is_finite(*y_l)); + BW_ASSERT(bw_is_finite(*y_r)); } -static inline void bw_balance_process(bw_balance_coeffs *BW_RESTRICT coeffs, const float *x_l, const float *x_r, float *y_l, float *y_r, size_t n_samples){ +static inline void bw_balance_process( + bw_balance_coeffs * BW_RESTRICT coeffs, + 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_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_reset_coeffs); + 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_balance_update_coeffs_ctrl(coeffs); for (size_t i = 0; i < n_samples; i++) { bw_balance_update_coeffs_audio(coeffs); bw_balance_process1(coeffs, x_l[i], x_r[i], y_l + i, y_r + i); } + + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_reset_coeffs); + 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_balance_process_multi(bw_balance_coeffs *BW_RESTRICT coeffs, 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_balance_process_multi( + bw_balance_coeffs * BW_RESTRICT coeffs, + 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_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_reset_coeffs); + BW_ASSERT(x_l != NULL); + BW_ASSERT(x_r != NULL); + BW_ASSERT(y_l != NULL); + BW_ASSERT(y_r != NULL); + bw_balance_update_coeffs_ctrl(coeffs); for (size_t i = 0; i < n_samples; i++) { bw_balance_update_coeffs_audio(coeffs); for (size_t j = 0; j < n_channels; j++) bw_balance_process1(coeffs, x_l[j][i], x_r[j][i], y_l[j] + i, y_r[j] + i); } + + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_reset_coeffs); } -static inline void bw_balance_set_balance(bw_balance_coeffs *BW_RESTRICT coeffs, float value) { +static inline void bw_balance_set_balance( + bw_balance_coeffs * BW_RESTRICT coeffs, + float value) { + BW_ASSERT(coeffs != NULL); + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_init); + BW_ASSERT(bw_is_finite(value)); + BW_ASSERT(value >= -1.f && value <= 1.f); + coeffs->balance = value; + + BW_ASSERT_DEEP(bw_balance_coeffs_is_valid(coeffs)); + BW_ASSERT_DEEP(coeffs->state >= bw_balance_coeffs_state_init); +} + +static inline char bw_balance_coeffs_is_valid( + const bw_balance_coeffs * BW_RESTRICT coeffs) { + BW_ASSERT(coeffs != NULL); + +#ifdef BW_DEBUG_DEEP + if (coeffs->hash != bw_hash_sdbm("bw_balance_coeffs")) + return 0; + if (coeffs->state < bw_balance_coeffs_state_init || coeffs->state > bw_balance_coeffs_state_reset_coeffs) + return 0; +#endif + + if (!bw_is_finite(coeffs->balance) || coeffs->balance < -1.f || coeffs->balance > 1.f) + return 0; + +#ifdef BW_DEBUG_DEEP + if (coeffs->state >= bw_balance_coeffs_state_reset_coeffs) { + if (!bw_is_finite(coeffs->balance_prev) || coeffs->balance_prev < -1.f || coeffs->balance_prev > 1.f) + return 0; + } +#endif + + return bw_gain_coeffs_is_valid(&coeffs->l_coeffs) && bw_gain_coeffs_is_valid(&coeffs->r_coeffs); } #ifdef __cplusplus @@ -250,22 +448,27 @@ class Balance { public: Balance(); - 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( - const std::array x_l, - const std::array x_r, - const std::array y_l, - const std::array y_r, - size_t nSamples); + void setSampleRate( + float sampleRate); - void setBalance(float value); + void reset(); + + 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 setBalance( + float value); /*! <<<... * } * ``` @@ -277,7 +480,7 @@ public: * change at any time in future versions. Please, do not use it directly. */ private: - bw_balance_coeffs coeffs; + bw_balance_coeffs coeffs; }; template @@ -286,7 +489,8 @@ inline Balance::Balance() { } template -inline void Balance::setSampleRate(float sampleRate) { +inline void Balance::setSampleRate( + float sampleRate) { bw_balance_set_sample_rate(&coeffs, sampleRate); } @@ -297,26 +501,27 @@ inline void Balance::reset() { template inline void Balance::process( - const float * const *x_l, - const float * const *x_r, - float * const *y_l, - float * const *y_r, - size_t nSamples) { - bw_balance_process_multi(&coeffs, 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_balance_process_multi(&coeffs, xL, xR, yL, yR, N_CHANNELS, nSamples); } template inline void Balance::process( - const std::array x_l, - const std::array x_r, - const std::array y_l, - const 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 Balance::setBalance(float value) { +inline void Balance::setBalance( + float value) { bw_balance_set_balance(&coeffs, value); }