summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Johnson <[email protected]>2025-03-18 11:24:59 -0400
committerSamuel Johnson <[email protected]>2025-03-18 11:24:59 -0400
commita2f29c295d3ebbbe733b69de9f7e64a221d1ee25 (patch)
treec718389eb5e4f826f230ed47a536a9bdd66c4e0a
parent3fac4d38c1f8a7fa9b58f47cc9a5e0689c8bb29d (diff)
Finish adsr implementation
-rw-r--r--mmband/adsr.c143
-rw-r--r--mmband/api/adsr.h12
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