Missing file.
[ardour.git] / libs / ardour / lv2_plugin.cc
index 3832a899e7a8280db40cb22462935f2829e242b2..57242afab3f67414c03e7b318cc2a1d3d07c9da2 100644 (file)
 #include "pbd/compose.h"
 #include "pbd/error.h"
 #include "pbd/pathscanner.h"
+#include "pbd/stl_delete.h"
 #include "pbd/xml++.h"
 
+#include "libardour-config.h"
+
 #include "ardour/ardour.h"
 #include "ardour/audio_buffer.h"
 #include "ardour/audioengine.h"
 #include "ardour/lv2_plugin.h"
 #include "ardour/session.h"
 
-#include "pbd/stl_delete.h"
-
 #include "i18n.h"
 #include <locale.h>
 
+#include "lv2ext/lv2_files.h"
 #include "lv2ext/lv2_persist.h"
 #include "rdff.h"
+#ifdef HAVE_SUIL
+#include <suil/suil.h>
+#endif
 
 #define NS_DC   "http://dublincore.org/documents/dcmi-namespace/"
 #define NS_LV2  "http://lv2plug.in/ns/lv2core#"
@@ -70,6 +75,7 @@ LV2Plugin::LV2Plugin (AudioEngine& engine,
        : Plugin(engine, session)
        , _world(world)
        , _features(NULL)
+       , _insert_id("0")
 {
        init(world, plugin, rate);
 }
@@ -78,6 +84,7 @@ LV2Plugin::LV2Plugin (const LV2Plugin& other)
        : Plugin(other)
        , _world(other._world)
        , _features(NULL)
+       , _insert_id(other._insert_id)
 {
        init(other._world, other._plugin, other._sample_rate);
 
@@ -90,31 +97,49 @@ LV2Plugin::LV2Plugin (const LV2Plugin& other)
 void
 LV2Plugin::init(LV2World& world, SLV2Plugin plugin, framecnt_t rate)
 {
-       DEBUG_TRACE(DEBUG::LV2, "LV2 plugin init\n");
+       DEBUG_TRACE(DEBUG::LV2, "init\n");
 
        _world                = world;
        _plugin               = plugin;
        _ui                   = NULL;
+       _ui_type              = NULL;
        _control_data         = 0;
        _shadow_data          = 0;
        _latency_control_port = 0;
        _was_activated        = false;
 
-       _instance_access_feature.URI = "http://lv2plug.in/ns/ext/instance-access";
-       _data_access_feature.URI     = "http://lv2plug.in/ns/ext/data-access";
-       _persist_feature.URI         = "http://lv2plug.in/ns/ext/persist";
-       _persist_feature.data        = NULL;
+       _instance_access_feature.URI  = "http://lv2plug.in/ns/ext/instance-access";
+       _data_access_feature.URI      = "http://lv2plug.in/ns/ext/data-access";
+       _path_support_feature.URI     = LV2_FILES_PATH_SUPPORT_URI;
+       _new_file_support_feature.URI = LV2_FILES_NEW_FILE_SUPPORT_URI;
+       _persist_feature.URI          = "http://lv2plug.in/ns/ext/persist";
+       _persist_feature.data         = NULL;
 
        SLV2Value persist_uri = slv2_value_new_uri(_world.world, _persist_feature.URI);
        _supports_persist = slv2_plugin_has_feature(plugin, persist_uri);
        slv2_value_free(persist_uri);
 
-       _features    = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * 5);
+       _features    = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * 7);
        _features[0] = &_instance_access_feature;
        _features[1] = &_data_access_feature;
-       _features[2] = &_persist_feature;
-       _features[3] = _uri_map.feature();
-       _features[4] = NULL;
+       _features[2] = &_path_support_feature;
+       _features[3] = &_new_file_support_feature;
+       _features[4] = &_persist_feature;
+       _features[5] = _uri_map.feature();
+       _features[6] = NULL;
+
+       LV2_Files_Path_Support* path_support = (LV2_Files_Path_Support*)malloc(
+               sizeof(LV2_Files_Path_Support));
+       path_support->host_data = this;
+       path_support->abstract_path = &lv2_files_abstract_path;
+       path_support->absolute_path = &lv2_files_absolute_path;
+       _path_support_feature.data = path_support;
+
+       LV2_Files_New_File_Support* new_file_support = (LV2_Files_New_File_Support*)malloc(
+               sizeof(LV2_Files_New_File_Support));
+       new_file_support->host_data = this;
+       new_file_support->new_file_path = &lv2_files_new_file_path;
+       _new_file_support_feature.data = new_file_support;
 
        _instance = slv2_plugin_instantiate(plugin, rate, _features);
        _name     = slv2_plugin_get_name(plugin);
@@ -182,6 +207,21 @@ LV2Plugin::init(LV2World& world, SLV2Plugin plugin, framecnt_t rate)
 
        SLV2UIs uis = slv2_plugin_get_uis(_plugin);
        if (slv2_uis_size(uis) > 0) {
+#if defined(HAVE_NEW_SLV2) and defined(HAVE_SUIL)
+               // Look for embeddable UI
+               SLV2Value ui_type = NULL;
+               SLV2_FOREACH(u, uis) {
+                       SLV2UI this_ui = slv2_uis_get(uis, u);
+                       if (slv2_ui_is_supported(this_ui,
+                                                suil_ui_supported,
+                                                _world.gtk_gui,
+                                                &_ui_type)) {
+                               // TODO: Multiple UI support
+                               _ui = this_ui;
+                               break;
+                       }
+               }
+#else
                // Look for Gtk native UI
                for (unsigned i = 0; i < slv2_uis_size(uis); ++i) {
                        SLV2UI ui = slv2_uis_get_at(uis, i);
@@ -190,6 +230,7 @@ LV2Plugin::init(LV2World& world, SLV2Plugin plugin, framecnt_t rate)
                                break;
                        }
                }
+#endif
 
                // If Gtk UI is not available, try to find external UI
                if (!_ui) {
@@ -216,6 +257,7 @@ LV2Plugin::~LV2Plugin ()
        slv2_instance_free(_instance);
        slv2_value_free(_name);
        slv2_value_free(_author);
+       slv2_value_free(_ui_type);
 
        delete [] _control_data;
        delete [] _shadow_data;
@@ -255,7 +297,7 @@ void
 LV2Plugin::set_parameter(uint32_t which, float val)
 {
        DEBUG_TRACE(DEBUG::LV2, string_compose(
-                       "%1 set parameter %2 to %3\n", name(), which, val));
+                           "%1 set parameter %2 to %3\n", name(), which, val));
 
        if (which < slv2_plugin_get_num_ports(_plugin)) {
                _shadow_data[which] = val;
@@ -297,15 +339,15 @@ LV2Plugin::nth_parameter(uint32_t n, bool& ok) const
 }
 
 struct PersistValue {
-       inline PersistValue(uint32_t k, const void* v, size_t s, uint32_t t, bool p)
-               : key(k), value(v), size(s), type(t), pod(p)
+       inline PersistValue(uint32_t k, const void* v, size_t s, uint32_t t, uint32_t f)
+               : key(k), value(v), size(s), type(t), flags(f)
        {}
 
        const uint32_t key;
        const void*    value;
        const size_t   size;
        const uint32_t type;
-       const bool     pod;
+       const bool     flags;
 };
 
 struct PersistState {
@@ -333,7 +375,7 @@ struct PersistState {
                      const void* value,
                      size_t      size,
                      uint32_t    file_type,
-                     bool        pod) {
+                     uint32_t    flags) {
                const uint32_t key  = file_id_to_runtime_id(file_key);
                const uint32_t type = file_id_to_runtime_id(file_type);
                if (!key || !type) {
@@ -349,7 +391,7 @@ struct PersistState {
                        memcpy(value_copy, value, size); // FIXME: leak
                        values.insert(
                                make_pair(key,
-                                         PersistValue(key, value_copy, size, type, pod)));
+                                         PersistValue(key, value_copy, size, type, flags)));
                        return 0;
                }
        }
@@ -360,34 +402,33 @@ struct PersistState {
 };
 
 int
-LV2Plugin::lv2_persist_store_callback(void*       callback_data,
+LV2Plugin::lv2_persist_store_callback(void*       host_data,
                                       uint32_t    key,
                                       const void* value,
                                       size_t      size,
                                       uint32_t    type,
-                                      bool        pod)
+                                      uint32_t    flags)
 {
-       cout << "LV2 PERSIST STORE " << key
-            << " = " << value
-            << " :: " << type
-            << " POD: " << pod << endl;
+       DEBUG_TRACE(DEBUG::LV2, string_compose(
+                           "persist store %1 (size: %2, type: %3)\n",
+                           _uri_map.id_to_uri(NULL, key),
+                           size,
+                           _uri_map.id_to_uri(NULL, type)));
 
-       PersistState* state = (PersistState*)callback_data;
+       PersistState* state = (PersistState*)host_data;
        state->add_uri(key,  _uri_map.id_to_uri(NULL, key)); 
        state->add_uri(type, _uri_map.id_to_uri(NULL, type)); 
-       return state->add_value(key, value, size, type, pod);
+       return state->add_value(key, value, size, type, flags);
 }
 
 const void*
-LV2Plugin::lv2_persist_retrieve_callback(void*     callback_data,
+LV2Plugin::lv2_persist_retrieve_callback(void*     host_data,
                                          uint32_t  key,
                                          size_t*   size,
                                          uint32_t* type,
-                                         bool*     pod)
+                                         uint32_t* flags)
 {
-       cout << "LV2 PERSIST RETRIEVE " << _uri_map.id_to_uri(NULL, key) << endl;
-
-       PersistState* state = (PersistState*)callback_data;
+       PersistState* state = (PersistState*)host_data;
        PersistState::Values::const_iterator i = state->values.find(key);
        if (i == state->values.end()) {
                warning << "LV2 plugin attempted to retrieve nonexistent key: "
@@ -396,13 +437,96 @@ LV2Plugin::lv2_persist_retrieve_callback(void*     callback_data,
        }
        *size = i->second.size;
        *type = i->second.type;
-       *pod  = true; // FIXME
+       *flags = LV2_PERSIST_IS_POD | LV2_PERSIST_IS_PORTABLE; // FIXME
+       DEBUG_TRACE(DEBUG::LV2, string_compose(
+                           "persist retrieve %1 = %2 (size: %3, type: %4)\n",
+                           _uri_map.id_to_uri(NULL, key),
+                           i->second.value, *size, *type));
        return i->second.value;
 }
 
+char*
+LV2Plugin::lv2_files_abstract_path(LV2_Files_Host_Data host_data,
+                                   const char*         absolute_path)
+{
+       LV2Plugin* me = (LV2Plugin*)host_data;
+       if (me->_insert_id == PBD::ID("0")) {
+               return g_strdup(absolute_path);
+       }
+
+       const std::string state_dir = Glib::build_filename(me->_session.plugins_dir(),
+                                                          me->_insert_id.to_s());
+
+       char* ret = NULL;
+       if (strncmp(absolute_path, state_dir.c_str(), state_dir.length())) {
+               ret = g_strdup(absolute_path);
+       } else {
+               const std::string path(absolute_path + state_dir.length() + 1);
+               ret = g_strndup(path.c_str(), path.length());
+       }
+
+       DEBUG_TRACE(DEBUG::LV2, string_compose("abstract path %1 => %2\n",
+                                              absolute_path, ret));
+
+       return ret;
+}
+
+char*
+LV2Plugin::lv2_files_absolute_path(LV2_Files_Host_Data host_data,
+                                   const char*         abstract_path)
+{
+       LV2Plugin* me = (LV2Plugin*)host_data;
+       if (me->_insert_id == PBD::ID("0")) {
+               return g_strdup(abstract_path);
+       }
+
+       char* ret = NULL;
+       if (g_path_is_absolute(abstract_path)) {
+               ret = g_strdup(abstract_path);
+       } else {
+               const std::string apath(abstract_path);
+               const std::string state_dir = Glib::build_filename(me->_session.plugins_dir(),
+                                                                  me->_insert_id.to_s());
+               const std::string path = Glib::build_filename(state_dir,
+                                                             apath);
+               ret = g_strndup(path.c_str(), path.length());
+       }
+
+       DEBUG_TRACE(DEBUG::LV2, string_compose("absolute path %1 => %2\n",
+                                              abstract_path, ret));
+
+       return ret;
+}
+
+char*
+LV2Plugin::lv2_files_new_file_path(LV2_Files_Host_Data host_data,
+                                   const char*         relative_path)
+{
+       LV2Plugin* me = (LV2Plugin*)host_data;
+       if (me->_insert_id == PBD::ID("0")) {
+               return g_strdup(relative_path);
+       }
+
+       const std::string state_dir = Glib::build_filename(me->_session.plugins_dir(),
+                                                          me->_insert_id.to_s());
+       const std::string path = Glib::build_filename(state_dir,
+                                                     relative_path);
+
+       char* dirname = g_path_get_dirname(path.c_str());
+       g_mkdir_with_parents(dirname, 0744);
+       free(dirname);
+
+       DEBUG_TRACE(DEBUG::LV2, string_compose("new file path %1 => %2\n",
+                                              relative_path, path));
+       
+       return g_strndup(path.c_str(), path.length());
+}
+
 void
 LV2Plugin::add_state(XMLNode* root) const
 {
+       assert(_insert_id != PBD::ID("0"));
+
        XMLNode*    child;
        char        buf[16];
        LocaleGuard lg(X_("POSIX"));
@@ -419,7 +543,7 @@ LV2Plugin::add_state(XMLNode* root) const
 
        if (_supports_persist) {
                // Create state directory for this plugin instance
-               const std::string state_filename = _id.to_s() + ".lv2f";
+               const std::string state_filename = _insert_id.to_s() + ".rdff";
                const std::string state_path     = Glib::build_filename(
                        _session.plugins_dir(), state_filename);
 
@@ -447,8 +571,10 @@ LV2Plugin::add_state(XMLNode* root) const
                // Write all referenced URIs to state file
                for (PersistState::URIs::const_iterator i = state.uris.begin();
                     i != state.uris.end(); ++i) {
-                       rdff_write_uri(file, i->first,
-                                           i->second.c_str(), i->second.length() + 1);
+                       rdff_write_uri(file,
+                                      i->first,
+                                      i->second.length(),
+                                      i->second.c_str());
                }
 
                // Write all values to state file
@@ -456,11 +582,12 @@ LV2Plugin::add_state(XMLNode* root) const
                     i != state.values.end(); ++i) {
                        const uint32_t      key = i->first;
                        const PersistValue& val = i->second;
-                       rdff_write_value(file,
-                                             key,
-                                             val.value,
-                                             val.size,
-                                             val.type);
+                       rdff_write_triple(file,
+                                         0,
+                                         key,
+                                         val.type,
+                                         val.size,
+                                         val.value);
                }
 
                // Close state file
@@ -569,6 +696,12 @@ LV2Plugin::has_editor() const
        return _ui != NULL;
 }
 
+void
+LV2Plugin::set_insert_info(const PluginInsert* insert)
+{
+       _insert_id = insert->id();
+}
+
 int
 LV2Plugin::set_state(const XMLNode& node, int version)
 {
@@ -626,6 +759,8 @@ LV2Plugin::set_state(const XMLNode& node, int version)
                std::string state_path = Glib::build_filename(_session.plugins_dir(),
                                                              prop->value());
 
+               cout << "LV2 state path " << state_path << endl;
+
                // Get LV2 Persist extension data from plugin instance
                LV2_Persist* persist = (LV2_Persist*)slv2_instance_get_extension_data(
                        _instance, "http://lv2plug.in/ns/ext/persist");
@@ -639,19 +774,16 @@ LV2Plugin::set_state(const XMLNode& node, int version)
                        RDFFChunk* chunk = (RDFFChunk*)malloc(sizeof(RDFFChunk));
                        chunk->size = 0;
                        while (!rdff_read_chunk(file, &chunk)) {
-                               if (!strncmp(chunk->type, "URID", 4)) {
+                               if (rdff_chunk_is_uri(chunk)) {
                                        RDFFURIChunk* body = (RDFFURIChunk*)chunk->data;
-                                       printf("READ URI %u: %s\n", body->id, body->uri);
                                        state.add_uri(body->id, body->uri);
-                               } else if (!strncmp(chunk->type, "KVAL", 4)) {
-                                       RDFFValueChunk* body = (RDFFValueChunk*)chunk->data;
-                                       printf("READ VAL %u = %s (size: %u type: %u)\n",
-                                              body->key, body->value, body->size, body->type);
-                                       state.add_value(body->key,
-                                                       body->value,
-                                                       body->size,
-                                                       body->type,
-                                                       true);
+                               } else if (rdff_chunk_is_triple(chunk)) {
+                                       RDFFTripleChunk* body = (RDFFTripleChunk*)chunk->data;
+                                       state.add_value(body->predicate,
+                                                       body->object,
+                                                       body->object_size,
+                                                       body->object_type,
+                                                       LV2_PERSIST_IS_POD | LV2_PERSIST_IS_PORTABLE);
                                }
                        }
                        free(chunk);