a-comp: Tweak control bounds and clear state on deactivate
[ardour.git] / libs / plugins / a-comp.lv2 / a-comp.c
1 /* a-comp
2  * Copyright (C) 2016 Damien Zammit <damien@zamaudio.com>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #include <math.h>
16 #include <stdlib.h>
17
18 #include "lv2/lv2plug.in/ns/lv2core/lv2.h"
19
20 #define ACOMP_URI "urn:ardour:a-comp"
21
22 typedef enum {
23         ACOMP_INPUT0 = 0,
24         ACOMP_INPUT1,
25         ACOMP_OUTPUT,
26
27         ACOMP_ATTACK,
28         ACOMP_RELEASE,
29         ACOMP_KNEE,
30         ACOMP_RATIO,
31         ACOMP_THRESHOLD,
32         ACOMP_MAKEUP,
33
34         ACOMP_GAINR,
35         ACOMP_OUTLEVEL,
36         ACOMP_SIDECHAIN,
37 } PortIndex;
38
39
40 typedef struct {
41         float* input0;
42         float* input1;
43         float* output;
44
45         float* attack;
46         float* release;
47         float* knee;
48         float* ratio;
49         float* thresdb;
50         float* makeup;
51
52         float* gainr;
53         float* outlevel;
54         float* sidechain;
55
56         float srate;
57         float old_yl;
58         float old_y1;
59         float old_yg;
60 } AComp;
61
62 static LV2_Handle
63 instantiate(const LV2_Descriptor* descriptor,
64             double rate,
65             const char* bundle_path,
66             const LV2_Feature* const* features)
67 {
68         AComp* acomp = (AComp*)malloc(sizeof(AComp));
69         acomp->srate = rate;
70
71         acomp->old_yl=acomp->old_y1=acomp->old_yg=0.f;
72
73         return (LV2_Handle)acomp;
74 }
75
76 static void
77 connect_port(LV2_Handle instance,
78              uint32_t port,
79              void* data)
80 {
81         AComp* acomp = (AComp*)instance;
82
83         switch ((PortIndex)port) {
84         case ACOMP_ATTACK:
85                 acomp->attack = (float*)data;
86                 break;
87         case ACOMP_RELEASE:
88                 acomp->release = (float*)data;
89                 break;
90         case ACOMP_KNEE:
91                 acomp->knee = (float*)data;
92                 break;
93         case ACOMP_RATIO:
94                 acomp->ratio = (float*)data;
95                 break;
96         case ACOMP_THRESHOLD:
97                 acomp->thresdb = (float*)data;
98                 break;
99         case ACOMP_MAKEUP:
100                 acomp->makeup = (float*)data;
101                 break;
102         case ACOMP_GAINR:
103                 acomp->gainr = (float*)data;
104                 break;
105         case ACOMP_OUTLEVEL:
106                 acomp->outlevel = (float*)data;
107                 break;
108         case ACOMP_SIDECHAIN:
109                 acomp->sidechain = (float*)data;
110                 break;
111         case ACOMP_INPUT0:
112                 acomp->input0 = (float*)data;
113                 break;
114         case ACOMP_INPUT1:
115                 acomp->input1 = (float*)data;
116                 break;
117         case ACOMP_OUTPUT:
118                 acomp->output = (float*)data;
119                 break;
120         }
121 }
122
123 // Force already-denormal float value to zero
124 static inline float
125 sanitize_denormal(float value) {
126         if (!isnormal(value)) {
127                 value = 0.f;
128         }
129         return value;
130 }
131
132 static inline float
133 from_dB(float gdb) {
134         return (exp(gdb/20.f*log(10.f)));
135 }
136
137 static inline float
138 to_dB(float g) {
139         return (20.f*log10(g));
140 }
141
142 static void
143 activate(LV2_Handle instance)
144 {
145         AComp* acomp = (AComp*)instance;
146
147         *(acomp->gainr) = 0.0f;
148         *(acomp->outlevel) = -45.0f;
149         acomp->old_yl=acomp->old_y1=acomp->old_yg=0.f;
150 }
151
152 static void
153 run(LV2_Handle instance, uint32_t n_samples)
154 {
155         AComp* acomp = (AComp*)instance;
156
157         const float* const input0 = acomp->input0;
158         const float* const input1 = acomp->input1;
159         float* const output = acomp->output;
160
161         float srate = acomp->srate;
162         float width = (6.f * *(acomp->knee)) + 0.01;
163         float cdb=0.f;
164         float attack_coeff = exp(-1000.f/(*(acomp->attack) * srate));
165         float release_coeff = exp(-1000.f/(*(acomp->release) * srate));
166
167         float max = 0.f;
168         float lgaininp = 0.f;
169         float Lgain = 1.f;
170         float Lxg, Lxl, Lyg, Lyl, Ly1;
171         int usesidechain = (*(acomp->sidechain) < 0.5) ? 0 : 1;
172         uint32_t i;
173         float ingain;
174         float in0;
175         float in1;
176         float ratio = *(acomp->ratio);
177         float thresdb = *(acomp->thresdb);
178
179         for (i = 0; i < n_samples; i++) {
180                 in0 = input0[i];
181                 in1 = input1[i];
182                 ingain = usesidechain ? in1 : in0;
183                 Lyg = 0.f;
184                 Lxg = (ingain==0.f) ? -160.f : to_dB(fabs(ingain));
185                 Lxg = sanitize_denormal(Lxg);
186
187                 Lyg = Lxg + (1.f/ratio-1.f)*(Lxg-thresdb+width/2.f)*(Lxg-thresdb+width/2.f)/(2.f*width);
188
189                 if (2.f*(Lxg-thresdb) < -width) {
190                         Lyg = Lxg;
191                 } else {
192                         Lyg = thresdb + (Lxg-thresdb)/ratio;
193                         Lyg = sanitize_denormal(Lyg);
194                 }
195
196                 Lxl = Lxg - Lyg;
197
198                 acomp->old_y1 = sanitize_denormal(acomp->old_y1);
199                 acomp->old_yl = sanitize_denormal(acomp->old_yl);
200                 Ly1 = fmaxf(Lxl, release_coeff * acomp->old_y1+(1.f-release_coeff)*Lxl);
201                 Lyl = attack_coeff * acomp->old_yl+(1.f-attack_coeff)*Ly1;
202                 Ly1 = sanitize_denormal(Ly1);
203                 Lyl = sanitize_denormal(Lyl);
204
205                 cdb = -Lyl;
206                 Lgain = from_dB(cdb);
207
208                 *(acomp->gainr) = Lyl;
209
210                 lgaininp = in0 * Lgain;
211                 output[i] = lgaininp * from_dB(*(acomp->makeup));
212
213                 max = (fabsf(output[i]) > max) ? fabsf(output[i]) : sanitize_denormal(max);
214
215                 acomp->old_yl = Lyl;
216                 acomp->old_y1 = Ly1;
217                 acomp->old_yg = Lyg;
218         }
219         *(acomp->outlevel) = (max == 0.f) ? -45.f : to_dB(max);
220 }
221
222 static void
223 deactivate(LV2_Handle instance)
224 {
225         activate(instance);
226 }
227
228 static void
229 cleanup(LV2_Handle instance)
230 {
231         free(instance);
232 }
233
234 static const void*
235 extension_data(const char* uri)
236 {
237         return NULL;
238 }
239
240 static const LV2_Descriptor descriptor = {
241         ACOMP_URI,
242         instantiate,
243         connect_port,
244         activate,
245         run,
246         deactivate,
247         cleanup,
248         extension_data
249 };
250
251 LV2_SYMBOL_EXPORT
252 const LV2_Descriptor*
253 lv2_descriptor(uint32_t index)
254 {
255         switch (index) {
256         case 0:
257                 return &descriptor;
258         default:
259                 return NULL;
260         }
261 }