diff options
author | Samuel Johnson <[email protected]> | 2025-03-18 11:24:59 -0400 |
---|---|---|
committer | Samuel Johnson <[email protected]> | 2025-03-18 11:24:59 -0400 |
commit | a2f29c295d3ebbbe733b69de9f7e64a221d1ee25 (patch) | |
tree | c718389eb5e4f826f230ed47a536a9bdd66c4e0a | |
parent | 3fac4d38c1f8a7fa9b58f47cc9a5e0689c8bb29d (diff) |
Finish adsr implementation
-rw-r--r-- | mmband/adsr.c | 143 | ||||
-rw-r--r-- | mmband/api/adsr.h | 12 |
2 files changed, 151 insertions, 4 deletions
diff --git a/mmband/adsr.c b/mmband/adsr.c index c50a5c1..b2a22e6 100644 --- a/mmband/adsr.c +++ b/mmband/adsr.c @@ -1,3 +1,146 @@ +#include <math.h> +#include <stdbool.h> +#include <stdlib.h> + #include <mmband/api/adsr.h> +#define SLOPE_CURVE(rate,target_curve) exp(-log((1.0 + target_curve) / target_curve) / rate) + +enum adsr_state { + ADSR_SLEEP, + ADSR_ATTACK, + ADSR_DECAY, + ADSR_SUSTAIN, + ADSR_RELEASE +}; + +struct adsr { + float attack; + float attack_base; + float attack_curve; + + float decay; + float decay_base; + float decay_curve; + + float sustain; + + float release; + float release_base; + float release_curve; + + float asc_target; + float desc_target; + + float out; + + bool gate; + enum adsr_state state; +}; + +adsr_ref adsr_new () { + adsr_ref adsr = malloc(sizeof(struct adsr)); + + adsr_set_attack(adsr, 0.0); + adsr_set_decay(adsr, 0.0); + adsr_set_sustain(adsr, 1.0); + adsr_set_release(adsr, 0.0); + adsr_set_asc_target(adsr, 0.2); + adsr_set_desc_target(adsr, 0.0001); + + adsr->gate = false; + adsr->state = ADSR_SLEEP; + adsr->out = 0.0; + + return adsr; +} + +void adsr_free (adsr_ref adsr) { + free(adsr); +} + +void adsr_reset (adsr_ref adsr) { + adsr->gate = false; + adsr->state = ADSR_SLEEP; + adsr->out = 0.0; +} + +void adsr_gate (adsr_ref adsr) { + if (!adsr->gate) { + adsr->state = ADSR_ATTACK; + adsr->gate = true; + } else if (adsr->state != ADSR_SLEEP) { + adsr->state = ADSR_RELEASE; + adsr->gate = false; + } +} + +float adsr_tick (adsr_ref adsr) { + switch (adsr->state) { + case ADSR_ATTACK: + adsr->out = adsr->attack_base + adsr->out * adsr->attack_curve; + + if (adsr->out >= 1.0) { + adsr->out = 1.0; + adsr->state = ADSR_DECAY; + } + + break; + case ADSR_DECAY: + adsr->out = adsr->decay_base + adsr->out * adsr->decay_curve; + + if (adsr->out <= adsr->sustain) { + adsr->out = adsr->sustain; + adsr->state = ADSR_SUSTAIN; + } + + break; + case ADSR_RELEASE: + adsr->out = adsr->release_base + adsr->out * adsr->release_curve; + + if (adsr->out <= 0.0) { + adsr->out = 0.0; + adsr->state = ADSR_SLEEP; + } + + break; + default: + break; + } + + return adsr->out; +} + +void adsr_set_attack (adsr_ref adsr, float attack_rate) { + adsr->attack = attack_rate; + adsr->attack_curve = SLOPE_CURVE(attack_rate, adsr->asc_target); + adsr->attack_base = (1.0 + adsr->asc_target) * (1.0 - adsr->attack_curve); +} + +void adsr_set_decay (adsr_ref adsr, float decay_rate) { + adsr->decay = decay_rate; + adsr->decay_curve = SLOPE_CURVE(decay_rate, adsr->desc_target); + adsr->decay_base = (adsr->sustain - adsr->desc_target); +} + +void adsr_set_sustain (adsr_ref adsr, float sustain_level) { + adsr->sustain = sustain_level; + adsr->decay_base = (adsr->sustain - adsr->desc_target) * (1.0 - adsr->decay_curve); +} + +void adsr_set_release (adsr_ref adsr, float release_rate) { + adsr->release = release_rate; + adsr->release_curve = SLOPE_CURVE(release_rate, adsr->desc_target); + adsr->release_base = -adsr->desc_target * (1.0 - adsr->release_curve); +} + +void adsr_set_asc_target (adsr_ref adsr, float target) { + adsr->asc_target = target; + adsr->attack_base = (1.0 + adsr->asc_target) * (1.0 - adsr->attack_curve); +} +void adsr_set_desc_target (adsr_ref adsr, float target) { + adsr->desc_target = target; + adsr->decay_base = (adsr->sustain - adsr->desc_target) * (1.0 - adsr->decay_curve); + adsr->release_base = -adsr->desc_target * (1.0 - adsr->release_curve); +} diff --git a/mmband/api/adsr.h b/mmband/api/adsr.h index 899d1fe..d9a9e1b 100644 --- a/mmband/api/adsr.h +++ b/mmband/api/adsr.h @@ -3,15 +3,19 @@ typedef struct adsr *adsr_ref; -adsr_ref adsr_new (float sample_rate); -void adsr_free (adsr_ref adsr); +adsr_ref adsr_new (); +void adsr_free (adsr_ref adsr); + +void adsr_reset (adsr_ref adsr); +void adsr_gate (adsr_ref adsr); +float adsr_tick (adsr_ref adsr); void adsr_set_attack (adsr_ref adsr, float attack_rate); void adsr_set_decay (adsr_ref adsr, float decay_rate); void adsr_set_sustain (adsr_ref adsr, float sustain_level); void adsr_set_release (adsr_ref adsr, float release_rate); -void adsr_set_asc_ratio (adsr_ref adsr, float ratio); -void adsr_set_desc_ratio (adsr_ref adsr, float ratio); +void adsr_set_asc_target (adsr_ref adsr, float target); +void adsr_set_desc_target (adsr_ref adsr, float target); #endif |