diff --git a/TODO b/TODO
index 79024e4..85c718e 100644
--- a/TODO
+++ b/TODO
@@ -28,6 +28,7 @@ finally:
* state that you need different output buffers to reset
* thank scientific paper authors
* change config.h in examples version 1.0.0
+* "Templates must have C++ linkage"?
build system:
* single header generation (vs modules in bwp... to think about)
@@ -75,6 +76,7 @@ code:
* get current compression level, knee
* src inside distortions? w/ control from outside?
* make all outs nullable?
+* make all reset state outs nullable (svf, phase_gen, ...)?
build system:
* make makefiles handle paths with spaces etc
diff --git a/examples/synth_simple/src/bw_example_synth_simple.c b/examples/synth_simple/src/bw_example_synth_simple.c
index 4669088..008dd8d 100644
--- a/examples/synth_simple/src/bw_example_synth_simple.c
+++ b/examples/synth_simple/src/bw_example_synth_simple.c
@@ -44,16 +44,18 @@ void bw_example_synth_simple_set_sample_rate(bw_example_synth_simple *instance,
void bw_example_synth_simple_reset(bw_example_synth_simple *instance) {
bw_phase_gen_reset_coeffs(&instance->phase_gen_coeffs);
- bw_phase_gen_reset_state(&instance->phase_gen_coeffs, &instance->phase_gen_state, 0.f);
+ float p, inc;
+ bw_phase_gen_reset_state(&instance->phase_gen_coeffs, &instance->phase_gen_state, 0.f, &p, &inc);
bw_osc_pulse_reset_coeffs(&instance->osc_pulse_coeffs);
bw_osc_filt_reset_state(&instance->osc_filt_state);
bw_svf_reset_coeffs(&instance->svf_coeffs);
- bw_svf_reset_state(&instance->svf_coeffs, &instance->svf_state, 0.f);
+ float lp, bp, hp;
+ bw_svf_reset_state(&instance->svf_coeffs, &instance->svf_state, 0.f, &lp, &bp, &hp);
bw_env_gen_reset_coeffs(&instance->env_gen_coeffs);
bw_env_gen_reset_state(&instance->env_gen_coeffs, &instance->env_gen_state);
bw_gain_reset_coeffs(&instance->gain_coeffs);
bw_ppm_reset_coeffs(&instance->ppm_coeffs);
- bw_ppm_reset_state(&instance->ppm_coeffs, &instance->ppm_state);
+ bw_ppm_reset_state(&instance->ppm_coeffs, &instance->ppm_state, 0.f);
instance->note = -1;
}
diff --git a/examples/synth_simple/vst3/Makefile b/examples/synth_simple/vst3/Makefile
index 070f1ba..e3d8d91 100644
--- a/examples/synth_simple/vst3/Makefile
+++ b/examples/synth_simple/vst3/Makefile
@@ -4,3 +4,6 @@ NAME := bw_example_synth_simple
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synth_simple.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/synthpp_simple/vst3/Makefile b/examples/synthpp_simple/vst3/Makefile
index 02790dc..ec6e210 100644
--- a/examples/synthpp_simple/vst3/Makefile
+++ b/examples/synthpp_simple/vst3/Makefile
@@ -4,3 +4,6 @@ NAME := bw_example_synthpp_simple
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synthpp_simple.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_phase_gen.h b/include/bw_phase_gen.h
index e8b77bd..d66be6c 100644
--- a/include/bw_phase_gen.h
+++ b/include/bw_phase_gen.h
@@ -31,6 +31,14 @@
*
* - Version 1.0.0:
*
+ * - Added initial input value to
+ *
bw_phase_gen_reset_state()
.
+ * - Added
bw_phase_gen_reset_state_multi()
and updated
+ * C++ API in this regard.
+ * - Now
bw_phase_gen_reset_state()
returns the initial
+ * output value.
+ * - Added overloaded C++
reset()
functions taking
+ * arrays as arguments.
* bw_phase_gen_process()
and
* bw_phase_gen_process_multi()
now use
* size_t
to count samples and channels.
@@ -39,7 +47,11 @@
* - Moved C++ code to C header.
* - Added overloaded C++
process()
function taking
* C-style arrays as arguments.
+ * - Fixed smoothing coefficients update in
+ *
bw_phase_gen_reset_coeffs()
.
* - Removed usage of reserved identifiers.
+ * - Clearly specified parameter validity ranges.
+ * - Added debugging code.
*
*
* - Version 0.6.0:
@@ -102,58 +114,105 @@ typedef struct bw_phase_gen_state bw_phase_gen_state;
*
* #### bw_phase_gen_init()
* ```>>> */
-static inline void bw_phase_gen_init(bw_phase_gen_coeffs *BW_RESTRICT coeffs);
+static inline void bw_phase_gen_init(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs);
/*! <<<```
* Initializes input parameter values in `coeffs`.
*
* #### bw_phase_gen_set_sample_rate()
* ```>>> */
-static inline void bw_phase_gen_set_sample_rate(bw_phase_gen_coeffs *BW_RESTRICT coeffs, float sample_rate);
+static inline void bw_phase_gen_set_sample_rate(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ float sample_rate);
/*! <<<```
* Sets the `sample_rate` (Hz) value in `coeffs`.
*
* #### bw_phase_gen_reset_coeffs()
* ```>>> */
-static inline void bw_phase_gen_reset_coeffs(bw_phase_gen_coeffs *BW_RESTRICT coeffs);
+static inline void bw_phase_gen_reset_coeffs(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs);
/*! <<<```
* Resets coefficients in `coeffs` to assume their target values.
*
* #### bw_phase_gen_reset_state()
* ```>>> */
-static inline void bw_phase_gen_reset_state(const bw_phase_gen_coeffs *BW_RESTRICT coeffs, bw_phase_gen_state *BW_RESTRICT state, float phase_0);
+static inline void bw_phase_gen_reset_state(
+ const bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ bw_phase_gen_state * BW_RESTRICT state,
+ float phase_0,
+ float * BW_RESTRICT y_0,
+ float * BW_RESTRICT y_inc_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 phase value `phase_0`.
+ *
+ * The corresponding initial output and phase increment values are put into
+ * `y_0` and `y_inc_0` respectively.
+ *
+ * #### bw_phase_gen_reset_state_multi()
+ * ```>>> */
+static inline void bw_phase_gen_reset_state_multi(
+ const bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ bw_phase_gen_state * BW_RESTRICT const * BW_RESTRICT state,
+ const float * phase_0,
+ float * y_0,
+ float * y_inc_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 phase value in the `phase_0`
+ * array.
+ *
+ * The corresponding initial output and phase increment values are put into
+ * the `y_0` and `y_inc_0` arrays, respectively, if they are not `NULL`.
*
* #### bw_phase_gen_update_coeffs_ctrl()
* ```>>> */
-static inline void bw_phase_gen_update_coeffs_ctrl(bw_phase_gen_coeffs *BW_RESTRICT coeffs);
+static inline void bw_phase_gen_update_coeffs_ctrl(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs);
/*! <<<```
* Triggers control-rate update of coefficients in `coeffs`.
*
* #### bw_phase_gen_update_coeffs_audio()
* ```>>> */
-static inline void bw_phase_gen_update_coeffs_audio(bw_phase_gen_coeffs *BW_RESTRICT coeffs);
+static inline void bw_phase_gen_update_coeffs_audio(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs);
/*! <<<```
* Triggers audio-rate update of coefficients in `coeffs`.
*
* #### bw_phase_gen_process1\*()
* ```>>> */
-static inline void bw_phase_gen_process1(const bw_phase_gen_coeffs *BW_RESTRICT coeffs, bw_phase_gen_state *BW_RESTRICT state, float *BW_RESTRICT y, float *BW_RESTRICT y_phase_inc);
-static inline void bw_phase_gen_process1_mod(const bw_phase_gen_coeffs *BW_RESTRICT coeffs, bw_phase_gen_state *BW_RESTRICT state, float x_mod, float *BW_RESTRICT y, float *BW_RESTRICT y_phase_inc);
+static inline void bw_phase_gen_process1(
+ const bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ bw_phase_gen_state * BW_RESTRICT state,
+ float * BW_RESTRICT y,
+ float * BW_RESTRICT y_inc);
+
+static inline void bw_phase_gen_process1_mod(
+ const bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ bw_phase_gen_state * BW_RESTRICT state,
+ float x_mod,
+ float * BW_RESTRICT y,
+ float * BW_RESTRICT y_inc);
/*! <<<```
- * These functions generate and return one sample using `coeffs`, while using
- * and updating `state`, and put the corresponding phase increment value in
- * `y_phase_inc`.
+ * These functions generate one output sample using `coeffs`, while using
+ * and updating `state`, putting its value in `y` and the corresponding phase
+ * increment value in `y_inc`.
*
* In particular:
- * * `bw_phase_gen_process1()` does not apply exponential frequency
- * modulation;
+ * * `bw_phase_gen_process1()` does not apply frequency modulation;
* * `bw_phase_gen_process1_mod()` applies exponential frequency modulation
* using `x_mod` as modulation input (scale `1.f`/octave).
*
* #### bw_phase_gen_process()
* ```>>> */
-static inline void bw_phase_gen_process(bw_phase_gen_coeffs *BW_RESTRICT coeffs, bw_phase_gen_state *BW_RESTRICT state, const float *x_mod, float *y, float *y_phase_inc, size_t n_samples);
+static inline void bw_phase_gen_process(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ bw_phase_gen_state * BW_RESTRICT state,
+ const float * x_mod,
+ float * y,
+ float * y_inc,
+ size_t n_samples);
/*! <<<```
* Generates and fills the first `n_samples` of the output buffer `y`, while
* using and updating both `coeffs` and `state` (control and audio rate).
@@ -165,7 +224,14 @@ static inline void bw_phase_gen_process(bw_phase_gen_coeffs *BW_RESTRICT coeffs,
*
* #### bw_phase_gen_process_multi()
* ```>>> */
-static inline void bw_phase_gen_process_multi(bw_phase_gen_coeffs *BW_RESTRICT coeffs, bw_phase_gen_state *BW_RESTRICT const *BW_RESTRICT state, const float * const *x_mod, float * const *y, float * const *y_phase_inc, size_t n_channels, size_t n_samples);
+static inline void bw_phase_gen_process_multi(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ bw_phase_gen_state * BW_RESTRICT const * BW_RESTRICT state,
+ const float * const * x_mod,
+ float * const * y,
+ float * const * y_inc,
+ size_t n_channels,
+ size_t n_samples);
/*! <<<```
* Generates and fills the first `n_samples` of the `n_channels` output
* buffers `y`, while using and updating both the common `coeffs` and each of
@@ -180,19 +246,55 @@ static inline void bw_phase_gen_process_multi(bw_phase_gen_coeffs *BW_RESTRICT c
*
* #### bw_phase_gen_set_frequency()
* ```>>> */
-static inline void bw_phase_gen_set_frequency(bw_phase_gen_coeffs *BW_RESTRICT coeffs, float value);
+static inline void bw_phase_gen_set_frequency(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ float value);
/*! <<<```
* Sets the base frequency to `value` (Hz) in `coeffs`.
- *
+ *
+ * `value` must be finite.
+ *
* Default value: `1.f`.
*
* #### bw_phase_gen_set_portamento_tau()
* ```>>> */
-static inline void bw_phase_gen_set_portamento_tau(bw_phase_gen_coeffs *BW_RESTRICT coeffs, float value);
+static inline void bw_phase_gen_set_portamento_tau(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ float value);
/*! <<<```
* Sets the portamento time constant `value` (s) in `coeffs`.
*
+ * `value` must be non-negative.
+ *
* Default value: `0.f`.
+ *
+ * #### bw_phase_gen_coeffs_is_valid()
+ * ```>>> */
+static inline char bw_phase_gen_coeffs_is_valid(
+ const bw_phase_gen_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_phase_gen_coeffs`.
+ *
+ * #### bw_phase_gen_state_is_valid()
+ * ```>>> */
+static inline char bw_phase_gen_state_is_valid(
+ const bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ const bw_phase_gen_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_phase_gen_state`.
* }}} */
#ifdef __cplusplus
@@ -211,36 +313,82 @@ static inline void bw_phase_gen_set_portamento_tau(bw_phase_gen_coeffs *BW_RESTR
extern "C" {
#endif
+#ifdef BW_DEBUG_DEEP
+enum bw_phase_gen_coeffs_state {
+ bw_phase_gen_coeffs_state_invalid,
+ bw_phase_gen_coeffs_state_init,
+ bw_phase_gen_coeffs_state_set_sample_rate,
+ bw_phase_gen_coeffs_state_reset_coeffs
+};
+#endif
+
struct bw_phase_gen_coeffs {
+#ifdef BW_DEBUG_DEEP
+ uint32_t hash;
+ enum bw_phase_gen_coeffs_state state;
+ uint32_t reset_id;
+#endif
+
// Sub-components
- bw_one_pole_coeffs portamento_coeffs;
- bw_one_pole_state portamento_state;
+ bw_one_pole_coeffs portamento_coeffs;
+ bw_one_pole_state portamento_state;
// Coefficients
- float T;
+ float T;
- float portamento_target;
+ float portamento_target;
// Parameters
- float frequency;
- float frequency_prev;
+ float frequency;
+ float frequency_prev;
};
struct bw_phase_gen_state {
- float phase;
+#ifdef BW_DEBUG_DEEP
+ uint32_t hash;
+ uint32_t coeffs_reset_id;
+#endif
+
+ // States
+ float phase;
};
-static inline void bw_phase_gen_init(bw_phase_gen_coeffs *BW_RESTRICT coeffs) {
+static inline void bw_phase_gen_init(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+
bw_one_pole_init(&coeffs->portamento_coeffs);
coeffs->frequency = 1.f;
+
+#ifdef BW_DEBUG_DEEP
+ coeffs->hash = bw_hash_sdbm("bw_phase_gen_coeffs");
+ coeffs->state = bw_phase_gen_coeffs_state_init;
+ coeffs->reset_id = coeffs->hash + 1;
+#endif
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state == bw_phase_gen_coeffs_state_init);
}
-static inline void bw_phase_gen_set_sample_rate(bw_phase_gen_coeffs *BW_RESTRICT coeffs, float sample_rate) {
+static inline void bw_phase_gen_set_sample_rate(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ float sample_rate) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_init);
+ BW_ASSERT(bw_is_finite(sample_rate) && sample_rate > 0.f);
+
bw_one_pole_set_sample_rate(&coeffs->portamento_coeffs, sample_rate);
coeffs->T = 1.f / sample_rate;
+
+#ifdef BW_DEBUG_DEEP
+ coeffs->state = bw_phase_gen_coeffs_state_set_sample_rate;
+#endif
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state == bw_phase_gen_coeffs_state_set_sample_rate);
}
-static inline void bw_phase_gen_do_update_coeffs_ctrl(bw_phase_gen_coeffs *BW_RESTRICT coeffs, char force) {
+static inline void bw_phase_gen_do_update_coeffs_ctrl(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs, char force) {
bw_one_pole_update_coeffs_ctrl(&coeffs->portamento_coeffs);
if (force || coeffs->frequency != coeffs->frequency_prev) {
coeffs->portamento_target = coeffs->T * coeffs->frequency;
@@ -248,49 +396,197 @@ static inline void bw_phase_gen_do_update_coeffs_ctrl(bw_phase_gen_coeffs *BW_RE
}
}
-static inline void bw_phase_gen_reset_coeffs(bw_phase_gen_coeffs *BW_RESTRICT coeffs) {
- bw_phase_gen_do_update_coeffs_ctrl(coeffs, 1);
+static inline void bw_phase_gen_reset_coeffs(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_set_sample_rate);
+
bw_one_pole_reset_coeffs(&coeffs->portamento_coeffs);
+ bw_phase_gen_do_update_coeffs_ctrl(coeffs, 1);
bw_one_pole_reset_state(&coeffs->portamento_coeffs, &coeffs->portamento_state, coeffs->portamento_target);
+
+#ifdef BW_DEBUG_DEEP
+ coeffs->state = bw_phase_gen_coeffs_state_reset_coeffs;
+ coeffs->reset_id++;
+#endif
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state == bw_phase_gen_coeffs_state_reset_coeffs);
}
-static inline void bw_phase_gen_reset_state(const bw_phase_gen_coeffs *BW_RESTRICT coeffs, bw_phase_gen_state *BW_RESTRICT state, float phase_0) {
- (void)coeffs;
+static inline void bw_phase_gen_reset_state(
+ const bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ bw_phase_gen_state * BW_RESTRICT state,
+ float phase_0,
+ float * BW_RESTRICT y_0,
+ float * BW_RESTRICT y_inc_0) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+ BW_ASSERT(state != NULL);
+ BW_ASSERT(bw_is_finite(phase_0));
+ BW_ASSERT(y_0 != NULL);
+ BW_ASSERT(y_inc_0 != NULL);
+
state->phase = phase_0;
+ *y_inc_0 = bw_one_pole_get_y_z1(&coeffs->portamento_state);
+ *y_0 = phase_0;
+
+#ifdef BW_DEBUG_DEEP
+ state->hash = bw_hash_sdbm("bw_phase_gen_state");
+ state->coeffs_reset_id = coeffs->reset_id;
+#endif
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+ BW_ASSERT_DEEP(bw_phase_gen_state_is_valid(coeffs, state));
+ BW_ASSERT(bw_is_finite(*y_0));
+ BW_ASSERT(bw_is_finite(*y_inc_0));
}
-static inline void bw_phase_gen_update_coeffs_ctrl(bw_phase_gen_coeffs *BW_RESTRICT coeffs) {
+static inline void bw_phase_gen_reset_state_multi(
+ const bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ bw_phase_gen_state * BW_RESTRICT const * BW_RESTRICT state,
+ const float * phase_0,
+ float * y_0,
+ float * y_inc_0,
+ size_t n_channels) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+ BW_ASSERT(state != NULL);
+ BW_ASSERT(phase_0 != NULL);
+
+ if (y_0 != NULL) {
+ if (y_inc_0 != NULL) {
+ for (size_t i = 0; i < n_channels; i++)
+ bw_phase_gen_reset_state(coeffs, state[i], phase_0[i], y_0 + i, y_inc_0 + i);
+ } else {
+ for (size_t i = 0; i < n_channels; i++) {
+ float v_inc;
+ bw_phase_gen_reset_state(coeffs, state[i], phase_0[i], y_0 + i, &v_inc);
+ }
+ }
+ } else {
+ if (y_inc_0 != NULL) {
+ for (size_t i = 0; i < n_channels; i++) {
+ float v;
+ bw_phase_gen_reset_state(coeffs, state[i], phase_0[i], &v, y_inc_0 + i);
+ }
+ } else {
+ for (size_t i = 0; i < n_channels; i++) {
+ float v, v_inc;
+ bw_phase_gen_reset_state(coeffs, state[i], phase_0[i], &v, &v_inc);
+ }
+ }
+ }
+
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+ BW_ASSERT_DEEP(y_0 != NULL ? bw_has_only_finite(y_0, n_channels) : 1);
+ BW_ASSERT_DEEP(y_inc_0 != NULL ? bw_has_only_finite(y_inc_0, n_channels) : 1);
+}
+
+static inline void bw_phase_gen_update_coeffs_ctrl(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+
bw_phase_gen_do_update_coeffs_ctrl(coeffs, 0);
+
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
}
-static inline void bw_phase_gen_update_coeffs_audio(bw_phase_gen_coeffs *BW_RESTRICT coeffs) {
+static inline void bw_phase_gen_update_coeffs_audio(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+
bw_one_pole_process1(&coeffs->portamento_coeffs, &coeffs->portamento_state, coeffs->portamento_target);
+
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
}
-static inline float _bw_phase_gen_update_phase(bw_phase_gen_state *BW_RESTRICT state, float phase_inc) {
- state->phase += phase_inc;
+static inline float bw_phase_gen_update_phase(
+ bw_phase_gen_state * BW_RESTRICT state,
+ float inc) {
+ state->phase += inc;
state->phase -= bw_floorf(state->phase);
return state->phase;
}
-static inline void bw_phase_gen_process1(const bw_phase_gen_coeffs *BW_RESTRICT coeffs, bw_phase_gen_state *BW_RESTRICT state, float *BW_RESTRICT y, float *BW_RESTRICT y_phase_inc) {
- *y_phase_inc = bw_one_pole_get_y_z1(&coeffs->portamento_state);
- *y = _bw_phase_gen_update_phase(state, *y_phase_inc);
+static inline void bw_phase_gen_process1(
+ const bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ bw_phase_gen_state * BW_RESTRICT state,
+ float * BW_RESTRICT y,
+ float * BW_RESTRICT y_inc) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+ BW_ASSERT(state != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_state_is_valid(coeffs, state));
+ BW_ASSERT(y != NULL);
+ BW_ASSERT(y_inc != NULL);
+
+ *y_inc = bw_one_pole_get_y_z1(&coeffs->portamento_state);
+ *y = bw_phase_gen_update_phase(state, *y_inc);
+
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+ BW_ASSERT_DEEP(bw_phase_gen_state_is_valid(coeffs, state));
+ BW_ASSERT(bw_is_finite(*y));
+ BW_ASSERT(bw_is_finite(*y_inc));
}
-static inline void bw_phase_gen_process1_mod(const bw_phase_gen_coeffs *BW_RESTRICT coeffs, bw_phase_gen_state *BW_RESTRICT state, float x_mod, float *BW_RESTRICT y, float *BW_RESTRICT y_phase_inc) {
- *y_phase_inc = bw_one_pole_get_y_z1(&coeffs->portamento_state) * bw_pow2f(x_mod);
- *y = _bw_phase_gen_update_phase(state, *y_phase_inc);
+static inline void bw_phase_gen_process1_mod(
+ const bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ bw_phase_gen_state * BW_RESTRICT state,
+ float x_mod,
+ float * BW_RESTRICT y,
+ float * BW_RESTRICT y_inc) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+ BW_ASSERT(state != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_state_is_valid(coeffs, state));
+ BW_ASSERT(bw_is_finite(x_mod));
+ BW_ASSERT(y != NULL);
+ BW_ASSERT(y_inc != NULL);
+
+ *y_inc = bw_one_pole_get_y_z1(&coeffs->portamento_state) * bw_pow2f(x_mod);
+ *y = bw_phase_gen_update_phase(state, *y_inc);
+
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+ BW_ASSERT_DEEP(bw_phase_gen_state_is_valid(coeffs, state));
+ BW_ASSERT(bw_is_finite(*y));
+ BW_ASSERT(bw_is_finite(*y_inc));
}
-static inline void bw_phase_gen_process(bw_phase_gen_coeffs *BW_RESTRICT coeffs, bw_phase_gen_state *BW_RESTRICT state, const float *x_mod, float* y, float *y_phase_inc, size_t n_samples) {
+static inline void bw_phase_gen_process(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ bw_phase_gen_state * BW_RESTRICT state,
+ const float * x_mod,
+ float * y,
+ float * y_inc,
+ size_t n_samples) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+ BW_ASSERT(state != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_state_is_valid(coeffs, state));
+ BW_ASSERT_DEEP(x_mod != NULL ? bw_has_only_finite(x_mod, n_samples) : 1);
+
bw_phase_gen_update_coeffs_ctrl(coeffs);
if (y != NULL) {
if (x_mod != NULL) {
- if (y_phase_inc != NULL)
+ if (y_inc != NULL)
for (size_t i = 0; i < n_samples; i++) {
bw_phase_gen_update_coeffs_audio(coeffs);
- bw_phase_gen_process1_mod(coeffs, state, x_mod[i], y + i, y_phase_inc + i);
+ bw_phase_gen_process1_mod(coeffs, state, x_mod[i], y + i, y_inc + i);
}
else
for (size_t i = 0; i < n_samples; i++) {
@@ -299,10 +595,10 @@ static inline void bw_phase_gen_process(bw_phase_gen_coeffs *BW_RESTRICT coeffs,
bw_phase_gen_process1_mod(coeffs, state, x_mod[i], y + i, &v_phase_inc);
}
} else {
- if (y_phase_inc != NULL)
+ if (y_inc != NULL)
for (size_t i = 0; i < n_samples; i++) {
bw_phase_gen_update_coeffs_audio(coeffs);
- bw_phase_gen_process1(coeffs, state, y + i, y_phase_inc + i);
+ bw_phase_gen_process1(coeffs, state, y + i, y_inc + i);
}
else
for (size_t i = 0; i < n_samples; i++) {
@@ -313,11 +609,11 @@ static inline void bw_phase_gen_process(bw_phase_gen_coeffs *BW_RESTRICT coeffs,
}
} else {
if (x_mod != NULL) {
- if (y_phase_inc != NULL)
+ if (y_inc != NULL)
for (size_t i = 0; i < n_samples; i++) {
bw_phase_gen_update_coeffs_audio(coeffs);
float v;
- bw_phase_gen_process1_mod(coeffs, state, x_mod[i], &v, y_phase_inc + i);
+ bw_phase_gen_process1_mod(coeffs, state, x_mod[i], &v, y_inc + i);
}
else
for (size_t i = 0; i < n_samples; i++) {
@@ -326,11 +622,11 @@ static inline void bw_phase_gen_process(bw_phase_gen_coeffs *BW_RESTRICT coeffs,
bw_phase_gen_process1_mod(coeffs, state, x_mod[i], &v, &v_phase_inc);
}
} else {
- if (y_phase_inc != NULL)
+ if (y_inc != NULL)
for (size_t i = 0; i < n_samples; i++) {
bw_phase_gen_update_coeffs_audio(coeffs);
float v;
- bw_phase_gen_process1(coeffs, state, &v, y_phase_inc + i);
+ bw_phase_gen_process1(coeffs, state, &v, y_inc + i);
}
else
for (size_t i = 0; i < n_samples; i++) {
@@ -340,13 +636,31 @@ static inline void bw_phase_gen_process(bw_phase_gen_coeffs *BW_RESTRICT coeffs,
}
}
}
+
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+ BW_ASSERT_DEEP(bw_phase_gen_state_is_valid(coeffs, state));
+ BW_ASSERT_DEEP(y != NULL ? bw_has_only_finite(y, n_samples) : 1);
+ BW_ASSERT_DEEP(y_inc != NULL ? bw_has_only_finite(y_inc, n_samples) : 1);
}
-static inline void bw_phase_gen_process_multi(bw_phase_gen_coeffs *BW_RESTRICT coeffs, bw_phase_gen_state *BW_RESTRICT const *BW_RESTRICT state, const float * const *x_mod, float * const *y, float * const *y_phase_inc, size_t n_channels, size_t n_samples) {
+static inline void bw_phase_gen_process_multi(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ bw_phase_gen_state * BW_RESTRICT const * BW_RESTRICT state,
+ const float * const * x_mod,
+ float * const * y,
+ float * const * y_inc,
+ size_t n_channels,
+ size_t n_samples) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
+ BW_ASSERT(state != NULL);
+
bw_phase_gen_update_coeffs_ctrl(coeffs);
if (y != NULL) {
if (x_mod != NULL) {
- if (y_phase_inc != NULL)
+ if (y_inc != NULL)
for (size_t i = 0; i < n_samples; i++) {
bw_phase_gen_update_coeffs_audio(coeffs);
for (size_t j = 0; j < n_channels; j++) {
@@ -357,8 +671,8 @@ static inline void bw_phase_gen_process_multi(bw_phase_gen_coeffs *BW_RESTRICT c
bw_phase_gen_process1(coeffs, state[j], &v, &v_phase_inc);
if (y[j])
y[j][i] = v;
- if (y_phase_inc[j])
- y_phase_inc[j][i] = v_phase_inc;
+ if (y_inc[j])
+ y_inc[j][i] = v_phase_inc;
}
}
else
@@ -375,7 +689,7 @@ static inline void bw_phase_gen_process_multi(bw_phase_gen_coeffs *BW_RESTRICT c
}
}
} else {
- if (y_phase_inc != NULL)
+ if (y_inc != NULL)
for (size_t i = 0; i < n_samples; i++) {
bw_phase_gen_update_coeffs_audio(coeffs);
for (size_t j = 0; j < n_channels; j++) {
@@ -383,8 +697,8 @@ static inline void bw_phase_gen_process_multi(bw_phase_gen_coeffs *BW_RESTRICT c
bw_phase_gen_process1(coeffs, state[j], &v, &v_phase_inc);
if (y[j])
y[j][i] = v;
- if (y_phase_inc[j])
- y_phase_inc[j][i] = v_phase_inc;
+ if (y_inc[j])
+ y_inc[j][i] = v_phase_inc;
}
}
else
@@ -400,7 +714,7 @@ static inline void bw_phase_gen_process_multi(bw_phase_gen_coeffs *BW_RESTRICT c
}
} else {
if (x_mod != NULL) {
- if (y_phase_inc != NULL)
+ if (y_inc != NULL)
for (size_t i = 0; i < n_samples; i++) {
bw_phase_gen_update_coeffs_audio(coeffs);
for (size_t j = 0; j < n_channels; j++) {
@@ -409,8 +723,8 @@ static inline void bw_phase_gen_process_multi(bw_phase_gen_coeffs *BW_RESTRICT c
bw_phase_gen_process1_mod(coeffs, state[j], x_mod[j][i], &v, &v_phase_inc);
else
bw_phase_gen_process1(coeffs, state[j], &v, &v_phase_inc);
- if (y_phase_inc[j])
- y_phase_inc[j][i] = v_phase_inc;
+ if (y_inc[j])
+ y_inc[j][i] = v_phase_inc;
}
}
else
@@ -425,14 +739,14 @@ static inline void bw_phase_gen_process_multi(bw_phase_gen_coeffs *BW_RESTRICT c
}
}
} else {
- if (y_phase_inc != NULL)
+ if (y_inc != NULL)
for (size_t i = 0; i < n_samples; i++) {
bw_phase_gen_update_coeffs_audio(coeffs);
for (size_t j = 0; j < n_channels; j++) {
float v, v_phase_inc;
bw_phase_gen_process1(coeffs, state[j], &v, &v_phase_inc);
- if (y_phase_inc[j])
- y_phase_inc[j][i] = v_phase_inc;
+ if (y_inc[j])
+ y_inc[j][i] = v_phase_inc;
}
}
else
@@ -445,14 +759,93 @@ static inline void bw_phase_gen_process_multi(bw_phase_gen_coeffs *BW_RESTRICT c
}
}
}
+
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs);
}
-static inline void bw_phase_gen_set_frequency(bw_phase_gen_coeffs *BW_RESTRICT coeffs, float value) {
+static inline void bw_phase_gen_set_frequency(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ float value) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_init);
+ BW_ASSERT(bw_is_finite(value));
+
coeffs->frequency = value;
+
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_init);
}
-static inline void bw_phase_gen_set_portamento_tau(bw_phase_gen_coeffs *BW_RESTRICT coeffs, float value) {
+static inline void bw_phase_gen_set_portamento_tau(
+ bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ float value) {
+ BW_ASSERT(coeffs != NULL);
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_init);
+ BW_ASSERT(bw_is_finite(value));
+ BW_ASSERT(value >= 0.f);
+
bw_one_pole_set_tau(&coeffs->portamento_coeffs, value);
+
+ BW_ASSERT_DEEP(bw_phase_gen_coeffs_is_valid(coeffs));
+ BW_ASSERT_DEEP(coeffs->state >= bw_phase_gen_coeffs_state_init);
+}
+
+static inline char bw_phase_gen_coeffs_is_valid(
+ const bw_phase_gen_coeffs * BW_RESTRICT coeffs) {
+ BW_ASSERT(coeffs != NULL);
+
+#ifdef BW_DEBUG_DEEP
+ if (coeffs->hash != bw_hash_sdbm("bw_phase_gen_coeffs"))
+ return 0;
+ if (coeffs->state < bw_phase_gen_coeffs_state_init || coeffs->state > bw_phase_gen_coeffs_state_reset_coeffs)
+ return 0;
+#endif
+
+ if (!bw_is_finite(coeffs->frequency))
+ return 0;
+
+ if (!bw_one_pole_coeffs_is_valid(&coeffs->portamento_coeffs))
+ return 0;
+
+#ifdef BW_DEBUG_DEEP
+ if (coeffs->state >= bw_phase_gen_coeffs_state_set_sample_rate) {
+ if (!bw_is_finite(coeffs->T) || coeffs->T <= 0.f)
+ return 0;
+ }
+
+ if (coeffs->state >= bw_phase_gen_coeffs_state_reset_coeffs) {
+ if (!bw_is_finite(coeffs->portamento_target) || coeffs->portamento_target < 0.f)
+ return 0;
+ if (!bw_is_finite(coeffs->frequency_prev))
+ return 0;
+
+ if (!bw_one_pole_state_is_valid(&coeffs->portamento_coeffs, &coeffs->portamento_state))
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
+static inline char bw_phase_gen_state_is_valid(
+ const bw_phase_gen_coeffs * BW_RESTRICT coeffs,
+ const bw_phase_gen_state * BW_RESTRICT state) {
+ BW_ASSERT(state != NULL);
+
+#ifdef BW_DEBUG_DEEP
+ if (state->hash != bw_hash_sdbm("bw_phase_gen_state"))
+ return 0;
+
+ if (coeffs != NULL && coeffs->reset_id != state->coeffs_reset_id)
+ return 0;
+#endif
+
+ (void)coeffs;
+
+ return bw_is_finite(state->phase) && state->phase >= 0.f && state->phase < 1.f;
}
#ifdef __cplusplus
@@ -472,21 +865,46 @@ class PhaseGen {
public:
PhaseGen();
- void setSampleRate(float sampleRate);
- void reset(float phase_0 = 0.f);
- void process(
- const float * const *x_mod,
- float * const *y,
- float * const *y_phase_inc,
- size_t nSamples);
- void process(
- std::array x_mod,
- std::array y,
- std::array y_phase_inc,
- size_t nSamples);
+ void setSampleRate(
+ float sampleRate);
- void setFrequency(float value);
- void setPortamentoTau(float value);
+ void reset(
+ float phase0 = 0.f,
+ float * BW_RESTRICT y0 = nullptr,
+ float * BW_RESTRICT yInc0 = nullptr);
+
+ void reset(
+ float phase0,
+ std::array * BW_RESTRICT y0,
+ std::array * BW_RESTRICT yInc0);
+
+ void reset(
+ const float * phase0,
+ float * y0 = nullptr,
+ float * yInc0 = nullptr);
+
+ void reset(
+ std::array phase0,
+ std::array * BW_RESTRICT y0 = nullptr,
+ std::array * BW_RESTRICT yInc0 = nullptr);
+
+ void process(
+ const float * const * xMod,
+ float * const * y,
+ float * const * yInc,
+ size_t nSamples);
+
+ void process(
+ std::array xMod,
+ std::array y,
+ std::array yInc,
+ size_t nSamples);
+
+ void setFrequency(
+ float value);
+
+ void setPortamentoTau(
+ float value);
/*! <<<...
* }
* ```
@@ -498,9 +916,9 @@ public:
* change at any time in future versions. Please, do not use it directly. */
private:
- bw_phase_gen_coeffs coeffs;
- bw_phase_gen_state states[N_CHANNELS];
- bw_phase_gen_state *BW_RESTRICT statesP[N_CHANNELS];
+ bw_phase_gen_coeffs coeffs;
+ bw_phase_gen_state states[N_CHANNELS];
+ bw_phase_gen_state * BW_RESTRICT statesP[N_CHANNELS];
};
template
@@ -511,42 +929,94 @@ inline PhaseGen::PhaseGen() {
}
template
-inline void PhaseGen::setSampleRate(float sampleRate) {
+inline void PhaseGen::setSampleRate(
+ float sampleRate) {
bw_phase_gen_set_sample_rate(&coeffs, sampleRate);
}
template
-inline void PhaseGen::reset(float phase_0) {
+inline void PhaseGen::reset(
+ float phase0,
+ float * BW_RESTRICT y0,
+ float * BW_RESTRICT yInc0) {
bw_phase_gen_reset_coeffs(&coeffs);
- for (size_t i = 0; i < N_CHANNELS; i++)
- bw_phase_gen_reset_state(&coeffs, states + i, phase_0);
+ if (y0 != nullptr) {
+ if (yInc0 != nullptr) {
+ for (size_t i = 0; i < N_CHANNELS; i++)
+ bw_phase_gen_reset_state(&coeffs, states + i, phase0, y0 + i, yInc0 + i);
+ } else {
+ for (size_t i = 0; i < N_CHANNELS; i++) {
+ float vInc;
+ bw_phase_gen_reset_state(&coeffs, states + i, phase0, y0 + i, &vInc);
+ }
+ }
+ } else {
+ if (yInc0 != nullptr) {
+ for (size_t i = 0; i < N_CHANNELS; i++) {
+ float v;
+ bw_phase_gen_reset_state(&coeffs, states + i, phase0, &v, yInc0 + i);
+ }
+ } else {
+ for (size_t i = 0; i < N_CHANNELS; i++) {
+ float v, vInc;
+ bw_phase_gen_reset_state(&coeffs, states + i, phase0, &v, &vInc);
+ }
+ }
+ }
+}
+
+template
+inline void PhaseGen::reset(
+ float phase0,
+ std::array * BW_RESTRICT y0,
+ std::array * BW_RESTRICT yInc0) {
+ reset(phase0, y0 != nullptr ? y0.data() : nullptr, yInc0 != nullptr ? yInc0.data() : nullptr);
+}
+
+template
+inline void PhaseGen::reset(
+ const float * phase0,
+ float * y0,
+ float * yInc0) {
+ bw_phase_gen_reset_coeffs(&coeffs);
+ bw_phase_gen_reset_state_multi(&coeffs, statesP, phase0, y0, yInc0, N_CHANNELS);
+}
+
+template
+inline void PhaseGen::reset(
+ std::array phase0,
+ std::array * BW_RESTRICT y0,
+ std::array * BW_RESTRICT yInc0) {
+ reset(phase0.data(), y0 != nullptr ? y0.data() : nullptr, yInc0 != nullptr ? yInc0.data() : nullptr);
}
template
inline void PhaseGen::process(
- const float * const *x_mod,
- float * const *y,
- float * const *y_phase_inc,
- size_t nSamples) {
- bw_phase_gen_process_multi(&coeffs, statesP, x_mod, y, y_phase_inc, N_CHANNELS, nSamples);
+ const float * const * xMod,
+ float * const * y,
+ float * const * yInc,
+ size_t nSamples) {
+ bw_phase_gen_process_multi(&coeffs, statesP, xMod, y, yInc, N_CHANNELS, nSamples);
}
template
inline void PhaseGen::process(
- std::array x_mod,
- std::array y,
- std::array y_phase_inc,
- size_t nSamples) {
- process(x_mod.data(), y.data(), y_phase_inc.data(), nSamples);
+ std::array xMod,
+ std::array y,
+ std::array yInc,
+ size_t nSamples) {
+ process(xMod.data(), y.data(), yInc.data(), nSamples);
}
template
-inline void PhaseGen::setFrequency(float value) {
+inline void PhaseGen::setFrequency(
+ float value) {
bw_phase_gen_set_frequency(&coeffs, value);
}
template
-inline void PhaseGen::setPortamentoTau(float value) {
+inline void PhaseGen::setPortamentoTau(
+ float value) {
bw_phase_gen_set_portamento_tau(&coeffs, value);
}
diff --git a/include/bw_svf.h b/include/bw_svf.h
index b4a28c1..e2212da 100644
--- a/include/bw_svf.h
+++ b/include/bw_svf.h
@@ -180,8 +180,8 @@ static inline void bw_svf_reset_state_multi(
* array.
*
* The corresponding initial lowpass, bandpass, and highpass output values
- * are put into `y_lp_0`, `y_bp_0`, and `y_hp_0` respectively, if they are
- * not `NULL`.
+ * are put into the `y_lp_0`, `y_bp_0`, and `y_hp_0` arrays, respectively, if
+ * they are not `NULL`.
*
* #### bw_svf_update_coeffs_ctrl()
* ```>>> */