5 * Copyright (C) 2018 GG
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "pluginlv2client.h"
30 #include "pluginserver.h"
36 #define LV2_SEQ_SIZE 9624
38 PluginLV2UriTable::PluginLV2UriTable()
43 PluginLV2UriTable::~PluginLV2UriTable()
48 LV2_URID PluginLV2UriTable::map(const char *uri)
50 for( int i=0; i<size(); ++i )
51 if( !strcmp(uri, get(i)) ) return i+1;
56 const char *PluginLV2UriTable::unmap(LV2_URID urid)
59 return idx>=0 && idx<size() ? get(idx) : 0;
62 PluginLV2Client_OptName:: PluginLV2Client_OptName(PluginLV2Client_Opt *opt)
65 set_text(opt->get_name());
68 PluginLV2Client_OptValue::PluginLV2Client_OptValue(PluginLV2Client_Opt *opt)
74 int PluginLV2Client_OptValue::update()
77 sprintf(val, "%f", opt->get_value());
78 if( !strcmp(val, get_text()) ) return 0;
84 PluginLV2Client_Opt::PluginLV2Client_Opt(PluginLV2ClientConfig *conf, const char *sym, int idx)
88 this->sym = cstrdup(sym);
89 item_name = new PluginLV2Client_OptName(this);
90 item_value = new PluginLV2Client_OptValue(this);
93 PluginLV2Client_Opt::~PluginLV2Client_Opt()
100 float PluginLV2Client_Opt::get_value()
102 return conf->ctls[idx];
105 void PluginLV2Client_Opt::set_value(float v)
110 int PluginLV2Client_Opt::update(float v)
113 return item_value->update();
116 const char *PluginLV2Client_Opt::get_name()
118 return conf->names[idx];
121 PluginLV2ClientConfig::PluginLV2ClientConfig()
130 PluginLV2ClientConfig::~PluginLV2ClientConfig()
133 remove_all_objects();
136 void PluginLV2ClientConfig::reset()
138 for( int i=0; i<nb_ports; ++i ) delete [] names[i];
139 delete [] names; names = 0;
140 delete [] mins; mins = 0;
141 delete [] maxs; maxs = 0;
142 delete [] ctls; ctls = 0;
147 int PluginLV2ClientConfig::equivalent(PluginLV2ClientConfig &that)
149 PluginLV2ClientConfig &conf = *this;
150 for( int i=0; i<that.size(); ++i ) {
151 PluginLV2Client_Opt *topt = conf[i], *vopt = that[i];
152 if( !EQUIV(topt->get_value(), vopt->get_value()) ) return 0;
157 void PluginLV2ClientConfig::copy_from(PluginLV2ClientConfig &that)
159 if( nb_ports != that.nb_ports ) {
161 nb_ports = that.nb_ports;
162 names = new const char *[nb_ports];
163 for( int i=0; i<nb_ports; ++i ) names[i] = 0;
164 mins = new float[nb_ports];
165 maxs = new float[nb_ports];
166 ctls = new float[nb_ports];
168 for( int i=0; i<nb_ports; ++i ) {
170 names[i] = cstrdup(that.names[i]);
171 mins[i] = that.mins[i];
172 maxs[i] = that.maxs[i];
173 ctls[i] = that.ctls[i];
175 remove_all_objects();
176 for( int i=0; i<that.size(); ++i ) {
177 append(new PluginLV2Client_Opt(this, that[i]->sym, that[i]->idx));
181 void PluginLV2ClientConfig::interpolate(PluginLV2ClientConfig &prev, PluginLV2ClientConfig &next,
182 int64_t prev_frame, int64_t next_frame, int64_t current_frame)
187 void PluginLV2ClientConfig::init_lv2(const LilvPlugin *lilv)
190 nb_ports = lilv_plugin_get_num_ports(lilv);
191 names = new const char *[nb_ports];
192 mins = new float[nb_ports];
193 maxs = new float[nb_ports];
194 ctls = new float[nb_ports];
195 lilv_plugin_get_port_ranges_float(lilv, mins, maxs, ctls);
196 for( int i=0; i<nb_ports; ++i ) {
197 const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
198 LilvNode *pnm = lilv_port_get_name(lilv, lp);
199 names[i] = cstrdup(lilv_node_as_string(pnm));
204 int PluginLV2ClientConfig::update()
207 PluginLV2ClientConfig &conf = *this;
208 for( int i=0; i<size(); ++i ) {
209 if( conf[i]->item_value->update() ) ++ret;
214 PluginLV2ClientReset::
215 PluginLV2ClientReset(PluginLV2ClientWindow *gui, int x, int y)
216 : BC_GenericButton(x, y, _("Reset"))
221 PluginLV2ClientReset::
222 ~PluginLV2ClientReset()
226 int PluginLV2ClientReset::handle_event()
228 PluginLV2Client *plugin = gui->plugin;
231 gui->update_selected();
232 gui->panel->update();
233 plugin->send_configure_change();
237 PluginLV2ClientText::
238 PluginLV2ClientText(PluginLV2ClientWindow *gui, int x, int y, int w)
239 : BC_TextBox(x, y, w, 1, (char *)"")
244 PluginLV2ClientText::
245 ~PluginLV2ClientText()
249 int PluginLV2ClientText::handle_event()
255 PluginLV2ClientApply::
256 PluginLV2ClientApply(PluginLV2ClientWindow *gui, int x, int y)
257 : BC_GenericButton(x, y, _("Apply"))
262 PluginLV2ClientApply::
263 ~PluginLV2ClientApply()
267 int PluginLV2ClientApply::handle_event()
269 const char *text = gui->text->get_text();
270 if( text && gui->selected ) {
271 gui->selected->update(atof(text));
272 gui->update_selected();
273 gui->plugin->send_configure_change();
279 PluginLV2Client_OptPanel::
280 PluginLV2Client_OptPanel(PluginLV2ClientWindow *gui, int x, int y, int w, int h)
281 : BC_ListBox(x, y, w, h, LISTBOX_TEXT), opts(items[0]), vals(items[1])
284 update(); // init col/wid/columns
287 PluginLV2Client_OptPanel::
288 ~PluginLV2Client_OptPanel()
292 int PluginLV2Client_OptPanel::selection_changed()
294 PluginLV2Client_Opt *opt = 0;
295 BC_ListBoxItem *item = get_selection(0, 0);
297 PluginLV2Client_OptName *opt_name = (PluginLV2Client_OptName *)item;
304 void PluginLV2Client_OptPanel::update()
308 PluginLV2ClientConfig &conf = gui->plugin->config;
309 for( int i=0; i<conf.size(); ++i ) {
310 PluginLV2Client_Opt *opt = conf[i];
311 opts.append(opt->item_name);
312 vals.append(opt->item_value);
314 const char *cols[] = { "option", "value", };
315 const int col1_w = 150;
316 int wids[] = { col1_w, get_w()-col1_w };
317 BC_ListBox::update(&items[0], &cols[0], &wids[0], sizeof(items)/sizeof(items[0]));
320 PluginLV2ClientWindow::PluginLV2ClientWindow(PluginLV2Client *plugin)
321 : PluginClientWindow(plugin, 500, 300, 500, 300, 0)
323 this->plugin = plugin;
327 PluginLV2ClientWindow::~PluginLV2ClientWindow()
332 void PluginLV2ClientWindow::create_objects()
336 add_subwindow(title = new BC_Title(x, y, plugin->title));
337 y += title->get_h() + 10;
338 add_subwindow(varbl = new BC_Title(x, y, ""));
339 add_subwindow(range = new BC_Title(x+160, y, ""));
340 int x1 = get_w() - BC_GenericButton::calculate_w(this, _("Reset")) - 8;
341 add_subwindow(reset = new PluginLV2ClientReset(this, x1, y));
342 y += title->get_h() + 10;
343 x1 = get_w() - BC_GenericButton::calculate_w(this, _("Apply")) - 8;
344 add_subwindow(apply = new PluginLV2ClientApply(this, x1, y));
345 add_subwindow(text = new PluginLV2ClientText(this, x, y, x1-x - 8));
346 y += title->get_h() + 10;
349 int panel_x = x, panel_y = y;
350 int panel_w = get_w()-10 - panel_x;
351 int panel_h = get_h()-10 - panel_y;
352 panel = new PluginLV2Client_OptPanel(this, panel_x, panel_y, panel_w, panel_h);
353 add_subwindow(panel);
358 void PluginLV2ClientWindow::update_selected()
363 int PluginLV2ClientWindow::scalar(float f, char *rp)
366 if( f == FLT_MAX ) cp = "FLT_MAX";
367 else if( f == FLT_MIN ) cp = "FLT_MIN";
368 else if( f == -FLT_MAX ) cp = "-FLT_MAX";
369 else if( f == -FLT_MIN ) cp = "-FLT_MIN";
370 else if( f == 0 ) cp = signbit(f) ? "-0" : "0";
371 else if( isnan(f) ) cp = signbit(f) ? "-NAN" : "NAN";
372 else if( isinf(f) ) cp = signbit(f) ? "-INF" : "INF";
373 else return sprintf(rp, "%g", f);
374 return sprintf(rp, "%s", cp);
377 void PluginLV2ClientWindow::update(PluginLV2Client_Opt *opt)
379 if( selected != opt ) {
380 if( selected ) selected->item_name->set_selected(0);
382 if( selected ) selected->item_name->set_selected(1);
384 char var[BCSTRLEN]; var[0] = 0;
385 char val[BCSTRLEN]; val[0] = 0;
386 char rng[BCTEXTLEN]; rng[0] = 0;
388 sprintf(var,"%s:", opt->conf->names[opt->idx]);
390 cp += sprintf(cp,"( ");
391 cp += scalar(opt->conf->mins[opt->idx], cp);
392 cp += sprintf(cp, " .. ");
393 cp += scalar(opt->conf->maxs[opt->idx], cp);
394 cp += sprintf(cp, " )");
395 sprintf(val, "%f", opt->get_value());
404 PluginLV2Client::PluginLV2Client(PluginServer *server)
405 : PluginAClient(server)
427 powerOf2BlockLength = 0;
428 fixedBlockLength = 0;
429 boundedBlockLength = 0;
433 PluginLV2Client::~PluginLV2Client()
436 lilv_world_free(world);
439 void PluginLV2Client::reset_lv2()
441 if( instance ) lilv_instance_deactivate(instance);
442 lilv_instance_free(instance); instance = 0;
443 lilv_node_free(powerOf2BlockLength); powerOf2BlockLength = 0;
444 lilv_node_free(fixedBlockLength); fixedBlockLength = 0;
445 lilv_node_free(boundedBlockLength); boundedBlockLength = 0;
446 lilv_node_free(urid_map); urid_map = 0;
447 lilv_node_free(atom_Sequence); atom_Sequence = 0;
448 lilv_node_free(atom_AtomPort); atom_AtomPort = 0;
449 lilv_node_free(lv2_Optional); lv2_Optional = 0;
450 lilv_node_free(lv2_ControlPort); lv2_ControlPort = 0;
451 lilv_node_free(lv2_AudioPort); lv2_AudioPort = 0;
452 lilv_node_free(lv2_CVPort); lv2_CVPort = 0;
453 lilv_node_free(lv2_OutputPort); lv2_OutputPort = 0;
454 lilv_node_free(lv2_InputPort); lv2_InputPort = 0;
455 delete [] (char *)seq_out; seq_out = 0;
456 uri_table.remove_all_objects();
462 config.remove_all_objects();
465 int PluginLV2Client::load_lv2(const char *path)
468 world = lilv_world_new();
470 printf("lv2: lilv_world_new failed");
473 lilv_world_load_all(world);
476 LilvNode *uri = lilv_new_uri(world, path);
478 printf("lv2: lilv_new_uri(%s) failed", path);
482 const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
483 lilv = lilv_plugins_get_by_uri(all_plugins, uri);
486 printf("lv2: lilv_plugins_get_by_uriPlugin(%s) failed", path);
490 LilvNode *name = lilv_plugin_get_name(lilv);
491 const char *nm = lilv_node_as_string(name);
492 snprintf(title,sizeof(title),"L2_%s",nm);
493 lilv_node_free(name);
497 int PluginLV2Client::init_lv2()
501 lv2_InputPort = lilv_new_uri(world, LV2_CORE__InputPort);
502 lv2_OutputPort = lilv_new_uri(world, LV2_CORE__OutputPort);
503 lv2_AudioPort = lilv_new_uri(world, LV2_CORE__AudioPort);
504 lv2_ControlPort = lilv_new_uri(world, LV2_CORE__ControlPort);
505 lv2_CVPort = lilv_new_uri(world, LV2_CORE__CVPort);
506 lv2_Optional = lilv_new_uri(world, LV2_CORE__connectionOptional);
507 atom_AtomPort = lilv_new_uri(world, LV2_ATOM__AtomPort);
508 atom_Sequence = lilv_new_uri(world, LV2_ATOM__Sequence);
509 urid_map = lilv_new_uri(world, LV2_URID__map);
510 powerOf2BlockLength = lilv_new_uri(world, LV2_BUF_SIZE__powerOf2BlockLength);
511 fixedBlockLength = lilv_new_uri(world, LV2_BUF_SIZE__fixedBlockLength);
512 boundedBlockLength = lilv_new_uri(world, LV2_BUF_SIZE__boundedBlockLength);
513 seq_out = (LV2_Atom_Sequence *) new char[sizeof(LV2_Atom_Sequence) + LV2_SEQ_SIZE];
515 config.init_lv2(lilv);
516 nb_inputs = nb_outputs = 0;
518 for( int i=0; i<config.nb_ports; ++i ) {
519 const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
520 int is_input = lilv_port_is_a(lilv, lp, lv2_InputPort);
521 if( !is_input && !lilv_port_is_a(lilv, lp, lv2_OutputPort) &&
522 !lilv_port_has_property(lilv, lp, lv2_Optional) ) {
523 printf("lv2: not input, not output, and not optional: %s\n", config.names[i]);
526 if( is_input && lilv_port_is_a(lilv, lp, lv2_ControlPort) ) {
527 const char *sym = lilv_node_as_string(lilv_port_get_symbol(lilv, lp));
528 config.append(new PluginLV2Client_Opt(&config, sym, i));
531 if( lilv_port_is_a(lilv, lp, lv2_AudioPort) ||
532 lilv_port_is_a(lilv, lp, lv2_CVPort ) ) {
533 if( is_input ) ++nb_inputs; else ++nb_outputs;
538 map.handle = (void*)&uri_table;
539 map.map = uri_table_map;
540 map_feature.URI = LV2_URID_MAP_URI;
541 map_feature.data = ↦
542 unmap.handle = (void*)&uri_table;
543 unmap.unmap = uri_table_unmap;
544 unmap_feature.URI = LV2_URID_UNMAP_URI;
545 unmap_feature.data = &unmap;
546 features[0] = &map_feature;
547 features[1] = &unmap_feature;
548 static const LV2_Feature buf_size_features[3] = {
549 { LV2_BUF_SIZE__powerOf2BlockLength, NULL },
550 { LV2_BUF_SIZE__fixedBlockLength, NULL },
551 { LV2_BUF_SIZE__boundedBlockLength, NULL },
553 features[2] = &buf_size_features[0];
554 features[3] = &buf_size_features[1];
555 features[4] = &buf_size_features[2];
558 instance = lilv_plugin_instantiate(lilv, sample_rate, features);
560 printf("lv2: lilv_plugin_instantiate failed: %s\n", server->title);
563 lilv_instance_activate(instance);
564 // not sure what to do with these
565 max_bufsz = nb_inputs &&
566 (lilv_plugin_has_feature(lilv, powerOf2BlockLength) ||
567 lilv_plugin_has_feature(lilv, fixedBlockLength) ||
568 lilv_plugin_has_feature(lilv, boundedBlockLength)) ? 4096 : 0;
572 LV2_URID PluginLV2Client::uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
574 return ((PluginLV2UriTable *)handle)->map(uri);
577 const char *PluginLV2Client::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
579 return ((PluginLV2UriTable *)handle)->unmap(urid);
582 NEW_WINDOW_MACRO(PluginLV2Client, PluginLV2ClientWindow)
584 int PluginLV2Client::load_configuration()
586 int64_t src_position = get_source_position();
587 KeyFrame *prev_keyframe = get_prev_keyframe(src_position);
588 PluginLV2ClientConfig curr_config;
589 curr_config.copy_from(config);
590 read_data(prev_keyframe);
591 int ret = !config.equivalent(curr_config) ? 1 : 0;
595 void PluginLV2Client::update_gui()
597 PluginClientThread *thread = get_thread();
598 if( !thread ) return;
599 PluginLV2ClientWindow *window = (PluginLV2ClientWindow*)thread->get_window();
600 window->lock_window("PluginFClient::update_gui");
601 load_configuration();
602 if( config.update() > 0 )
603 window->update_selected();
604 window->unlock_window();
607 int PluginLV2Client::is_realtime() { return 1; }
608 int PluginLV2Client::is_multichannel() { return nb_inputs > 1 || nb_outputs > 1 ? 1 : 0; }
609 const char* PluginLV2Client::plugin_title() { return title; }
610 int PluginLV2Client::uses_gui() { return 1; }
611 int PluginLV2Client::is_synthesis() { return 1; }
613 char* PluginLV2Client::to_string(char *string, const char *input)
616 for( const char *cp=input; *cp; ++cp )
617 *bp++ = isalnum(*cp) ? *cp : '_';
622 void PluginLV2Client::save_data(KeyFrame *keyframe)
625 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
626 char name[BCTEXTLEN]; to_string(name, plugin_title());
627 output.tag.set_title(name);
628 for( int i=0; i<config.size(); ++i ) {
629 PluginLV2Client_Opt *opt = config[i];
630 output.tag.set_property(opt->sym, opt->get_value());
633 output.terminate_string();
636 void PluginLV2Client::read_data(KeyFrame *keyframe)
639 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
640 char name[BCTEXTLEN]; to_string(name, plugin_title());
642 while( !input.read_tag() ) {
643 if( !input.tag.title_is(name) ) continue;
644 for( int i=0; i<config.size(); ++i ) {
645 PluginLV2Client_Opt *opt = config[i];
646 float value = input.tag.get_property(opt->sym, 0.);
647 opt->set_value(value);
652 void PluginLV2Client::connect_ports()
654 int ich = 0, och = 0;
655 for( int i=0; i<config.nb_ports; ++i ) {
656 const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
657 if( lilv_port_is_a(lilv, lp, lv2_AudioPort) ||
658 lilv_port_is_a(lilv, lp, lv2_CVPort) ) {
659 if( lilv_port_is_a(lilv, lp, lv2_InputPort) )
660 lilv_instance_connect_port(instance, i, in_buffers[ich++]);
661 else if( lilv_port_is_a(lilv, lp, lv2_OutputPort))
662 lilv_instance_connect_port(instance, i, out_buffers[och++]);
665 if( lilv_port_is_a(lilv, lp, atom_AtomPort) ) {
666 if( lilv_port_is_a(lilv, lp, lv2_InputPort) )
667 lilv_instance_connect_port(instance, i, &seq_in);
669 lilv_instance_connect_port(instance, i, seq_out);
672 if( lilv_port_is_a(lilv, lp, lv2_ControlPort) ) {
673 lilv_instance_connect_port(instance, i, &config.ctls[i]);
678 seq_in[0].atom.size = sizeof(LV2_Atom_Sequence_Body);
679 seq_in[0].atom.type = uri_table.map(LV2_ATOM__Sequence);
680 seq_in[1].atom.size = 0;
681 seq_in[1].atom.type = 0;
682 seq_out->atom.size = LV2_SEQ_SIZE;
683 seq_out->atom.type = uri_table.map(LV2_ATOM__Chunk);
686 void PluginLV2Client::init_plugin(int size)
691 load_configuration();
696 bfrsz = MAX(size, bfrsz);
698 in_buffers = new float*[nb_in_bfrs=nb_inputs];
699 for(int i=0; i<nb_in_bfrs; ++i )
700 in_buffers[i] = new float[bfrsz];
703 out_buffers = new float*[nb_out_bfrs=nb_outputs];
704 for( int i=0; i<nb_out_bfrs; ++i )
705 out_buffers[i] = new float[bfrsz];
709 void PluginLV2Client::delete_buffers()
712 for( int i=0; i<nb_in_bfrs; ++i ) delete [] in_buffers[i];
713 delete [] in_buffers; in_buffers = 0; nb_in_bfrs = 0;
716 for( int i=0; i<nb_out_bfrs; ++i ) delete [] out_buffers[i];
717 delete [] out_buffers; out_buffers = 0; nb_out_bfrs = 0;
722 int PluginLV2Client::process_realtime(int64_t size,
723 Samples *input_ptr, Samples *output_ptr)
727 for( int i=0; i<nb_in_bfrs; ++i ) {
728 double *inp = input_ptr->get_data();
729 float *ip = in_buffers[i];
730 for( int j=size; --j>=0; *ip++=*inp++ );
732 for( int i=0; i<nb_out_bfrs; ++i )
733 bzero(out_buffers[i], size*sizeof(float));
736 lilv_instance_run(instance, size);
738 double *outp = output_ptr->get_data();
739 float *op = out_buffers[0];
740 for( int i=size; --i>=0; *outp++=*op++ );
744 int PluginLV2Client::process_realtime(int64_t size,
745 Samples **input_ptr, Samples **output_ptr)
749 for( int i=0; i<nb_in_bfrs; ++i ) {
750 int k = i < PluginClient::total_in_buffers ? i : 0;
751 double *inp = input_ptr[k]->get_data();
752 float *ip = in_buffers[i];
753 for( int j=size; --j>=0; *ip++=*inp++ );
755 for( int i=0; i<nb_out_bfrs; ++i )
756 bzero(out_buffers[i], size*sizeof(float));
759 lilv_instance_run(instance, size);
761 int nbfrs = PluginClient::total_out_buffers;
762 if( nb_out_bfrs < nbfrs ) nbfrs = nb_out_bfrs;
763 for( int i=0; i<nbfrs; ++i ) {
764 double *outp = output_ptr[i]->get_data();
765 float *op = out_buffers[i];
766 for( int j=size; --j>=0; *outp++=*op++ );
771 PluginServer* MWindow::new_lv2_server(MWindow *mwindow, const char *name)
773 return new PluginServer(mwindow, name, PLUGIN_TYPE_LV2);
776 PluginClient *PluginServer::new_lv2_plugin()
778 PluginLV2Client *client = new PluginLV2Client(this);
779 if( client->load_lv2(path) ) { delete client; client = 0; }
780 else client->init_lv2();
784 int MWindow::init_lv2_index(MWindow *mwindow, Preferences *preferences, FILE *fp)
786 printf("init lv2 index:\n");
787 LilvWorld *world = lilv_world_new();
788 lilv_world_load_all(world);
789 const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
791 LILV_FOREACH(plugins, i, all_plugins) {
792 const LilvPlugin *lilv = lilv_plugins_get(all_plugins, i);
793 const char *uri = lilv_node_as_uri(lilv_plugin_get_uri(lilv));
794 PluginServer server(mwindow, uri, PLUGIN_TYPE_LV2);
795 int result = server.open_plugin(1, preferences, 0, 0);
797 server.write_table(fp, uri, PLUGIN_LV2_ID, 0);
798 server.close_plugin();
802 lilv_world_free(world);
808 #include "pluginserver.h"
810 PluginServer* MWindow::new_lv2_server(MWindow *mwindow, const char *name) { return 0; }
811 PluginClient *PluginServer::new_lv2_plugin() { return 0; }
812 int MWindow::init_lv2_index(MWindow *mwindow, Preferences *preferences, FILE *fp) { return 0; }
814 #endif /* HAVE_LV2 */