summaryrefslogtreecommitdiff
path: root/mmband/adsr.c
blob: b2a22e656471ec664dae14bca46d1c60a9d9837b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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);
}