6870b20c4ef1a84c0871be9b7257a5e38838cd8b
[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 #include <string.h>
18 #include <stdbool.h>
19
20 #ifdef LV2_EXTENDED
21 #include <cairo/cairo.h>
22 #include "ardour/lv2_extensions.h"
23 #endif
24
25 #include "lv2/lv2plug.in/ns/lv2core/lv2.h"
26
27 #define ACOMP_URI "urn:ardour:a-comp"
28
29 typedef enum {
30         ACOMP_INPUT0 = 0,
31         ACOMP_INPUT1,
32         ACOMP_OUTPUT,
33
34         ACOMP_ATTACK,
35         ACOMP_RELEASE,
36         ACOMP_KNEE,
37         ACOMP_RATIO,
38         ACOMP_THRESHOLD,
39         ACOMP_MAKEUP,
40
41         ACOMP_GAINR,
42         ACOMP_OUTLEVEL,
43         ACOMP_SIDECHAIN,
44 } PortIndex;
45
46
47 typedef struct {
48         float* input0;
49         float* input1;
50         float* output;
51
52         float* attack;
53         float* release;
54         float* knee;
55         float* ratio;
56         float* thresdb;
57         float* makeup;
58
59         float* gainr;
60         float* outlevel;
61         float* sidechain;
62
63         float srate;
64         float old_yl;
65         float old_y1;
66         float old_yg;
67
68 #ifdef LV2_EXTENDED
69         bool                     need_expose;
70         LV2_Inline_Display_Image_Surface surf;
71         cairo_surface_t*         display;
72         LV2_Inline_Display*      queue_draw;
73         uint32_t                 w, h;
74
75         /* ports pointers are only valid during run so we'll
76          * have to cache them for the display, besides
77          * we do want to check for changes
78          */
79         float v_knee;
80         float v_ratio;
81         float v_thresdb;
82         float v_lvl;
83         float v_lvl_in;
84         float v_lvl_out;
85 #endif
86 } AComp;
87
88 static LV2_Handle
89 instantiate(const LV2_Descriptor* descriptor,
90             double rate,
91             const char* bundle_path,
92             const LV2_Feature* const* features)
93 {
94         AComp* acomp = (AComp*)calloc(1, sizeof(AComp));
95
96         for (int i=0; features[i]; ++i) {
97 #ifdef LV2_EXTENDED
98                 if (!strcmp(features[i]->URI, LV2_INLINEDISPLAY__queue_draw)) {
99                         acomp->queue_draw = (LV2_Inline_Display*) features[i]->data;
100                 }
101 #endif
102         }
103
104         acomp->srate = rate;
105         acomp->old_yl=acomp->old_y1=acomp->old_yg=0.f;
106         acomp->need_expose = true;
107
108         return (LV2_Handle)acomp;
109 }
110
111 static void
112 connect_port(LV2_Handle instance,
113              uint32_t port,
114              void* data)
115 {
116         AComp* acomp = (AComp*)instance;
117
118         switch ((PortIndex)port) {
119         case ACOMP_ATTACK:
120                 acomp->attack = (float*)data;
121                 break;
122         case ACOMP_RELEASE:
123                 acomp->release = (float*)data;
124                 break;
125         case ACOMP_KNEE:
126                 acomp->knee = (float*)data;
127                 break;
128         case ACOMP_RATIO:
129                 acomp->ratio = (float*)data;
130                 break;
131         case ACOMP_THRESHOLD:
132                 acomp->thresdb = (float*)data;
133                 break;
134         case ACOMP_MAKEUP:
135                 acomp->makeup = (float*)data;
136                 break;
137         case ACOMP_GAINR:
138                 acomp->gainr = (float*)data;
139                 break;
140         case ACOMP_OUTLEVEL:
141                 acomp->outlevel = (float*)data;
142                 break;
143         case ACOMP_SIDECHAIN:
144                 acomp->sidechain = (float*)data;
145                 break;
146         case ACOMP_INPUT0:
147                 acomp->input0 = (float*)data;
148                 break;
149         case ACOMP_INPUT1:
150                 acomp->input1 = (float*)data;
151                 break;
152         case ACOMP_OUTPUT:
153                 acomp->output = (float*)data;
154                 break;
155         }
156 }
157
158 // Force already-denormal float value to zero
159 static inline float
160 sanitize_denormal(float value) {
161         if (!isnormal(value)) {
162                 value = 0.f;
163         }
164         return value;
165 }
166
167 static inline float
168 from_dB(float gdb) {
169         return (exp(gdb/20.f*log(10.f)));
170 }
171
172 static inline float
173 to_dB(float g) {
174         return (20.f*log10(g));
175 }
176
177 static void
178 activate(LV2_Handle instance)
179 {
180         AComp* acomp = (AComp*)instance;
181
182         *(acomp->gainr) = 0.0f;
183         *(acomp->outlevel) = -45.0f;
184         acomp->old_yl=acomp->old_y1=acomp->old_yg=0.f;
185 }
186
187 static void
188 run(LV2_Handle instance, uint32_t n_samples)
189 {
190         AComp* acomp = (AComp*)instance;
191
192         const float* const input0 = acomp->input0;
193         const float* const input1 = acomp->input1;
194         float* const output = acomp->output;
195
196         float srate = acomp->srate;
197         float width = (6.f * *(acomp->knee)) + 0.01;
198         float cdb=0.f;
199         float attack_coeff = exp(-1000.f/(*(acomp->attack) * srate));
200         float release_coeff = exp(-1000.f/(*(acomp->release) * srate));
201
202         float max = 0.f;
203         float lgaininp = 0.f;
204         float Lgain = 1.f;
205         float Lxg, Lxl, Lyg, Lyl, Ly1;
206         int usesidechain = (*(acomp->sidechain) < 0.5) ? 0 : 1;
207         uint32_t i;
208         float ingain;
209         float in0;
210         float in1;
211         float ratio = *(acomp->ratio);
212         float thresdb = *(acomp->thresdb);
213
214 #ifdef LV2_EXTENDED
215         if (acomp->v_knee != *acomp->knee) {
216                 acomp->v_knee = *acomp->knee;
217                 acomp->need_expose = true;
218         }
219
220         if (acomp->v_ratio != *acomp->ratio) {
221                 acomp->v_ratio = *acomp->ratio;
222                 acomp->need_expose = true;
223         }
224
225         if (acomp->v_thresdb != *acomp->thresdb) {
226                 acomp->v_thresdb = *acomp->thresdb;
227                 acomp->need_expose = true;
228         }
229 #endif
230
231         float in_peak = 0;
232
233         for (i = 0; i < n_samples; i++) {
234                 in0 = input0[i];
235                 in1 = input1[i];
236                 ingain = usesidechain ? in1 : in0;
237                 Lyg = 0.f;
238                 Lxg = (ingain==0.f) ? -160.f : to_dB(fabs(ingain));
239                 Lxg = sanitize_denormal(Lxg);
240
241                 Lyg = Lxg + (1.f/ratio-1.f)*(Lxg-thresdb+width/2.f)*(Lxg-thresdb+width/2.f)/(2.f*width);
242
243                 if (2.f*(Lxg-thresdb) < -width) {
244                         Lyg = Lxg;
245                 } else {
246                         Lyg = thresdb + (Lxg-thresdb)/ratio;
247                         Lyg = sanitize_denormal(Lyg);
248                 }
249
250                 Lxl = Lxg - Lyg;
251
252                 acomp->old_y1 = sanitize_denormal(acomp->old_y1);
253                 acomp->old_yl = sanitize_denormal(acomp->old_yl);
254                 Ly1 = fmaxf(Lxl, release_coeff * acomp->old_y1+(1.f-release_coeff)*Lxl);
255                 Lyl = attack_coeff * acomp->old_yl+(1.f-attack_coeff)*Ly1;
256                 Ly1 = sanitize_denormal(Ly1);
257                 Lyl = sanitize_denormal(Lyl);
258
259                 cdb = -Lyl;
260                 Lgain = from_dB(cdb);
261
262                 *(acomp->gainr) = Lyl;
263
264                 if (in0 > in_peak) {
265                         in_peak = in0;
266                 }
267                 lgaininp = in0 * Lgain;
268                 output[i] = lgaininp * from_dB(*(acomp->makeup));
269
270                 max = (fabsf(output[i]) > max) ? fabsf(output[i]) : sanitize_denormal(max);
271
272                 // TODO re-use local variables on stack
273                 // store values back to acomp at the end of the inner-loop
274                 acomp->old_yl = Lyl;
275                 acomp->old_y1 = Ly1;
276                 acomp->old_yg = Lyg;
277         }
278
279         *(acomp->outlevel) = (max < 0.0056f) ? -45.f : to_dB(max);
280
281 #ifdef LV2_EXTENDED
282         acomp->v_lvl += .1 * (in_peak - acomp->v_lvl);  // crude LPF TODO use n_samples/rate TC
283         const float v_lvl_in = (acomp->v_lvl < 0.001f) ? -60.f : to_dB(acomp->v_lvl);
284         const float v_lvl_out = (max < 0.001f) ? -60.f : to_dB(max);
285         if (fabsf (acomp->v_lvl_out - v_lvl_out) >= 1 || fabsf (acomp->v_lvl_in - v_lvl_in) >= 1) {
286                 // >= 1dB difference
287                 acomp->need_expose = true;
288                 acomp->v_lvl_in = v_lvl_in;
289                 acomp->v_lvl_out = v_lvl_out - *acomp->makeup;
290         }
291         if (acomp->need_expose && acomp->queue_draw) {
292                 acomp->need_expose = false;
293                 acomp->queue_draw->queue_draw (acomp->queue_draw->handle);
294         }
295 #endif
296 }
297
298 static void
299 deactivate(LV2_Handle instance)
300 {
301         activate(instance);
302 }
303
304 static void
305 cleanup(LV2_Handle instance)
306 {
307 #ifdef LV2_EXTENDED
308         AComp* acomp = (AComp*)instance;
309         if (acomp->display) {
310                 cairo_surface_destroy (acomp->display);
311         }
312 #endif
313
314         free(instance);
315 }
316
317
318 #ifndef MIN
319 #define MIN(A,B) ((A) < (B)) ? (A) : (B)
320 #endif
321
322 #ifdef LV2_EXTENDED
323 static float
324 comp_curve (AComp* self, float xg) {
325         const float knee = self->v_knee;
326         const float ratio = self->v_ratio;
327         const float thresdb = self->v_thresdb;
328
329         const float width = 6.f * knee + 0.01f;
330         float yg = 0.f;
331
332         if (2.f * (xg - thresdb) < -width) {
333                 yg = xg;
334         } else if (2.f * fabs (xg - thresdb) <= width) {
335                 yg = xg + (1.f / ratio - 1.f ) * (xg - thresdb + width / 2.f) * (xg - thresdb + width / 2.f) / (2.f * width);
336         } else if (2.f * (xg - thresdb) > width) {
337                 yg = thresdb + (xg - thresdb) / ratio;
338         }
339         return yg;
340 }
341
342 static LV2_Inline_Display_Image_Surface *
343 render_inline (LV2_Handle instance, uint32_t w, uint32_t max_h)
344 {
345         AComp* self = (AComp*)instance;
346         uint32_t h = MIN (w, max_h);
347
348         if (!self->display || self->w != w || self->h != h) {
349                 if (self->display) cairo_surface_destroy(self->display);
350                 self->display = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
351                 self->w = w;
352                 self->h = h;
353         }
354
355         cairo_t* cr = cairo_create (self->display);
356
357         // clear background
358         cairo_rectangle (cr, 0, 0, w, h);
359         cairo_set_source_rgba (cr, .2, .2, .2, 1.0);
360         cairo_fill (cr);
361
362         cairo_set_line_width(cr, 1.0);
363
364         // draw grid 10dB steps
365         const double dash1[] = {1, 2};
366         const double dash2[] = {1, 3};
367         cairo_save (cr);
368         cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
369         cairo_set_dash(cr, dash2, 2, 2);
370         cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.5);
371
372         for (uint32_t d = 1; d < 6; ++d) {
373                 const float x = -.5 + floorf (w * (d * 10.f / 60.f));
374                 const float y = -.5 + floorf (h * (d * 10.f / 60.f));
375
376                 cairo_move_to (cr, x, 0);
377                 cairo_line_to (cr, x, h);
378                 cairo_stroke (cr);
379
380                 cairo_move_to (cr, 0, y);
381                 cairo_line_to (cr, w, y);
382                 cairo_stroke (cr);
383         }
384         if (self->v_thresdb < 0) {
385                 cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 1.0);
386                 const float y = -.5 + floorf (h * (self->v_thresdb / -60.f));
387                 cairo_set_dash(cr, dash1, 2, 2);
388                 cairo_move_to (cr, 0, y);
389                 cairo_line_to (cr, w, y);
390                 cairo_stroke (cr);
391                 cairo_move_to (cr, 0, h);
392                 cairo_line_to (cr, w, 0);
393                 cairo_stroke (cr);
394         }
395         cairo_restore (cr);
396
397
398         // draw curve
399         cairo_set_source_rgba (cr, .8, .8, .8, 1.0);
400         cairo_move_to (cr, 0, h);
401
402         for (uint32_t x = 0; x < w; ++x) {
403                 // plot -60..0  dB
404                 const float x_db = 60.f * (-1.f + x / (float)w);
405                 const float y_db = comp_curve (self, x_db);
406                 const float y = h * (y_db / -60.f);
407                 cairo_line_to (cr, x, y);
408         }
409         cairo_stroke_preserve (cr);
410
411         cairo_line_to (cr, w, h);
412         cairo_close_path (cr);
413         cairo_clip (cr);
414
415         // draw signal level & reduction/gradient
416         const float top = comp_curve (self, 0);
417         cairo_pattern_t* pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, h);
418         if (top > self->v_thresdb) {
419                 cairo_pattern_add_color_stop_rgba (pat, 0.0, 0.8, 0.1, 0.1, 0.5);
420                 cairo_pattern_add_color_stop_rgba (pat, top / -60.f, 0.8, 0.1, 0.1, 0.5);
421         }
422         if (self->v_knee > 0) {
423                 cairo_pattern_add_color_stop_rgba (pat, (self->v_thresdb / -60.f), 0.7, 0.7, 0.2, 0.5);
424                 cairo_pattern_add_color_stop_rgba (pat, ((self->v_thresdb - self->v_knee) / -60.f), 0.5, 0.5, 0.5, 0.5);
425         } else {
426                 cairo_pattern_add_color_stop_rgba (pat, (self->v_thresdb / -60.f), 0.7, 0.7, 0.2, 0.5);
427                 cairo_pattern_add_color_stop_rgba (pat, ((self->v_thresdb - .01) / -60.f), 0.5, 0.5, 0.5, 0.5);
428         }
429         cairo_pattern_add_color_stop_rgba (pat, 1.0, 0.5, 0.5, 0.5, 0.5);
430
431         // maybe cut off at x-position?
432         const float x = w * (self->v_lvl_in + 60) / 60.f;
433         const float y = h * (self->v_lvl_out + 60) / 60.f;
434         cairo_rectangle (cr, 0, h - y, x, y);
435         if (self->v_ratio > 1.0) {
436                 cairo_set_source (cr, pat);
437         } else {
438                 cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.5);
439         }
440         cairo_fill (cr);
441
442         cairo_pattern_destroy (pat); // TODO cache pattern
443
444
445         // create RGBA surface
446         cairo_destroy (cr);
447         cairo_surface_flush (self->display);
448         self->surf.width = cairo_image_surface_get_width (self->display);
449         self->surf.height = cairo_image_surface_get_height (self->display);
450         self->surf.stride = cairo_image_surface_get_stride (self->display);
451         self->surf.data = cairo_image_surface_get_data  (self->display);
452
453         return &self->surf;
454 }
455 #endif
456
457 static const void*
458 extension_data(const char* uri)
459 {
460         static const LV2_Inline_Display_Interface display  = { render_inline };
461         if (!strcmp(uri, LV2_INLINEDISPLAY__interface)) {
462                 return &display;
463         }
464         return NULL;
465 }
466
467 static const LV2_Descriptor descriptor = {
468         ACOMP_URI,
469         instantiate,
470         connect_port,
471         activate,
472         run,
473         deactivate,
474         cleanup,
475         extension_data
476 };
477
478 LV2_SYMBOL_EXPORT
479 const LV2_Descriptor*
480 lv2_descriptor(uint32_t index)
481 {
482         switch (index) {
483         case 0:
484                 return &descriptor;
485         default:
486                 return NULL;
487         }
488 }