add lv2ui support
[goodguy/history.git] / cinelerra-5.1 / cinelerra / pluginlv2gui.C
diff --git a/cinelerra-5.1/cinelerra/pluginlv2gui.C b/cinelerra-5.1/cinelerra/pluginlv2gui.C
new file mode 100644 (file)
index 0000000..baa03f1
--- /dev/null
@@ -0,0 +1,306 @@
+#ifdef HAVE_LV2UI
+
+#include "file.h"
+#include "language.h"
+#include "pluginlv2gui.h"
+
+#include <ctype.h>
+#include <string.h>
+
+
+PluginLV2ChildGUI::PluginLV2ChildGUI()
+{
+       lv2_gui = 0;
+}
+
+PluginLV2ChildGUI::~PluginLV2ChildGUI()
+{
+       delete lv2_gui;
+}
+
+void PluginLV2ChildGUI::run()
+{
+       ArrayList<char *> av;
+       av.set_array_delete();
+       char arg[BCTEXTLEN];
+       const char *exec_path = File::get_cinlib_path();
+       snprintf(arg, sizeof(arg), "%s/%s", exec_path, "lv2ui");
+       av.append(cstrdup(arg));
+       sprintf(arg, "%d", child_fd);
+       av.append(cstrdup(arg));
+       sprintf(arg, "%d", parent_fd);
+       av.append(cstrdup(arg));
+       av.append(0);
+       execv(av[0], &av.values[0]);
+       fprintf(stderr, "execv failed: %s\n %m\n", av.values[0]);
+       av.remove_all_objects();
+       _exit(1);
+}
+
+
+#define NS_EXT "http://lv2plug.in/ns/ext/"
+
+PluginLV2GUI::PluginLV2GUI()
+{
+       world = 0;
+       lilv = 0;
+       uri_map = 0;
+       ext_data = 0;
+       inst = 0;
+       sinst = 0;
+       ui_host = 0;
+       uis = 0;
+       ui = 0;
+       ui_type = 0;
+       lv2_InputPort = 0;
+       lv2_ControlPort = 0;
+
+       updates = 0;
+       last = 0;
+       done = 0;
+       running = 0;
+       redraw = 0;
+
+// only gtk-2
+       gtk_type = "http://lv2plug.in/ns/extensions/ui#GtkUI";
+}
+
+PluginLV2GUI::~PluginLV2GUI ()
+{
+       reset_gui();
+       if( world ) lilv_world_free(world);
+}
+
+void PluginLV2GUI::reset_gui()
+{
+       if( inst ) lilv_instance_deactivate(inst);
+       if( uri_map )   { delete uri_map;             uri_map = 0; }
+       if( ext_data )  { delete ext_data;            ext_data = 0; }
+       if( inst )      { lilv_instance_free(inst);   inst = 0; }
+       if( sinst )     { suil_instance_free(sinst);  sinst = 0; }
+       if( ui_host )   { suil_host_free(ui_host);    ui_host = 0; }
+       if( uis )       { lilv_uis_free(uis);         uis = 0; }
+       if( lv2_InputPort )  { lilv_node_free(lv2_InputPort);   lv2_InputPort = 0; }
+       if( lv2_ControlPort ) { lilv_node_free(lv2_ControlPort);  lv2_ControlPort = 0; }
+       ui_features.remove_all_objects();
+       uri_table.remove_all_objects();
+       config.reset();
+       config.remove_all_objects();
+}
+
+int PluginLV2GUI::init_gui(const char *path)
+{
+       reset_gui();
+       if( !world )
+               world = lilv_world_new();
+       if( !world ) {
+               printf("lv2_gui: lilv_world_new failed");
+               return 1;
+       }
+       lilv_world_load_all(world);
+       LilvNode *uri = lilv_new_uri(world, path);
+       if( !uri ) {
+               printf("lv2_gui: lilv_new_uri(%s) failed", path);
+               return 1;
+       }
+
+       const LilvPlugins *all_plugins = lilv_world_get_all_plugins(world);
+       lilv = lilv_plugins_get_by_uri(all_plugins, uri);
+       lilv_node_free(uri);
+       if( !lilv ) {
+               printf("lv2_gui: lilv_plugins_get_by_uriPlugin(%s) failed", path);
+               return 1;
+       }
+
+       LilvNode *name = lilv_plugin_get_name(lilv);
+       const char *nm = lilv_node_as_string(name);
+       snprintf(title,sizeof(title),"L2_%s",nm);
+       lilv_node_free(name);
+
+       config.init_lv2(lilv);
+
+       lv2_InputPort  = lilv_new_uri(world, LV2_CORE__InputPort);
+       lv2_ControlPort = lilv_new_uri(world, LV2_CORE__ControlPort);
+
+       for( int i=0; i<config.nb_ports; ++i ) {
+               const LilvPort *lp = lilv_plugin_get_port_by_index(lilv, i);
+               if( lilv_port_is_a(lilv, lp, lv2_InputPort) &&
+                   lilv_port_is_a(lilv, lp, lv2_ControlPort) ) {
+                       config.append(new PluginLV2Client_Opt(&config, i));
+               }
+       }
+
+       uri_map = new LV2_URI_Map_Feature();
+       uri_map->callback_data = (LV2_URI_Map_Callback_Data)this;
+       uri_map->uri_to_id = uri_to_id;
+       ui_features.append(new Lv2Feature(NS_EXT "uri-map", uri_map));
+       map.handle = (void*)&uri_table;
+       map.map = uri_table_map;
+       ui_features.append(new Lv2Feature(LV2_URID__map, &map));
+       unmap.handle = (void*)&uri_table;
+       unmap.unmap  = uri_table_unmap;
+       ui_features.append(new Lv2Feature(LV2_URID__unmap, &unmap));
+       ui_features.append(0);
+
+       int sample_rate = 64; // cant be too low
+       inst = lilv_plugin_instantiate(lilv, sample_rate, ui_features);
+       if( !inst ) {
+               printf("lv2_gui: lilv_plugin_instantiate failed: %s\n", title);
+               return 1;
+       }
+
+       uis = lilv_plugin_get_uis(lilv);
+       if( gtk_type ) {
+               LilvNode *gui_type = lilv_new_uri(world, gtk_type);
+               LILV_FOREACH(uis, i, uis) {
+                       const LilvUI *gui = lilv_uis_get(uis, i);
+                       if( lilv_ui_is_supported(gui, suil_ui_supported, gui_type, &ui_type)) {
+                               ui = gui;
+                               break;
+                       }
+               }
+               lilv_node_free(gui_type);
+       }
+       if( !ui )
+               ui = lilv_uis_get(uis, lilv_uis_begin(uis));
+       if( !ui ) {
+               printf("lv2_gui: init_ui failed: %s\n", title);
+               return 1;
+       }
+
+       lilv_instance_activate(inst);
+       return 0;
+}
+
+void PluginLV2GUI::update_value(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
+{
+       if( idx >= config.nb_ports ) return;
+       for( int i=0, sz=config.size(); i<sz; ++i ) {
+               PluginLV2Client_Opt *opt = config[i];
+               if( opt->idx == idx ) {
+                       opt->set_value(*(const float*)bfr);
+//printf("set %s = %f\n", opt->get_symbol(), opt->get_value());
+                       ++updates;
+                       break;
+               }
+       }
+}
+void PluginLV2GUI::write_from_ui(void *the, uint32_t idx,
+               uint32_t bfrsz, uint32_t typ, const void *bfr)
+{
+       ((PluginLV2GUI*)the)->update_value(idx, bfrsz, typ, bfr);
+}
+
+
+uint32_t PluginLV2GUI::port_index(void* obj, const char* sym)
+{
+       PluginLV2GUI *the = (PluginLV2GUI*)obj;
+       for( int i=0, sz=the->config.size(); i<sz; ++i ) {
+               PluginLV2Client_Opt *opt = the->config[i];
+               if( !strcmp(sym, opt->sym) ) return opt->idx;
+       }
+       return UINT32_MAX;
+}
+
+void PluginLV2GUI::update_control(int idx, uint32_t bfrsz, uint32_t typ, const void *bfr)
+{
+       if( !sinst || idx >= config.nb_ports ) return;
+       suil_instance_port_event(sinst, idx, bfrsz, typ, bfr);
+}
+
+
+#if 0
+void PluginLV2GUI::touch(void *obj, uint32_t pidx, bool grabbed)
+{
+       PluginLV2GUI* the = (PluginLV2GUI*)obj;
+       int idx = pidx;
+       if( idx >= the->config.nb_ports ) return;
+printf("%s %s(%u)\n", (grabbed? _("press") : _("release")),
+  the->config.names[idx], idx);
+}
+#endif
+
+uint32_t PluginLV2GUI::uri_to_id(LV2_URID_Map_Handle handle, const char *map, const char *uri)
+{
+       return ((PluginLV2UriTable *)handle)->map(uri);
+}
+
+LV2_URID PluginLV2GUI::uri_table_map(LV2_URID_Map_Handle handle, const char *uri)
+{
+       return ((PluginLV2UriTable *)handle)->map(uri);
+}
+
+const char *PluginLV2GUI::uri_table_unmap(LV2_URID_Map_Handle handle, LV2_URID urid)
+{
+       return ((PluginLV2UriTable *)handle)->unmap(urid);
+}
+
+void PluginLV2GUI::lv2ui_instantiate(void *parent)
+{
+       if ( !ui_host ) {
+               ui_host = suil_host_new(
+                       PluginLV2GUI::write_from_ui,
+                       PluginLV2GUI::port_index,
+                       0, 0);
+//             suil_host_set_touch_func(ui_host,
+//                     PluginLV2GUI::touch);
+       }
+
+       ui_features.remove();
+       LV2_Handle lilv_handle = lilv_instance_get_handle(inst);
+       ui_features.append(new Lv2Feature(NS_EXT "instance-access", lilv_handle));
+       const LV2_Descriptor *lilv_desc = lilv_instance_get_descriptor(inst);
+       ext_data = new LV2_Extension_Data_Feature();
+       ext_data->data_access = lilv_desc->extension_data;
+       ui_features.append(new Lv2Feature(LV2_DATA_ACCESS_URI, ext_data));
+       ui_features.append(new Lv2Feature(LV2_UI__parent, parent));
+       ui_features.append(new Lv2Feature(LV2_UI__idleInterface, 0));
+       ui_features.append(0);
+
+       const char* bundle_uri  = lilv_node_as_uri(lilv_ui_get_bundle_uri(ui));
+       char*       bundle_path = lilv_file_uri_parse(bundle_uri, NULL);
+       const char* binary_uri  = lilv_node_as_uri(lilv_ui_get_binary_uri(ui));
+       char*       binary_path = lilv_file_uri_parse(binary_uri, NULL);
+       sinst = suil_instance_new(ui_host, this, gtk_type,
+               lilv_node_as_uri(lilv_plugin_get_uri(lilv)),
+               lilv_node_as_uri(lilv_ui_get_uri(ui)),
+               lilv_node_as_uri(ui_type),
+               bundle_path, binary_path, ui_features);
+
+       lilv_free(binary_path);
+       lilv_free(bundle_path);
+}
+
+bool PluginLV2GUI::lv2ui_resizable()
+{
+       if( !ui ) return false;
+        const LilvNode* s   = lilv_ui_get_uri(ui);
+        LilvNode *p   = lilv_new_uri(world, LV2_CORE__optionalFeature);
+        LilvNode *fs  = lilv_new_uri(world, LV2_UI__fixedSize);
+        LilvNode *nrs = lilv_new_uri(world, LV2_UI__noUserResize);
+        LilvNodes *fs_matches = lilv_world_find_nodes(world, s, p, fs);
+        LilvNodes *nrs_matches = lilv_world_find_nodes(world, s, p, nrs);
+        lilv_nodes_free(nrs_matches);
+        lilv_nodes_free(fs_matches);
+        lilv_node_free(nrs);
+        lilv_node_free(fs);
+        lilv_node_free(p);
+        return !fs_matches && !nrs_matches;
+}
+
+int PluginLV2GUI::update_lv2(float *vals, int force)
+{
+       int ret = 0;
+       float *ctls = (float *)config.ctls;
+       for( int i=0; i<config.size(); ++i ) {
+               int idx = config[i]->idx;
+               float val = vals[idx];
+               if( !force && ctls[idx] == val ) continue;
+               update_control(idx, sizeof(val), 0, &val);
+               ++ret;
+       }
+       for( int i=0; i<config.nb_ports; ++i ) ctls[i] = vals[i];
+       return ret;
+}
+
+#endif /* HAVE_LV2UI */