Credit Andrew - fix vorbis audio which was scratchy and ensure aging plugin does...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / pluginlv2ui.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2016-2020 William Morrow
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published
7  * by the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20
21 // shared between parent/child fork
22 #include "language.h"
23 #include "pluginlv2ui.h"
24
25 #include <gtk/gtk.h>
26 #include <gdk/gdk.h>
27
28 #include <ctype.h>
29 #include <string.h>
30
31 void PluginLV2UI::update_value(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
32 {
33         if( idx >= config.nb_ports ) return;
34         for( int i=0, sz=config.size(); i<sz; ++i ) {
35                 PluginLV2Client_Opt *opt = config[i];
36                 if( opt->idx == idx ) {
37                         opt->set_value(*(const float*)bfr);
38                         updates |= UPDATE_HOST;
39                         break;
40                 }
41         }
42 }
43 void PluginLV2UI::write_from_ui(void *the, uint32_t idx,
44                 uint32_t bfrsz, uint32_t typ, const void *bfr)
45 {
46         ((PluginLV2UI*)the)->update_value(idx, bfrsz, typ, bfr);
47 }
48
49 uint32_t PluginLV2UI::port_index(void* obj, const char* sym)
50 {
51         PluginLV2UI *the = (PluginLV2UI*)obj;
52         for( int i=0, sz=the->config.size(); i<sz; ++i ) {
53                 PluginLV2Client_Opt *opt = the->config[i];
54                 if( !strcmp(sym, opt->sym) ) return opt->idx;
55         }
56         return UINT32_MAX;
57 }
58
59 void PluginLV2UI::update_control(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
60 {
61         if( !sinst || idx >= config.nb_ports ) return;
62         suil_instance_port_event(sinst, idx, bfrsz, typ, bfr);
63 }
64
65
66 uint32_t PluginLV2UI::uri_to_id(LV2_URID_Map_Handle handle, const char *map, const char *uri)
67 {
68         return ((PluginLV2UriTable *)handle)->map(uri);
69 }
70
71 LV2_URID PluginLV2UI::uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
72 {
73         return ((PluginLV2UriTable *)handle)->map(uri);
74 }
75
76 const char *PluginLV2UI::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
77 {
78         return ((PluginLV2UriTable *)handle)->unmap(urid);
79 }
80
81 void PluginLV2UI::lv2ui_instantiate(void *parent)
82 {
83         if ( !ui_host ) {
84                 ui_host = suil_host_new(
85                         PluginLV2UI::write_from_ui,
86                         PluginLV2UI::port_index,
87                         0, 0);
88 //              suil_host_set_touch_func(ui_host, cb_touch);
89         }
90
91         features.remove();  // remove terminating zero
92         ui_features = features.size();
93         LV2_Handle lilv_handle = lilv_instance_get_handle(inst);
94         features.append(new Lv2Feature(NS_EXT "instance-access", lilv_handle));
95         const LV2_Descriptor *lilv_desc = lilv_instance_get_descriptor(inst);
96         ext_data = new LV2_Extension_Data_Feature();
97         ext_data->data_access = lilv_desc->extension_data;
98         features.append(new Lv2Feature(LV2_DATA_ACCESS_URI, ext_data));
99         features.append(new Lv2Feature(LV2_UI__parent, parent));
100         features.append(new Lv2Feature(LV2_UI__idleInterface, 0));
101         features.append(new Lv2Feature(LV2_EXTERNAL_UI_URI, &extui_host));
102         features.append(new Lv2Feature(LV2_EXTERNAL_UI_URI__KX__Host, &extui_host));
103         features.append(0); // add new terminating zero
104
105         const char* bundle_uri  = lilv_node_as_uri(lilv_ui_get_bundle_uri(lilv_ui));
106         char*       bundle_path = lilv_file_uri_parse(bundle_uri, NULL);
107         const char* binary_uri  = lilv_node_as_uri(lilv_ui_get_binary_uri(lilv_ui));
108         char*       binary_path = lilv_file_uri_parse(binary_uri, NULL);
109         sinst = suil_instance_new(ui_host, this, ui_type,
110                 lilv_node_as_uri(lilv_plugin_get_uri(lilv)),
111                 lilv_node_as_uri(lilv_ui_get_uri(lilv_ui)),
112                 lilv_node_as_uri(lilv_type),
113                 bundle_path, binary_path, features);
114
115         lilv_free(binary_path);
116         lilv_free(bundle_path);
117 }
118
119 bool PluginLV2UI::lv2ui_resizable()
120 {
121         if( !lilv_ui ) return false;
122         const LilvNode* s   = lilv_ui_get_uri(lilv_ui);
123         LilvNode *p   = lilv_new_uri(world, LV2_CORE__optionalFeature);
124         LilvNode *fs  = lilv_new_uri(world, LV2_UI__fixedSize);
125         LilvNode *nrs = lilv_new_uri(world, LV2_UI__noUserResize);
126         LilvNodes *fs_matches = lilv_world_find_nodes(world, s, p, fs);
127         LilvNodes *nrs_matches = lilv_world_find_nodes(world, s, p, nrs);
128         lilv_nodes_free(nrs_matches);
129         lilv_nodes_free(fs_matches);
130         lilv_node_free(nrs);
131         lilv_node_free(fs);
132         lilv_node_free(p);
133         return !fs_matches && !nrs_matches;
134 }
135
136 //force= 1:ctls(all),gui(all)  0:changed(ctls)  -1:gui(all)
137 int PluginLV2UI::update_lv2_input(float *vals, int force)
138 {
139         int ret = 0;
140         float *ctls = config.ctls;
141         for( int i=0; i<config.size(); ++i ) {
142                 int idx = config[i]->idx;
143                 float val = vals[idx];
144                 if( !force && ctls[idx] == val ) continue;
145                 if( force >= 0 ) {
146                         ctls[idx] = val;
147                         ++ret;
148                 }
149                 if( force )
150                         update_control(idx, sizeof(val), 0, &val);
151         }
152         return ret;
153 }
154
155 void PluginLV2UI::update_lv2_output()
156 {
157         int *ports = config.ports;
158         float *ctls = config.ctls;
159         for( int i=0; i<config.nb_ports; ++i ) {
160                 if( !(ports[i] & PORTS_UPDATE) ) continue;
161                 ports[i] &= ~PORTS_UPDATE;
162                 update_control(i, sizeof(ctls[i]), 0, &ctls[i]);
163         }
164 }
165
166 void PluginLV2UI::run_lilv(int samples)
167 {
168         float ctls[config.nb_ports];
169         for( int i=0; i<config.nb_ports; ++i ) ctls[i] = config.ctls[i];
170
171         lilv_instance_run(inst, samples);
172
173         if( worker_iface ) worker_responses();
174
175         for( int i=0; i<config.nb_ports; ++i ) {
176                 if( !(config.ports[i] & PORTS_OUTPUT) ) continue;
177                 if( !(config.ports[i] & PORTS_CONTROL) ) continue;
178                 if( config.ctls[i] == ctls[i] ) continue;
179                 config.ports[i] |= PORTS_UPDATE;
180                 updates |= UPDATE_PORTS;
181         }
182 }
183
184 void PluginLV2ChildUI::start_gui()
185 {
186         if( gui ) gui->start_gui();
187         update_lv2_input(config.ctls, 1);
188         connect_ports(config, PORTS_CONTROL | PORTS_ATOM);
189         int n = 0;
190
191         updates = 0;
192         run_lilv(n);
193         if( gui ) {
194                 send_host(LV2_SHOW, 0, 0);
195                 hidden = 0;
196         }
197 }
198
199 void PluginLV2UI::update_host()
200 {
201 // ignore update
202         if( running < 0 ) { running = 1;  return; }
203         send_host(LV2_UPDATE, config.ctls, sizeof(float)*config.nb_ports);
204 }
205
206
207 static void lv2ui_extui_host_ui_closed(void *p)
208 {
209         PluginLV2UI *ui = (PluginLV2UI *)p;
210         ui->hidden = -1;
211 }
212
213 int PluginLV2ChildUI::init_ui(const char *path, int sample_rate, int bfrsz)
214 {
215         char *title = PluginLV2UI::title;
216         if( load_lv2(path, title) ) return 1;
217         if( init_lv2(config, sample_rate, bfrsz) ) return 1;
218
219         lilv_uis = lilv_plugin_get_uis(lilv);
220         if( !gui && lilv_uis && wgt_type ) {
221                 LilvNode *gui_type = lilv_new_uri(world, wgt_type);
222                 LILV_FOREACH(uis, i, lilv_uis) {
223                         const LilvUI *ui = lilv_uis_get(lilv_uis, i);
224                         if( lilv_ui_is_supported(ui, suil_ui_supported, gui_type, &lilv_type)) {
225                                 extui_host.ui_closed = lv2ui_extui_host_ui_closed;
226                                 gui = new PluginLV2ChildWgtUI(this);
227                                 lilv_ui = ui;
228                                 ui_type = wgt_type;
229                                 break;
230                         }
231                 }
232                 lilv_node_free(gui_type);
233         }
234         if( !gui && lilv_uis && gtk_type ) {
235                 LilvNode *gui_type = lilv_new_uri(world, gtk_type);
236                 LILV_FOREACH(uis, i, lilv_uis) {
237                         const LilvUI *ui = lilv_uis_get(lilv_uis, i);
238                         if( lilv_ui_is_supported(ui, suil_ui_supported, gui_type, &lilv_type)) {
239                                 gui = new PluginLV2ChildGtkUI(this);
240                                 lilv_ui = ui;
241                                 ui_type = gtk_type;
242                                 break;
243                         }
244                 }
245                 lilv_node_free(gui_type);
246                 ui_type = gtk_type;
247         }
248
249         lilv_instance_activate(inst);
250         return 0;
251 }
252
253 void PluginLV2ChildUI::reset_gui()
254 {
255         if( gui ) gui->reset_gui();
256         if( sinst )     { suil_instance_free(sinst);  sinst = 0; }
257         if( ui_host )   { suil_host_free(ui_host);    ui_host = 0; }
258
259         while( features.size() > ui_features ) features.remove_object();
260         features.append(0);
261         send_host(LV2_HIDE, 0, 0);
262         hidden = 1;
263 }
264
265 // main loop
266 int PluginLV2ChildUI::run_ui()
267 {
268         double last_time = 0;
269         int64_t frame_usecs = 1e6 / refreshrate;
270         running = 1;
271         done = 0;
272         while( !done ) {
273                 if( hidden < 0 ) {
274                         if( gui ) gui->top_level = 0;
275                         reset_gui();
276                         if( !is_forked() ) {
277                                 done = -1;
278                                 break;
279                         }
280                         hidden = 1;
281                 }
282                 if( updates ) {
283                         if( (updates & UPDATE_PORTS) )
284                                 update_lv2_output();
285                         if( (updates & UPDATE_HOST) )
286                                 update_host();
287                         updates = 0;
288                 }
289                 struct timeval tv;  gettimeofday(&tv, 0);
290                 double t = tv.tv_sec + tv.tv_usec / 1e6;
291                 double dt = t - last_time;  last_time = t;
292                 int64_t usec = frame_usecs - dt*1e6;
293                 if( usec < 0 ) usec = 0;
294                 if( child_iteration(usec) < 0 )
295                         done = 1;
296         }
297         running = 0;
298         return 0;
299 }
300
301 void PluginLV2ChildUI::run_buffer(int shmid)
302 {
303         if( !shm_buffer(shmid) ) return;
304         map_buffer();
305         connect_ports(config, PORTS_ALL);
306         run_lilv(shm_bfr->samples);
307         shm_bfr->done = 1;
308 }
309
310 int PluginLV2ChildUI::child_iteration(int64_t usec)
311 {
312         int ret = 0;
313         if( is_forked() )
314                 ret = read_child(usec);
315         else if( gui )
316                 usleep(usec);
317         else
318                 ret = -1;
319
320         if( ret > 0 ) {
321                 switch( child_token ) {
322                 case LV2_OPEN: {
323                         open_bfr_t *open_bfr = (open_bfr_t *)child_data;
324                         if( init_ui(open_bfr->path, open_bfr->sample_rate, open_bfr->bfrsz) ) {
325                                 printf("lv2ui: unable to init: %s\n", open_bfr->path);
326                                 exit(1);
327                         }
328                         break; }
329                 case LV2_LOAD: {
330                         float *vals = (float *)child_data;
331                         update_lv2_input(vals, 1);
332                         break; }
333                 case LV2_CONFIG: {
334                         float *vals = (float *)child_data;
335                         update_lv2_input(vals, 0);
336                         break; }
337                 case LV2_UPDATE: {
338                         float *vals = (float *)child_data;
339                         update_lv2_input(vals, -1);
340                         break; }
341                 case LV2_SHOW: {
342                         start_gui();
343                         break; }
344                 case LV2_HIDE: {
345                         reset_gui();
346                         break; }
347                 case LV2_SHMID: {
348                         int shmid = *(int *)child_data;
349                         run_buffer(shmid);
350                         send_parent(LV2_SHMID, 0, 0);
351                         break; }
352                 case EXIT_CODE:
353                         return -1;
354                 }
355         }
356         if( ret >= 0 && gui )
357                 gui->handle_child();
358         return ret;
359 }
360
361 int PluginLV2ChildUI::send_host(int64_t token, const void *data, int bytes)
362 {
363         return is_forked() ? send_parent(token, data, bytes) : 0;
364 }
365
366 PluginLV2GUI::PluginLV2GUI(PluginLV2ChildUI *child_ui)
367 {
368         this->child_ui = child_ui;
369         top_level = 0;
370 }
371
372 PluginLV2GUI::~PluginLV2GUI()
373 {
374 }
375
376 PluginLV2ChildGtkUI::PluginLV2ChildGtkUI(PluginLV2ChildUI *child_ui)
377  : PluginLV2GUI(child_ui)
378 {
379         gtk_set_locale();
380         gtk_init(&child_ui->ac, &child_ui->av);
381 }
382
383 PluginLV2ChildGtkUI::~PluginLV2ChildGtkUI()
384 {
385 }
386
387 void PluginLV2ChildGtkUI::reset_gui()
388 {
389         GtkWidget *top_level = (GtkWidget *)this->top_level;
390         if( top_level ) { gtk_widget_destroy(top_level); this->top_level = 0; }
391 }
392
393 static void lilv_gtk_destroy(GtkWidget* widget, gpointer data)
394 {
395         PluginLV2ChildGtkUI *gui = (PluginLV2ChildGtkUI*)data;
396         gui->child_ui->hidden = -1;
397 }
398
399 void PluginLV2ChildGtkUI::start_gui()
400 {
401         this->top_level = (void *)gtk_window_new(GTK_WINDOW_TOPLEVEL);
402         GtkWidget *top_level = (GtkWidget *)this->top_level;
403         g_signal_connect(top_level, "destroy", G_CALLBACK(lilv_gtk_destroy), this);
404         char *title = child_ui->PluginLV2UI::title;
405         gtk_window_set_title(GTK_WINDOW(top_level), title);
406
407         GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
408         gtk_window_set_role(GTK_WINDOW(top_level), "plugin_ui");
409         gtk_container_add(GTK_CONTAINER(top_level), vbox);
410
411         GtkWidget *alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
412         gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
413         gtk_widget_show(alignment);
414         child_ui->lv2ui_instantiate(alignment);
415         GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(child_ui->sinst);
416         gtk_container_add(GTK_CONTAINER(alignment), widget);
417         gtk_window_set_resizable(GTK_WINDOW(top_level), child_ui->lv2ui_resizable());
418         gtk_widget_show_all(vbox);
419         gtk_widget_grab_focus(widget);
420         gtk_window_present(GTK_WINDOW(top_level));
421 }
422
423 int PluginLV2ChildGtkUI::handle_child()
424 {
425         if( gtk_events_pending() ) {
426                 gtk_main_iteration();
427         }
428         return 1;
429 }
430
431 PluginLV2ChildWgtUI::PluginLV2ChildWgtUI(PluginLV2ChildUI *child_ui)
432  : PluginLV2GUI(child_ui)
433 {
434 }
435
436 PluginLV2ChildWgtUI::~PluginLV2ChildWgtUI()
437 {
438 }
439
440 void PluginLV2ChildWgtUI::reset_gui()
441 {
442         lv2_external_ui *top_level = (lv2_external_ui *)this->top_level;
443         if( top_level ) { LV2_EXTERNAL_UI_HIDE(top_level); this->top_level = 0; }
444 }
445
446 void PluginLV2ChildWgtUI::start_gui()
447 {
448         child_ui->lv2ui_instantiate(0);
449         this->top_level = (void *)suil_instance_get_widget(child_ui->sinst);
450         lv2_external_ui *top_level = (lv2_external_ui *)this->top_level;
451         if( top_level ) LV2_EXTERNAL_UI_SHOW(top_level);
452 }
453
454 int PluginLV2ChildWgtUI::handle_child()
455 {
456         lv2_external_ui *top_level = (lv2_external_ui *)this->top_level;
457         if( top_level )
458                 LV2_EXTERNAL_UI_RUN(top_level);
459         return 1;
460 }
461