add prewarp ctrl in bw_{lp1,mm1}, used in bw_{l,h}s1, fixed bw_{l,h}s2

This commit is contained in:
Stefano D'Angelo 2022-12-30 11:30:21 +01:00
parent fe21e533f9
commit 1eeba2252a
7 changed files with 163 additions and 37 deletions

2
TODO
View File

@ -19,7 +19,7 @@ code:
* common smoothing policy (as control rate as possible?) - smoothing control?
* avoid "force" in coeffs update by using inline functions?
* should rather use backward Euler in bw_onepole?
* Q to slope and viceversa functions in 2nd order shelf filters?
* Q to slope and viceversa functions in 2nd order shelf filters? keep updated values (seamless switch, syncrhonicity)?
build system:
* make makefiles handle paths with spaces etc

View File

@ -150,6 +150,11 @@ static inline void bw_hs1_set_high_gain_dB(bw_hs1_coeffs *BW_RESTRICT coeffs, fl
struct _bw_hs1_coeffs {
// Sub-components
bw_mm1_coeffs mm1_coeffs;
// Parameters
float cutoff;
float high_gain;
char update;
};
struct _bw_hs1_state {
@ -158,15 +163,30 @@ struct _bw_hs1_state {
static inline void bw_hs1_init(bw_hs1_coeffs *BW_RESTRICT coeffs) {
bw_mm1_init(&coeffs->mm1_coeffs);
bw_mm1_set_prewarp_at_cutoff(&coeffs->mm1_coeffs, 0);
bw_mm1_set_coeffs_x(&coeffs->mm1_coeffs, 0.f);
bw_mm1_set_coeffs_lp(&coeffs->mm1_coeffs, 1.f);
coeffs->cutoff = 1e3f;
coeffs->dc_gain = 1.f;
}
static inline void bw_hs1_set_sample_rate(bw_hs1_coeffs *BW_RESTRICT coeffs, float sample_rate) {
bw_mm1_set_sample_rate(&coeffs->mm1_coeffs, sample_rate);
}
static inline void _bw_hs1_update_mm1_params(bw_ls1_coeffs *BW_RESTRICT coeffs) {
if (coeffs->update) {
bw_mm1_set_cutoff(&coeffs->mm1_coeffs, coeffs->cutoff * bw_sqrtf_2(coeffs->dc_gain));
bw_mm1_set_coeff_x(&coeffs->mm1_coeffs, coeffs->dc_gain);
bw_mm1_set_coeff_lp(&coeffs->mm1_coeffs, 1.f - coeffs->dc_gain);
bw_mm1_set_prewarp_freq(&coeffs->mm1_coeffs, coeffs->cutoff);
coeffs->update = 0;
}
}
static inline void bw_hs1_reset_coeffs(bw_hs1_coeffs *BW_RESTRICT coeffs) {
coeffs->update = 1;
_bw_hs1_update_mm1_params(coeffs);
bw_mm1_reset_coeffs(&coeffs->mm1_coeffs);
}
@ -175,6 +195,7 @@ static inline void bw_hs1_reset_state(const bw_hs1_coeffs *BW_RESTRICT coeffs, b
}
static inline void bw_hs1_update_coeffs_ctrl(bw_hs1_coeffs *BW_RESTRICT coeffs) {
_bw_hs1_update_mm1_params(coeffs);
bw_mm1_update_coeffs_ctrl(&coeffs->mm1_coeffs);
}
@ -195,12 +216,17 @@ static inline void bw_hs1_process(bw_hs1_coeffs *BW_RESTRICT coeffs, bw_hs1_stat
}
static inline void bw_hs1_set_cutoff(bw_hs1_coeffs *BW_RESTRICT coeffs, float value) {
bw_mm1_set_cutoff(&coeffs->mm1_coeffs, value);
if (value != coeffs->cutoff) {
coeffs->cutoff = value;
coeffs->update = 1;
}
}
static inline void bw_hs1_set_high_gain_lin(bw_hs1_coeffs *BW_RESTRICT coeffs, float value) {
bw_mm1_set_coeff_x(&coeffs->mm1_coeffs, value);
bw_mm1_set_coeff_lp(&coeffs->mm1_coeffs, 1.f - value);
if (value != coeffs->high_gain) {
coeffs->high_gain = value;
coeffs->update = 1;
}
}
static inline void bw_hs1_set_high_gain_dB(bw_hs1_coeffs *BW_RESTRICT coeffs, float value) {

View File

@ -213,17 +213,7 @@ static inline void bw_hs2_set_sample_rate(bw_hs2_coeffs *BW_RESTRICT coeffs, flo
bw_mm2_set_sample_rate(&coeffs->mm2_coeffs, sample_rate);
}
static inline void bw_hs2_reset_coeffs(bw_hs2_coeffs *BW_RESTRICT coeffs) {
bw_mm2_reset_coeffs(&coeffs->mm2_coeffs);
coeffs->param_changed = ~0;
bw_hs2_update_coeffs_ctrl(coeffs);
}
static inline void bw_hs2_reset_state(const bw_hs2_coeffs *BW_RESTRICT coeffs, bw_hs2_state *BW_RESTRICT state) {
bw_mm2_reset_state(&coeffs->mm2_coeffs, &state->mm2_state);
}
static inline void bw_hs2_update_coeffs_ctrl(bw_hs2_coeffs *BW_RESTRICT coeffs) {
static inline void _bw_ls2_update_mm2_params(bw_ls1_coeffs *BW_RESTRICT coeffs) {
if (coeffs->param_changed) {
if (coeffs->param_changed & _BW_HS2_PARAM_GAIN) {
coeffs->sg = bw_math_sqrtf_2(coeffs->gain);
@ -244,6 +234,20 @@ static inline void bw_hs2_update_coeffs_ctrl(bw_hs2_coeffs *BW_RESTRICT coeffs)
}
coeffs->param_changed = 0;
}
}
static inline void bw_hs2_reset_coeffs(bw_hs2_coeffs *BW_RESTRICT coeffs) {
coeffs->param_changed = ~0;
_bw_hs2_update_mm2_params(coeffs);
bw_mm2_reset_coeffs(&coeffs->mm2_coeffs);
}
static inline void bw_hs2_reset_state(const bw_hs2_coeffs *BW_RESTRICT coeffs, bw_hs2_state *BW_RESTRICT state) {
bw_mm2_reset_state(&coeffs->mm2_coeffs, &state->mm2_state);
}
static inline void bw_hs2_update_coeffs_ctrl(bw_hs2_coeffs *BW_RESTRICT coeffs) {
_bw_hs2_update_mm2_params(coeffs);
bw_mm2_update_coeffs_ctrl(&coeffs->mm2_coeffs);
}

View File

@ -119,6 +119,25 @@ static inline void bw_lp1_set_cutoff(bw_lp1_coeffs *BW_RESTRICT coeffs, float va
* Sets the cutoff frequency `value` (Hz) in `coeffs`.
*
* Default value: `1e3f`.
*
* #### bw_lp1_set_prewarp_at_cutoff()
* ```>>> */
static inline void bw_lp1_set_prewarp_at_cutoff(bw_lp1_coeffs *BW_RESTRICT coeffs, char value);
/*! <<<```
* Sets whether bilinear transform prewarping frequency should match the
* cutoff frequency (non-`0`) or not (`0`).
*
* Default value: non-`0` (on).
*
* #### bw_lp1_set_prewarp_freq()
* ```>>> */
static inline void bw_lp1_set_prewarp_freq(bw_lp1_coeffs *BW_RESTRICT coeffs, float value);
/*! <<<```
* Sets the prewarping frequency `value` (Hz) in `coeffs`.
*
* Only used when the prewarp\_at\_cutoff parameter is off.
*
* Default value: `1e3f`.
* }}} */
/*** Implementation ***/
@ -132,11 +151,13 @@ static inline void bw_lp1_set_cutoff(bw_lp1_coeffs *BW_RESTRICT coeffs, float va
struct _bw_lp1_coeffs {
// Sub-components
bw_one_pole_coeffs smooth_coeffs;
bw_one_pole_state smooth_state;
bw_one_pole_state smooth_cutoff_state;
bw_one_pole_state smooth_prewarp_freq_state;
// Coefficients
float t_k;
float prewarp_k;
float t;
float X_x;
float X_X_z1;
@ -144,6 +165,7 @@ struct _bw_lp1_coeffs {
// Parameters
float cutoff;
float prewarp_freq;
};
struct _bw_lp1_state {
@ -156,6 +178,8 @@ static inline void bw_lp1_init(bw_lp1_coeffs *BW_RESTRICT coeffs) {
bw_one_pole_set_tau(&coeffs->smooth_coeffs, 0.005f);
bw_one_pole_set_sticky_thresh(&coeffs->smooth_coeffs, 1e-3f);
coeffs->cutoff = 1e3f;
coeffs->prewarp_freq = 1e3f;
coeffs->prewarp_k = 1.f;
}
static inline void bw_lp1_set_sample_rate(bw_lp1_coeffs *BW_RESTRICT coeffs, float sample_rate) {
@ -165,20 +189,29 @@ static inline void bw_lp1_set_sample_rate(bw_lp1_coeffs *BW_RESTRICT coeffs, flo
}
static inline void _bw_lp1_do_update_coeffs(bw_lp1_coeffs *BW_RESTRICT coeffs, char force) {
float cutoff_cur = bw_one_pole_get_y_z1(&coeffs->smooth_state);
const float prewarp_freq = coeffs->prewarp_freq + coeffs->prewarp_k * (coeffs->cutoff - coeffs->prewarp_freq);
float prewarp_freq_cur = bw_one_pole_get_y_z1(&coeffs->smooth_prewarp_freq_state);
float cutoff_cur = bw_one_pole_get_y_z1(&coeffs->smooth_cutoff_state);
const char prewarp_freq_changed = force || prewarp_freq != prewarp_freq_cur;
const char cutoff_changed = force || coeffs->cutoff != cutoff_cur;
if (prewarp_freq_changed || cutoff_changed) {
if (prewarp_freq_changed) {
prewarp_freq_cur = bw_one_pole_process1_sticky_rel(&coeffs->smooth_coeffs, &coeffs->smooth_prewarp_freq_state, prewarp_freq);
coeffs->t = bw_tanf_3(coeffs->t_k * prewarp_freq_cur);
}
if (cutoff_changed) {
cutoff_cur = bw_one_pole_process1_sticky_rel(&coeffs->smooth_coeffs, &coeffs->smooth_state, coeffs->cutoff);
coeffs->t = bw_tanf_3(coeffs->t_k * cutoff_cur);
const float k = bw_rcpf_2(1.f + coeffs->t);
coeffs->X_x = k * cutoff_cur;
coeffs->X_X_z1 = k * coeffs->t;
cutoff_cur = bw_one_pole_process1_sticky_rel(&coeffs->smooth_coeffs, &coeffs->smooth_cutoff_state, coeffs->cutoff);
coeffs->y_X = bw_rcpf_2(cutoff_cur);
}
const float k = cutoff_cur * bw_rcpf_2(cutoff_cur * coeffs->t + prewarp_freq_cur);
coeffs->X_x = k * prewarp_freq_cur;
coeffs->X_X_z1 = k * coeffs->t;
}
}
static inline void bw_lp1_reset_coeffs(bw_lp1_coeffs *BW_RESTRICT coeffs) {
bw_one_pole_reset_state(&coeffs->smooth_coeffs, &coeffs->smooth_state, coeffs->cutoff);
bw_one_pole_reset_state(&coeffs->smooth_coeffs, &coeffs->smooth_cutoff_state, coeffs->cutoff);
bw_one_pole_reset_state(&coeffs->smooth_coeffs, &coeffs->smooth_prewarp_freq_state, coeffs->prewarp_freq + coeffs->prewarp_k * (coeffs->cutoff - coeffs->prewarp_freq));
_bw_lp1_do_update_coeffs(coeffs, 1);
}
@ -213,6 +246,14 @@ static inline void bw_lp1_set_cutoff(bw_lp1_coeffs *BW_RESTRICT coeffs, float va
coeffs->cutoff = value;
}
static inline void bw_lp1_set_prewarp_at_cutoff(bw_lp1_coeffs *BW_RESTRICT coeffs, char value) {
coeffs->prewarp_k = value ? 1.f : 0.f;
}
static inline void bw_lp1_set_prewarp_freq(bw_lp1_coeffs *BW_RESTRICT coeffs, float value) {
coeffs->prewarp_freq = value;
}
#ifdef __cplusplus
}
#endif

View File

@ -148,6 +148,11 @@ static inline void bw_ls1_set_dc_gain_dB(bw_ls1_coeffs *BW_RESTRICT coeffs, floa
struct _bw_ls1_coeffs {
// Sub-components
bw_mm1_coeffs mm1_coeffs;
// Parameters
float cutoff;
float dc_gain;
char update;
};
struct _bw_ls1_state {
@ -156,15 +161,29 @@ struct _bw_ls1_state {
static inline void bw_ls1_init(bw_ls1_coeffs *BW_RESTRICT coeffs) {
bw_mm1_init(&coeffs->mm1_coeffs);
bw_mm1_set_prewarp_at_cutoff(&coeffs->mm1_coeffs, 0);
bw_mm1_set_coeffs_x(&coeffs->mm1_coeffs, 1.f);
bw_mm1_set_coeffs_lp(&coeffs->mm1_coeffs, 0.f);
coeffs->cutoff = 1e3f;
coeffs->dc_gain = 1.f;
}
static inline void bw_ls1_set_sample_rate(bw_ls1_coeffs *BW_RESTRICT coeffs, float sample_rate) {
bw_mm1_set_sample_rate(&coeffs->mm1_coeffs, sample_rate);
}
static inline void _bw_ls1_update_mm1_params(bw_ls1_coeffs *BW_RESTRICT coeffs) {
if (coeffs->update) {
bw_mm1_set_cutoff(&coeffs->mm1_coeffs, coeffs->cutoff * bw_rcpf_2(bw_sqrtf_2(coeffs->dc_gain)));
bw_mm1_set_coeff_lp(&coeffs->mm1_coeffs, coeffs->dc_gain - 1.f);
bw_mm1_set_prewarp_freq(&coeffs->mm1_coeffs, coeffs->cutoff);
coeffs->update = 0;
}
}
static inline void bw_ls1_reset_coeffs(bw_ls1_coeffs *BW_RESTRICT coeffs) {
coeffs->update = 1;
_bw_ls1_update_mm1_params(coeffs);
bw_mm1_reset_coeffs(&coeffs->mm1_coeffs);
}
@ -173,6 +192,7 @@ static inline void bw_ls1_reset_state(const bw_ls1_coeffs *BW_RESTRICT coeffs, b
}
static inline void bw_ls1_update_coeffs_ctrl(bw_ls1_coeffs *BW_RESTRICT coeffs) {
_bw_ls1_update_mm1_params(coeffs);
bw_mm1_update_coeffs_ctrl(&coeffs->mm1_coeffs);
}
@ -193,11 +213,17 @@ static inline void bw_ls1_process(bw_ls1_coeffs *BW_RESTRICT coeffs, bw_ls1_stat
}
static inline void bw_ls1_set_cutoff(bw_ls1_coeffs *BW_RESTRICT coeffs, float value) {
bw_mm1_set_cutoff(&coeffs->mm1_coeffs, value);
if (value != coeffs->cutoff) {
coeffs->cutoff = value;
coeffs->update = 1;
}
}
static inline void bw_ls1_set_dc_gain_lin(bw_ls1_coeffs *BW_RESTRICT coeffs, float value) {
bw_mm1_set_coeff_lp(&coeffs->mm1_coeffs, value - 1.f);
if (value != coeffs->dc_gain) {
coeffs->dc_gain = value;
coeffs->update = 1;
}
}
static inline void bw_ls1_set_dc_gain_dB(bw_ls1_coeffs *BW_RESTRICT coeffs, float value) {

View File

@ -214,17 +214,7 @@ static inline void bw_ls2_set_sample_rate(bw_ls2_coeffs *BW_RESTRICT coeffs, flo
bw_mm2_set_sample_rate(&coeffs->mm2_coeffs, sample_rate);
}
static inline void bw_ls2_reset_coeffs(bw_ls2_coeffs *BW_RESTRICT coeffs) {
bw_mm2_reset_coeffs(&coeffs->mm2_coeffs);
coeffs->param_changed = ~0;
bw_ls2_update_coeffs_ctrl(coeffs);
}
static inline void bw_ls2_reset_state(const bw_ls2_coeffs *BW_RESTRICT coeffs, bw_ls2_state *BW_RESTRICT state) {
bw_mm2_reset_state(&coeffs->mm2_coeffs, &state->mm2_state);
}
static inline void bw_ls2_update_coeffs_ctrl(bw_ls2_coeffs *BW_RESTRICT coeffs) {
static inline void _bw_ls2_update_mm2_params(bw_ls1_coeffs *BW_RESTRICT coeffs) {
if (coeffs->param_changed) {
if (coeffs->param_changed & _BW_LS2_PARAM_GAIN) {
coeffs->sg = bw_math_sqrtf_2(coeffs->gain);
@ -245,6 +235,20 @@ static inline void bw_ls2_update_coeffs_ctrl(bw_ls2_coeffs *BW_RESTRICT coeffs)
}
coeffs->param_changed = 0;
}
}
static inline void bw_ls2_reset_coeffs(bw_ls2_coeffs *BW_RESTRICT coeffs) {
coeffs->param_changed = ~0;
_bw_ls2_update_mm2_params(coeffs);
bw_mm2_reset_coeffs(&coeffs->mm2_coeffs);
}
static inline void bw_ls2_reset_state(const bw_ls2_coeffs *BW_RESTRICT coeffs, bw_ls2_state *BW_RESTRICT state) {
bw_mm2_reset_state(&coeffs->mm2_coeffs, &state->mm2_state);
}
static inline void bw_ls2_update_coeffs_ctrl(bw_ls2_coeffs *BW_RESTRICT coeffs) {
_bw_ls2_update_mm2_params(coeffs);
bw_mm2_update_coeffs_ctrl(&coeffs->mm2_coeffs);
}

View File

@ -117,6 +117,23 @@ static inline void bw_mm1_set_cutoff(bw_mm1_coeffs *BW_RESTRICT coeffs, float va
*
* Default value: `1e3f`.
*
* #### bw_mm1_set_prewarp_at_cutoff()
* ```>>> */
static inline void bw_mm1_set_prewarp_at_cutoff(bw_mm1_coeffs *BW_RESTRICT coeffs, char value);
/*! <<<```
* Sets whether bilinear transform prewarping frequency should match the
* cutoff frequency (non-`0`) or not (`0`).
*
* Default value: non-`0` (on).
*
* #### bw_mm1_set_prewarp_freq()
* ```>>> */
static inline void bw_mm1_set_prewarp_freq(bw_mm1_coeffs *BW_RESTRICT coeffs, float value);
/*! <<<```
* Sets the prewarping frequency `value` (Hz) in `coeffs`.
*
* Only used when the prewarp\_at\_cutoff parameter is off.
*
* #### bw_mm1_set_coeff_x()
* ```>>> */
static inline void bw_mm1_set_coeff_x(bw_mm1_coeffs *BW_RESTRICT coeffs, float value);
@ -210,6 +227,14 @@ static inline void bw_mm1_set_cutoff(bw_mm1_coeffs *BW_RESTRICT coeffs, float va
bw_lp1_set_cutoff(&coeffs->lp1_coeffs, value);
}
static inline void bw_mm1_set_prewarp_at_cutoff(bw_mm1_coeffs *BW_RESTRICT coeffs, char value) {
bw_lp1_set_prewarp_at_cutoff(&coeffs->lp1_coeffs, value);
}
static inline void bw_mm1_set_prewarp_freq(bw_mm1_coeffs *BW_RESTRICT coeffs, float value) {
bw_lp1_set_prewarp_freq(&coeffs->lp1_coeffs, value);
}
static inline void bw_mm1_set_coeff_x(bw_mm1_coeffs *BW_RESTRICT coeffs, float value) {
bw_gain_set_gain_lin(&coeffs->gain_x_coeffs, value);
}