diff --git a/examples/synth/src/bw_example_synth.c b/examples/synth/src/bw_example_synth.c index 2e93b9f..0e63860 100644 --- a/examples/synth/src/bw_example_synth.c +++ b/examples/synth/src/bw_example_synth.c @@ -33,14 +33,11 @@ #include #include #include +#include #include #include #include -/* -#include -*/ - enum { p_volume, p_master_tune, diff --git a/include/bw_env_gen.h b/include/bw_env_gen.h index 729c638..a3a96ea 100644 --- a/include/bw_env_gen.h +++ b/include/bw_env_gen.h @@ -41,7 +41,7 @@ *
    *
  • Version 0.2.0: *
      - *
    • Refactored API to avoid dynamic memory allocation.
    • + *
    • Refactored API.
    • *
    *
  • *
  • Version 0.1.0: @@ -60,42 +60,70 @@ extern "C" { #endif -/*! api {{{ - * #### bw_env_gen - * ```>>> */ -typedef struct _bw_env_gen bw_env_gen; -/*! <<<``` - * Instance object. - * >>> */ +#include -/*! ... +/*! api {{{ + * #### bw_env_gen_coeffs + * ```>>> */ +typedef struct _bw_env_gen_coeffs bw_env_gen_coeffs; +/*! <<<``` + * Coefficients. + * + * ### bw_env_gen_state + * ```>>> */ +typedef struct _bw_env_gen_state bw_env_gen_state; +/*! <<<``` + * State. + * + * #### bw_env_gen_phase + * ```>>> */ +typedef enum { + bw_env_gen_phase_off, + bw_env_gen_phase_attack, + bw_env_gen_phase_decay, + bw_env_gen_phase_sustain, + bw_env_gen_phase_release +} bw_env_gen_phase; +/*! <<<``` + * Phase: + * * `bw_env_gen_phase_off`: ... + * * `bw_env_gen_phase_attack`: ... + * * `bw_env_gen_phase_decay`: ... + * * `bw_env_gen_phase_sustain`: ... + * * `bw_env_gen_phase_release`: ... + * * #### bw_env_gen_init() * ```>>> */ -void bw_env_gen_init(bw_env_gen *instance); +static inline void bw_env_gen_init(bw_env_gen_coeffs *BW_RESTRICT coeffs); /*! <<<``` - * Initializes the `instance` object. - * >>> */ - -/*! ... + * Initializes `coeffs`. + * * #### bw_env_gen_set_sample_rate() * ```>>> */ -void bw_env_gen_set_sample_rate(bw_env_gen *instance, float sample_rate); +static inline void bw_env_gen_set_sample_rate(bw_env_gen_coeffs *BW_RESTRICT coeffs, float sample_rate); /*! <<<``` * Sets the `sample_rate` (Hz) value for the given `instance`. + * + * #### bw_env_gen_reset_state() + * ```>>> */ +static inline void bw_env_gen_reset_state(const bw_env_gen_coeffs *BW_RESTRICT coeffs, bw_env_gen_state *BW_RESTRICT state); +/*! <<<``` + * Resets the given `state` to the initial state using the given `coeffs`. * >>> */ -/*! ... - * #### bw_env_gen_reset() - * ```>>> */ -void bw_env_gen_reset(bw_env_gen *instance); -/*! <<<``` - * Resets the given `instance` to its initial state. - * >>> */ +static inline void bw_env_gen_reset_coeffs(bw_env_gen_coeffs *BW_RESTRICT coeffs); + +static inline void bw_env_gen_update_coeffs_ctrl(bw_env_gen_coeffs *BW_RESTRICT coeffs); +static inline void bw_env_gen_update_coeffs_audio(bw_env_gen_coeffs *BW_RESTRICT coeffs); + +static inline void bw_env_gen_update_state_ctrl(const bw_env_gen_coeffs *BW_RESTRICT coeffs, bw_env_gen_state *BW_RESTRICT state); + +static inline float bw_env_gen_process1(const bw_env_gen_coeffs *BW_RESTRICT coeffs, bw_env_gen_state *BW_RESTRICT state); /*! ... * #### bw_env_gen_process() * ```>>> */ -void bw_env_gen_process(bw_env_gen *instance, float* y, int n_samples); +static inline void bw_env_gen_process(bw_env_gen_coeffs *BW_RESTRICT coeffs, bw_env_gen_state *BW_RESTRICT state, float* y, int n_samples); /*! <<<``` * Lets the given `instance` generate `n_samples` samples and puts them in * the output buffer `y`. @@ -104,7 +132,7 @@ void bw_env_gen_process(bw_env_gen *instance, float* y, int n_samples); /*! ... * #### bw_env_gen_set_gate() * ```>>> */ -void bw_env_gen_set_gate(bw_env_gen *instance, char value); +static inline void bw_env_gen_set_gate(bw_env_gen_coeffs *BW_RESTRICT coeffs, char value); /*! <<<``` * Sets the input gate to be either off (`0`) or on (non-`0`) for the given * `instance`. @@ -115,7 +143,7 @@ void bw_env_gen_set_gate(bw_env_gen *instance, char value); /*! ... * #### bw_env_gen_set_attack() * ```>>> */ -void bw_env_gen_set_attack(bw_env_gen *instance, float value); +static inline void bw_env_gen_set_attack(bw_env_gen_coeffs *BW_RESTRICT coeffs, float value); /*! <<<``` * Sets the attack time to `value` (s) for the given `instance`. * @@ -127,7 +155,7 @@ void bw_env_gen_set_attack(bw_env_gen *instance, float value); /*! ... * #### bw_env_gen_set_decay() * ```>>> */ -void bw_env_gen_set_decay(bw_env_gen *instance, float value); +static inline void bw_env_gen_set_decay(bw_env_gen_coeffs *BW_RESTRICT coeffs, float value); /*! <<<``` * Sets the decay time to `value` (s) for the given `instance`. * @@ -139,7 +167,7 @@ void bw_env_gen_set_decay(bw_env_gen *instance, float value); /*! ... * #### bw_env_gen_set_sustain() * ```>>> */ -void bw_env_gen_set_sustain(bw_env_gen *instance, float value); +static inline void bw_env_gen_set_sustain(bw_env_gen_coeffs *BW_RESTRICT coeffs, float value); /*! <<<``` * Sets the sustain level to `value` for the given `instance`. * @@ -149,7 +177,7 @@ void bw_env_gen_set_sustain(bw_env_gen *instance, float value); /*! ... * #### bw_env_gen_set_release() * ```>>> */ -void bw_env_gen_set_release(bw_env_gen *instance, float value); +static inline void bw_env_gen_set_release(bw_env_gen_coeffs *BW_RESTRICT coeffs, float value); /*! <<<``` * Sets the release time to `value` (s) for the given `instance`. * @@ -159,29 +187,27 @@ void bw_env_gen_set_release(bw_env_gen *instance, float value); * >>> */ /*! ... - * #### bw_env_gen_get_is_off() + * #### bw_env_gen_get_phase() * ```>>> */ -char bw_env_gen_get_is_off(bw_env_gen *instance); +static inline bw_env_gen_phase bw_env_gen_get_phase(bw_env_gen_state *state); /*! <<<``` - * Returns `0` if the given `instance` is in the off state, non-`0` otherwise. + * ... * }}} */ -/* WARNING: this enum is not part of the public API. Please, do not use it. */ -typedef enum { - _bw_env_gen_state_off, - _bw_env_gen_state_attack, - _bw_env_gen_state_decay, - _bw_env_gen_state_sustain, - _bw_env_gen_state_release -} _bw_env_gen_state; +/*** Implementation ***/ -/* 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_env_gen { +/* 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 +#include + +struct _bw_env_gen_coeffs { + // Sub-components + bw_one_pole_coeffs smooth_coeffs; + // Coefficients float T; - float smooth_mA1; float attack_inc; float decay_inc; @@ -194,13 +220,152 @@ struct _bw_env_gen { float sustain; float release; int param_changed; - - // State - char first_run; - _bw_env_gen_state state; - float y_z1; }; +struct _bw_env_gen_state { + bw_env_gen_phase phase; + float y_z1; + bw_one_pole_state smooth_state; +}; + +#define _BW_ENV_GEN_PARAM_ATTACK 1 +#define _BW_ENV_GEN_PARAM_DECAY (1<<1) +#define _BW_ENV_GEN_PARAM_SUSTAIN (1<<2) +#define _BW_ENV_GEN_PARAM_RELEASE (1<<3) + +static inline void bw_env_gen_init(bw_env_gen_coeffs *BW_RESTRICT coeffs) { + bw_one_pole_init(&coeffs->smooth_coeffs); + bw_one_pole_set_tau(&coeffs->smooth_coeffs, 0.05f); + coeffs->gate = 0; + coeffs->attack = 0.f; + coeffs->decay = 0.f; + coeffs->sustain = 1.f; + coeffs->release = 0.f; +} + +static inline void bw_env_gen_set_sample_rate(bw_env_gen_coeffs *BW_RESTRICT coeffs, float sample_rate) { + coeffs->T = 1.f / sample_rate; +} + +static inline void bw_env_gen_reset_coeffs(bw_env_gen_coeffs *BW_RESTRICT coeffs) { + coeffs->param_changed = ~0; + bw_env_gen_update_coeffs_ctrl(coeffs); +} + +static inline void bw_env_gen_reset_state(const bw_env_gen_coeffs *BW_RESTRICT coeffs, bw_env_gen_state *BW_RESTRICT state) { + state->phase = bw_env_gen_phase_off; + state->y_z1 = 0.f; +} + +static inline void bw_env_gen_update_coeffs_ctrl(bw_env_gen_coeffs *BW_RESTRICT coeffs) { + if (coeffs->param_changed) { + // 1 ns considered instantaneous + if (coeffs->param_changed & _BW_ENV_GEN_PARAM_ATTACK) + coeffs->attack_inc = coeffs->attack > 1e-9f ? coeffs->T * bw_rcpf_2(coeffs->attack) : INFINITY; + if (coeffs->param_changed & (_BW_ENV_GEN_PARAM_DECAY | _BW_ENV_GEN_PARAM_SUSTAIN)) + coeffs->decay_inc = coeffs->decay > 1e-9f ? (coeffs->sustain - 1.f) * coeffs->T * bw_rcpf_2(coeffs->decay) : -INFINITY; + if (coeffs->param_changed & (_BW_ENV_GEN_PARAM_SUSTAIN | _BW_ENV_GEN_PARAM_RELEASE)) + coeffs->release_inc = coeffs->release > 1e-9f ? -coeffs->sustain * coeffs->T * bw_rcpf_2(coeffs->release) : -INFINITY; + } +} + +static inline void bw_env_gen_update_coeffs_audio(bw_env_gen_coeffs *BW_RESTRICT coeffs) { +} + +static inline void bw_env_gen_update_state_ctrl(const bw_env_gen_coeffs *BW_RESTRICT coeffs, bw_env_gen_state *BW_RESTRICT state) { + if (coeffs->gate) { + if (state->phase == bw_env_gen_phase_off || state->phase == bw_env_gen_phase_release) + state->phase = bw_env_gen_phase_attack; + } else { + if (state->phase != bw_env_gen_phase_off) + state->phase = bw_env_gen_phase_release; + } +} + +static inline float bw_env_gen_process1(const bw_env_gen_coeffs *BW_RESTRICT coeffs, bw_env_gen_state *BW_RESTRICT state) { + float v; + switch (state->phase) { + case bw_env_gen_phase_attack: + v = state->y_z1 + coeffs->attack_inc; + if (v >= 1.f) { + v = 1.f; + state->phase = bw_env_gen_phase_decay; + } + break; + case bw_env_gen_phase_decay: + v = state->y_z1 + coeffs->decay_inc; + if (v <= coeffs->sustain) { + v = coeffs->sustain; + state->phase = bw_env_gen_phase_sustain; + bw_one_pole_reset_state(&coeffs->smooth_coeffs, &state->smooth_state, coeffs->sustain); + } + break; + case bw_env_gen_phase_sustain: + v = bw_one_pole_process1(&coeffs->smooth_coeffs, &state->smooth_state, coeffs->sustain); + break; + case bw_env_gen_phase_release: + v = state->y_z1 + coeffs->release_inc; + if (v <= 0.f) { + v = 0.f; + state->phase = bw_env_gen_phase_off; + } + break; + case bw_env_gen_phase_off: + v = 0.f; + break; + } + state->y_z1 = v; + return v; +} + +static inline void bw_env_gen_process(bw_env_gen_coeffs *BW_RESTRICT coeffs, bw_env_gen_state *BW_RESTRICT state, float* y, int n_samples) { + bw_env_gen_update_coeffs_ctrl(coeffs); + bw_env_gen_update_state_ctrl(coeffs, state); + for (int i = 0; i < n_samples; i++) + y[i] = bw_env_gen_process1(coeffs, state); +} + +static inline void bw_env_gen_set_gate(bw_env_gen_coeffs *BW_RESTRICT coeffs, char value) { + coeffs->gate = value; +} + +static inline void bw_env_gen_set_attack(bw_env_gen_coeffs *BW_RESTRICT coeffs, float value) { + if (coeffs->attack != value) { + coeffs->attack = value; + coeffs->param_changed |= _BW_ENV_GEN_PARAM_ATTACK; + } +} + +static inline void bw_env_gen_set_decay(bw_env_gen_coeffs *BW_RESTRICT coeffs, float value) { + if (coeffs->decay != value) { + coeffs->decay = value; + coeffs->param_changed |= _BW_ENV_GEN_PARAM_DECAY; + } +} + +static inline void bw_env_gen_set_sustain(bw_env_gen_coeffs *BW_RESTRICT coeffs, float value) { + if (coeffs->sustain != value) { + coeffs->sustain = value; + coeffs->param_changed |= _BW_ENV_GEN_PARAM_SUSTAIN; + } +} + +static inline void bw_env_gen_set_release(bw_env_gen_coeffs *BW_RESTRICT coeffs, float value) { + if (coeffs->release != value) { + coeffs->release = value; + coeffs->param_changed |= _BW_ENV_GEN_PARAM_RELEASE; + } +} + +static inline bw_env_gen_phase bw_env_gen_get_phase(bw_env_gen_state *state) { + return state->phase; +} + +#undef _BW_ENV_GEN_PARAM_ATTACK +#undef _BW_ENV_GEN_PARAM_DECAY +#undef _BW_ENV_GEN_PARAM_SUSTAIN +#undef _BW_ENV_GEN_PARAM_RELEASE + #ifdef __cplusplus } #endif diff --git a/include/bw_one_pole.h b/include/bw_one_pole.h index 37e7c6e..95284e4 100644 --- a/include/bw_one_pole.h +++ b/include/bw_one_pole.h @@ -235,6 +235,8 @@ static inline float bw_one_pole_get_y_z1(const bw_one_pole_state *BW_RESTRICT st /* 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 + struct _bw_one_pole_coeffs { // Coefficients float Ttm2pi; @@ -259,8 +261,6 @@ struct _bw_one_pole_state { #define _BW_ONE_POLE_PARAM_CUTOFF_DOWN (1<<1) #define _BW_ONE_POLE_PARAM_STICKY_THRESH (1<<2) -#include - static inline void bw_one_pole_init(bw_one_pole_coeffs *BW_RESTRICT coeffs) { coeffs->cutoff_up = INFINITY; coeffs->cutoff_down = INFINITY; @@ -455,6 +455,10 @@ static inline float bw_one_pole_get_y_z1(const bw_one_pole_state *BW_RESTRICT st return state->y_z1; } +#undef _BW_ONE_POLE_PARAM_CUTOFF_UP +#undef _BW_ONE_POLE_PARAM_CUTOFF_DOWN +#undef _BW_ONE_POLE_PARAM_STICKY_THRESH + #ifdef __cplusplus } #endif diff --git a/src/bw_env_gen.c b/src/bw_env_gen.c deleted file mode 100644 index 9eef6d2..0000000 --- a/src/bw_env_gen.c +++ /dev/null @@ -1,144 +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 - -#include -#include - -void bw_env_gen_init(bw_env_gen *instance) { - instance->gate = 0; - instance->attack = 0.f; - instance->decay = 0.f; - instance->sustain = 1.f; - instance->release = 0.f; -} - -void bw_env_gen_set_sample_rate(bw_env_gen *instance, float sample_rate) { - instance->T = 1.f / sample_rate; - instance->smooth_mA1 = bw_inline_one_pole_get_mA1(sample_rate, 0.05f); -} - -void bw_env_gen_reset(bw_env_gen *instance) { - instance->first_run = 1; - instance->param_changed = ~0; -} - -#define PARAM_ATTACK 1 -#define PARAM_DECAY (1<<1) -#define PARAM_SUSTAIN (1<<2) -#define PARAM_RELEASE (1<<3) - -void bw_env_gen_process(bw_env_gen *instance, float* y, int n_samples) { - if (instance->param_changed) { - // 1 ns considered instantaneous - if (instance->param_changed & PARAM_ATTACK) - instance->attack_inc = instance->attack > 1e-9f ? instance->T * bw_rcpf_2(instance->attack) : INFINITY; - if (instance->param_changed & (PARAM_DECAY | PARAM_SUSTAIN)) - instance->decay_inc = instance->decay > 1e-9f ? (instance->sustain - 1.f) * instance->T * bw_rcpf_2(instance->decay) : -INFINITY; - if (instance->param_changed & (PARAM_SUSTAIN | PARAM_RELEASE)) - instance->release_inc = instance->release > 1e-9f ? -instance->sustain * instance->T * bw_rcpf_2(instance->release) : -INFINITY; - } - - if (instance->first_run) { - instance->state = _bw_env_gen_state_off; - instance->y_z1 = 0.f; - instance->first_run = 0; - } - - if (instance->gate) { - if (instance->state == _bw_env_gen_state_off || instance->state == _bw_env_gen_state_release) - instance->state = _bw_env_gen_state_attack; - } else { - if (instance->state != _bw_env_gen_state_off) - instance->state = _bw_env_gen_state_release; - } - - for (int i = 0; i < n_samples; i++) { - float v; - switch (instance->state) { - case _bw_env_gen_state_attack: - v = instance->y_z1 + instance->attack_inc; - if (v >= 1.f) { - v = 1.f; - instance->state = _bw_env_gen_state_decay; - } - break; - case _bw_env_gen_state_decay: - v = instance->y_z1 + instance->decay_inc; - if (v <= instance->sustain) { - v = instance->sustain; - instance->state = _bw_env_gen_state_sustain; - } - break; - case _bw_env_gen_state_sustain: - v = bw_inline_one_pole(instance->sustain, instance->y_z1, instance->smooth_mA1); - break; - case _bw_env_gen_state_release: - v = instance->y_z1 + instance->release_inc; - if (v <= 0.f) { - v = 0.f; - instance->state = _bw_env_gen_state_off; - } - break; - case _bw_env_gen_state_off: - v = 0.f; - break; - } - - y[i] = v; - instance->y_z1 = y[i]; - } -} - -void bw_env_gen_set_gate(bw_env_gen *instance, char value) { - instance->gate = value; -} - -void bw_env_gen_set_attack(bw_env_gen *instance, float value) { - if (instance->attack != value) { - instance->attack = value; - instance->param_changed |= PARAM_ATTACK; - } -} - -void bw_env_gen_set_decay(bw_env_gen *instance, float value) { - if (instance->decay != value) { - instance->decay = value; - instance->param_changed |= PARAM_DECAY; - } -} - -void bw_env_gen_set_sustain(bw_env_gen *instance, float value) { - if (instance->sustain != value) { - instance->sustain = value; - instance->param_changed |= PARAM_SUSTAIN; - } -} - -void bw_env_gen_set_release(bw_env_gen *instance, float value) { - if (instance->release != value) { - instance->release = value; - instance->param_changed |= PARAM_RELEASE; - } -} - -char bw_env_gen_get_is_off(bw_env_gen *instance) { - return instance->state == _bw_env_gen_state_off; -}