connect lv2 output controls
[goodguy/history.git] / cinelerra-5.1 / cinelerra / pluginlv2ui.C
1 #ifdef HAVE_LV2UI
2
3 // shared between parent/child fork
4 #include "language.h"
5 #include "pluginlv2ui.h"
6
7 #include <gtk/gtk.h>
8 #include <gdk/gdk.h>
9
10 #include <ctype.h>
11 #include <string.h>
12
13 int PluginLV2UI::init_ui(const char *path, int sample_rate)
14 {
15         if( load_lv2(path, title) ) return 1;
16         if( init_lv2(config, sample_rate) ) return 1;
17
18         lilv_uis = lilv_plugin_get_uis(lilv);
19         if( !lilv_uis ) {
20                 printf("lv2: lilv_plugin_get_uis(%s) failed\n", path);
21                 return 1;
22         }
23
24         if( gtk_type ) {
25                 LilvNode *gui_type = lilv_new_uri(world, gtk_type);
26                 LILV_FOREACH(uis, i, lilv_uis) {
27                         const LilvUI *ui = lilv_uis_get(lilv_uis, i);
28                         if( lilv_ui_is_supported(ui, suil_ui_supported, gui_type, &lilv_type)) {
29                                 lilv_ui = ui;
30                                 break;
31                         }
32                 }
33                 lilv_node_free(gui_type);
34         }
35         if( !lilv_ui )
36                 lilv_ui = lilv_uis_get(lilv_uis, lilv_uis_begin(lilv_uis));
37         if( !lilv_ui ) {
38                 printf("lv2_gui: init_ui failed: %s\n", title);
39                 return 1;
40         }
41
42         lilv_instance_activate(inst);
43         return 0;
44 }
45
46 void PluginLV2UI::update_value(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
47 {
48         if( idx >= config.nb_ports ) return;
49         for( int i=0, sz=config.size(); i<sz; ++i ) {
50                 PluginLV2Client_Opt *opt = config[i];
51                 if( opt->idx == idx ) {
52                         opt->set_value(*(const float*)bfr);
53                         updates = UPDATE_HOST;
54                         break;
55                 }
56         }
57 }
58 void PluginLV2UI::write_from_ui(void *the, uint32_t idx,
59                 uint32_t bfrsz, uint32_t typ, const void *bfr)
60 {
61         ((PluginLV2UI*)the)->update_value(idx, bfrsz, typ, bfr);
62 }
63
64 uint32_t PluginLV2UI::port_index(void* obj, const char* sym)
65 {
66         PluginLV2UI *the = (PluginLV2UI*)obj;
67         for( int i=0, sz=the->config.size(); i<sz; ++i ) {
68                 PluginLV2Client_Opt *opt = the->config[i];
69                 if( !strcmp(sym, opt->sym) ) return opt->idx;
70         }
71         return UINT32_MAX;
72 }
73
74 void PluginLV2UI::update_control(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
75 {
76         if( !sinst || idx >= config.nb_ports ) return;
77         suil_instance_port_event(sinst, idx, bfrsz, typ, bfr);
78 }
79
80
81 #if 0
82 void PluginLV2UI::touch(void *obj, uint32_t pidx, bool grabbed)
83 {
84         PluginLV2UI* the = (PluginLV2GUI*)obj;
85         int idx = pidx;
86         if( idx >= the->config.nb_ports ) return;
87 printf("%s %s(%u)\n", (grabbed? _("press") : _("release")),
88   the->config.names[idx], idx);
89 }
90 #endif
91
92 uint32_t PluginLV2UI::uri_to_id(LV2_URID_Map_Handle handle, const char *map, const char *uri)
93 {
94         return ((PluginLV2UriTable *)handle)->map(uri);
95 }
96
97 LV2_URID PluginLV2UI::uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
98 {
99         return ((PluginLV2UriTable *)handle)->map(uri);
100 }
101
102 const char *PluginLV2UI::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
103 {
104         return ((PluginLV2UriTable *)handle)->unmap(urid);
105 }
106
107 void PluginLV2UI::lv2ui_instantiate(void *parent)
108 {
109         if ( !ui_host ) {
110                 ui_host = suil_host_new(
111                         PluginLV2UI::write_from_ui,
112                         PluginLV2UI::port_index,
113                         0, 0);
114 //              suil_host_set_touch_func(ui_host,
115 //                      PluginLV2GUI::touch);
116         }
117
118         features.remove();  // remove terminating zero
119         ui_features = features.size();
120         LV2_Handle lilv_handle = lilv_instance_get_handle(inst);
121         features.append(new Lv2Feature(NS_EXT "instance-access", lilv_handle));
122         const LV2_Descriptor *lilv_desc = lilv_instance_get_descriptor(inst);
123         ext_data = new LV2_Extension_Data_Feature();
124         ext_data->data_access = lilv_desc->extension_data;
125         features.append(new Lv2Feature(LV2_DATA_ACCESS_URI, ext_data));
126         features.append(new Lv2Feature(LV2_UI__parent, parent));
127         features.append(new Lv2Feature(LV2_UI__idleInterface, 0));
128         features.append(0); // add new terminating zero
129
130         const char* bundle_uri  = lilv_node_as_uri(lilv_ui_get_bundle_uri(lilv_ui));
131         char*       bundle_path = lilv_file_uri_parse(bundle_uri, NULL);
132         const char* binary_uri  = lilv_node_as_uri(lilv_ui_get_binary_uri(lilv_ui));
133         char*       binary_path = lilv_file_uri_parse(binary_uri, NULL);
134         sinst = suil_instance_new(ui_host, this, gtk_type,
135                 lilv_node_as_uri(lilv_plugin_get_uri(lilv)),
136                 lilv_node_as_uri(lilv_ui_get_uri(lilv_ui)),
137                 lilv_node_as_uri(lilv_type),
138                 bundle_path, binary_path, features);
139
140         lilv_free(binary_path);
141         lilv_free(bundle_path);
142 }
143
144 bool PluginLV2UI::lv2ui_resizable()
145 {
146         if( !lilv_ui ) return false;
147         const LilvNode* s   = lilv_ui_get_uri(lilv_ui);
148         LilvNode *p   = lilv_new_uri(world, LV2_CORE__optionalFeature);
149         LilvNode *fs  = lilv_new_uri(world, LV2_UI__fixedSize);
150         LilvNode *nrs = lilv_new_uri(world, LV2_UI__noUserResize);
151         LilvNodes *fs_matches = lilv_world_find_nodes(world, s, p, fs);
152         LilvNodes *nrs_matches = lilv_world_find_nodes(world, s, p, nrs);
153         lilv_nodes_free(nrs_matches);
154         lilv_nodes_free(fs_matches);
155         lilv_node_free(nrs);
156         lilv_node_free(fs);
157         lilv_node_free(p);
158         return !fs_matches && !nrs_matches;
159 }
160
161 int PluginLV2UI::send_host(int64_t token, const void *data, int bytes)
162 {
163         return !child ? 0 : child->send_parent(token, data, bytes);
164 }
165
166 int PluginLV2UI::update_lv2_input(float *vals, int force)
167 {
168         int ret = 0;
169         float *ctls = config.ctls;
170         for( int i=0; i<config.size(); ++i ) {
171                 int idx = config[i]->idx;
172                 float val = vals[idx];
173                 if( !force && ctls[idx] == val ) continue;
174                 ctls[idx] = val;
175                 update_control(idx, sizeof(ctls[idx]), 0, &ctls[idx]);
176                 ++ret;
177         }
178         return ret;
179 }
180
181 void PluginLV2UI::update_lv2_output()
182 {
183         int *ports = config.ports;
184         float *ctls = config.ctls;
185         for( int i=0; i<config.nb_ports; ++i ) {
186                 if( !(ports[i] & PORTS_UPDATE) ) continue;
187                 ports[i] &= ~PORTS_UPDATE;
188                 update_control(i, sizeof(ctls[i]), 0, &ctls[i]);
189         }
190 }
191
192 static void lilv_destroy(GtkWidget* widget, gpointer data)
193 {
194         PluginLV2UI *the = (PluginLV2UI*)data;
195         the->top_level = 0;
196 }
197
198 void PluginLV2UI::start_gui()
199 {
200         if( !hidden ) return;
201         top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
202         g_signal_connect(top_level, "destroy", G_CALLBACK(lilv_destroy), this);
203         gtk_window_set_title(GTK_WINDOW(top_level), title);
204
205         GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
206         gtk_window_set_role(GTK_WINDOW(top_level), "plugin_ui");
207         gtk_container_add(GTK_CONTAINER(top_level), vbox);
208
209         GtkWidget *alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
210         gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
211         gtk_widget_show(alignment);
212         lv2ui_instantiate(alignment);
213         GtkWidget* widget = (GtkWidget*)suil_instance_get_widget(sinst);
214         gtk_container_add(GTK_CONTAINER(alignment), widget);
215         gtk_window_set_resizable(GTK_WINDOW(top_level), lv2ui_resizable());
216         gtk_widget_show_all(vbox);
217         gtk_widget_grab_focus(widget);
218         update_lv2_input(config.ctls, 1);
219         connect_ports(config, PORTS_CONTROL);
220         updates = 0;
221         run_lilv(0);
222         gtk_window_present(GTK_WINDOW(top_level));
223         send_host(LV2_SHOW, 0, 0);
224         hidden = 0;
225 }
226
227 void PluginLV2UI::update_host()
228 {
229 // ignore update
230         if( running < 0 ) { running = 1;  return; }
231         send_host(LV2_UPDATE, config.ctls, sizeof(float)*config.nb_ports);
232 }
233
234 void PluginLV2UI::reset_gui()
235 {
236         if( hidden ) return;
237         if( sinst )     { suil_instance_free(sinst);  sinst = 0; }
238         if( ui_host )   { suil_host_free(ui_host);    ui_host = 0; }
239         if( top_level ) { gtk_widget_destroy(top_level); top_level = 0; }
240
241         while( features.size() > ui_features ) features.remove_object();
242         features.append(0);
243         hidden = 1;
244         send_host(LV2_HIDE, 0, 0);
245 }
246
247
248 // child main
249 int PluginLV2UI::run_ui(PluginLV2ChildUI *child)
250 {
251         this->child = child;
252         running = 1;
253         done = 0;
254         while( !done ) {
255                 if( gtk_events_pending() ) {
256                         gtk_main_iteration();
257                         continue;
258                 }
259                 if( !top_level && !hidden )
260                         reset_gui();
261                 if( updates ) {
262                         if( (updates & UPDATE_PORTS) )
263                                 update_lv2_output();
264                         if( (updates & UPDATE_HOST) )
265                                 update_host();
266                         updates = 0;
267                 }
268                 if( !child ) usleep(10000);
269                 else if( child->child_iteration() < 0 )
270                         done = 1;
271         }
272         running = 0;
273         return 0;
274 }
275
276 void PluginLV2UI::run_lilv(int samples)
277 {
278         float ctls[config.nb_ports];
279         for( int i=0; i<config.nb_ports; ++i ) ctls[i] = config.ctls[i];
280
281         lilv_instance_run(inst, samples);
282
283         for( int i=0; i<config.nb_ports; ++i ) {
284                 if( !(config.ports[i] & PORTS_OUTPUT) ) continue;
285                 if( !(config.ports[i] & PORTS_CONTROL) ) continue;
286                 if( config.ctls[i] == ctls[i] ) continue;
287                 config.ports[i] |= PORTS_UPDATE;
288                 updates |= UPDATE_PORTS;
289         }
290 }
291
292 void PluginLV2ChildUI::run_buffer(int shmid)
293 {
294         if( !shm_buffer(shmid) ) return;
295         map_buffer();
296         connect_ports(config, PORTS_ALL);
297         run_lilv(shm_bfr->samples);
298         shm_bfr->done = 1;
299 }
300
301 int PluginLV2ChildUI::handle_child()
302 {
303         switch( child_token ) {
304         case LV2_OPEN: {
305                 open_bfr_t *open_bfr = (open_bfr_t *)child_data;
306                 if( init_ui(open_bfr->path, open_bfr->sample_rate) ) exit(1);
307                 break; }
308         case LV2_LOAD: {
309                 float *vals = (float *)child_data;
310                 update_lv2_input(vals, 1);
311                 break; }
312         case LV2_UPDATE: {
313                 float *vals = (float *)child_data;
314                 update_lv2_input(vals, 0);
315                 break; }
316         case LV2_SHOW: {
317                 start_gui();
318                 break; }
319         case LV2_HIDE: {
320                 reset_gui();
321                 break; }
322         case LV2_SHMID: {
323                 int shmid = *(int *)child_data;
324                 run_buffer(shmid);
325                 send_parent(LV2_SHMID, 0, 0);
326                 break; }
327         case EXIT_CODE:
328                 return -1;
329         }
330         return 1;
331 }
332
333 #endif /* HAVE_LV2UI */