diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h index b720c36146..189fb33586 100644 --- a/libs/ardour/ardour/lv2_plugin.h +++ b/libs/ardour/ardour/lv2_plugin.h @@ -90,8 +90,8 @@ class LV2Plugin : public ARDOUR::Plugin int set_block_size (pframes_t /*nframes*/) { return 0; } int connect_and_run (BufferSet& bufs, - ChanMapping in, ChanMapping out, - pframes_t nframes, framecnt_t offset); + ChanMapping in, ChanMapping out, + pframes_t nframes, framecnt_t offset); std::string describe_parameter (Evoral::Parameter); std::string state_node_name () const { return "lv2"; } @@ -109,6 +109,8 @@ class LV2Plugin : public ARDOUR::Plugin static uint32_t midi_event_type () { return _midi_event_type; } + void set_insert_info(const PluginInsert* insert); + int set_state (const XMLNode& node, int version); bool save_preset (std::string uri); void remove_preset (std::string uri); @@ -137,6 +139,8 @@ class LV2Plugin : public ARDOUR::Plugin std::map _port_indices; + PBD::ID _insert_id; + typedef struct { const void* (*extension_data) (const char* uri); } LV2_DataAccess; @@ -144,23 +148,29 @@ class LV2Plugin : public ARDOUR::Plugin LV2_DataAccess _data_access_extension_data; LV2_Feature _data_access_feature; LV2_Feature _instance_access_feature; + LV2_Feature _files_feature; LV2_Feature _persist_feature; static URIMap _uri_map; static uint32_t _midi_event_type; - static int lv2_persist_store_callback (void* callback_data, + static int lv2_persist_store_callback (void* host_data, uint32_t key, const void* value, size_t size, uint32_t type, uint32_t flags); - - static const void* lv2_persist_retrieve_callback (void* callback_data, + static const void* lv2_persist_retrieve_callback (void* host_data, uint32_t key, size_t* size, uint32_t* type, uint32_t* flags); + static char* lv2_files_abstract_path (void* host_data, + const char* absolute_path); + static char* lv2_files_absolute_path (void* host_data, + const char* abstract_path); + static char* lv2_files_new_file_path (void* host_data, + const char* relative_path); void init (LV2World& world, SLV2Plugin plugin, framecnt_t rate); void run (pframes_t nsamples); diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h index f2af0360fc..649d045d41 100644 --- a/libs/ardour/ardour/plugin.h +++ b/libs/ardour/ardour/plugin.h @@ -102,6 +102,8 @@ class Plugin : public PBD::StatefulDestructible, public Latent XMLNode& get_state (); virtual int set_state (const XMLNode &, int version); + virtual void set_insert_info(const PluginInsert* insert) {} + virtual std::string unique_id() const = 0; virtual const char * label() const = 0; virtual const char * name() const = 0; diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 359e1aa5f5..f4ecb50154 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -45,6 +45,7 @@ #include "i18n.h" #include +#include "lv2ext/lv2_files.h" #include "lv2ext/lv2_persist.h" #include "rdff.h" @@ -70,6 +71,7 @@ LV2Plugin::LV2Plugin (AudioEngine& engine, : Plugin(engine, session) , _world(world) , _features(NULL) + , _insert_id("0") { init(world, plugin, rate); } @@ -78,6 +80,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,7 +93,7 @@ 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; @@ -102,6 +105,7 @@ LV2Plugin::init(LV2World& world, SLV2Plugin plugin, framecnt_t rate) _instance_access_feature.URI = "http://lv2plug.in/ns/ext/instance-access"; _data_access_feature.URI = "http://lv2plug.in/ns/ext/data-access"; + _files_feature.URI = LV2_FILES_FILE_SUPPORT_URI; _persist_feature.URI = "http://lv2plug.in/ns/ext/persist"; _persist_feature.data = NULL; @@ -109,12 +113,22 @@ LV2Plugin::init(LV2World& world, SLV2Plugin plugin, framecnt_t rate) _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*) * 6); _features[0] = &_instance_access_feature; _features[1] = &_data_access_feature; - _features[2] = &_persist_feature; - _features[3] = _uri_map.feature(); - _features[4] = NULL; + _features[2] = &_files_feature; + _features[3] = &_persist_feature; + _features[4] = _uri_map.feature(); + _features[5] = NULL; + + LV2_Files_File_Support* file_support = (LV2_Files_File_Support*)malloc( + sizeof(LV2_Files_File_Support)); + file_support->host_data = this; + file_support->abstract_path = &lv2_files_abstract_path; + file_support->absolute_path = &lv2_files_absolute_path; + file_support->new_file_path = &lv2_files_new_file_path; + + _files_feature.data = file_support; _instance = slv2_plugin_instantiate(plugin, rate, _features); _name = slv2_plugin_get_name(plugin); @@ -255,7 +269,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; @@ -360,30 +374,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, uint32_t flags) { - DEBUG_TRACE(DEBUG::LV2, string_compose("persist store %1\n", - _uri_map.id_to_uri(NULL, key))); + 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, 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, uint32_t* flags) { - 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: " @@ -394,14 +411,88 @@ LV2Plugin::lv2_persist_retrieve_callback(void* callback_data, *type = i->second.type; *flags = LV2_PERSIST_IS_POD | LV2_PERSIST_IS_PORTABLE; // FIXME DEBUG_TRACE(DEBUG::LV2, string_compose( - "persist retrieve %1 = %s (size: %u, type: %u)\n", - _uri_map.id_to_uri(NULL, key), i->second.value, *size, *type)); + "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; + assert(me->_insert_id != PBD::ID("0")); + + 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; + assert(me->_insert_id != PBD::ID("0")); + + 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; + assert(me->_insert_id != PBD::ID("0")); + + 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")); @@ -418,7 +509,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() + ".rdff"; + const std::string state_filename = _insert_id.to_s() + ".rdff"; const std::string state_path = Glib::build_filename( _session.plugins_dir(), state_filename); @@ -571,6 +662,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) { @@ -643,13 +740,9 @@ LV2Plugin::set_state(const XMLNode& node, int version) while (!rdff_read_chunk(file, &chunk)) { 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 (rdff_chunk_is_triple(chunk)) { RDFFTripleChunk* body = (RDFFTripleChunk*)chunk->data; - printf("READ VAL %u = %s (size: %u type: %u)\n", - body->predicate, body->object, - body->object_size, body->object_type); state.add_value(body->predicate, body->object, body->object_size, diff --git a/libs/ardour/lv2ext/lv2_files.h b/libs/ardour/lv2ext/lv2_files.h new file mode 100644 index 0000000000..2ffb9643c6 --- /dev/null +++ b/libs/ardour/lv2ext/lv2_files.h @@ -0,0 +1,125 @@ +/* + Copyright 2010-2011 David Robillard + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ + +/** + @file files.h + C API for the LV2 Files extension . +*/ + +#ifndef LV2_FILES_H +#define LV2_FILES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define LV2_FILES_URI "http://lv2plug.in/ns/ext/files" +#define LV2_FILES_FILE_SUPPORT_URI LV2_FILES_URI "#fileSupport" + +typedef void* LV2_Files_Host_Data; + +/** + files:fileSupport feature struct. + + To support this feature, the host MUST pass an LV2_Feature struct with @a + URI "http://lv2plug.in/ns/ext/files#fileSupport" and @a data pointed to an + instance of this struct. +*/ +typedef struct { + + /** + Opaque host data. + */ + LV2_Files_Host_Data host_data; + + /** + Map an absolute path to an abstract path for use in plugin state. + @param host_data MUST be the @a host_data member of this struct. + @param absolute_path The absolute path of a file. + @return An abstract path suitable for use in plugin state. + + The plugin MUST use this function to map any paths that will be stored + in plugin state. The returned value is an abstract path which MAY not + be an actual file system path; @ref absolute_path MUST be used to map it + to an actual path in order to use the file. + + Hosts MAY map paths in any way (e.g. by creating symbolic links within + the plugin's state directory or storing a list of referenced files for + later export). Plugins MUST NOT make any assumptions about abstract + paths except that they can be mapped to an absolute path using @ref + absolute_path. Particularly when restoring from state, this absolute + path MAY not be the same as the original absolute path, but the host + MUST guarantee it refers to a file with contents equivalent to the + original. + + This function may only be called within the context of + LV2_Persist.save() or LV2_Persist.restore(). The caller is responsible + for freeing the returned value. + */ + char* (*abstract_path)(LV2_Files_Host_Data host_data, + const char* absolute_path); + + /** + Map an abstract path from plugin state to an absolute path. + @param host_data MUST be the @a host_data member of this struct. + @param abstract_path An abstract path (e.g. a path from plugin state). + @return An absolute file system path. + + Since abstract paths are not necessarily actual file paths (or at least + not necessarily absolute paths), this function MUST be used in order to + actually open or otherwise use the file referred to by an abstract path. + + This function may only be called within the context of + LV2_Persist.save() or LV2_Persist.restore(). The caller is responsible + for freeing the returned value. + */ + char* (*absolute_path)(LV2_Files_Host_Data host_data, + const char* abstract_path); + + + /** + Return an absolute path the plugin may use to create a new file. + @param host_data MUST be the @a host_data member of this struct. + @param relative_path The relative path of the file. + @return The absolute path to use for the new file. + + The plugin can assume @a relative_path is relative to a namespace + dedicated to that plugin instance; hosts MUST ensure this, e.g. by + giving each plugin instance its own state directory. The returned path + is absolute and thus suitable for creating and using a file, but NOT + suitable for storing in plugin state (it MUST be mapped to an abstract + path using @ref abstract_path in order to do so). + + This function may be called from any non-realtime context. The caller + is responsible for freeing the returned value. + */ + char* (*new_file_path)(LV2_Files_Host_Data host_data, + const char* relative_path); + + +} LV2_Files_File_Support; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_FILES_H */ diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index b7d95d9483..71205c2c74 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -70,6 +70,7 @@ PluginInsert::PluginInsert (Session& s, boost::shared_ptr plug) /* the first is the master */ if (plug) { + plug->set_insert_info (this); _plugins.push_back (plug); set_automatable (); @@ -847,13 +848,13 @@ PluginInsert::set_state(const XMLNode& node, int version) } } - boost::shared_ptr plugin; - - plugin = find_plugin (_session, prop->value(), type); + boost::shared_ptr plugin = find_plugin (_session, prop->value(), type); if (plugin == 0) { - error << string_compose(_("Found a reference to a plugin (\"%1\") that is unknown.\n" - "Perhaps it was removed or moved since it was last used."), prop->value()) + error << string_compose( + _("Found a reference to a plugin (\"%1\") that is unknown.\n" + "Perhaps it was removed or moved since it was last used."), + prop->value()) << endmsg; return -1; } @@ -868,6 +869,8 @@ PluginInsert::set_state(const XMLNode& node, int version) need_automatables = true; } + Processor::set_state (node, version); + if ((prop = node.property ("count")) != 0) { sscanf (prop->value().c_str(), "%u", &count); } @@ -898,8 +901,6 @@ PluginInsert::set_state(const XMLNode& node, int version) } } - Processor::set_state (node, version); - if (version < 3000) { /* Only 2.X sessions need a call to set_parameter_state() - in 3.X and above @@ -1167,6 +1168,7 @@ PluginInsert::collect_signal_for_analysis (framecnt_t nframes) void PluginInsert::add_plugin_with_activation (boost::shared_ptr plugin) { + plugin->set_insert_info (this); _plugins.push_back (plugin); if (active()) { plugin->activate ();