diff --git a/examples/synth_mono/src/bw_example_synth_mono.c b/examples/synth_mono/src/bw_example_synth_mono.c
index 0dbabe7..eb0ec0a 100644
--- a/examples/synth_mono/src/bw_example_synth_mono.c
+++ b/examples/synth_mono/src/bw_example_synth_mono.c
@@ -87,35 +87,37 @@ void bw_example_synth_mono_reset(bw_example_synth_mono *instance) {
const float cutoff = 20.f + (20e3f - 20.f) * v * v * v;
bw_svf_set_cutoff(&instance->vcf_coeffs, bw_clipf(cutoff, 20.f, 20e3f));
+ float p, pi;
bw_phase_gen_reset_coeffs(&instance->vco1_phase_gen_coeffs);
- bw_phase_gen_reset_state(&instance->vco1_phase_gen_coeffs, &instance->vco1_phase_gen_state, 0.f);
+ bw_phase_gen_reset_state(&instance->vco1_phase_gen_coeffs, &instance->vco1_phase_gen_state, 0.f, &p, &pi);
bw_osc_pulse_reset_coeffs(&instance->vco1_pulse_coeffs);
bw_osc_tri_reset_coeffs(&instance->vco1_tri_coeffs);
bw_gain_reset_coeffs(&instance->vco1_gain_coeffs);
bw_phase_gen_reset_coeffs(&instance->vco2_phase_gen_coeffs);
- bw_phase_gen_reset_state(&instance->vco2_phase_gen_coeffs, &instance->vco2_phase_gen_state, 0.f);
+ bw_phase_gen_reset_state(&instance->vco2_phase_gen_coeffs, &instance->vco2_phase_gen_state, 0.f, &p, &pi);
bw_osc_pulse_reset_coeffs(&instance->vco2_pulse_coeffs);
bw_osc_tri_reset_coeffs(&instance->vco2_tri_coeffs);
bw_gain_reset_coeffs(&instance->vco2_gain_coeffs);
bw_phase_gen_reset_coeffs(&instance->vco3_phase_gen_coeffs);
- bw_phase_gen_reset_state(&instance->vco3_phase_gen_coeffs, &instance->vco3_phase_gen_state, 0.f);
+ bw_phase_gen_reset_state(&instance->vco3_phase_gen_coeffs, &instance->vco3_phase_gen_state, 0.f, &p, &pi);
bw_osc_pulse_reset_coeffs(&instance->vco3_pulse_coeffs);
bw_osc_tri_reset_coeffs(&instance->vco3_tri_coeffs);
bw_gain_reset_coeffs(&instance->vco3_gain_coeffs);
- bw_osc_filt_reset_state(&instance->osc_filt_state);
+ bw_osc_filt_reset_state(&instance->osc_filt_state, 0.f);
bw_pink_filt_reset_state(&instance->pink_filt_coeffs, &instance->pink_filt_state);
bw_gain_reset_coeffs(&instance->noise_gain_coeffs);
bw_env_gen_reset_coeffs(&instance->vcf_env_gen_coeffs);
- bw_env_gen_reset_state(&instance->vcf_env_gen_coeffs, &instance->vcf_env_gen_state);
+ bw_env_gen_reset_state(&instance->vcf_env_gen_coeffs, &instance->vcf_env_gen_state, 0);
bw_svf_reset_coeffs(&instance->vcf_coeffs);
- bw_svf_reset_state(&instance->vcf_coeffs, &instance->vcf_state, 0.f);
+ float lp, bp, hp;
+ bw_svf_reset_state(&instance->vcf_coeffs, &instance->vcf_state, 0.f, &lp, &bp, &hp);
bw_env_gen_reset_coeffs(&instance->vca_env_gen_coeffs);
- bw_env_gen_reset_state(&instance->vca_env_gen_coeffs, &instance->vca_env_gen_state);
+ bw_env_gen_reset_state(&instance->vca_env_gen_coeffs, &instance->vca_env_gen_state, 0);
bw_phase_gen_reset_coeffs(&instance->a440_phase_gen_coeffs);
- bw_phase_gen_reset_state(&instance->a440_phase_gen_coeffs, &instance->a440_phase_gen_state, 0.f);
+ bw_phase_gen_reset_state(&instance->a440_phase_gen_coeffs, &instance->a440_phase_gen_state, 0.f, &p, &pi);
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 = 60;
instance->gate = 0;
instance->pitch_bend = 0.f;
diff --git a/examples/synth_mono/vst3/Makefile b/examples/synth_mono/vst3/Makefile
index 4296ac9..5fa53d4 100644
--- a/examples/synth_mono/vst3/Makefile
+++ b/examples/synth_mono/vst3/Makefile
@@ -4,3 +4,6 @@ NAME := bw_example_synth_mono
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synth_mono.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_mono/vst3/Makefile b/examples/synthpp_mono/vst3/Makefile
index f4146f5..fdc83a1 100644
--- a/examples/synthpp_mono/vst3/Makefile
+++ b/examples/synthpp_mono/vst3/Makefile
@@ -4,3 +4,6 @@ NAME := bw_example_synthpp_mono
SOURCES = ${SOURCES_COMMON} ${ROOT_DIR}/../src/bw_example_synthpp_mono.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_osc_filt.h b/include/bw_osc_filt.h
index 8fb5de4..d67a031 100644
--- a/include/bw_osc_filt.h
+++ b/include/bw_osc_filt.h
@@ -35,6 +35,14 @@
*
* - Version 1.0.0:
*
+ * - Added initial input value to
+ *
bw_osc_filt_reset_state()
.
+ * - Added
bw_osc_filt_reset_state_multi()
and updated C++
+ * API in this regard.
+ * - Now
bw_osc_filt_reset_state()
returns the initial
+ * output value.
+ * - Added overloaded C++
reset()
functions taking
+ * arrays as arguments.
* bw_osc_filt_process()
and
* bw_osc_filt_process_multi()
now use
* size_t
to count samples and channels.
@@ -44,6 +52,7 @@
* - Added overloaded C++
process()
function taking
* C-style arrays as arguments.
* - Removed usage of reserved identifiers.
+ * - Added debugging code.
*
*
* - Version 0.6.0:
@@ -89,20 +98,45 @@ typedef struct bw_osc_filt_state bw_osc_filt_state;
*
* #### bw_osc_filt_reset()
* ```>>> */
-static inline void bw_osc_filt_reset_state(bw_osc_filt_state *BW_RESTRICT state);
+static inline float bw_osc_filt_reset_state(
+ bw_osc_filt_state * BW_RESTRICT state,
+ float x_0);
/*! <<<```
- * Resets the given `state` to its initial values.
+ * Resets the given `state` to its initial values using the given initial
+ * input value `x_0`.
+ *
+ * Returns the corresponding initial output value.
+ *
+ * #### bw_osc_filt_reset_state_multi()
+ * ```>>> */
+static inline void bw_osc_filt_reset_state_multi(
+ bw_osc_filt_state * BW_RESTRICT const * BW_RESTRICT state,
+ const float * x_0,
+ float * y_0,
+ size_t n_channels);
+/*! <<<```
+ * Resets each of the `n_channels` `state`s to its initial values using the
+ * corresponding initial input value in the `x_0` array.
+ *
+ * The corresponding initial output values are written into the `y_0` array,
+ * if not `NULL`.
*
* #### bw_osc_filt_process1()
* ```>>> */
-static inline float bw_osc_filt_process1(bw_osc_filt_state *BW_RESTRICT state, float x);
+static inline float bw_osc_filt_process1(
+ bw_osc_filt_state * BW_RESTRICT state,
+ float x);
/*! <<<```
- * Processes one input sample `x` usign and updating `state`. Returns the
+ * Processes one input sample `x` using and updating `state`. Returns the
* corresponding output sample.
*
* #### bw_osc_filt_process()
* ```>>> */
-static inline void bw_osc_filt_process(bw_osc_filt_state *BW_RESTRICT state, const float *x, float *y, size_t n_samples);
+static inline void bw_osc_filt_process(
+ bw_osc_filt_state * BW_RESTRICT state,
+ const float * x,
+ float * y,
+ size_t n_samples);
/*! <<<```
* Processes the first `n_samples` of the input buffer `x` and fills the
* first `n_samples` of the output buffer `y`, while using and updating
@@ -110,11 +144,28 @@ static inline void bw_osc_filt_process(bw_osc_filt_state *BW_RESTRICT state, con
*
* #### bw_osc_filt_process_multi()
* ```>>> */
-static inline void bw_osc_filt_process_multi(bw_osc_filt_state *BW_RESTRICT const *BW_RESTRICT state, const float * const *x, float * const *y, size_t n_channels, size_t n_samples);
+static inline void bw_osc_filt_process_multi(
+ bw_osc_filt_state * BW_RESTRICT const * BW_RESTRICT state,
+ const float * const * x,
+ float * const * y,
+ size_t n_channels,
+ size_t n_samples);
/*! <<<```
* Processes the first `n_samples` of the `n_channels` input buffers `x` and
* fills the first `n_samples` of the `n_channels` output buffers `y`, while
* using and updating each of the `n_channels` `state`s.
+ *
+ * #### bw_osc_filt_state_is_valid()
+ * ```>>> */
+static inline char bw_osc_filt_state_is_valid(
+ const bw_osc_filt_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.
+ *
+ * `state` must at least point to a readable memory block of size greater
+ * than or equal to that of `bw_osc_filt_state`.
* }}} */
#ifdef __cplusplus
@@ -131,32 +182,110 @@ extern "C" {
#endif
struct bw_osc_filt_state {
- float x_z1;
- float y_z1;
+#ifdef BW_DEBUG_DEEP
+ uint32_t hash;
+#endif
+
+ // States
+ float z1;
};
-static inline void bw_osc_filt_reset_state(bw_osc_filt_state *BW_RESTRICT state) {
- state->x_z1 = 0.f;
- state->y_z1 = 0.f;
-}
+static inline float bw_osc_filt_reset_state(
+ bw_osc_filt_state * BW_RESTRICT state,
+ float x_0) {
+ BW_ASSERT(state != NULL);
+ BW_ASSERT(bw_is_finite(x_0));
+
+ state->z1 = 0.f;
+ const float y = x_0;
+
+#ifdef BW_DEBUG_DEEP
+ state->hash = bw_hash_sdbm("bw_osc_filt_state");
+#endif
+ BW_ASSERT_DEEP(bw_osc_filt_state_is_valid(state));
+ BW_ASSERT(bw_is_finite(y));
-static inline float bw_osc_filt_process1(bw_osc_filt_state *BW_RESTRICT state, float x) {
- const float y = 1.371308261611209f * x + 0.08785458027104826f * state->x_z1 - 4.591628418822578e-1f * state->y_z1;
- state->x_z1 = x;
- state->y_z1 = y;
return y;
}
-static inline void bw_osc_filt_process(bw_osc_filt_state *BW_RESTRICT state, const float *x, float *y, size_t n_samples) {
- for (size_t i = 0; i < n_samples; i++)
- y[i] = bw_osc_filt_process1(state, x[i]);
+static inline void bw_osc_filt_reset_state_multi(
+ bw_osc_filt_state * BW_RESTRICT const * BW_RESTRICT state,
+ const float * x_0,
+ float * y_0,
+ size_t n_channels) {
+ BW_ASSERT(state != NULL);
+ BW_ASSERT(x_0 != NULL);
+
+ if (y_0 != NULL)
+ for (size_t i = 0; i < n_channels; i++)
+ y_0[i] = bw_osc_filt_reset_state(state[i], x_0[i]);
+ else
+ for (size_t i = 0; i < n_channels; i++)
+ bw_osc_filt_reset_state(state[i], x_0[i]);
+
+ BW_ASSERT_DEEP(y_0 != NULL ? bw_has_only_finite(y_0, n_channels) : 1);
}
-static inline void bw_osc_filt_process_multi(bw_osc_filt_state *BW_RESTRICT const *BW_RESTRICT state, const float * const *x, float * const *y, size_t n_channels, size_t n_samples) {
+static inline float bw_osc_filt_process1(
+ bw_osc_filt_state * BW_RESTRICT state,
+ float x) {
+ BW_ASSERT(state != NULL);
+ BW_ASSERT_DEEP(bw_osc_filt_state_is_valid(state));
+ BW_ASSERT(bw_is_finite(x));
+
+ const float y = 1.371308261611209f * x + state->z1;
+ state->z1 = 0.08785458027104826f * x - 4.591628418822578e-1f * y;
+
+ BW_ASSERT_DEEP(bw_osc_filt_state_is_valid(state));
+ BW_ASSERT(bw_is_finite(y));
+
+ return y;
+}
+
+static inline void bw_osc_filt_process(
+ bw_osc_filt_state * BW_RESTRICT state,
+ const float * x,
+ float * y,
+ size_t n_samples) {
+ BW_ASSERT(state != NULL);
+ BW_ASSERT_DEEP(bw_osc_filt_state_is_valid(state));
+ BW_ASSERT(x != NULL);
+ BW_ASSERT_DEEP(bw_has_only_finite(x, n_samples));
+ BW_ASSERT(y != NULL);
+
+ for (size_t i = 0; i < n_samples; i++)
+ y[i] = bw_osc_filt_process1(state, x[i]);
+
+ BW_ASSERT_DEEP(bw_osc_filt_state_is_valid(state));
+ BW_ASSERT_DEEP(bw_has_only_finite(y, n_samples));
+}
+
+static inline void bw_osc_filt_process_multi(
+ bw_osc_filt_state * BW_RESTRICT const * BW_RESTRICT state,
+ const float * const * x,
+ float * const * y,
+ size_t n_channels,
+ size_t n_samples) {
+ BW_ASSERT(state != NULL);
+ BW_ASSERT(x != NULL);
+ BW_ASSERT(y != NULL);
+
for (size_t i = 0; i < n_channels; i++)
bw_osc_filt_process(state[i], x[i], y[i], n_samples);
}
+static inline char bw_osc_filt_state_is_valid(
+ const bw_osc_filt_state * BW_RESTRICT state) {
+ BW_ASSERT(state != NULL);
+
+#ifdef BW_DEBUG_DEEP
+ if (state->hash != bw_hash_sdbm("bw_osc_filt_state"))
+ return 0;
+#endif
+
+ return bw_is_finite(state->z1);
+}
+
#ifdef __cplusplus
}
@@ -174,15 +303,31 @@ class OscFilt {
public:
OscFilt();
- void reset();
+ void reset(
+ float x0 = 0.f,
+ float * BW_RESTRICT y0 = nullptr);
+
+ void reset(
+ float x0,
+ std::array * BW_RESTRICT y0);
+
+ void reset(
+ const float * x0,
+ float * y0 = nullptr);
+
+ void reset(
+ std::array x0,
+ std::array * BW_RESTRICT y0 = nullptr);
+
void process(
- const float * const *x,
- float * const *y,
- size_t nSamples);
+ const float * const * x,
+ float * const * y,
+ size_t nSamples);
+
void process(
std::array x,
- std::array y,
- size_t nSamples);
+ std::array y,
+ size_t nSamples);
/*! <<<...
* }
* ```
@@ -194,8 +339,8 @@ public:
* change at any time in future versions. Please, do not use it directly. */
private:
- bw_osc_filt_state states[N_CHANNELS];
- bw_osc_filt_state *BW_RESTRICT statesP[N_CHANNELS];
+ bw_osc_filt_state states[N_CHANNELS];
+ bw_osc_filt_state * BW_RESTRICT statesP[N_CHANNELS];
};
template
@@ -205,24 +350,51 @@ inline OscFilt::OscFilt() {
}
template
-inline void OscFilt::reset() {
- for (size_t i = 0; i < N_CHANNELS; i++)
- bw_osc_filt_reset_state(states + i);
+inline void OscFilt::reset(
+ float x0,
+ float * BW_RESTRICT y0) {
+ if (y0 != nullptr)
+ for (size_t i = 0; i < N_CHANNELS; i++)
+ y0[i] = bw_osc_filt_reset_state(states + i, x0);
+ else
+ for (size_t i = 0; i < N_CHANNELS; i++)
+ bw_osc_filt_reset_state(states + i, x0);
+}
+
+template
+inline void OscFilt::reset(
+ float x0,
+ std::array * BW_RESTRICT y0) {
+ reset(x0, y0 != nullptr ? y0->data() : nullptr);
+}
+
+template
+inline void OscFilt::reset(
+ const float * x0,
+ float * y0) {
+ bw_osc_filt_reset_state_multi(statesP, x0, y0, N_CHANNELS);
+}
+
+template
+inline void OscFilt::reset(
+ std::array x0,
+ std::array * BW_RESTRICT y0) {
+ reset(x0.data(), y0 != nullptr ? y0->data() : nullptr);
}
template
inline void OscFilt::process(
- const float * const *x,
- float * const *y,
- size_t nSamples) {
+ const float * const * x,
+ float * const * y,
+ size_t nSamples) {
bw_osc_filt_process_multi(statesP, x, y, N_CHANNELS, nSamples);
}
template
inline void OscFilt::process(
std::array x,
- std::array y,
- size_t nSamples) {
+ std::array y,
+ size_t nSamples) {
process(x.data(), y.data(), nSamples);
}