1 /* fil4.lv2
2 *
3 * Copyright (C) 2004-2009 Fons Adriaensen <fons@kokkinizita.net>
4 * Copyright (C) 2015,2016 Robin Gareus <robin@gareus.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include "filters.h"
24 #include "uris.h"
25 #include "iir.h"
26 #include "hip.h"
27 #include "lop.h"
28
29 #include "lv2/lv2plug.in/ns/lv2core/lv2.h"
30 #include "lv2/lv2plug.in/ns/ext/state/state.h"
31 #include "lv2/lv2plug.in/ns/ext/options/options.h"
32
33 #ifdef DISPLAY_INTERFACE
34 #include <cairo/cairo.h>
35 #include <pango/pangocairo.h>
36 #include "lv2_rgext.h"
37 #endif
38
39 static bool printed_capacity_warning = false;
40
41 typedef struct {
42 Fil4Paramsect _sect [NSECT];
43 HighPass hip;
44 LowPass lop;
45
46 IIRProc iir_lowshelf;
47 IIRProc iir_highshelf;
48
49 int _fade;
50 float _gain;
51 } FilterChannel;
52
53 typedef struct {
54 float *_port [FIL_LAST];
55 float rate;
56 float below_nyquist;
57
58 FilterChannel fc[2];
59 uint32_t n_channels;
60
61 /* atom-forge & fft related */
62 const LV2_Atom_Sequence *control;
63 LV2_Atom_Sequence *notify;
64 LV2_URID_Map *map;
65 Fil4LV2URIs uris;
66 LV2_Atom_Forge forge;
67 LV2_Atom_Forge_Frame frame;
68
69 /* peak hold */
70 int peak_reset;
71 float peak_signal;
72
73 /* GUI state */
74 bool ui_active;
75 bool send_state_to_ui;
76 uint32_t resend_peak;
77
78 int32_t fft_mode;
79 int32_t fft_chan;
80 float fft_gain;
81 float db_scale;
82 float ui_scale;
83 float kb_tuning;
84
85 bool need_expose;
86 bool enabled;
87 #ifdef DISPLAY_INTERFACE
88 LV2_Inline_Display_Image_Surface surf;
89 cairo_surface_t* display;
90 LV2_Inline_Display* queue_draw;
91 uint32_t w, h;
92 #endif
93 } Fil4;
94
init_filter_channel(FilterChannel * fc,double rate)95 static void init_filter_channel (FilterChannel *fc, double rate) {
96 fc->_fade = 0;
97 fc->_gain = 1.f;
98 for (int j = 0; j < NSECT; ++j) {
99 fc->_sect [j].init ();
100 }
101
102 iir_init (&fc->iir_lowshelf, rate);
103 iir_init (&fc->iir_highshelf, rate);
104
105 fc->iir_lowshelf.freq = 50;
106 fc->iir_highshelf.freq = 8000;
107
108 iir_calc_lowshelf (&fc->iir_lowshelf);
109 iir_calc_highshelf (&fc->iir_highshelf);
110
111 hip_setup (&fc->hip, rate, 20, .7);
112 lop_setup (&fc->lop, rate, 10000, .7);
113 }
114
115 static LV2_Handle
instantiate(const LV2_Descriptor * descriptor,double rate,const char * bundle_path,const LV2_Feature * const * features)116 instantiate(const LV2_Descriptor* descriptor,
117 double rate,
118 const char* bundle_path,
119 const LV2_Feature* const* features)
120 {
121 Fil4* self = (Fil4*)calloc(1, sizeof(Fil4));
122
123 if (!strcmp (descriptor->URI, FIL4_URI "mono")) {
124 self->n_channels = 1;
125 } else if (!strcmp (descriptor->URI, FIL4_URI "stereo")) {
126 self->n_channels = 2;
127 } else {
128 free (self);
129 return NULL;
130 }
131
132 const LV2_Options_Option* options = NULL;
133
134 for (int i=0; features[i]; ++i) {
135 if (!strcmp(features[i]->URI, LV2_URID__map)) {
136 self->map = (LV2_URID_Map*)features[i]->data;
137 } else if (!strcmp(features[i]->URI, LV2_OPTIONS__options)) {
138 options = (LV2_Options_Option*)features[i]->data;
139 }
140 #ifdef DISPLAY_INTERFACE
141 else if (!strcmp(features[i]->URI, LV2_INLINEDISPLAY__queue_draw)) {
142 self->queue_draw = (LV2_Inline_Display*) features[i]->data;
143 }
144 #endif
145 }
146
147 if (!self->map) {
148 fprintf (stderr, "fil4.lv2 error: Host does not support urid:map\n");
149 free (self);
150 return NULL;
151 }
152
153 self->rate = rate;
154 self->below_nyquist = rate * 0.4998;
155 lv2_atom_forge_init (&self->forge, self->map);
156 map_fil4_uris (self->map, &self->uris);
157
158 for (uint32_t c = 0; c < self->n_channels; ++c) {
159 init_filter_channel (&self->fc[c], rate);
160 }
161
162 self->ui_active = false;
163 self->fft_mode = 0x1201;
164 self->fft_gain = 0;
165 self->fft_chan = -1;
166 self->resend_peak = 0;
167 self->db_scale = DEFAULT_YZOOM;
168 self->ui_scale = 1.0;
169 self->kb_tuning = 440.0;
170
171 if (options) {
172 LV2_URID atom_Float = self->map->map (self->map->handle, LV2_ATOM__Float);
173 LV2_URID ui_scale = self->map->map (self->map->handle, "http://lv2plug.in/ns/extensions/ui#scaleFactor");
174 for (const LV2_Options_Option* o = options; o->key; ++o) {
175 if (o->context == LV2_OPTIONS_INSTANCE && o->key == ui_scale && o->type == atom_Float) {
176 float ui_scale = *(const float*)o->value;
177 if (ui_scale < 1.0) { ui_scale = 1.0; }
178 if (ui_scale > 2.0) { ui_scale = 2.0; }
179 self->ui_scale = ui_scale;
180 }
181 }
182 }
183
184 return (LV2_Handle)self;
185 }
186
187 static void
connect_port(LV2_Handle instance,uint32_t port,void * data)188 connect_port(LV2_Handle instance,
189 uint32_t port,
190 void* data)
191 {
192 Fil4* self = (Fil4*)instance;
193 if (port == FIL_ATOM_CONTROL) {
194 self->control = (const LV2_Atom_Sequence*) data;
195 } else if (port == FIL_ATOM_NOTIFY) {
196 self->notify = (LV2_Atom_Sequence*) data;
197 } else if (port <= FIL_OUTPUT1) {
198 self->_port[port] = (float*) data;
199 }
200 }
201
exp2ap(float x)202 static float exp2ap (float x) {
203 int i;
204
205 i = (int)(floorf (x));
206 x -= i;
207 return ldexpf (1 + x * (0.6930f + x * (0.2416f + x * (0.0517f + x * 0.0137f))), i);
208 }
209
210 /** forge atom-vector of raw data */
tx_rawaudio(LV2_Atom_Forge * forge,Fil4LV2URIs * uris,const float sr,const uint32_t chn,const size_t n_samples,void * data)211 static void tx_rawaudio (LV2_Atom_Forge *forge, Fil4LV2URIs *uris,
212 const float sr, const uint32_t chn,
213 const size_t n_samples, void *data)
214 {
215 LV2_Atom_Forge_Frame frame;
216 /* forge container object of type 'rawaudio' */
217 lv2_atom_forge_frame_time(forge, 0);
218 x_forge_object(forge, &frame, 1, uris->rawaudio);
219
220 /* add float attribute 'samplerate' */
221 lv2_atom_forge_property_head(forge, uris->samplerate, 0);
222 lv2_atom_forge_float(forge, sr);
223
224 /* add integer attribute 'channelid' */
225 lv2_atom_forge_property_head(forge, uris->channelid, 0);
226 lv2_atom_forge_int(forge, chn);
227
228 /* add vector of floats raw 'audiodata' */
229 lv2_atom_forge_property_head(forge, uris->audiodata, 0);
230 lv2_atom_forge_vector(forge, sizeof(float), uris->atom_Float, n_samples, data);
231
232 /* close off atom-object */
233 lv2_atom_forge_pop(forge, &frame);
234 }
235
tx_state(Fil4 * self)236 static void tx_state (Fil4* self)
237 {
238 LV2_Atom_Forge_Frame frame;
239 lv2_atom_forge_frame_time(&self->forge, 0);
240 x_forge_object(&self->forge, &frame, 1, self->uris.state);
241
242 lv2_atom_forge_property_head(&self->forge, self->uris.samplerate, 0);
243 lv2_atom_forge_float(&self->forge, self->rate);
244
245 lv2_atom_forge_property_head(&self->forge, self->uris.s_dbscale, 0);
246 lv2_atom_forge_float(&self->forge, self->db_scale);
247
248 lv2_atom_forge_property_head(&self->forge, self->uris.s_fftgain, 0);
249 lv2_atom_forge_float(&self->forge, self->fft_gain);
250
251 lv2_atom_forge_property_head(&self->forge, self->uris.s_fftmode, 0);
252 lv2_atom_forge_int(&self->forge, self->fft_mode);
253
254 lv2_atom_forge_property_head(&self->forge, self->uris.s_fftchan, 0);
255 lv2_atom_forge_int(&self->forge, self->fft_chan);
256
257 lv2_atom_forge_property_head(&self->forge, self->uris.s_uiscale, 0);
258 lv2_atom_forge_float(&self->forge, self->ui_scale);
259
260 lv2_atom_forge_property_head(&self->forge, self->uris.s_kbtuning, 0);
261 lv2_atom_forge_float(&self->forge, self->kb_tuning);
262
263 lv2_atom_forge_pop(&self->forge, &frame);
264 }
265
process_channel(Fil4 * self,FilterChannel * fc,uint32_t p_samples,uint32_t chn)266 static void process_channel(Fil4* self, FilterChannel *fc, uint32_t p_samples, uint32_t chn) {
267
268 /* localize variables */
269 const float ls_gain = *self->_port[IIR_LS_EN] > 0 ? powf (10.f, .05f * self->_port[IIR_LS_GAIN][0]) : 1.f;
270 const float hs_gain = *self->_port[IIR_HS_EN] > 0 ? powf (10.f, .05f * self->_port[IIR_HS_GAIN][0]) : 1.f;
271 const float ls_freq = *self->_port[IIR_LS_FREQ];
272 const float hs_freq = *self->_port[IIR_HS_FREQ];
273 // map [2^-4 .. 4] to [2^(-3/2) .. 2]
274 const float ls_q = .2129f + self->_port[IIR_LS_Q][0] / 2.25f;
275 const float hs_q = .2129f + self->_port[IIR_HS_Q][0] / 2.25f;
276 const bool hipass = *self->_port[FIL_HIPASS] > 0 ? true : false;
277 const bool lopass = *self->_port[FIL_LOPASS] > 0 ? true : false;
278 float hifreq = *self->_port[FIL_HIFREQ];
279 float hi_q = *self->_port[FIL_HIQ];
280 float lofreq = *self->_port[FIL_LOFREQ];
281 float lo_q = *self->_port[FIL_LOQ];
282
283 float *aip = self->_port [FIL_INPUT0 + (chn<<1)];
284 float *aop = self->_port [FIL_OUTPUT0 + (chn<<1)];
285
286 float sfreq [NSECT];
287 float sband [NSECT];
288 float sgain [NSECT];
289
290
291 /* clamp inputs to legal range - see lv2ttl/fil4.ports.ttl.in */
292 if (lofreq > self->below_nyquist) lofreq = self->below_nyquist;
293 if (lofreq < 630) lofreq = 630;
294 if (lofreq > 20000) lofreq = 20000;
295 if (lo_q < 0.0625) lo_q = 0.0625;
296 if (lo_q > 4.0) lo_q = 4.0;
297
298 if (hifreq > self->below_nyquist) hifreq = self->below_nyquist;
299 if (hifreq < 10) hifreq = 10;
300 if (hifreq > 1000) hifreq = 1000;
301 if (hi_q < 0.0625) hi_q = 0.0625;
302 if (hi_q > 4.0) hi_q = 4.0;
303
304 // shelf-filter freq,q is clamped in src/iir.h
305
306
307 /* calculate target values, parameter smoothing */
308 const float fgain = exp2ap (0.1661 * self->_port [FIL_GAIN][0]);
309
310 for (int j = 0; j < NSECT; ++j) {
311 float t = self->_port [FIL_SEC1 + 4 * j + Fil4Paramsect::FREQ][0] / self->rate;
312 if (t < 0.0002) t = 0.0002;
313 if (t > 0.4998) t = 0.4998;
314
315 sfreq [j] = t;
316 sband [j] = self->_port [FIL_SEC1 + 4 * j + Fil4Paramsect::BAND][0];
317
318 if (self->_port [FIL_SEC1 + 4 * j + Fil4Paramsect::SECT][0] > 0) {
319 sgain [j] = exp2ap (0.1661 * self->_port [FIL_SEC1 + 4 * j + Fil4Paramsect::GAIN][0]);
320 } else {
321 sgain [j] = 1.0;
322 }
323 }
324
325 while (p_samples) {
326 uint32_t i;
327 float sig [48];
328 const uint32_t k = (p_samples > 48) ? 32 : p_samples;
329
330 float t = fgain;
331 float g = fc->_gain;
332 if (t > 1.25 * g) t = 1.25 * g;
333 else if (t < 0.80 * g) t = 0.80 * g;
334 fc->_gain = t;
335 float d = (t - g) / k;
336
337 /* apply gain */
338 for (i = 0; i < k; i++) {
339 g += d;
340 sig [i] = g * aip [i];
341 }
342
343 /* update IIR */
344 if (iir_interpolate (&fc->iir_lowshelf, ls_gain, ls_freq, ls_q)) {
345 iir_calc_lowshelf (&fc->iir_lowshelf);
346 self->need_expose = true;
347 }
348 if (iir_interpolate (&fc->iir_highshelf, hs_gain, hs_freq, hs_q)) {
349 iir_calc_highshelf (&fc->iir_highshelf);
350 self->need_expose = true;
351 }
352
353 if (hip_interpolate (&fc->hip, hipass, hifreq, hi_q)) {
354 self->need_expose = true;
355 }
356 if (lop_interpolate (&fc->lop, lopass, lofreq, lo_q)) {
357 self->need_expose = true;
358 }
359
360 /* run filters */
361
362 hip_compute (&fc->hip, k, sig);
363 lop_compute (&fc->lop, k, sig);
364
365 for (int j = 0; j < NSECT; ++j) {
366 if (fc->_sect [j].proc (k, sig, sfreq [j], sband [j], sgain [j])) {
367 self->need_expose = true;
368 }
369 }
370
371 iir_compute (&fc->iir_lowshelf, k, sig);
372 iir_compute (&fc->iir_highshelf, k, sig);
373
374 /* fade 16 * 32 samples when enable changes */
375 int j = fc->_fade;
376 g = j / 16.0;
377
378 float *p = NULL;
379
380 if (self->_port [FIL_ENABLE][0] > 0) {
381 if (j == 16) p = sig;
382 else ++j;
383 }
384 else
385 {
386 if (j == 0) p = aip;
387 else --j;
388 }
389 fc->_fade = j;
390
391 if (p) {
392 /* active or bypassed */
393 if (aop != p) { // no in-place bypass
394 memcpy (aop, p, k * sizeof (float));
395 }
396 } else {
397 /* fade in/out */
398 self->need_expose = true;
399 d = (j / 16.0 - g) / k;
400 for (uint32_t i = 0; i < k; ++i) {
401 g += d;
402 aop [i] = g * sig [i] + (1 - g) * aip [i];
403 }
404 }
405
406 aip += k;
407 aop += k;
408 p_samples -= k;
409 }
410 }
411
412 static void
run(LV2_Handle instance,uint32_t n_samples)413 run(LV2_Handle instance, uint32_t n_samples)
414 {
415 Fil4* self = (Fil4*)instance;
416
417 /* check atom buffer size */
418 const size_t size = (sizeof(float) * self->n_channels * n_samples + 64);
419 const uint32_t capacity = self->notify->atom.size;
420 bool capacity_ok = true;
421 if (capacity < size + 128) {
422 capacity_ok = false;
423 if (!printed_capacity_warning) {
424 #ifdef _WIN32
425 fprintf (stderr, "fil4.lv2 error: LV2 comm-buffersize is insufficient %d/%d bytes.\n",
426 capacity, (int)size + 160);
427 #else
428 fprintf (stderr, "fil4.lv2 error: LV2 comm-buffersize is insufficient %d/%zu bytes.\n",
429 capacity, size + 160);
430 #endif
431 printed_capacity_warning = true;
432 }
433 }
434
435 /* prepare forge buffer and initialize atom-sequence */
436 lv2_atom_forge_set_buffer(&self->forge, (uint8_t*)self->notify, capacity);
437 lv2_atom_forge_sequence_head(&self->forge, &self->frame, 0);
438
439 // process messages from GUI;
440 if (self->control) {
441 LV2_Atom_Event* ev = lv2_atom_sequence_begin(&(self->control)->body);
442 while(!lv2_atom_sequence_is_end(&(self->control)->body, (self->control)->atom.size, ev)) {
443 if (ev->body.type == self->uris.atom_Blank || ev->body.type == self->uris.atom_Object) {
444 const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;
445 if (obj->body.otype == self->uris.ui_off) {
446 self->ui_active = false;
447 }
448 else if (obj->body.otype == self->uris.ui_on) {
449 self->ui_active = true;
450 self->send_state_to_ui = true;
451 }
452 else if (obj->body.otype == self->uris.state) {
453 const LV2_Atom* v = NULL;
454 lv2_atom_object_get(obj, self->uris.s_fftmode, &v, 0);
455 if (v) { self->fft_mode = ((LV2_Atom_Int*)v)->body; }
456
457 v = NULL;
458 lv2_atom_object_get(obj, self->uris.s_fftgain, &v, 0);
459 if (v) { self->fft_gain = ((LV2_Atom_Float*)v)->body; }
460
461 v = NULL;
462 lv2_atom_object_get(obj, self->uris.s_fftchan, &v, 0);
463 if (v) { self->fft_chan = ((LV2_Atom_Int*)v)->body; }
464
465 v = NULL;
466 lv2_atom_object_get(obj, self->uris.s_dbscale, &v, 0);
467 if (v) { self->db_scale = ((LV2_Atom_Float*)v)->body; }
468
469 v = NULL;
470 lv2_atom_object_get(obj, self->uris.s_uiscale, &v, 0);
471 if (v) { self->ui_scale = ((LV2_Atom_Float*)v)->body; }
472
473 v = NULL;
474 lv2_atom_object_get(obj, self->uris.s_kbtuning, &v, 0);
475 if (v) { self->kb_tuning = ((LV2_Atom_Float*)v)->body; }
476 }
477 }
478 ev = lv2_atom_sequence_next(ev);
479 }
480 }
481
482 if (self->ui_active && self->send_state_to_ui) {
483 self->send_state_to_ui = false;
484 self->resend_peak = self->rate / n_samples;
485 tx_state (self);
486 }
487
488 const int32_t fft_mode = self->ui_active ? (self->fft_mode & 0xf) : 0;
489
490 // send raw input to GUI (for spectrum analysis)
491 if (fft_mode > 0 && (fft_mode & 1) == 0 && capacity_ok) {
492 for (uint32_t c = 0; c < self->n_channels; ++c) {
493 tx_rawaudio (&self->forge, &self->uris, self->rate, c, n_samples, self->_port [FIL_INPUT0 + (c<<1)]);
494 }
495 }
496
497 if (self->peak_reset != (int)(floorf (self->_port [FIL_PEAK_RESET][0]))) {
498 self->peak_signal = 0;
499 self->peak_reset = (int)(floorf (self->_port [FIL_PEAK_RESET][0]));
500 }
501
502 // audio processing & peak calc.
503 float peak = self->peak_signal;
504 for (uint32_t c = 0; c < self->n_channels; ++c) {
505 process_channel (self, &self->fc[c], n_samples, c);
506
507 const float * const d = self->_port [FIL_OUTPUT0 + (c<<1)];
508 for (uint32_t i = 0; i < n_samples; ++i) {
509 const float pk = fabsf (d[i]);
510 if (pk > peak) {
511 peak = pk;
512 }
513 }
514 }
515
516 self->enabled = self->_port [FIL_ENABLE][0] > 0;
517
518 self->peak_signal = peak;
519 if (self->resend_peak > 0) {
520 --self->resend_peak;
521 *self->_port [FIL_PEAK_DB] = -120 - self->resend_peak / 100.f;
522 } else {
523 *self->_port [FIL_PEAK_DB] = (peak > 1e-6) ? 20.f * log10f (peak) : -120;
524 }
525
526 // send processed output to GUI (for analysis)
527 if ((fft_mode & 1) == 1 && capacity_ok) {
528 for (uint32_t c = 0; c < self->n_channels; ++c) {
529 tx_rawaudio (&self->forge, &self->uris, self->rate, c, n_samples, self->_port [FIL_OUTPUT0 + (c<<1)]);
530 }
531 }
532
533 /* close off atom-sequence */
534 lv2_atom_forge_pop(&self->forge, &self->frame);
535
536 #ifdef DISPLAY_INTERFACE
537 if (self->need_expose && self->queue_draw) {
538 self->need_expose = false;
539 self->queue_draw->queue_draw (self->queue_draw->handle);
540 }
541 #endif
542 }
543
544 #define STATESTORE(URI, TYPE, VALUE) \
545 store(handle, self->uris.URI, \
546 (void*) &(VALUE), sizeof(uint32_t), \
547 self->uris.atom_ ## TYPE, \
548 LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); \
549
550 static LV2_State_Status
fil4_save(LV2_Handle instance,LV2_State_Store_Function store,LV2_State_Handle handle,uint32_t flags,const LV2_Feature * const * features)551 fil4_save(LV2_Handle instance,
552 LV2_State_Store_Function store,
553 LV2_State_Handle handle,
554 uint32_t flags,
555 const LV2_Feature* const* features)
556 {
557 Fil4* self = (Fil4*)instance;
558
559 STATESTORE(s_dbscale, Float, self->db_scale)
560 STATESTORE(s_fftgain, Float, self->fft_gain)
561 STATESTORE(s_uiscale, Float, self->ui_scale)
562 STATESTORE(s_kbtuning, Float, self->kb_tuning)
563 STATESTORE(s_fftmode, Int, self->fft_mode)
564 STATESTORE(s_fftchan, Int, self->fft_chan)
565
566 return LV2_STATE_SUCCESS;
567 }
568
569 #define STATEREAD(URI, TYPE, CAST, PARAM) \
570 value = retrieve(handle, self->uris.URI, &size, &type, &valflags); \
571 if (value && size == sizeof(uint32_t) && type == self->uris.atom_ ## TYPE) { \
572 PARAM = *((const CAST *)value); \
573 }
574
575
576 static LV2_State_Status
fil4_restore(LV2_Handle instance,LV2_State_Retrieve_Function retrieve,LV2_State_Handle handle,uint32_t flags,const LV2_Feature * const * features)577 fil4_restore(LV2_Handle instance,
578 LV2_State_Retrieve_Function retrieve,
579 LV2_State_Handle handle,
580 uint32_t flags,
581 const LV2_Feature* const* features)
582 {
583 Fil4* self = (Fil4*)instance;
584 const void* value;
585 size_t size;
586 uint32_t type;
587 uint32_t valflags;
588
589 STATEREAD(s_dbscale, Float, float, self->db_scale)
590 STATEREAD(s_fftgain, Float, float, self->fft_gain)
591 STATEREAD(s_uiscale, Float, float, self->ui_scale)
592 STATEREAD(s_kbtuning, Float, float, self->kb_tuning)
593 STATEREAD(s_fftmode, Int, int32_t, self->fft_mode)
594 STATEREAD(s_fftchan, Int, int32_t, self->fft_chan)
595
596 self->send_state_to_ui = true;
597 return LV2_STATE_SUCCESS;
598 }
599
600 static void
cleanup(LV2_Handle instance)601 cleanup(LV2_Handle instance)
602 {
603 #ifdef DISPLAY_INTERFACE
604 Fil4* self = (Fil4*)instance;
605 if (self->display) {
606 cairo_surface_destroy (self->display);
607 }
608 #endif
609 free(instance);
610 }
611 #ifdef WITH_SIGNATURE
612 #define RTK_URI FIL4_URI
613 #include "gpg_init.c"
614 #include WITH_SIGNATURE
615 struct license_info license_infos = {
616 "x42-Equalizer",
617 "http://x42-plugins.com/x42/x42-eq"
618 };
619 #include "gpg_lv2ext.c"
620 #endif
621
622 #include "idpy.c"
623
624 const void*
extension_data(const char * uri)625 extension_data(const char* uri)
626 {
627 static const LV2_State_Interface state = { fil4_save, fil4_restore };
628 if (!strcmp(uri, LV2_STATE__interface)) {
629 return &state;
630 }
631 #ifdef DISPLAY_INTERFACE
632 static const LV2_Inline_Display_Interface display = { fil4_render };
633 if (!strcmp(uri, LV2_INLINEDISPLAY__interface)) {
634 #if (defined _WIN32 && defined RTK_STATIC_INIT)
635 static int once = 0;
636 if (!once) {once = 1; gobject_init_ctor();}
637 #endif
638 return &display;
639 }
640 #endif
641 #ifdef WITH_SIGNATURE
642 LV2_LICENSE_EXT_C
643 #endif
644 return NULL;
645 }
646
647 static const LV2_Descriptor descriptor_mono = {
648 FIL4_URI "mono",
649 instantiate,
650 connect_port,
651 NULL,
652 run,
653 NULL,
654 cleanup,
655 extension_data
656 };
657
658 static const LV2_Descriptor descriptor_stereo = {
659 FIL4_URI "stereo",
660 instantiate,
661 connect_port,
662 NULL,
663 run,
664 NULL,
665 cleanup,
666 extension_data
667 };
668
669 #undef LV2_SYMBOL_EXPORT
670 #ifdef _WIN32
671 # define LV2_SYMBOL_EXPORT __declspec(dllexport)
672 #else
673 # define LV2_SYMBOL_EXPORT __attribute__ ((visibility ("default")))
674 #endif
675 LV2_SYMBOL_EXPORT
676 const LV2_Descriptor*
lv2_descriptor(uint32_t index)677 lv2_descriptor(uint32_t index)
678 {
679 switch (index) {
680 case 0:
681 return &descriptor_mono;
682 case 1:
683 return &descriptor_stereo;
684 default:
685 return NULL;
686 }
687 }
688