diff --git a/examples/synth/src/bw_example_synth.c b/examples/synth/src/bw_example_synth.c index 8effc7a..3d0b99d 100644 --- a/examples/synth/src/bw_example_synth.c +++ b/examples/synth/src/bw_example_synth.c @@ -27,6 +27,7 @@ #include <bw_math.h> #include <bw_phase_gen.h> +#include <bw_osc_saw.h> #include <bw_osc_pulse.h> #include <bw_osc_filt.h> #include <bw_noise_gen.h> @@ -61,7 +62,6 @@ struct _bw_example_synth { bw_phase_gen_coeffs phase_gen_coeffs; bw_phase_gen_state phase_gen_state; bw_osc_pulse_coeffs osc_pulse_coeffs; - bw_osc_pulse_state osc_pulse_state; bw_osc_filt_state osc_filt_state; bw_noise_gen_coeffs noise_gen_coeffs; bw_svf_coeffs svf_coeffs; @@ -170,10 +170,8 @@ void bw_example_synth_process(bw_example_synth instance, const float** x, float* //bw_env_gen_set_gate(&instance->env_gen, 0); if (instance->note != -1) { - if (instance->note_prev < 0) { - bw_osc_pulse_reset_state(&instance->osc_pulse_coeffs, &instance->osc_pulse_state); + if (instance->note_prev < 0) bw_osc_filt_reset_state(&instance->osc_filt_state); - } bw_phase_gen_update_coeffs_ctrl(&instance->phase_gen_coeffs); bw_osc_pulse_update_coeffs_ctrl(&instance->osc_pulse_coeffs); for (int i = 0; i < n_samples; i++) { @@ -181,7 +179,7 @@ void bw_example_synth_process(bw_example_synth instance, const float** x, float* bw_phase_gen_update_coeffs_audio(&instance->phase_gen_coeffs); bw_phase_gen_process1(&instance->phase_gen_coeffs, &instance->phase_gen_state, &phase, &phase_inc); bw_osc_pulse_update_coeffs_audio(&instance->osc_pulse_coeffs); - y[0][i] = bw_osc_pulse_process1_antialias(&instance->osc_pulse_coeffs, &instance->osc_pulse_state, phase, phase_inc); + y[0][i] = bw_osc_pulse_process1_antialias(&instance->osc_pulse_coeffs, phase, phase_inc); } bw_osc_filt_process(&instance->osc_filt_state, y[0], y[0], n_samples); } else { diff --git a/include/bw_osc_pulse.h b/include/bw_osc_pulse.h index ef43b7a..68567e7 100644 --- a/include/bw_osc_pulse.h +++ b/include/bw_osc_pulse.h @@ -48,6 +48,8 @@ extern "C" { #endif +#include <bw_common.h> + /*! api {{{ * #### bw_osc_pulse_coeffs * ```>>> */ @@ -55,12 +57,6 @@ typedef struct _bw_osc_pulse_coeffs bw_osc_pulse_coeffs; /*! <<<``` * Coefficients. * - * ### bw_one_pole_state - * >>> */ -typedef struct _bw_osc_pulse_state bw_osc_pulse_state; -/*! <<<``` - * State. - * * #### bw_osc_pulse_init() * ```>>> */ static inline void bw_osc_pulse_init(bw_osc_pulse_coeffs *BW_RESTRICT coeffs); @@ -72,12 +68,6 @@ static inline void bw_osc_pulse_init(bw_osc_pulse_coeffs *BW_RESTRICT coeffs); static inline void bw_osc_pulse_set_sample_rate(bw_osc_pulse_coeffs *BW_RESTRICT coeffs, float sample_rate); /*! <<<``` * Sets the `sample_rate` (Hz) value for the given `coeffs`. - * - * #### bw_osc_pulse_reset_state() - * ```>>> */ -static inline void bw_osc_pulse_reset_state(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state); -/*! <<<``` - * Resets the given `instance` to its initial state. * >>> */ static inline void bw_osc_pulse_reset_coeffs(bw_osc_pulse_coeffs *BW_RESTRICT coeffs); @@ -85,13 +75,13 @@ static inline void bw_osc_pulse_reset_coeffs(bw_osc_pulse_coeffs *BW_RESTRICT co static inline void bw_osc_pulse_update_coeffs_ctrl(bw_osc_pulse_coeffs *BW_RESTRICT coeffs); static inline void bw_osc_pulse_update_coeffs_audio(bw_osc_pulse_coeffs *BW_RESTRICT coeffs); -static inline float bw_osc_pulse_process1(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state, float x); -static inline float bw_osc_pulse_process1_antialias(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state, float x, float x_phase_inc); +static inline float bw_osc_pulse_process1(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, float x); +static inline float bw_osc_pulse_process1_antialias(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, float x, float x_phase_inc); /*! ... * #### bw_osc_pulse_process() * ```>>> */ -static inline void bw_osc_pulse_process(bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state, const float *x, const float *x_phase_inc, float *y, int n_samples); +static inline void bw_osc_pulse_process(bw_osc_pulse_coeffs *BW_RESTRICT coeffs, const float *x, const float *x_phase_inc, float *y, int n_samples); /*! <<<``` * Lets the given `instance` process `n_samples` samples from the input * buffer `x` containing the normalized phase signal and fills the @@ -141,11 +131,6 @@ struct _bw_osc_pulse_coeffs { float pulse_width; }; -struct _bw_osc_pulse_state { - // empty, but we keep for potential forward API compatibility - char unused; -}; - static inline void bw_osc_pulse_init(bw_osc_pulse_coeffs *BW_RESTRICT coeffs) { bw_one_pole_init(&coeffs->smooth_coeffs); bw_one_pole_set_tau(&coeffs->smooth_coeffs, 0.005f); @@ -162,9 +147,6 @@ static inline void bw_osc_pulse_reset_coeffs(bw_osc_pulse_coeffs *BW_RESTRICT co bw_one_pole_reset_state(&coeffs->smooth_coeffs, &coeffs->smooth_state, coeffs->pulse_width); } -static inline void bw_osc_pulse_reset_state(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state) { -} - static inline void bw_osc_pulse_update_coeffs_ctrl(bw_osc_pulse_coeffs *BW_RESTRICT coeffs) { } @@ -172,7 +154,7 @@ static inline void bw_osc_pulse_update_coeffs_audio(bw_osc_pulse_coeffs *BW_REST bw_one_pole_process1(&coeffs->smooth_coeffs, &coeffs->smooth_state, coeffs->pulse_width); } -static inline float bw_osc_pulse_process1(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state, float x) { +static inline float bw_osc_pulse_process1(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, float x) { const float pw = bw_one_pole_get_y_z1(&coeffs->smooth_state); return bw_signf(pw - x); } @@ -184,7 +166,7 @@ static inline float _bw_osc_pulse_blep_diff(float x) { : x * (x * ((0.6666666666666666f - 0.08333333333333333f * x) * x - 2.f) + 2.666666666666667f) - 1.333333333333333f; } -static inline float bw_osc_pulse_process1_antialias(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state, float x, float x_phase_inc) { +static inline float bw_osc_pulse_process1_antialias(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, float x, float x_phase_inc) { const float pw = bw_one_pole_get_y_z1(&coeffs->smooth_state); const float pw_m_phase = pw - x; float v = bw_copysignf(1.f, pw_m_phase); // pw = phase case should be properly compensated by the AA @@ -206,16 +188,16 @@ static inline float bw_osc_pulse_process1_antialias(const bw_osc_pulse_coeffs *B return v; } -static inline void bw_osc_pulse_process(bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state, const float *x, const float *x_phase_inc, float *y, int n_samples) { +static inline void bw_osc_pulse_process(bw_osc_pulse_coeffs *BW_RESTRICT coeffs, const float *x, const float *x_phase_inc, float *y, int n_samples) { if (coeffs->antialiasing) for (int i = 0; i < n_samples; i++) { bw_osc_pulse_update_coeffs_audio(coeffs); - y[i] = bw_osc_pulse_process1_antialias(coeffs, state, x[i], x_phase_inc[i]); + y[i] = bw_osc_pulse_process1_antialias(coeffs, x[i], x_phase_inc[i]); } else for (int i = 0; i < n_samples; i++) { bw_osc_pulse_update_coeffs_audio(coeffs); - y[i] = bw_osc_pulse_process1(coeffs, state, x[i]); + y[i] = bw_osc_pulse_process1(coeffs, x[i]); } } diff --git a/include/bw_osc_saw.h b/include/bw_osc_saw.h index ebf6996..cb8c65c 100644 --- a/include/bw_osc_saw.h +++ b/include/bw_osc_saw.h @@ -28,7 +28,7 @@ * <ul> * <li>Version <strong>0.2.0</strong>: * <ul> - * <li>Refactored API to avoid dynamic memory allocation.</li> + * <li>Refactored API.</li> * </ul> * </li> * <li>Version <strong>0.1.0</strong>: @@ -47,32 +47,29 @@ extern "C" { #endif -/*! api {{{ - * #### bw_osc_saw - * ```>>> */ -typedef struct _bw_osc_saw bw_osc_saw; -/*! <<<``` - * Instance object. - * >>> */ +#include <bw_common.h> -/*! ... +/*! api {{{ + * #### bw_osc_saw_coeffs + * ```>>> */ +typedef struct _bw_osc_saw_coeffs bw_osc_saw_coeffs; +/*! <<<``` + * Coefficients. + * * #### bw_osc_saw_init() * ```>>> */ -void bw_osc_saw_init(bw_osc_saw *instance); +static inline void bw_osc_saw_init(bw_osc_saw_coeffs *BW_RESTRICT coeffs); /*! <<<``` - * Initializes the `instance` object. + * Initializes the `coeffs`. * >>> */ -/*! ... - * #### bw_osc_saw_set_sample_rate() and bw_osc_saw_reset() - * - * These do not exist (not needed). - * >>> */ +static inline float bw_osc_saw_process1(const bw_osc_saw_coeffs *BW_RESTRICT coeffs, float x); +static inline float bw_osc_saw_process1_antialias(const bw_osc_saw_coeffs *BW_RESTRICT coeffs, float x, float x_phase_inc); /*! ... * #### bw_osc_saw_process() * ```>>> */ -void bw_osc_saw_process(bw_osc_saw *instance, const float *x, const float *x_phase_inc, float* y, int n_samples); +static inline void bw_osc_saw_process(bw_osc_saw_coeffs *BW_RESTRICT coeffs, const float *x, const float *x_phase_inc, float *y, int n_samples); /*! <<<``` * Lets the given `instance` process `n_samples` samples from the input * buffer `x` containing the normalized phase signal and fills the @@ -82,7 +79,7 @@ void bw_osc_saw_process(bw_osc_saw *instance, const float *x, const float *x_pha /*! ... * #### bw_osc_saw_set_antialiasing() * ```>>> */ -void bw_osc_saw_set_antialiasing(bw_osc_saw *instance, char value); +static inline void bw_osc_saw_set_antialiasing(bw_osc_saw_coeffs *BW_RESTRICT coeffs, char value); /*! <<<``` * Sets whether the antialiasing is on (`value` non-`0`) or off (`0`) for the * given `instance`. @@ -90,14 +87,60 @@ void bw_osc_saw_set_antialiasing(bw_osc_saw *instance, char value); * Default value: `0`. * }}} */ -/* WARNING: the internal definition of this struct is not part of the public - * API. Its content may change at any time in future versions. Please, do not - * access its members directly. */ -struct _bw_osc_saw { +/*** Implementation ***/ + +/* WARNING: This part of the file is not part of the public API. Its content may + * change at any time in future versions. Please, do not use it directly. */ + +#include <bw_math.h> + +struct _bw_osc_saw_coeffs { // Parameters char antialiasing; }; +static inline void bw_osc_saw_init(bw_osc_saw_coeffs *BW_RESTRICT coeffs) { + coeffs->antialiasing = 0; +} + +static inline float bw_osc_saw_process1(const bw_osc_saw_coeffs *BW_RESTRICT coeffs, float x) { + return x + x - 1.f; +} + +// PolyBLEP residual based on Parzen window (4th-order B-spline), one-sided (x in [0, 2]) +static inline float _bw_osc_saw_blep_diff(float x) { + return x < 1.f + ? x * ((0.25f * x - 0.6666666666666666f) * x * x + 1.333333333333333f) - 1.f + : x * (x * ((0.6666666666666666f - 0.08333333333333333f * x) * x - 2.f) + 2.666666666666667f) - 1.333333333333333f; +} + +static inline float bw_osc_saw_process1_antialias(const bw_osc_saw_coeffs *BW_RESTRICT coeffs, float x, float x_phase_inc) { + const float s_1_m_phase = 1.f - x; + float v = x - s_1_m_phase; + if (x_phase_inc != 0.f) { + const float phase_inc_2 = x_phase_inc + x_phase_inc; + const float phase_inc_rcp = bw_rcpf_2(x_phase_inc); + if (s_1_m_phase < phase_inc_2) + v += _bw_osc_saw_blep_diff(s_1_m_phase * phase_inc_rcp); + if (x < phase_inc_2) + v -= _bw_osc_saw_blep_diff(x * phase_inc_rcp); + } + return v; +} + +static inline void bw_osc_saw_process(bw_osc_saw_coeffs *BW_RESTRICT coeffs, const float *x, const float *x_phase_inc, float *y, int n_samples) { + if (coeffs->antialiasing) + for (int i = 0; i < n_samples; i++) + y[i] = bw_osc_saw_process1_antialias(coeffs, x[i], x_phase_inc[i]); + else + for (int i = 0; i < n_samples; i++) + y[i] = bw_osc_saw_process1(coeffs, x[i]); +} + +static inline void bw_osc_saw_set_antialiasing(bw_osc_saw_coeffs *BW_RESTRICT coeffs, char value) { + coeffs->antialiasing = value; +} + #ifdef __cplusplus } #endif diff --git a/src/bw_osc_saw.c b/src/bw_osc_saw.c deleted file mode 100644 index 282a71d..0000000 --- a/src/bw_osc_saw.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Brickworks - * - * Copyright (C) 2022 Orastron Srl unipersonale - * - * Brickworks is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License. - * - * Brickworks is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * - * File author: Stefano D'Angelo - */ - -#include <bw_osc_saw.h> - -#include <bw_math.h> - -void bw_osc_saw_init(bw_osc_saw *instance) { - instance->antialiasing = 0; -} - -// PolyBLEP residual based on Parzen window (4th-order B-spline), one-sided (x in [0, 2]) -static inline float blep_diff(float x) { - return x < 1.f - ? x * ((0.25f * x - 0.6666666666666666f) * x * x + 1.333333333333333f) - 1.f - : x * (x * ((0.6666666666666666f - 0.08333333333333333f * x) * x - 2.0f) + 2.666666666666667f) - 1.333333333333333f; -} - -void bw_osc_saw_process(bw_osc_saw *instance, const float *x, const float *x_phase_inc, float* y, int n_samples) { - if (instance->antialiasing) { - for (int i = 0; i < n_samples; i++) { - const float s_1_m_phase = 1.f - x[i]; - float v = x[i] - s_1_m_phase; - - if (x_phase_inc[i] != 0.f) { - const float phase_inc_2 = x_phase_inc[i] + x_phase_inc[i]; - const float phase_inc_rcp = bw_rcpf_2(x_phase_inc[i]); - if (s_1_m_phase < phase_inc_2) - v += blep_diff(s_1_m_phase * phase_inc_rcp); - if (x[i] < phase_inc_2) - v -= blep_diff(x[i] * phase_inc_rcp); - } - - y[i] = v; - } - } else { - for (int i = 0; i < n_samples; i++) - y[i] = x[i] + x[i] - 1.f; - } -} - -void bw_osc_saw_set_antialiasing(bw_osc_saw *instance, char value) { - instance->antialiasing = value; -}