fix compilation with lv2 < 1.10 (lv2 extended)
[ardour.git] / libs / plugins / a-eq.lv2 / a-eq.c
1 /* a-eq
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 #ifndef _GNU_SOURCE
16 #define _GNU_SOURCE // needed for M_PI
17 #endif
18
19 #include <math.h>
20 #include <complex.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdbool.h>
24 #include <stdio.h>
25
26 #include "lv2/lv2plug.in/ns/lv2core/lv2.h"
27
28 #ifdef LV2_EXTENDED
29 #include <cairo/cairo.h>
30 #include "ardour/lv2_extensions.h"
31 #endif
32
33 #define AEQ_URI "urn:ardour:a-eq"
34 #define BANDS   6
35
36 #ifndef MIN
37 #define MIN(A,B) ((A) < (B)) ? (A) : (B)
38 #endif
39
40 typedef enum {
41         AEQ_SHELFTOGL = 0,
42         AEQ_FREQL,
43         AEQ_GAINL,
44         AEQ_FREQ1,
45         AEQ_GAIN1,
46         AEQ_BW1,
47         AEQ_FREQ2,
48         AEQ_GAIN2,
49         AEQ_BW2,
50         AEQ_FREQ3,
51         AEQ_GAIN3,
52         AEQ_BW3,
53         AEQ_FREQ4,
54         AEQ_GAIN4,
55         AEQ_BW4,
56         AEQ_SHELFTOGH,
57         AEQ_FREQH,
58         AEQ_GAINH,
59         AEQ_MASTER,
60         AEQ_FILTOGL,
61         AEQ_FILTOG1,
62         AEQ_FILTOG2,
63         AEQ_FILTOG3,
64         AEQ_FILTOG4,
65         AEQ_FILTOGH,
66         AEQ_INPUT,
67         AEQ_OUTPUT,
68 } PortIndex;
69
70 static inline float
71 to_dB(float g) {
72         return (20.f*log10(g));
73 }
74
75 static inline float
76 from_dB(float gdb) {
77         return (exp(gdb/20.f*log(10.f)));
78 }
79
80 struct linear_svf {
81         double g, k;
82         double a[3];
83         double m[3];
84         double s[2];
85 };
86
87 static void linear_svf_reset(struct linear_svf *self)
88 {
89         self->s[0] = self->s[1] = 0.0;
90 }
91
92 typedef struct {
93         float* shelftogl;
94         float* shelftogh;
95         float* f0[BANDS];
96         float* g[BANDS];
97         float* bw[BANDS];
98         float* filtog[BANDS];
99         float* master;
100
101         float srate;
102
103         float* input;
104         float* output;
105
106         struct linear_svf v_filter[BANDS];
107         float v_g[BANDS];
108         float v_bw[BANDS];
109         float v_f0[BANDS];
110         float v_filtog[BANDS];
111         float v_shelftogl;
112         float v_shelftogh;
113         float v_master;
114
115         bool need_expose;
116
117 #ifdef LV2_EXTENDED
118         LV2_Inline_Display_Image_Surface surf;
119         cairo_surface_t*                 display;
120         LV2_Inline_Display*              queue_draw;
121         uint32_t                         w, h;
122 #endif
123 } Aeq;
124
125 static LV2_Handle
126 instantiate(const LV2_Descriptor* descriptor,
127             double rate,
128             const char* bundle_path,
129             const LV2_Feature* const* features)
130 {
131         Aeq* aeq = (Aeq*)malloc(sizeof(Aeq));
132         aeq->srate = rate;
133         
134 #ifdef LV2_EXTENDED
135         for (int i=0; features[i]; ++i) {
136                 if (!strcmp(features[i]->URI, LV2_INLINEDISPLAY__queue_draw)) {
137                         aeq->queue_draw = (LV2_Inline_Display*) features[i]->data;
138                 }
139         }
140 #endif
141
142         for (int i = 0; i < BANDS; i++)
143                 linear_svf_reset(&aeq->v_filter[i]);
144
145         aeq->need_expose = true;
146 #ifdef LV2_EXTENDED
147         aeq->display = NULL;
148 #endif
149
150         return (LV2_Handle)aeq;
151 }
152
153 static void
154 connect_port(LV2_Handle instance,
155              uint32_t port,
156              void* data)
157 {
158         Aeq* aeq = (Aeq*)instance;
159
160         switch ((PortIndex)port) {
161         case AEQ_SHELFTOGL:
162                 aeq->shelftogl = (float*)data;
163                 break;
164         case AEQ_FREQL:
165                 aeq->f0[0] = (float*)data;
166                 break;
167         case AEQ_GAINL:
168                 aeq->g[0] = (float*)data;
169                 break;
170         case AEQ_FREQ1:
171                 aeq->f0[1] = (float*)data;
172                 break;
173         case AEQ_GAIN1:
174                 aeq->g[1] = (float*)data;
175                 break;
176         case AEQ_BW1:
177                 aeq->bw[1] = (float*)data;
178                 break;
179         case AEQ_FREQ2:
180                 aeq->f0[2] = (float*)data;
181                 break;
182         case AEQ_GAIN2:
183                 aeq->g[2] = (float*)data;
184                 break;
185         case AEQ_BW2:
186                 aeq->bw[2] = (float*)data;
187                 break;
188         case AEQ_FREQ3:
189                 aeq->f0[3] = (float*)data;
190                 break;
191         case AEQ_GAIN3:
192                 aeq->g[3] = (float*)data;
193                 break;
194         case AEQ_BW3:
195                 aeq->bw[3] = (float*)data;
196                 break;
197         case AEQ_FREQ4:
198                 aeq->f0[4] = (float*)data;
199                 break;
200         case AEQ_GAIN4:
201                 aeq->g[4] = (float*)data;
202                 break;
203         case AEQ_BW4:
204                 aeq->bw[4] = (float*)data;
205                 break;
206         case AEQ_SHELFTOGH:
207                 aeq->shelftogh = (float*)data;
208                 break;
209         case AEQ_FREQH:
210                 aeq->f0[5] = (float*)data;
211                 break;
212         case AEQ_GAINH:
213                 aeq->g[5] = (float*)data;
214                 break;
215         case AEQ_MASTER:
216                 aeq->master = (float*)data;
217                 break;
218         case AEQ_FILTOGL:
219                 aeq->filtog[0] = (float*)data;
220                 break;
221         case AEQ_FILTOG1:
222                 aeq->filtog[1] = (float*)data;
223                 break;
224         case AEQ_FILTOG2:
225                 aeq->filtog[2] = (float*)data;
226                 break;
227         case AEQ_FILTOG3:
228                 aeq->filtog[3] = (float*)data;
229                 break;
230         case AEQ_FILTOG4:
231                 aeq->filtog[4] = (float*)data;
232                 break;
233         case AEQ_FILTOGH:
234                 aeq->filtog[5] = (float*)data;
235                 break;
236         case AEQ_INPUT:
237                 aeq->input = (float*)data;
238                 break;
239         case AEQ_OUTPUT:
240                 aeq->output = (float*)data;
241                 break;
242         }
243 }
244
245 static void
246 activate(LV2_Handle instance)
247 {
248         int i;
249         Aeq* aeq = (Aeq*)instance;
250
251         for (i = 0; i < BANDS; i++)
252                 linear_svf_reset(&aeq->v_filter[i]);
253 }
254
255 // SVF filters
256 // http://www.cytomic.com/files/dsp/SvfLinearTrapOptimised2.pdf
257
258 static void linear_svf_set_hp(struct linear_svf *self, float sample_rate, float cutoff, float resonance)
259 {
260         double f0 = (double)cutoff;
261         double q = (double)resonance;
262         double sr = (double)sample_rate;
263
264         self->g = tan(M_PI * (f0 / sr));
265         self->k = 1.0 / q;
266
267         self->a[0] = 1.0 / (1.0 + self->g * (self->g + self->k));
268         self->a[1] = self->g * self->a[0];
269         self->a[2] = self->g * self->a[1];
270
271         self->m[0] = 1.0;
272         self->m[1] = -self->k;
273         self->m[2] = -1.0;
274 }
275
276 static void linear_svf_set_lp(struct linear_svf *self, float sample_rate, float cutoff, float resonance)
277 {
278         double f0 = (double)cutoff;
279         double q = (double)resonance;
280         double sr = (double)sample_rate;
281
282         self->g = tan(M_PI * (f0 / sr));
283         self->k = 1.0 / q;
284
285         self->a[0] = 1.0 / (1.0 + self->g * (self->g + self->k));
286         self->a[1] = self->g * self->a[0];
287         self->a[2] = self->g * self->a[1];
288
289         self->m[0] = 0.0;
290         self->m[1] = 0.0;
291         self->m[2] = 1.0;
292 }
293
294 static void linear_svf_set_peq(struct linear_svf *self, float gdb, float sample_rate, float cutoff, float bandwidth)
295 {
296         double f0 = (double)cutoff;
297         double q = (double)pow(2.0, 1.0 / bandwidth) / (pow(2.0, bandwidth) - 1.0);
298         double sr = (double)sample_rate;
299         double A = pow(10.0, gdb/40.0);
300
301         self->g = tan(M_PI * (f0 / sr));
302         self->k = 1.0 / (q * A);
303
304         self->a[0] = 1.0 / (1.0 + self->g * (self->g + self->k));
305         self->a[1] = self->g * self->a[0];
306         self->a[2] = self->g * self->a[1];
307
308         self->m[0] = 1.0;
309         self->m[1] = self->k * (A * A - 1.0);
310         self->m[2] = 0.0;
311 }
312
313 static void linear_svf_set_highshelf(struct linear_svf *self, float gdb, float sample_rate, float cutoff, float resonance)
314 {
315         double f0 = (double)cutoff;
316         double q = (double)resonance;
317         double sr = (double)sample_rate;
318         double A = pow(10.0, gdb/40.0);
319
320         self->g = tan(M_PI * (f0 / sr));
321         self->k = 1.0 / q;
322
323         self->a[0] = 1.0 / (1.0 + self->g * (self->g + self->k));
324         self->a[1] = self->g * self->a[0];
325         self->a[2] = self->g * self->a[1];
326
327         self->m[0] = A * A;
328         self->m[1] = self->k * (1.0 - A) * A;
329         self->m[2] = 1.0 - A * A;
330 }
331
332 static void linear_svf_set_lowshelf(struct linear_svf *self, float gdb, float sample_rate, float cutoff, float resonance)
333 {
334         double f0 = (double)cutoff;
335         double q = (double)resonance;
336         double sr = (double)sample_rate;
337         double A = pow(10.0, gdb/40.0);
338
339         self->g = tan(M_PI * (f0 / sr));
340         self->k = 1.0 / q;
341
342         self->a[0] = 1.0 / (1.0 + self->g * (self->g + self->k));
343         self->a[1] = self->g * self->a[0];
344         self->a[2] = self->g * self->a[1];
345
346         self->m[0] = 1.0;
347         self->m[1] = self->k * (A - 1.0);
348         self->m[2] = A * A - 1.0;
349 }
350
351 static float run_linear_svf(struct linear_svf *self, float in)
352 {
353         double v[3];
354         double din = (double)in;
355         double out;
356
357         v[2] = din - self->s[1];
358         v[0] = (self->a[0] * self->s[0]) + (self->a[1] * v[2]);
359         v[1] = self->s[1] + (self->a[1] * self->s[0]) + (self->a[2] * v[2]);
360
361         self->s[0] = (2.0 * v[0]) - self->s[0];
362         self->s[1] = (2.0 * v[1]) - self->s[1];
363
364         out = (self->m[0] * din)
365                 + (self->m[1] * v[0])
366                 + (self->m[2] * v[1]);
367
368         return (float)out;
369 }
370
371 static void
372 run(LV2_Handle instance, uint32_t n_samples)
373 {
374         Aeq* aeq = (Aeq*)instance;
375
376         const float* const input = aeq->input;
377         float* const output = aeq->output;
378
379         float srate = aeq->srate;
380         float in0, out;
381         uint32_t i, j;
382
383         if (*(aeq->shelftogl) > 0.5) {
384                 linear_svf_set_lowshelf(&aeq->v_filter[0], *(aeq->g[0]), srate, *(aeq->f0[0]), 0.7071068);
385         } else {
386                 linear_svf_set_hp(&aeq->v_filter[0], srate, *(aeq->f0[0]), 0.7071068);
387         }
388         linear_svf_set_peq(&aeq->v_filter[1], *(aeq->g[1]), srate, *(aeq->f0[1]), *(aeq->bw[1]));
389         linear_svf_set_peq(&aeq->v_filter[2], *(aeq->g[2]), srate, *(aeq->f0[2]), *(aeq->bw[2]));
390         linear_svf_set_peq(&aeq->v_filter[3], *(aeq->g[3]), srate, *(aeq->f0[3]), *(aeq->bw[3]));
391         linear_svf_set_peq(&aeq->v_filter[4], *(aeq->g[4]), srate, *(aeq->f0[4]), *(aeq->bw[4]));
392
393         if (*(aeq->shelftogh) > 0.5) {
394                 linear_svf_set_highshelf(&aeq->v_filter[5], *(aeq->g[5]), srate, *(aeq->f0[5]), 0.7071068);
395         } else {
396                 linear_svf_set_lp(&aeq->v_filter[5], srate, *(aeq->f0[5]), 0.7071068);
397         }
398
399         for (i = 0; i < n_samples; i++) {
400                 in0 = input[i];
401                 out = in0;
402                 for (j = 0; j < BANDS; j++) {
403                         if (*(aeq->filtog[j]) > 0.5)
404                                 out = run_linear_svf(&aeq->v_filter[j], out);
405                 }
406                 output[i] = out * from_dB(*(aeq->master));
407         }
408
409         for (i = 0; i < BANDS; i++) {
410                 if (aeq->v_f0[i] != *(aeq->f0[i])) {
411                         aeq->v_f0[i] = *(aeq->f0[i]);
412                         aeq->need_expose = true;
413                 }
414                 if (aeq->v_g[i] != *(aeq->g[i])) {
415                         aeq->v_g[i] = *(aeq->g[i]);
416                         aeq->need_expose = true;
417                 }
418                 if (i != 0 && i != 5 && aeq->v_bw[i] != *(aeq->bw[i])) {
419                         aeq->v_bw[i] = *(aeq->bw[i]);
420                         aeq->need_expose = true;
421                 }
422                 if (aeq->v_filtog[i] != *(aeq->filtog[i])) {
423                         aeq->v_filtog[i] = *(aeq->filtog[i]);
424                         aeq->need_expose = true;
425                 }
426                 if (aeq->v_shelftogl != *(aeq->shelftogl)) {
427                         aeq->v_shelftogl = *(aeq->shelftogl);
428                         aeq->need_expose = true;
429                 }
430                 if (aeq->v_shelftogh != *(aeq->shelftogh)) {
431                         aeq->v_shelftogh = *(aeq->shelftogh);
432                         aeq->need_expose = true;
433                 }
434                 if (aeq->v_master != *(aeq->master)) {
435                         aeq->v_master = *(aeq->master);
436                         aeq->need_expose = true;
437                 }
438         }
439
440 #ifdef LV2_EXTENDED
441         if (aeq->need_expose && aeq->queue_draw) {
442                 aeq->need_expose = false;
443                 aeq->queue_draw->queue_draw (aeq->queue_draw->handle);
444         }
445 #endif
446 }
447
448
449 #ifdef LV2_EXTENDED
450 static float
451 eq_curve (Aeq* self, float f) {
452         float SR = self->srate;
453         double complex H = 1.0;
454         double theta = f * 2. * M_PI / SR;
455         double complex z = cexp(I * theta);
456         double A;
457         double m0, m1, m2, g, k;
458         int j = 0;
459
460         // low
461         if (self->v_filtog[0]) {
462                 A = pow(10.0, self->v_g[0]/40.0);
463                 m0 = self->v_filter[0].m[0];
464                 m1 = self->v_filter[0].m[1];
465                 m2 = self->v_filter[0].m[2];
466                 g = self->v_filter[0].g;
467                 k = self->v_filter[0].k;
468                 if (self->v_shelftogl) {
469                         // lowshelf
470                         H *= (A*m0*(z-1.0)*(z-1.0) + g*g*(m0+m2)*(1.0+z)*(1.0+z) + sqrt(A)*g*(k*m0+m1) * (z*z-1.0)) / (A*(z-1.0)*(z-1.0) + g*g*(1.0+z)*(1.0+z) + sqrt(A)*g*k*(z*z-1.0));
471                 } else {
472                         // hp:
473                         H *= ((z-1.0)*(z-1.0)) / ((z-1.0)*(z-1.0) + g*g*(1.0+z)*(1.0+z) + g*k*(z*z-1.0));
474                 }
475                 j++;
476         }
477
478         // peq1:
479         if (self->v_filtog[1]) {
480                 A = pow(10.0, self->v_g[1]/40.0);
481                 m0 = self->v_filter[1].m[0];
482                 m1 = self->v_filter[1].m[1];
483                 m2 = self->v_filter[1].m[2];
484                 g = self->v_filter[1].g;
485                 k = self->v_filter[1].k;
486                 H *= (g*k*m0*(z*z-1.0) + A*(g*(1.0+z)*(m1*(z-1.0) + g*m2*(1.0+z)) + m0*((z-1.0)*(z-1.0) + g*g*(1.0+z)*(1.0+z)))) / (g*k*(z*z-1.0) + A*((z-1.0)*(z-1.0) + g*g*(1.0+z)*(1.0+z)));
487                 j++;
488         }
489
490         // peq2:
491         if (self->v_filtog[2]) {
492                 A = pow(10.0, self->v_g[2]/40.0);
493                 m0 = self->v_filter[2].m[0];
494                 m1 = self->v_filter[2].m[1];
495                 m2 = self->v_filter[2].m[2];
496                 g = self->v_filter[2].g;
497                 k = self->v_filter[2].k;
498                 H *= (g*k*m0*(z*z-1.0) + A*(g*(1.0+z)*(m1*(z-1.0) + g*m2*(1.0+z)) + m0*((z-1.0)*(z-1.0) + g*g*(1.0+z)*(1.0+z)))) / (g*k*(z*z-1.0) + A*((z-1.0)*(z-1.0) + g*g*(1.0+z)*(1.0+z)));
499                 j++;
500         }
501
502         // peq3:
503         if (self->v_filtog[3]) {
504                 A = pow(10.0, self->v_g[3]/40.0);
505                 m0 = self->v_filter[3].m[0];
506                 m1 = self->v_filter[3].m[1];
507                 m2 = self->v_filter[3].m[2];
508                 g = self->v_filter[3].g;
509                 k = self->v_filter[3].k;
510                 H *= (g*k*m0*(z*z-1.0) + A*(g*(1.0+z)*(m1*(z-1.0) + g*m2*(1.0+z)) + m0*((z-1.0)*(z-1.0) + g*g*(1.0+z)*(1.0+z)))) / (g*k*(z*z-1.0) + A*((z-1.0)*(z-1.0) + g*g*(1.0+z)*(1.0+z)));
511                 j++;
512         }
513
514         // peq4:
515         if (self->v_filtog[4]) {
516                 A = pow(10.0, self->v_g[4]/40.0);
517                 m0 = self->v_filter[4].m[0];
518                 m1 = self->v_filter[4].m[1];
519                 m2 = self->v_filter[4].m[2];
520                 g = self->v_filter[4].g;
521                 k = self->v_filter[4].k;
522                 H *= (g*k*m0*(z*z-1.0) + A*(g*(1.0+z)*(m1*(z-1.0) + g*m2*(1.0+z)) + m0*((z-1.0)*(z-1.0) + g*g*(1.0+z)*(1.0+z)))) / (g*k*(z*z-1.0) + A*((z-1.0)*(z-1.0) + g*g*(1.0+z)*(1.0+z)));
523                 j++;
524         }
525
526         // high
527         if (self->v_filtog[5]) {
528                 A = pow(10.0, self->v_g[5]/40.0);
529                 m0 = self->v_filter[5].m[0];
530                 m1 = self->v_filter[5].m[1];
531                 m2 = self->v_filter[5].m[2];
532                 g = self->v_filter[5].g;
533                 k = self->v_filter[5].k;
534                 if (self->v_shelftogh) {
535                         // highshelf:
536                         H *= ( sqrt(A) * g * (1.0 + z) * (m1 * (z - 1.0) + sqrt(A)*g*m2*(1.0+z)) + m0 * ( (z-1.0)*(z-1.0) + A*g*g*(1.0+z)*(1.0+z) + sqrt(A)*g*k*(z*z-1.0))) / ((z-1.0)*(z-1.0) + A*g*g*(1.0+z)*(1.0+z) + sqrt(A)*g*k*(z*z-1.0));
537                 } else {
538                         // lp:
539                         H *= (g*g*(1.0+z)*(1.0+z)) / ((z-1.0)*(z-1.0) + g*g*(1.0+z)*(1.0+z) + g*k*(z*z-1.0));
540                 }
541                 j++;
542         }
543
544         return cabs(H);
545 }
546
547 static LV2_Inline_Display_Image_Surface *
548 render_inline (LV2_Handle instance, uint32_t w, uint32_t max_h)
549 {
550         Aeq* self = (Aeq*)instance;
551         uint32_t h = MIN (w * 9 / 16, max_h);
552
553         if (!self->display || self->w != w || self->h != h) {
554                 if (self->display) cairo_surface_destroy(self->display);
555                 self->display = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
556                 self->w = w;
557                 self->h = h;
558         }
559
560         cairo_t* cr = cairo_create (self->display);
561
562         // clear background
563         cairo_rectangle (cr, 0, 0, w, h);
564         cairo_set_source_rgba (cr, .2, .2, .2, 1.0);
565         cairo_fill (cr);
566
567         cairo_set_line_width(cr, 1.0);
568
569         // draw grid 5dB steps
570         const double dash2[] = {1, 3};
571         cairo_save (cr);
572         cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
573         cairo_set_dash(cr, dash2, 2, 2);
574         cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.5);
575
576         for (uint32_t d = 1; d < 8; ++d) {
577                 const float y = -.5 + floorf (h * (d * 5.f / 40.f));
578                 cairo_move_to (cr, 0, y);
579                 cairo_line_to (cr, w, y);
580                 cairo_stroke (cr);
581         }
582         cairo_restore (cr);
583
584
585         // draw curve
586         cairo_set_source_rgba (cr, .8, .8, .8, 1.0);
587         cairo_move_to (cr, 0, h);
588
589         for (uint32_t x = 0; x < w; ++x) {
590                 // plot 20..20kHz +-20dB
591                 const float x_hz = 20.f * powf (1000.f, (float)x / (float)w);
592                 const float y_db = to_dB(eq_curve(self, x_hz)) + self->v_master;
593                 const float y = h * -y_db / 40.0 + h / 2;
594                 cairo_line_to (cr, x, y);
595                 //printf("(hz,H,db)=(%f, %f, %f)\n", x_hz, from_dB(y_db), y_db);
596         }
597         cairo_stroke_preserve (cr);
598
599         cairo_line_to (cr, w, h);
600         cairo_close_path (cr);
601         cairo_clip (cr);
602
603         // create RGBA surface
604         cairo_destroy (cr);
605         cairo_surface_flush (self->display);
606         self->surf.width = cairo_image_surface_get_width (self->display);
607         self->surf.height = cairo_image_surface_get_height (self->display);
608         self->surf.stride = cairo_image_surface_get_stride (self->display);
609         self->surf.data = cairo_image_surface_get_data  (self->display);
610
611         return &self->surf;
612 }
613 #endif
614
615 static const void*
616 extension_data(const char* uri)
617 {
618 #ifdef LV2_EXTENDED
619         static const LV2_Inline_Display_Interface display  = { render_inline };
620         if (!strcmp(uri, LV2_INLINEDISPLAY__interface)) {
621                 return &display;
622         }
623 #endif
624         return NULL;
625 }
626
627 static void
628 cleanup(LV2_Handle instance)
629 {
630 #ifdef LV2_EXTENDED
631         Aeq* aeq = (Aeq*)instance;
632         if (aeq->display) {
633                 cairo_surface_destroy (aeq->display);
634         }
635 #endif
636         free(instance);
637 }
638
639 static const LV2_Descriptor descriptor = {
640         AEQ_URI,
641         instantiate,
642         connect_port,
643         activate,
644         run,
645         NULL,
646         cleanup,
647         extension_data
648 };
649
650 LV2_SYMBOL_EXPORT
651 const LV2_Descriptor*
652 lv2_descriptor(uint32_t index)
653 {
654         switch (index) {
655         case 0:
656                 return &descriptor;
657         default:
658                 return NULL;
659         }
660 }