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