add lv2ui support
[goodguy/history.git] / cinelerra-5.1 / cinelerra / pluginlv2gui.C
1 #ifdef HAVE_LV2UI
2
3 #include "file.h"
4 #include "language.h"
5 #include "pluginlv2gui.h"
6
7 #include <ctype.h>
8 #include <string.h>
9
10
11 PluginLV2ChildGUI::PluginLV2ChildGUI()
12 {
13         lv2_gui = 0;
14 }
15
16 PluginLV2ChildGUI::~PluginLV2ChildGUI()
17 {
18         delete lv2_gui;
19 }
20
21 void PluginLV2ChildGUI::run()
22 {
23         ArrayList<char *> av;
24         av.set_array_delete();
25         char arg[BCTEXTLEN];
26         const char *exec_path = File::get_cinlib_path();
27         snprintf(arg, sizeof(arg), "%s/%s", exec_path, "lv2ui");
28         av.append(cstrdup(arg));
29         sprintf(arg, "%d", child_fd);
30         av.append(cstrdup(arg));
31         sprintf(arg, "%d", parent_fd);
32         av.append(cstrdup(arg));
33         av.append(0);
34         execv(av[0], &av.values[0]);
35         fprintf(stderr, "execv failed: %s\n %m\n", av.values[0]);
36         av.remove_all_objects();
37         _exit(1);
38 }
39
40
41 #define NS_EXT "http://lv2plug.in/ns/ext/"
42
43 PluginLV2GUI::PluginLV2GUI()
44 {
45         world = 0;
46         lilv = 0;
47         uri_map = 0;
48         ext_data = 0;
49         inst = 0;
50         sinst = 0;
51         ui_host = 0;
52         uis = 0;
53         ui = 0;
54         ui_type = 0;
55         lv2_InputPort = 0;
56         lv2_ControlPort = 0;
57
58         updates = 0;
59         last = 0;
60         done = 0;
61         running = 0;
62         redraw = 0;
63
64 // only gtk-2
65         gtk_type = "http://lv2plug.in/ns/extensions/ui#GtkUI";
66 }
67
68 PluginLV2GUI::~PluginLV2GUI ()
69 {
70         reset_gui();
71         if( world ) lilv_world_free(world);
72 }
73
74 void PluginLV2GUI::reset_gui()
75 {
76         if( inst ) lilv_instance_deactivate(inst);
77         if( uri_map )   { delete uri_map;             uri_map = 0; }
78         if( ext_data )  { delete ext_data;            ext_data = 0; }
79         if( inst )      { lilv_instance_free(inst);   inst = 0; }
80         if( sinst )     { suil_instance_free(sinst);  sinst = 0; }
81         if( ui_host )   { suil_host_free(ui_host);    ui_host = 0; }
82         if( uis )       { lilv_uis_free(uis);         uis = 0; }
83         if( lv2_InputPort )  { lilv_node_free(lv2_InputPort);   lv2_InputPort = 0; }
84         if( lv2_ControlPort ) { lilv_node_free(lv2_ControlPort);  lv2_ControlPort = 0; }
85         ui_features.remove_all_objects();
86         uri_table.remove_all_objects();
87         config.reset();
88         config.remove_all_objects();
89 }
90
91 int PluginLV2GUI::init_gui(const char *path)
92 {
93         reset_gui();
94         if( !world )
95                 world = lilv_world_new();
96         if( !world ) {
97                 printf("lv2_gui: lilv_world_new failed");
98                 return 1;
99         }
100         lilv_world_load_all(world);
101         LilvNode *uri = lilv_new_uri(world, path);
102         if( !uri ) {
103                 printf("lv2_gui: lilv_new_uri(%s) failed", path);
104                 return 1;
105         }
106
107         const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
108         lilv = lilv_plugins_get_by_uri(all_plugins, uri);
109         lilv_node_free(uri);
110         if( !lilv ) {
111                 printf("lv2_gui: lilv_plugins_get_by_uriPlugin(%s) failed", path);
112                 return 1;
113         }
114
115         LilvNode *name = lilv_plugin_get_name(lilv);
116         const char *nm = lilv_node_as_string(name);
117         snprintf(title,sizeof(title),"L2_%s",nm);
118         lilv_node_free(name);
119
120         config.init_lv2(lilv);
121
122         lv2_InputPort  = lilv_new_uri(world, LV2_CORE__InputPort);
123         lv2_ControlPort = lilv_new_uri(world, LV2_CORE__ControlPort);
124
125         for( int i=0; i<config.nb_ports; ++i ) {
126                 const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
127                 if( lilv_port_is_a(lilv, lp, lv2_InputPort) &&
128                     lilv_port_is_a(lilv, lp, lv2_ControlPort) ) {
129                         config.append(new PluginLV2Client_Opt(&config, i));
130                 }
131         }
132
133         uri_map = new LV2_URI_Map_Feature();
134         uri_map->callback_data = (LV2_URI_Map_Callback_Data)this;
135         uri_map->uri_to_id = uri_to_id;
136         ui_features.append(new Lv2Feature(NS_EXT "uri-map", uri_map));
137         map.handle = (void*)&uri_table;
138         map.map = uri_table_map;
139         ui_features.append(new Lv2Feature(LV2_URID__map, &map));
140         unmap.handle = (void*)&uri_table;
141         unmap.unmap  = uri_table_unmap;
142         ui_features.append(new Lv2Feature(LV2_URID__unmap, &unmap));
143         ui_features.append(0);
144
145         int sample_rate = 64; // cant be too low
146         inst = lilv_plugin_instantiate(lilv, sample_rate, ui_features);
147         if( !inst ) {
148                 printf("lv2_gui: lilv_plugin_instantiate failed: %s\n", title);
149                 return 1;
150         }
151
152         uis = lilv_plugin_get_uis(lilv);
153         if( gtk_type ) {
154                 LilvNode *gui_type = lilv_new_uri(world, gtk_type);
155                 LILV_FOREACH(uis, i, uis) {
156                         const LilvUI *gui = lilv_uis_get(uis, i);
157                         if( lilv_ui_is_supported(gui, suil_ui_supported, gui_type, &ui_type)) {
158                                 ui = gui;
159                                 break;
160                         }
161                 }
162                 lilv_node_free(gui_type);
163         }
164         if( !ui )
165                 ui = lilv_uis_get(uis, lilv_uis_begin(uis));
166         if( !ui ) {
167                 printf("lv2_gui: init_ui failed: %s\n", title);
168                 return 1;
169         }
170
171         lilv_instance_activate(inst);
172         return 0;
173 }
174
175 void PluginLV2GUI::update_value(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
176 {
177         if( idx >= config.nb_ports ) return;
178         for( int i=0, sz=config.size(); i<sz; ++i ) {
179                 PluginLV2Client_Opt *opt = config[i];
180                 if( opt->idx == idx ) {
181                         opt->set_value(*(const float*)bfr);
182 //printf("set %s = %f\n", opt->get_symbol(), opt->get_value());
183                         ++updates;
184                         break;
185                 }
186         }
187 }
188 void PluginLV2GUI::write_from_ui(void *the, uint32_t idx,
189                 uint32_t bfrsz, uint32_t typ, const void *bfr)
190 {
191         ((PluginLV2GUI*)the)->update_value(idx, bfrsz, typ, bfr);
192 }
193
194
195 uint32_t PluginLV2GUI::port_index(void* obj, const char* sym)
196 {
197         PluginLV2GUI *the = (PluginLV2GUI*)obj;
198         for( int i=0, sz=the->config.size(); i<sz; ++i ) {
199                 PluginLV2Client_Opt *opt = the->config[i];
200                 if( !strcmp(sym, opt->sym) ) return opt->idx;
201         }
202         return UINT32_MAX;
203 }
204
205 void PluginLV2GUI::update_control(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
206 {
207         if( !sinst || idx >= config.nb_ports ) return;
208         suil_instance_port_event(sinst, idx, bfrsz, typ, bfr);
209 }
210
211
212 #if 0
213 void PluginLV2GUI::touch(void *obj, uint32_t pidx, bool grabbed)
214 {
215         PluginLV2GUI* the = (PluginLV2GUI*)obj;
216         int idx = pidx;
217         if( idx >= the->config.nb_ports ) return;
218 printf("%s %s(%u)\n", (grabbed? _("press") : _("release")),
219   the->config.names[idx], idx);
220 }
221 #endif
222
223 uint32_t PluginLV2GUI::uri_to_id(LV2_URID_Map_Handle handle, const char *map, const char *uri)
224 {
225         return ((PluginLV2UriTable *)handle)->map(uri);
226 }
227
228 LV2_URID PluginLV2GUI::uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
229 {
230         return ((PluginLV2UriTable *)handle)->map(uri);
231 }
232
233 const char *PluginLV2GUI::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
234 {
235         return ((PluginLV2UriTable *)handle)->unmap(urid);
236 }
237
238 void PluginLV2GUI::lv2ui_instantiate(void *parent)
239 {
240         if ( !ui_host ) {
241                 ui_host = suil_host_new(
242                         PluginLV2GUI::write_from_ui,
243                         PluginLV2GUI::port_index,
244                         0, 0);
245 //              suil_host_set_touch_func(ui_host,
246 //                      PluginLV2GUI::touch);
247         }
248
249         ui_features.remove();
250         LV2_Handle lilv_handle = lilv_instance_get_handle(inst);
251         ui_features.append(new Lv2Feature(NS_EXT "instance-access", lilv_handle));
252         const LV2_Descriptor *lilv_desc = lilv_instance_get_descriptor(inst);
253         ext_data = new LV2_Extension_Data_Feature();
254         ext_data->data_access = lilv_desc->extension_data;
255         ui_features.append(new Lv2Feature(LV2_DATA_ACCESS_URI, ext_data));
256         ui_features.append(new Lv2Feature(LV2_UI__parent, parent));
257         ui_features.append(new Lv2Feature(LV2_UI__idleInterface, 0));
258         ui_features.append(0);
259
260         const char* bundle_uri  = lilv_node_as_uri(lilv_ui_get_bundle_uri(ui));
261         char*       bundle_path = lilv_file_uri_parse(bundle_uri, NULL);
262         const char* binary_uri  = lilv_node_as_uri(lilv_ui_get_binary_uri(ui));
263         char*       binary_path = lilv_file_uri_parse(binary_uri, NULL);
264         sinst = suil_instance_new(ui_host, this, gtk_type,
265                 lilv_node_as_uri(lilv_plugin_get_uri(lilv)),
266                 lilv_node_as_uri(lilv_ui_get_uri(ui)),
267                 lilv_node_as_uri(ui_type),
268                 bundle_path, binary_path, ui_features);
269
270         lilv_free(binary_path);
271         lilv_free(bundle_path);
272 }
273
274 bool PluginLV2GUI::lv2ui_resizable()
275 {
276         if( !ui ) return false;
277         const LilvNode* s   = lilv_ui_get_uri(ui);
278         LilvNode *p   = lilv_new_uri(world, LV2_CORE__optionalFeature);
279         LilvNode *fs  = lilv_new_uri(world, LV2_UI__fixedSize);
280         LilvNode *nrs = lilv_new_uri(world, LV2_UI__noUserResize);
281         LilvNodes *fs_matches = lilv_world_find_nodes(world, s, p, fs);
282         LilvNodes *nrs_matches = lilv_world_find_nodes(world, s, p, nrs);
283         lilv_nodes_free(nrs_matches);
284         lilv_nodes_free(fs_matches);
285         lilv_node_free(nrs);
286         lilv_node_free(fs);
287         lilv_node_free(p);
288         return !fs_matches && !nrs_matches;
289 }
290
291 int PluginLV2GUI::update_lv2(float *vals, int force)
292 {
293         int ret = 0;
294         float *ctls = (float *)config.ctls;
295         for( int i=0; i<config.size(); ++i ) {
296                 int idx = config[i]->idx;
297                 float val = vals[idx];
298                 if( !force && ctls[idx] == val ) continue;
299                 update_control(idx, sizeof(val), 0, &val);
300                 ++ret;
301         }
302         for( int i=0; i<config.nb_ports; ++i ) ctls[i] = vals[i];
303         return ret;
304 }
305
306 #endif /* HAVE_LV2UI */