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