add lv2ui support
[goodguy/history.git] / cinelerra-5.1 / cinelerra / pluginlv2client.C
1 #ifdef HAVE_LV2
2
3 /*
4  * CINELERRA
5  * Copyright (C) 2018 GG
6  *
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.
11  *
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.
16  *
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
20  *
21  */
22
23 #include "clip.h"
24 #include "cstrdup.h"
25 #include "bchash.h"
26 #include "filexml.h"
27 #include "language.h"
28 #include "mwindow.h"
29 #include "pluginlv2client.h"
30 #include "pluginlv2config.h"
31 #include "pluginserver.h"
32 #include "samples.h"
33
34 #include <ctype.h>
35 #include <string.h>
36
37
38 PluginLV2ClientUI::
39 PluginLV2ClientUI(PluginLV2ClientWindow *gui, int x, int y)
40  : BC_GenericButton(x, y, _("UI"))
41 {
42         this->gui = gui;
43 }
44
45 PluginLV2ClientUI::
46 ~PluginLV2ClientUI()
47 {
48 }
49
50 int PluginLV2ClientUI::handle_event()
51 {
52         if( !gui->plugin->open_lv2_gui(gui) )
53                 flicker(8, 64);
54         return 1;
55 }
56
57 PluginLV2ClientReset::
58 PluginLV2ClientReset(PluginLV2ClientWindow *gui, int x, int y)
59  : BC_GenericButton(x, y, _("Reset"))
60 {
61         this->gui = gui;
62 }
63
64 PluginLV2ClientReset::
65 ~PluginLV2ClientReset()
66 {
67 }
68
69 int PluginLV2ClientReset::handle_event()
70 {
71         PluginLV2Client *plugin = gui->plugin;
72         plugin->init_lv2();
73         gui->selected = 0;
74         gui->update_selected();
75         gui->panel->update();
76         plugin->send_configure_change();
77         return 1;
78 }
79
80 PluginLV2ClientText::
81 PluginLV2ClientText(PluginLV2ClientWindow *gui, int x, int y, int w)
82  : BC_TextBox(x, y, w, 1, (char *)"")
83 {
84         this->gui = gui;
85 }
86
87 PluginLV2ClientText::
88 ~PluginLV2ClientText()
89 {
90 }
91
92 int PluginLV2ClientText::handle_event()
93 {
94         return 0;
95 }
96
97
98 PluginLV2ClientApply::
99 PluginLV2ClientApply(PluginLV2ClientWindow *gui, int x, int y)
100  : BC_GenericButton(x, y, _("Apply"))
101 {
102         this->gui = gui;
103 }
104
105 PluginLV2ClientApply::
106 ~PluginLV2ClientApply()
107 {
108 }
109
110 int PluginLV2ClientApply::handle_event()
111 {
112         const char *text = gui->text->get_text();
113         if( text && gui->selected ) {
114                 gui->selected->update(atof(text));
115                 gui->update_selected();
116                 gui->plugin->send_configure_change();
117         }
118         return 1;
119 }
120
121
122
123 PluginLV2Client_OptPanel::
124 PluginLV2Client_OptPanel(PluginLV2ClientWindow *gui, int x, int y, int w, int h)
125  : BC_ListBox(x, y, w, h, LISTBOX_TEXT), opts(items[0]), vals(items[1])
126 {
127         this->gui = gui;
128         update();  // init col/wid/columns
129 }
130
131 PluginLV2Client_OptPanel::
132 ~PluginLV2Client_OptPanel()
133 {
134 }
135
136 int PluginLV2Client_OptPanel::selection_changed()
137 {
138         PluginLV2Client_Opt *opt = 0;
139         BC_ListBoxItem *item = get_selection(0, 0);
140         if( item ) {
141                 PluginLV2Client_OptName *opt_name = (PluginLV2Client_OptName *)item;
142                 opt = opt_name->opt;
143         }
144         gui->update(opt);
145         return 1;
146 }
147
148 void PluginLV2Client_OptPanel::update()
149 {
150         opts.remove_all();
151         vals.remove_all();
152         PluginLV2ClientConfig &conf = gui->plugin->config;
153         for( int i=0; i<conf.size(); ++i ) {
154                 PluginLV2Client_Opt *opt = conf[i];
155                 opts.append(opt->item_name);
156                 vals.append(opt->item_value);
157         }
158         const char *cols[] = { "option", "value", };
159         const int col1_w = 150;
160         int wids[] = { col1_w, get_w()-col1_w };
161         BC_ListBox::update(&items[0], &cols[0], &wids[0], sizeof(items)/sizeof(items[0]));
162 }
163
164 PluginLV2ClientWindow::PluginLV2ClientWindow(PluginLV2Client *plugin)
165  : PluginClientWindow(plugin, 500, 300, 500, 300, 1)
166 {
167         this->plugin = plugin;
168         selected = 0;
169 }
170
171 PluginLV2ClientWindow::~PluginLV2ClientWindow()
172 {
173 }
174
175
176 void PluginLV2ClientWindow::create_objects()
177 {
178         BC_Title *title;
179         int x = 10, y = 10, x1;
180         add_subwindow(title = new BC_Title(x, y, plugin->title));
181 #ifdef HAVE_LV2UI
182         x1 = get_w() - BC_GenericButton::calculate_w(this, _("UI")) - 8;
183         add_subwindow(ui = new PluginLV2ClientUI(this, x1, y));
184 #else
185         ui = 0;
186 #endif
187         y += title->get_h() + 10;
188         add_subwindow(varbl = new BC_Title(x, y, ""));
189         add_subwindow(range = new BC_Title(x+160, y, ""));
190         x1 = get_w() - BC_GenericButton::calculate_w(this, _("Reset")) - 8;
191         add_subwindow(reset = new PluginLV2ClientReset(this, x1, y));
192         y += title->get_h() + 10;
193         x1 = get_w() - BC_GenericButton::calculate_w(this, _("Apply")) - 8;
194         add_subwindow(apply = new PluginLV2ClientApply(this, x1, y));
195         add_subwindow(text = new PluginLV2ClientText(this, x, y, x1-x - 8));
196         y += title->get_h() + 10;
197         add_subwindow(pot = new PluginLV2ClientPot(this, x, y));
198         x1 = x + pot->get_w() + 10;
199         add_subwindow(slider = new PluginLV2ClientSlider(this, x1, y+10));
200         y += pot->get_h() + 10;
201
202         plugin->init_lv2();
203         plugin->load_configuration();
204         plugin->config.update();
205
206         int panel_x = x, panel_y = y;
207         int panel_w = get_w()-10 - panel_x;
208         int panel_h = get_h()-10 - panel_y;
209         panel = new PluginLV2Client_OptPanel(this, panel_x, panel_y, panel_w, panel_h);
210         add_subwindow(panel);
211         panel->update();
212         show_window(1);
213 }
214
215 int PluginLV2ClientWindow::resize_event(int w, int h)
216 {
217         int x1;
218 #ifdef HAVE_LV2UI
219         x1 = w - ui->get_w() - 8;
220         ui->reposition_window(x1, ui->get_y());
221 #endif
222         x1 = w - reset->get_w() - 8;
223         reset->reposition_window(x1, reset->get_y());
224         x1 = w - apply->get_w() - 8;
225         apply->reposition_window(x1, apply->get_y());
226         text->reposition_window(text->get_x(), text->get_y(), x1-text->get_x() - 8);
227         x1 = pot->get_x() + pot->get_w() + 10;
228         int w1 = w - slider->get_x() - 20;
229         slider->set_pointer_motion_range(w1);
230         slider->reposition_window(x1, slider->get_y(), w1, slider->get_h());
231         int panel_x = panel->get_x(), panel_y = panel->get_y();
232         panel->reposition_window(panel_x, panel_y, w-10-panel_x, h-10-panel_y);
233         return 1;
234 }
235
236 PluginLV2ClientPot::PluginLV2ClientPot(PluginLV2ClientWindow *gui, int x, int y)
237  : BC_FPot(x, y, 0.f, 0.f, 0.f)
238 {
239         this->gui = gui;
240 }
241
242 int PluginLV2ClientPot::handle_event()
243 {
244         if( gui->selected ) {
245                 gui->selected->update(get_value());
246                 gui->update_selected();
247                 gui->plugin->send_configure_change();
248         }
249         return 1;
250 }
251
252 PluginLV2ClientSlider::PluginLV2ClientSlider(PluginLV2ClientWindow *gui, int x, int y)
253  : BC_FSlider(x, y, 0, gui->get_w()-x-20, gui->get_w()-x-20, 0.f, 0.f, 0.f)
254 {
255         this->gui = gui;
256 }
257
258 int PluginLV2ClientSlider::handle_event()
259 {
260         if( gui->selected ) {
261                 gui->selected->update(get_value());
262                 gui->update_selected();
263                 gui->plugin->send_configure_change();
264         }
265         return 1;
266 }
267
268 void PluginLV2ClientWindow::update_selected()
269 {
270         update(selected);
271         if( selected && plugin->parent_gui ) {
272                 control_t bfr;
273                 bfr.idx = selected->idx;
274                 bfr.value = selected->get_value();
275                 plugin->parent_gui->send_child(LV2_SET, &bfr, sizeof(bfr));
276         }
277 }
278
279 int PluginLV2ClientWindow::scalar(float f, char *rp)
280 {
281         const char *cp = 0;
282              if( f == FLT_MAX ) cp = "FLT_MAX";
283         else if( f == FLT_MIN ) cp = "FLT_MIN";
284         else if( f == -FLT_MAX ) cp = "-FLT_MAX";
285         else if( f == -FLT_MIN ) cp = "-FLT_MIN";
286         else if( f == 0 ) cp = signbit(f) ? "-0" : "0";
287         else if( isnan(f) ) cp = signbit(f) ? "-NAN" : "NAN";
288         else if( isinf(f) ) cp = signbit(f) ? "-INF" : "INF";
289         else return sprintf(rp, "%g", f);
290         return sprintf(rp, "%s", cp);
291 }
292
293 void PluginLV2ClientWindow::update(PluginLV2Client_Opt *opt)
294 {
295         if( selected != opt ) {
296                 if( selected ) selected->item_name->set_selected(0);
297                 selected = opt;
298                 if( selected ) selected->item_name->set_selected(1);
299         }
300         char var[BCSTRLEN];  var[0] = 0;
301         char val[BCSTRLEN];  val[0] = 0;
302         char rng[BCTEXTLEN]; rng[0] = 0;
303         if( opt ) {
304                 sprintf(var,"%s:", opt->conf->names[opt->idx]);
305                 char *cp = rng;
306                 cp += sprintf(cp,"( ");
307                 float min = opt->conf->mins[opt->idx];
308                 cp += scalar(min, cp);
309                 cp += sprintf(cp, " .. ");
310                 float max = opt->conf->maxs[opt->idx];
311                 cp += scalar(max, cp);
312                 cp += sprintf(cp, " )");
313                 float v = opt->get_value();
314                 sprintf(val, "%f", v);
315                 slider->update(slider->get_w(), v, min, max);
316                 pot->update(v, min, max);
317         }
318         else {
319                 slider->update(slider->get_w(), 0.f, 0.f, 0.f);
320                 pot->update(0.f, 0.f, 0.f);
321         }
322         varbl->update(var);
323         range->update(rng);
324         text->update(val);
325         panel->update();
326 }
327
328
329 PluginLV2Client::PluginLV2Client(PluginServer *server)
330  : PluginAClient(server)
331 {
332         in_buffers = 0;
333         out_buffers = 0;
334         nb_in_bfrs = 0;
335         nb_out_bfrs = 0;
336         bfrsz = 0;
337         nb_inputs = 0;
338         nb_outputs = 0;
339         max_bufsz = 0;
340
341         world = 0;
342         instance = 0;
343         lv2_InputPort = 0;
344         lv2_OutputPort = 0;
345         lv2_AudioPort = 0;
346         lv2_CVPort = 0;
347         lv2_ControlPort = 0;
348         lv2_Optional = 0;
349         atom_AtomPort = 0;
350         atom_Sequence = 0;
351         powerOf2BlockLength = 0;
352         fixedBlockLength = 0;
353         boundedBlockLength = 0;
354         seq_out = 0;
355         parent_gui = 0;
356 }
357
358 PluginLV2Client::~PluginLV2Client()
359 {
360         reset_lv2();
361         lilv_world_free(world);
362 }
363
364 void PluginLV2Client::reset_lv2()
365 {
366         delete parent_gui;                    parent_gui = 0;
367         if( instance ) lilv_instance_deactivate(instance);
368         lilv_instance_free(instance);         instance = 0;
369         lilv_node_free(powerOf2BlockLength);  powerOf2BlockLength = 0;
370         lilv_node_free(fixedBlockLength);     fixedBlockLength = 0;
371         lilv_node_free(boundedBlockLength);   boundedBlockLength = 0;
372         lilv_node_free(atom_Sequence);        atom_Sequence = 0;
373         lilv_node_free(atom_AtomPort);        atom_AtomPort = 0;
374         lilv_node_free(lv2_Optional);         lv2_Optional = 0;
375         lilv_node_free(lv2_ControlPort);      lv2_ControlPort = 0;
376         lilv_node_free(lv2_AudioPort);        lv2_AudioPort = 0;
377         lilv_node_free(lv2_CVPort);           lv2_CVPort = 0;
378         lilv_node_free(lv2_OutputPort);       lv2_OutputPort = 0;
379         lilv_node_free(lv2_InputPort);        lv2_InputPort = 0;
380         delete [] (char *)seq_out;            seq_out = 0;
381         uri_table.remove_all_objects();
382         delete_buffers();
383         nb_inputs = 0;
384         nb_outputs = 0;
385         max_bufsz = 0;
386         config.reset();
387         config.remove_all_objects();
388 }
389
390 int PluginLV2Client::load_lv2(const char *path)
391 {
392         if( !world ) {
393                 world = lilv_world_new();
394                 if( !world ) {
395                         printf("lv2: lilv_world_new failed");
396                         return 1;
397                 }
398                 lilv_world_load_all(world);
399         }
400
401         LilvNode *uri = lilv_new_uri(world, path);
402         if( !uri ) {
403                 printf("lv2: lilv_new_uri(%s) failed", path);
404                 return 1;
405         }
406
407         const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
408         lilv = lilv_plugins_get_by_uri(all_plugins, uri);
409         lilv_node_free(uri);
410         if( !lilv ) {
411                 printf("lv2: lilv_plugins_get_by_uriPlugin(%s) failed", path);
412                 return 1;
413         }
414
415         LilvNode *name = lilv_plugin_get_name(lilv);
416         const char *nm = lilv_node_as_string(name);
417         snprintf(title,sizeof(title),"L2_%s",nm);
418         lilv_node_free(name);
419         return 0;
420 }
421
422 int PluginLV2Client::init_lv2()
423 {
424         reset_lv2();
425
426         lv2_InputPort       = lilv_new_uri(world, LV2_CORE__InputPort);
427         lv2_OutputPort      = lilv_new_uri(world, LV2_CORE__OutputPort);
428         lv2_AudioPort       = lilv_new_uri(world, LV2_CORE__AudioPort);
429         lv2_ControlPort     = lilv_new_uri(world, LV2_CORE__ControlPort);
430         lv2_CVPort          = lilv_new_uri(world, LV2_CORE__CVPort);
431         lv2_Optional        = lilv_new_uri(world, LV2_CORE__connectionOptional);
432         atom_AtomPort       = lilv_new_uri(world, LV2_ATOM__AtomPort);
433         atom_Sequence       = lilv_new_uri(world, LV2_ATOM__Sequence);
434         powerOf2BlockLength = lilv_new_uri(world, LV2_BUF_SIZE__powerOf2BlockLength);
435         fixedBlockLength    = lilv_new_uri(world, LV2_BUF_SIZE__fixedBlockLength);
436         boundedBlockLength  = lilv_new_uri(world, LV2_BUF_SIZE__boundedBlockLength);
437         seq_out = (LV2_Atom_Sequence *) new char[sizeof(LV2_Atom_Sequence) + LV2_SEQ_SIZE];
438
439         config.init_lv2(lilv);
440         nb_inputs = nb_outputs = 0;
441
442         for( int i=0; i<config.nb_ports; ++i ) {
443                 const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
444                 int is_input = lilv_port_is_a(lilv, lp, lv2_InputPort);
445                 if( !is_input && !lilv_port_is_a(lilv, lp, lv2_OutputPort) &&
446                     !lilv_port_has_property(lilv, lp, lv2_Optional) ) {
447                         printf("lv2: not input, not output, and not optional: %s\n", config.names[i]);
448                         continue;
449                 }
450                 if( is_input && lilv_port_is_a(lilv, lp, lv2_ControlPort) ) {
451                         config.append(new PluginLV2Client_Opt(&config, i));
452                         continue;
453                 }
454                 if( lilv_port_is_a(lilv, lp, lv2_AudioPort) ||
455                     lilv_port_is_a(lilv, lp, lv2_CVPort ) ) {
456                         if( is_input ) ++nb_inputs; else ++nb_outputs;
457                         continue;
458                 }
459         }
460
461         map.handle = (void*)&uri_table;
462         map.map = uri_table_map;
463         features.append(new Lv2Feature(LV2_URID_MAP_URI, &map));
464         unmap.handle = (void*)&uri_table;
465         unmap.unmap  = uri_table_unmap;
466         features.append(new Lv2Feature(LV2_URID_UNMAP_URI, &unmap));
467         features.append(new Lv2Feature(LV2_BUF_SIZE__powerOf2BlockLength, 0));
468         features.append(new Lv2Feature(LV2_BUF_SIZE__fixedBlockLength,    0));
469         features.append(new Lv2Feature(LV2_BUF_SIZE__boundedBlockLength,  0));
470         features.append(0);
471
472         instance = lilv_plugin_instantiate(lilv, sample_rate, features);
473         if( !instance ) {
474                 printf("lv2: lilv_plugin_instantiate failed: %s\n", server->title);
475                 return 1;
476         }
477         lilv_instance_activate(instance);
478 // not sure what to do with these
479         max_bufsz = nb_inputs &&
480                 (lilv_plugin_has_feature(lilv, powerOf2BlockLength) ||
481                  lilv_plugin_has_feature(lilv, fixedBlockLength) ||
482                  lilv_plugin_has_feature(lilv, boundedBlockLength)) ? 4096 : 0;
483         return 0;
484 }
485
486 LV2_URID PluginLV2Client::uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
487 {
488         return ((PluginLV2UriTable *)handle)->map(uri);
489 }
490
491 const char *PluginLV2Client::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
492 {
493         return ((PluginLV2UriTable *)handle)->unmap(urid);
494 }
495
496 NEW_WINDOW_MACRO(PluginLV2Client, PluginLV2ClientWindow)
497
498 int PluginLV2Client::load_configuration()
499 {
500         int64_t src_position =  get_source_position();
501         KeyFrame *prev_keyframe = get_prev_keyframe(src_position);
502         PluginLV2ClientConfig curr_config;
503         curr_config.copy_from(config);
504         read_data(prev_keyframe);
505         int ret = !config.equivalent(curr_config) ? 1 : 0;
506         return ret;
507 }
508
509 void PluginLV2Client::update_gui()
510 {
511         PluginClientThread *thread = get_thread();
512         if( !thread ) return;
513         PluginLV2ClientWindow *gui = (PluginLV2ClientWindow*)thread->get_window();
514         gui->lock_window("PluginFClient::update_gui");
515         load_configuration();
516         if( config.update() > 0 ) {
517                 gui->update_selected();
518                 update_lv2();
519         }
520         gui->unlock_window();
521 }
522
523 void PluginLV2Client::update_lv2()
524 {
525         if( !parent_gui ) return;
526         parent_gui->send_child(LV2_UPDATE, config.ctls, sizeof(float)*config.nb_ports);
527 }
528
529 void PluginLV2Client::lv2_update()
530 {
531         PluginClientThread *thread = get_thread();
532         if( !thread ) return;
533         PluginLV2ClientWindow *gui = (PluginLV2ClientWindow*)thread->get_window();
534         gui->lock_window("PluginLV2ParentGUI::handle_parent");
535         int ret = config.update();
536         if( ret ) gui->update(0);
537         gui->unlock_window();
538         if( ret ) send_configure_change();
539 }
540
541 void PluginLV2Client::lv2_update(float *vals)
542 {
543         int nb_ports = config.nb_ports;
544         float *ctls = config.ctls;
545         for( int i=0; i<nb_ports; ++i ) *ctls++ = *vals++;
546         lv2_update();
547 }
548
549 void PluginLV2Client::lv2_set(int idx, float val)
550 {
551         config[idx]->set_value(val);
552         lv2_update();
553 }
554
555
556 int PluginLV2Client::is_realtime() { return 1; }
557 int PluginLV2Client::is_multichannel() { return nb_inputs > 1 || nb_outputs > 1 ? 1 : 0; }
558 const char* PluginLV2Client::plugin_title() { return title; }
559 int PluginLV2Client::uses_gui() { return 1; }
560 int PluginLV2Client::is_synthesis() { return 1; }
561
562 char* PluginLV2Client::to_string(char *string, const char *input)
563 {
564         char *bp = string;
565         for( const char *cp=input; *cp; ++cp )
566                 *bp++ = isalnum(*cp) ? *cp : '_';
567         *bp = 0;
568         return string;
569 }
570
571 void PluginLV2Client::save_data(KeyFrame *keyframe)
572 {
573         FileXML output;
574         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
575         char name[BCTEXTLEN];  to_string(name, plugin_title());
576         output.tag.set_title(name);
577         for( int i=0; i<config.size(); ++i ) {
578                 PluginLV2Client_Opt *opt = config[i];
579                 output.tag.set_property(opt->get_symbol(), opt->get_value());
580         }
581         output.append_tag();
582         output.terminate_string();
583 }
584
585 void PluginLV2Client::read_data(KeyFrame *keyframe)
586 {
587         FileXML input;
588         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
589         char name[BCTEXTLEN];  to_string(name, plugin_title());
590
591         while( !input.read_tag() ) {
592                 if( !input.tag.title_is(name) ) continue;
593                 for( int i=0; i<config.size(); ++i ) {
594                         PluginLV2Client_Opt *opt = config[i];
595                         float value = input.tag.get_property(opt->get_symbol(), 0.);
596                         opt->set_value(value);
597                 }
598         }
599 }
600
601 void PluginLV2Client::connect_ports()
602 {
603         int ich = 0, och = 0;
604         for( int i=0; i<config.nb_ports; ++i ) {
605                 const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
606                 if( lilv_port_is_a(lilv, lp, lv2_AudioPort) ||
607                     lilv_port_is_a(lilv, lp, lv2_CVPort) ) {
608                         if( lilv_port_is_a(lilv, lp, lv2_InputPort) )
609                                 lilv_instance_connect_port(instance, i, in_buffers[ich++]);
610                         else if( lilv_port_is_a(lilv, lp, lv2_OutputPort))
611                                 lilv_instance_connect_port(instance, i, out_buffers[och++]);
612                         continue;
613                 }
614                 if( lilv_port_is_a(lilv, lp, atom_AtomPort) ) {
615                         if( lilv_port_is_a(lilv, lp, lv2_InputPort) )
616                                 lilv_instance_connect_port(instance, i, &seq_in);
617                         else
618                                 lilv_instance_connect_port(instance, i, seq_out);
619                         continue;
620                 }
621                 if( lilv_port_is_a(lilv, lp, lv2_ControlPort) ) {
622                         lilv_instance_connect_port(instance, i, &config.ctls[i]);
623                         continue;
624                 }
625         }
626
627         seq_in[0].atom.size = sizeof(LV2_Atom_Sequence_Body);
628         seq_in[0].atom.type = uri_table.map(LV2_ATOM__Sequence);
629         seq_in[1].atom.size = 0;
630         seq_in[1].atom.type = 0;
631         seq_out->atom.size  = LV2_SEQ_SIZE;
632         seq_out->atom.type  = uri_table.map(LV2_ATOM__Chunk);
633 }
634
635 void PluginLV2Client::init_plugin(int size)
636 {
637         if( !instance )
638                 init_lv2();
639
640         load_configuration();
641
642         if( bfrsz < size )
643                 delete_buffers();
644
645         bfrsz = MAX(size, bfrsz);
646         if( !in_buffers ) {
647                 in_buffers = new float*[nb_in_bfrs=nb_inputs];
648                 for(int i=0; i<nb_in_bfrs; ++i )
649                         in_buffers[i] = new float[bfrsz];
650         }
651         if( !out_buffers ) {
652                 out_buffers = new float*[nb_out_bfrs=nb_outputs];
653                 for( int i=0; i<nb_out_bfrs; ++i )
654                         out_buffers[i] = new float[bfrsz];
655         }
656 }
657
658 void PluginLV2Client::delete_buffers()
659 {
660         if( in_buffers ) {
661                 for( int i=0; i<nb_in_bfrs; ++i ) delete [] in_buffers[i];
662                 delete [] in_buffers;  in_buffers = 0;  nb_in_bfrs = 0;
663         }
664         if( out_buffers ) {
665                 for( int i=0; i<nb_out_bfrs; ++i ) delete [] out_buffers[i];
666                 delete [] out_buffers;  out_buffers = 0;  nb_out_bfrs = 0;
667         }
668         bfrsz = 0;
669 }
670
671 int PluginLV2Client::open_lv2_gui(PluginLV2ClientWindow *gui)
672 {
673 #ifdef HAVE_LV2UI
674         if( parent_gui ) {
675                 if( !parent_gui->done ) return 0;
676                 delete parent_gui;
677         }
678         parent_gui = new PluginLV2ParentGUI(gui);
679         parent_gui->start_child();
680         parent_gui->start_parent();
681         return 1;
682 #else
683         return 0;
684 #endif
685 }
686
687 int PluginLV2Client::process_realtime(int64_t size,
688         Samples *input_ptr, Samples *output_ptr)
689 {
690         init_plugin(size);
691
692         for( int i=0; i<nb_in_bfrs; ++i ) {
693                 double *inp = input_ptr->get_data();
694                 float *ip = in_buffers[i];
695                 for( int j=size; --j>=0; *ip++=*inp++ );
696         }
697         for( int i=0; i<nb_out_bfrs; ++i )
698                 bzero(out_buffers[i], size*sizeof(float));
699
700         connect_ports();
701         lilv_instance_run(instance, size);
702
703         double *outp = output_ptr->get_data();
704         float *op = out_buffers[0];
705         for( int i=size; --i>=0; *outp++=*op++ );
706         return size;
707 }
708
709 int PluginLV2Client::process_realtime(int64_t size,
710         Samples **input_ptr, Samples **output_ptr)
711 {
712         init_plugin(size);
713
714         for( int i=0; i<nb_in_bfrs; ++i ) {
715                 int k = i < PluginClient::total_in_buffers ? i : 0;
716                 double *inp = input_ptr[k]->get_data();
717                 float *ip = in_buffers[i];
718                 for( int j=size; --j>=0; *ip++=*inp++ );
719         }
720         for( int i=0; i<nb_out_bfrs; ++i )
721                 bzero(out_buffers[i], size*sizeof(float));
722
723         connect_ports();
724         lilv_instance_run(instance, size);
725
726         int nbfrs = PluginClient::total_out_buffers;
727         if( nb_out_bfrs < nbfrs ) nbfrs = nb_out_bfrs;
728         for( int i=0; i<nbfrs; ++i ) {
729                 double *outp = output_ptr[i]->get_data();
730                 float *op = out_buffers[i];
731                 for( int j=size; --j>=0; *outp++=*op++ );
732         }
733         return size;
734 }
735
736 PluginServer* MWindow::new_lv2_server(MWindow *mwindow, const char *name)
737 {
738         return new PluginServer(mwindow, name, PLUGIN_TYPE_LV2);
739 }
740
741 PluginClient *PluginServer::new_lv2_plugin()
742 {
743         PluginLV2Client *client = new PluginLV2Client(this);
744 //for some lv2 clients
745         if( client->sample_rate < 64 ) client->sample_rate = 64;
746         if( client->project_sample_rate < 64 ) client->project_sample_rate = 64;
747         if( client->load_lv2(path) ) { delete client;  client = 0; }
748         else client->init_lv2();
749         return client;
750 }
751
752 int MWindow::init_lv2_index(MWindow *mwindow, Preferences *preferences, FILE *fp)
753 {
754         printf("init lv2 index:\n");
755         LilvWorld *world = lilv_world_new();
756         lilv_world_load_all(world);
757         const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
758
759         LILV_FOREACH(plugins, i, all_plugins) {
760                 const LilvPlugin *lilv = lilv_plugins_get(all_plugins, i);
761                 const char *uri = lilv_node_as_uri(lilv_plugin_get_uri(lilv));
762                 PluginServer server(mwindow, uri, PLUGIN_TYPE_LV2);
763                 int result = server.open_plugin(1, preferences, 0, 0);
764                 if( !result ) {
765                         server.write_table(fp, uri, PLUGIN_LV2_ID, 0);
766                         server.close_plugin();
767                 }
768         }
769
770         lilv_world_free(world);
771         return 0;
772 }
773
774 ForkChild *PluginLV2ParentGUI::new_fork()
775 {
776 #ifdef HAVE_LV2UI
777         return new PluginLV2ChildGUI();
778 #else
779         return 0;
780 #endif
781 }
782
783 PluginLV2ParentGUI::PluginLV2ParentGUI(PluginLV2ClientWindow *gui)
784 {
785         this->gui = gui;
786 }
787
788 PluginLV2ParentGUI::~PluginLV2ParentGUI()
789 {
790         stop();
791 }
792
793 void PluginLV2ParentGUI::start_parent()
794 {
795         start();
796         const char *path = gui->plugin->server->path;
797         send_child(LV2_OPEN, path, strlen(path)+1);
798         PluginLV2ClientConfig &conf = gui->plugin->config;
799         send_child(LV2_START, 0, 0);
800         send_child(LV2_LOAD, conf.ctls, conf.nb_ports*sizeof(float));
801 }
802
803 int PluginLV2ParentGUI::handle_parent()
804 {
805         int result = 1;
806
807         switch( parent_token ) {
808         case LV2_UPDATE:
809                 gui->plugin->lv2_update((float *)parent_data);
810                 break;
811         case LV2_SET: {
812                 control_t *ctl = (control_t *)parent_data;
813                 gui->plugin->lv2_set(ctl->idx, ctl->value);
814                 break; }
815         case EXIT_CODE: {
816                 result = -1;
817                 break; }
818         default:
819                 result = 0;
820                 break;
821         }
822
823         return result;
824 }
825
826 // stub in parent
827 int PluginLV2ChildGUI::handle_child()
828 {
829         return 0;
830 }
831
832 #else
833 #include "mwindow.h"
834 #include "pluginserver.h"
835
836 PluginServer* MWindow::new_lv2_server(MWindow *mwindow, const char *name) { return 0; }
837 PluginClient *PluginServer::new_lv2_plugin() { return 0; }
838 int MWindow::init_lv2_index(MWindow *mwindow, Preferences *preferences, FILE *fp) { return 0; }
839
840 #endif /* HAVE_LV2 */