diff --git a/TODO b/TODO index eb537ec..cabde07 100644 --- a/TODO +++ b/TODO @@ -45,6 +45,7 @@ code: * bw_fuzz gain compensation? * remove union value = {.f = v};? (std c++ latest) * make gain of distortions homogeneous? +* max_delay -> set sample rate? see reverb build system: * make makefiles handle paths with spaces etc diff --git a/include/bw_reverb.h b/include/bw_reverb.h index 2b4349e..3ef9f6f 100644 --- a/include/bw_reverb.h +++ b/include/bw_reverb.h @@ -22,7 +22,8 @@ * module_type {{{ dsp }}} * version {{{ 0.5.0 }}} * requires {{{ - * bw_buf bw_common bw_config bw_delay bw_drywet bw_gain bw_math bw_one_pole + * bw_buf bw_common bw_config bw_delay bw_drywet bw_gain bw_lp1 bw_math + * bw_one_pole * }}} * description {{{ * Stereo reverb. @@ -180,61 +181,217 @@ static inline void bw_reverb_set_wet(bw_reverb_coeffs *BW_RESTRICT coeffs, float * change at any time in future versions. Please, do not use it directly. */ #include +#include #include +#include +#include struct _bw_reverb_coeffs { // Sub-components bw_delay_coeffs predelay_coeffs; + bw_lp1_coeffs bandwidth_coeffs; + bw_delay_coeffs delay_id1_coeffs; + bw_delay_coeffs delay_id2_coeffs; + bw_delay_coeffs delay_id3_coeffs; + bw_delay_coeffs delay_id4_coeffs; + bw_delay_coeffs delay_dd1_coeffs; + bw_delay_coeffs delay_dd2_coeffs; + bw_delay_coeffs delay_dd3_coeffs; + bw_delay_coeffs delay_dd4_coeffs; + bw_delay_coeffs delay_d1_coeffs; + bw_delay_coeffs delay_d2_coeffs; + bw_delay_coeffs delay_d3_coeffs; + bw_delay_coeffs delay_d4_coeffs; bw_drywet_coeffs drywet_coeffs; + bw_one_pole_coeffs smooth_coeffs; + bw_one_pole_state smooth_predelay_state; + + // Coefficients + float fs; + float T; + BW_SIZE_T id1; + BW_SIZE_T id2; + BW_SIZE_T id3; + BW_SIZE_T id4; + BW_SIZE_T dd2; + BW_SIZE_T dd4; + BW_SIZE_T d1; + BW_SIZE_T d2; + BW_SIZE_T d3; + BW_SIZE_T d4; + + // Parameters + float predelay; }; struct _bw_reverb_state { bw_delay_state predelay_state; + bw_lp1_state bandwidth_state; }; static inline void bw_reverb_init(bw_reverb_coeffs *BW_RESTRICT coeffs) { bw_delay_init(&coeffs->predelay_coeffs, 0.1f); + bw_lp1_init(&coeffs->bandwidth_coeffs); + bw_delay_init(&coeffs->delay_id1_coeffs, 142.f / 29761.f); + bw_delay_init(&coeffs->delay_id2_coeffs, 107.f / 29761.f); + bw_delay_init(&coeffs->delay_id3_coeffs, 379.f / 29761.f); + bw_delay_init(&coeffs->delay_id4_coeffs, 277.f / 29761.f); + bw_delay_init(&coeffs->delay_dd1_coeffs, (672.f + 16.f) / 29761.f); + bw_delay_init(&coeffs->delay_dd2_coeffs, 1800.f / 29761.f); + bw_delay_init(&coeffs->delay_dd3_coeffs, (908.f + 16.f) / 29761.f); + bw_delay_init(&coeffs->delay_dd4_coeffs, 2656.f / 29761.f); + bw_delay_init(&coeffs->delay_d1_coeffs, 4453.f / 29761.f); + bw_delay_init(&coeffs->delay_d2_coeffs, 3720.f / 29761.f); + bw_delay_init(&coeffs->delay_d3_coeffs, 4217.f / 29761.f); + bw_delay_init(&coeffs->delay_d4_coeffs, 3163.f / 29761.f); bw_drywet_init(&coeffs->drywet_coeffs); + bw_one_pole_init(&coeffs->smooth_coeffs); + + bw_lp1_set_cutoff(&coeffs->bandwidth_coeffs, 20e3f); bw_drywet_set_wet(&coeffs->drywet_coeffs, 0.5f); + bw_one_pole_set_tau(&coeffs->smooth_coeffs, 0.05f); + bw_one_pole_set_sticky_thresh(&coeffs->smooth_coeffs, 1e-6f); + + coeffs->predelay = 0.f; } static inline void bw_reverb_set_sample_rate(bw_reverb_coeffs *BW_RESTRICT coeffs, float sample_rate) { bw_delay_set_sample_rate(&coeffs->predelay_coeffs, sample_rate); + bw_lp1_set_sample_rate(&coeffs->bandwidth_coeffs, sample_rate); + bw_delay_set_sample_rate(&coeffs->delay_id1_coeffs, sample_rate); + bw_delay_set_sample_rate(&coeffs->delay_id2_coeffs, sample_rate); + bw_delay_set_sample_rate(&coeffs->delay_id3_coeffs, sample_rate); + bw_delay_set_sample_rate(&coeffs->delay_id4_coeffs, sample_rate); + bw_delay_set_sample_rate(&coeffs->delay_dd1_coeffs, sample_rate); + bw_delay_set_sample_rate(&coeffs->delay_dd2_coeffs, sample_rate); + bw_delay_set_sample_rate(&coeffs->delay_dd3_coeffs, sample_rate); + bw_delay_set_sample_rate(&coeffs->delay_dd4_coeffs, sample_rate); + bw_delay_set_sample_rate(&coeffs->delay_d1_coeffs, sample_rate); + bw_delay_set_sample_rate(&coeffs->delay_d2_coeffs, sample_rate); + bw_delay_set_sample_rate(&coeffs->delay_d3_coeffs, sample_rate); + bw_delay_set_sample_rate(&coeffs->delay_d4_coeffs, sample_rate); bw_drywet_set_sample_rate(&coeffs->drywet_coeffs, sample_rate); + bw_one_pole_set_sample_rate(&coeffs->smooth_coeffs, sample_rate); + bw_one_pole_reset_coeffs(&coeffs->smooth_coeffs); + coeffs->fs = sample_rate; + coeffs->T = 1.f / sample_rate; + coeffs->id1 = (BW_SIZE_T)bw_roundf(coeffs->fs * (142.f / 29761.f)); + coeffs->id2 = (BW_SIZE_T)bw_roundf(coeffs->fs * (107.f / 29761.f)); + coeffs->id3 = (BW_SIZE_T)bw_roundf(coeffs->fs * (379.f / 29761.f)); + coeffs->id4 = (BW_SIZE_T)bw_roundf(coeffs->fs * (277.f / 29761.f)); + coeffs->dd2 = (BW_SIZE_T)bw_roundf(coeffs->fs * (1800.f / 29761.f)); + coeffs->dd4 = (BW_SIZE_T)bw_roundf(coeffs->fs * (2656.f / 29761.f)); + coeffs->d1 = (BW_SIZE_T)bw_roundf(coeffs->fs * (4453.f / 29761.f)); + coeffs->d2 = (BW_SIZE_T)bw_roundf(coeffs->fs * (3720.f / 29761.f)); + coeffs->d3 = (BW_SIZE_T)bw_roundf(coeffs->fs * (4217.f / 29761.f)); + coeffs->d4 = (BW_SIZE_T)bw_roundf(coeffs->fs * (3163.f / 29761.f)); + //set delay... } static inline BW_SIZE_T bw_reverb_mem_req(bw_reverb_coeffs *BW_RESTRICT coeffs) { - return bw_delay_mem_req(&coeffs->predelay_coeffs); + return bw_delay_mem_req(&coeffs->predelay_coeffs) + + bw_delay_mem_req(&coeffs->delay_id1_coeffs) + + bw_delay_mem_req(&coeffs->delay_id2_coeffs) + + bw_delay_mem_req(&coeffs->delay_id3_coeffs) + + bw_delay_mem_req(&coeffs->delay_id4_coeffs) + + bw_delay_mem_req(&coeffs->delay_dd1_coeffs) + + bw_delay_mem_req(&coeffs->delay_dd2_coeffs) + + bw_delay_mem_req(&coeffs->delay_dd3_coeffs) + + bw_delay_mem_req(&coeffs->delay_dd4_coeffs) + + bw_delay_mem_req(&coeffs->delay_d1_coeffs) + + bw_delay_mem_req(&coeffs->delay_d2_coeffs) + + bw_delay_mem_req(&coeffs->delay_d3_coeffs) + + bw_delay_mem_req(&coeffs->delay_d4_coeffs); } static inline void bw_reverb_mem_set(bw_reverb_state *BW_RESTRICT state, void *mem) { bw_delay_mem_set(&state->predelay_state, mem); + mem += bw_delay_mem_req(&coeffs->predelay_coeffs) * sizeof(float); + bw_delay_mem_set(&state->delay_id1_state, mem); + mem += bw_delay_mem_req(&coeffs->delay_id1_coeffs) * sizeof(float); + bw_delay_mem_set(&state->delay_id2_state, mem); + mem += bw_delay_mem_req(&coeffs->delay_id2_coeffs) * sizeof(float); + bw_delay_mem_set(&state->delay_id3_state, mem); + mem += bw_delay_mem_req(&coeffs->delay_id3_coeffs) * sizeof(float); + bw_delay_mem_set(&state->delay_id4_state, mem); + mem += bw_delay_mem_req(&coeffs->delay_id4_coeffs) * sizeof(float); + bw_delay_mem_set(&state->delay_dd1_state, mem); + mem += bw_delay_mem_req(&coeffs->delay_dd1_coeffs) * sizeof(float); + bw_delay_mem_set(&state->delay_dd2_state, mem); + mem += bw_delay_mem_req(&coeffs->delay_dd2_coeffs) * sizeof(float); + bw_delay_mem_set(&state->delay_dd3_state, mem); + mem += bw_delay_mem_req(&coeffs->delay_dd3_coeffs) * sizeof(float); + bw_delay_mem_set(&state->delay_dd4_state, mem); + mem += bw_delay_mem_req(&coeffs->delay_dd4_coeffs) * sizeof(float); + bw_delay_mem_set(&state->delay_d1_state, mem); + mem += bw_delay_mem_req(&coeffs->delay_d1_coeffs) * sizeof(float); + bw_delay_mem_set(&state->delay_d2_state, mem); + mem += bw_delay_mem_req(&coeffs->delay_d2_coeffs) * sizeof(float); + bw_delay_mem_set(&state->delay_d3_state, mem); + mem += bw_delay_mem_req(&coeffs->delay_d3_coeffs) * sizeof(float); + bw_delay_mem_set(&state->delay_d4_state, mem); } static inline void bw_reverb_reset_coeffs(bw_reverb_coeffs *BW_RESTRICT coeffs) { bw_delay_reset_coeffs(&coeffs->predelay_coeffs); + bw_lp1_reset_coeffs(&coeffs->bandwidth_coeffs); + bw_delay_reset_coeffs(&coeffs->delay_id1_coeffs); + bw_delay_reset_coeffs(&coeffs->delay_id2_coeffs); + bw_delay_reset_coeffs(&coeffs->delay_id3_coeffs); + bw_delay_reset_coeffs(&coeffs->delay_id4_coeffs); + bw_delay_reset_coeffs(&coeffs->delay_dd1_coeffs); + bw_delay_reset_coeffs(&coeffs->delay_dd2_coeffs); + bw_delay_reset_coeffs(&coeffs->delay_dd3_coeffs); + bw_delay_reset_coeffs(&coeffs->delay_dd4_coeffs); + bw_delay_reset_coeffs(&coeffs->delay_d1_coeffs); + bw_delay_reset_coeffs(&coeffs->delay_d2_coeffs); + bw_delay_reset_coeffs(&coeffs->delay_d3_coeffs); + bw_delay_reset_coeffs(&coeffs->delay_d4_coeffs); bw_drywet_reset_coeffs(&coeffs->drywet_coeffs); + bw_reverb_set_predelay(coeffs, coeffs->predelay); // to get it rounded + bw_one_pole_reset_state(&coeffs->smooth_coeffs, &coeffs->smooth_predelay_state, coeffs->predelay); } static inline void bw_reverb_reset_state(const bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT state) { bw_delay_reset_state(&coeffs->predelay_coeffs, &state->predelay_state); + bw_lp1_reset_state(&coeffs->bandwidth_coeffs, &state->bandwidth_state, 0.f); + bw_delay_reset_state(&coeffs->delay_id1_coeffs, &state->predelay_state); + bw_delay_reset_state(&coeffs->delay_id2_coeffs, &state->predelay_state); + bw_delay_reset_state(&coeffs->delay_id3_coeffs, &state->predelay_state); + bw_delay_reset_state(&coeffs->delay_id4_coeffs, &state->predelay_state); + bw_delay_reset_state(&coeffs->delay_dd1_coeffs, &state->predelay_state); + bw_delay_reset_state(&coeffs->delay_dd2_coeffs, &state->predelay_state); + bw_delay_reset_state(&coeffs->delay_dd3_coeffs, &state->predelay_state); + bw_delay_reset_state(&coeffs->delay_dd4_coeffs, &state->predelay_state); + bw_delay_reset_state(&coeffs->delay_d1_coeffs, &state->predelay_state); + bw_delay_reset_state(&coeffs->delay_d2_coeffs, &state->predelay_state); + bw_delay_reset_state(&coeffs->delay_d3_coeffs, &state->predelay_state); + bw_delay_reset_state(&coeffs->delay_d4_coeffs, &state->predelay_state); } static inline void bw_reverb_update_coeffs_ctrl(bw_reverb_coeffs *BW_RESTRICT coeffs) { - bw_delay_update_coeffs_ctrl(&coeffs->predelay_coeffs); bw_drywet_update_coeffs_ctrl(&coeffs->drywet_coeffs); + bw_lp1_update_coeffs_ctrl(&coeffs->bandwidth_coeffs); } static inline void bw_reverb_update_coeffs_audio(bw_reverb_coeffs *BW_RESTRICT coeffs) { bw_delay_update_coeffs_audio(&coeffs->predelay_coeffs); + bw_lp1_update_coeffs_audio(&coeffs->bandwidth_coeffs); bw_drywet_update_coeffs_audio(&coeffs->drywet_coeffs); + const float pd = bw_one_pole_process1_sticky_abs(&coeffs->smooth_coeffs, &coeffs->smooth_predelay_state, coeffs->predelay); + bw_delay_set_delay(&coeffs->predelay_coeffs, pd); + bw_delay_update_coeffs_ctrl(&coeffs->predelay_coeffs); + bw_delay_update_coeffs_audio(&coeffs->predelay_coeffs); + //... } static inline void bw_reverb_process1(const bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT state, float xl, float xr, float *yl, float *yr) { const float i = 0.5f * (xl + xr); const float pd = bw_delay_process1(&coeffs->predelay_coeffs, &state->predelay_state, i); - *yl = bw_drywet_process1(&coeffs->drywet_coeffs, xl, pd); - *yr = bw_drywet_process1(&coeffs->drywet_coeffs, xr, pd); + const float bw = bw_lp1_process1(&coeffs->bandwidth_coeffs, &state->bandwidth_state, pd); + //... + *yl = bw_drywet_process1(&coeffs->drywet_coeffs, xl, bw); + *yr = bw_drywet_process1(&coeffs->drywet_coeffs, xr, bw); } static inline void bw_reverb_process(bw_reverb_coeffs *BW_RESTRICT coeffs, bw_reverb_state *BW_RESTRICT state, const float *xl, const float *xr, float *yl, float *yr, int n_samples) { @@ -246,10 +403,11 @@ static inline void bw_reverb_process(bw_reverb_coeffs *BW_RESTRICT coeffs, bw_re } static inline void bw_reverb_set_predelay(bw_reverb_coeffs *BW_RESTRICT coeffs, float value) { - bw_delay_set_delay(&coeffs->predelay_coeffs, value); + coeffs->predelay = coeffs->T * bw_roundf(coeffs->fs * value); } static inline void bw_reverb_set_bandwidth(bw_reverb_coeffs *BW_RESTRICT coeffs, float value) { + bw_lp1_set_cutoff(&coeffs->bandwidth_coeffs, value); } static inline void bw_reverb_set_damping(bw_reverb_coeffs *BW_RESTRICT coeffs, float value) {