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