diff options
-rw-r--r-- | libs/ardour/ardour/lv2_plugin.h | 53 | ||||
-rw-r--r-- | libs/ardour/ardour/lv2_state.h | 147 | ||||
-rw-r--r-- | libs/ardour/ardour/session.h | 7 | ||||
-rw-r--r-- | libs/ardour/ardour/uri_map.h | 21 | ||||
-rw-r--r-- | libs/ardour/lv2/lv2plug.in/ns/ext/state/state.h | 9 | ||||
-rw-r--r-- | libs/ardour/lv2_plugin.cc | 333 | ||||
-rw-r--r-- | libs/ardour/session_state.cc | 13 | ||||
-rw-r--r-- | libs/ardour/wscript | 3 | ||||
-rw-r--r-- | wscript | 1 |
9 files changed, 177 insertions, 410 deletions
diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h index a627c2eb43..6d68ca2674 100644 --- a/libs/ardour/ardour/lv2_plugin.h +++ b/libs/ardour/ardour/lv2_plugin.h @@ -120,16 +120,16 @@ class LV2Plugin : public ARDOUR::Plugin private: struct Impl; - Impl* _impl; - void* _module; - LV2_Feature** _features; - framecnt_t _sample_rate; - float* _control_data; - float* _shadow_data; - float* _defaults; - float* _latency_control_port; - bool _was_activated; - bool _has_state_interface; + Impl* _impl; + void* _module; + LV2_Feature** _features; + framecnt_t _sample_rate; + float* _control_data; + float* _shadow_data; + float* _defaults; + float* _latency_control_port; + PBD::ID _insert_id; + std::vector<bool> _port_is_input; std::vector<bool> _port_is_output; std::vector<bool> _port_is_midi; @@ -138,8 +138,6 @@ class LV2Plugin : public ARDOUR::Plugin std::map<std::string,uint32_t> _port_indices; - PBD::ID _insert_id; - typedef struct { const void* (*extension_data) (const char* uri); } LV2_DataAccess; @@ -147,34 +145,21 @@ class LV2Plugin : public ARDOUR::Plugin LV2_DataAccess _data_access_extension_data; LV2_Feature _data_access_feature; LV2_Feature _instance_access_feature; - LV2_Feature _map_path_feature; LV2_Feature _make_path_feature; + mutable unsigned _state_version; + + bool _was_activated; + bool _has_state_interface; + static URIMap _uri_map; static uint32_t _midi_event_type; static uint32_t _state_path_type; - const std::string state_dir () const; - - static int - lv2_state_store_callback (void* handle, - uint32_t key, - const void* value, - size_t size, - uint32_t type, - uint32_t flags); - - static const void* - lv2_state_retrieve_callback (void* handle, - uint32_t key, - size_t* size, - uint32_t* type, - uint32_t* flags); - - static char* lv2_state_abstract_path (void* host_data, - const char* absolute_path); - static char* lv2_state_absolute_path (void* host_data, - const char* abstract_path); + const std::string scratch_dir () const; + const std::string file_dir () const; + const std::string state_dir (unsigned num) const; + static char* lv2_state_make_path (void* host_data, const char* path); diff --git a/libs/ardour/ardour/lv2_state.h b/libs/ardour/ardour/lv2_state.h deleted file mode 100644 index 6c37ebcd0b..0000000000 --- a/libs/ardour/ardour/lv2_state.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - Copyright (C) 2011 Paul Davis - Author: David Robillard - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef __ardour_lv2_state_h__ -#define __ardour_lv2_state_h__ - -#include <stdint.h> -#include <stdlib.h> - -#include <map> -#include <string> - -#include "pbd/error.h" - -#include "ardour/uri_map.h" -#include "lv2/lv2plug.in/ns/ext/state/state.h" -#include "rdff.h" - -namespace ARDOUR { - -class LV2Plugin; - -struct LV2State { - LV2State(const LV2Plugin& plug, URIMap& map) : plugin(plug), uri_map(map) {} - - struct Value { - inline Value(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 uint32_t flags; - }; - - typedef std::map<uint32_t, std::string> URIs; - typedef std::map<uint32_t, Value> Values; - - uint32_t file_id_to_runtime_id(uint32_t file_id) const { - URIs::const_iterator i = uris.find(file_id); - if (i == uris.end()) { - PBD::error << "LV2 state refers to undefined URI ID" << endmsg; - return 0; - } - return uri_map.uri_to_id(NULL, i->second.c_str()); - } - - int add_uri(uint32_t file_id, const char* str) { - // TODO: check for clashes (invalid file) - uris.insert(std::make_pair(file_id, str)); - return 0; - } - - int add_value(uint32_t file_key, - const void* value, - size_t size, - uint32_t file_type, - 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) { - PBD::error << "Invalid file key or type" << endmsg; - return 1; - } - - Values::const_iterator i = values.find(key); - if (i != values.end()) { - PBD::error << "LV2 state contains duplicate keys" << endmsg; - return 1; - } else { - void* value_copy = malloc(size); - memcpy(value_copy, value, size); // FIXME: leak - values.insert( - std::make_pair(key, - Value(key, value_copy, size, type, flags))); - return 0; - } - } - - void read(RDFF file) { - RDFFChunk* chunk = (RDFFChunk*)malloc(sizeof(RDFFChunk)); - chunk->size = 0; - while (!rdff_read_chunk(file, &chunk)) { - if (rdff_chunk_is_uri(chunk)) { - RDFFURIChunk* body = (RDFFURIChunk*)chunk->data; - add_uri(body->id, body->uri); - } else if (rdff_chunk_is_triple(chunk)) { - RDFFTripleChunk* body = (RDFFTripleChunk*)chunk->data; - add_value(body->predicate, - body->object, - body->object_size, - body->object_type, - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - } - } - free(chunk); - } - - void write(RDFF file) { - // Write all referenced URIs to state file - for (URIs::const_iterator i = uris.begin(); i != uris.end(); ++i) { - rdff_write_uri(file, - i->first, - i->second.length(), - i->second.c_str()); - } - - // Write all values to state file - for (Values::const_iterator i = values.begin(); i != values.end(); ++i) { - const uint32_t key = i->first; - const LV2State::Value& val = i->second; - rdff_write_triple(file, - 0, - key, - val.type, - val.size, - val.value); - } - } - - const LV2Plugin& plugin; - URIMap& uri_map; - URIs uris; - Values values; -}; - -} // namespace ARDOUR - -#endif /* __ardour_lv2_state_h__ */ diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 823e5e4bbc..e702612338 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -182,9 +182,10 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi int ensure_subdirs (); - std::string automation_dir () const; - std::string analysis_dir() const; - std::string plugins_dir() const; + std::string automation_dir () const; ///< Automation data + std::string analysis_dir () const; ///< Analysis data + std::string plugins_dir () const; ///< Plugin state + std::string externals_dir () const; ///< Links to external files std::string peak_path (std::string) const; diff --git a/libs/ardour/ardour/uri_map.h b/libs/ardour/ardour/uri_map.h index 3fb8560022..a6c9dc7a8b 100644 --- a/libs/ardour/ardour/uri_map.h +++ b/libs/ardour/ardour/uri_map.h @@ -41,11 +41,12 @@ public: LV2_Feature* urid_map_feature() { return &_urid_map_feature; } LV2_Feature* urid_unmap_feature() { return &_urid_unmap_feature; } - uint32_t uri_to_id(const char* map, - const char* uri); + LV2_URID_Map* urid_map() { return &_urid_map_feature_data; } + LV2_URID_Unmap* urid_unmap() { return &_urid_unmap_feature_data; } - const char* id_to_uri(const char* map, - uint32_t id); + uint32_t uri_to_id(const char* map, const char* uri); + + const char* id_to_uri(const char* map, uint32_t id); private: static uint32_t uri_map_uri_to_id(LV2_URI_Map_Callback_Data callback_data, @@ -64,12 +65,12 @@ private: EventToGlobal _event_to_global; GlobalToEvent _global_to_event; - LV2_Feature _uri_map_feature; - LV2_URI_Map_Feature _uri_map_feature_data; - LV2_Feature _urid_map_feature; - LV2_URID_Map _urid_map_feature_data; - LV2_Feature _urid_unmap_feature; - LV2_URID_Unmap _urid_unmap_feature_data; + LV2_Feature _uri_map_feature; + LV2_URI_Map_Feature _uri_map_feature_data; + LV2_Feature _urid_map_feature; + LV2_URID_Map _urid_map_feature_data; + LV2_Feature _urid_unmap_feature; + LV2_URID_Unmap _urid_unmap_feature_data; }; diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/state/state.h b/libs/ardour/lv2/lv2plug.in/ns/ext/state/state.h index 3d390120d7..bbb8fad5b5 100644 --- a/libs/ardour/lv2/lv2plug.in/ns/ext/state/state.h +++ b/libs/ardour/lv2/lv2plug.in/ns/ext/state/state.h @@ -37,8 +37,8 @@ extern "C" { #define LV2_STATE_INTERFACE_URI LV2_STATE_URI "#Interface" #define LV2_STATE_PATH_URI LV2_STATE_URI "#Path" -#define LV2_STATE_MAP_PATH_URI LV2_STATE_URI "#pathMap" -#define LV2_STATE_MAKE_PATH_URI LV2_STATE_URI "#newPath" +#define LV2_STATE_MAP_PATH_URI LV2_STATE_URI "#mapPath" +#define LV2_STATE_MAKE_PATH_URI LV2_STATE_URI "#makePath" typedef void* LV2_State_Handle; typedef void* LV2_State_Map_Path_Handle; @@ -55,7 +55,7 @@ typedef enum { /** Plain Old Data. - Values with this flag contain no references to non-stateent or + Values with this flag contain no references to non-persistent or non-global resources (e.g. pointers, handles, local paths, etc.). It is safe to copy POD values with a simple memcpy and store them for use at any time in the future on a machine with a compatible architecture @@ -221,7 +221,6 @@ typedef struct _LV2_State_Interface { uint32_t flags, const LV2_Feature *const * features); - /** Restore plugin state using a host-provided @c retrieve callback. @@ -257,7 +256,7 @@ typedef struct _LV2_State_Interface { } LV2_State_Interface; /** - Feature data for state:pathMap (LV2_STATE_MAP_PATH_URI). + Feature data for state:mapPath (LV2_STATE_MAP_PATH_URI). */ typedef struct { diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 2e9f49c4e7..b645f8bfa8 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -15,7 +15,6 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ #include <string> @@ -44,7 +43,6 @@ #include "ardour/debug.h" #include "ardour/lv2_event_buffer.h" #include "ardour/lv2_plugin.h" -#include "ardour/lv2_state.h" #include "ardour/session.h" #include "i18n.h" @@ -59,11 +57,9 @@ #endif #define NS_DC "http://dublincore.org/documents/dcmi-namespace/" -#define NS_LV2 "http://lv2plug.in/ns/lv2core#" #define NS_OLDPSET "http://lv2plug.in/ns/dev/presets#" #define NS_PSET "http://lv2plug.in/ns/ext/presets#" #define NS_UI "http://lv2plug.in/ns/extensions/ui#" -#define NS_RDFS "http://www.w3.org/2000/01/rdf-schema#" using namespace std; using namespace ARDOUR; @@ -100,13 +96,20 @@ public: static LV2World _world; struct LV2Plugin::Impl { - Impl() : plugin(0), ui(0), ui_type(0), name(0), author(0), instance(0) {} + Impl() : plugin(0), ui(0), ui_type(0), name(0), author(0), instance(0) +#ifdef HAVE_NEW_LILV + , state(0) +#endif + {} LilvPlugin* plugin; const LilvUI* ui; const LilvNode* ui_type; LilvNode* name; LilvNode* author; LilvInstance* instance; +#ifdef HAVE_NEW_LILV + LilvState* state; +#endif }; LV2Plugin::LV2Plugin (AudioEngine& engine, @@ -146,46 +149,36 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate) _control_data = 0; _shadow_data = 0; _latency_control_port = 0; + _state_version = 0; _was_activated = false; - + _has_state_interface = false; + _instance_access_feature.URI = "http://lv2plug.in/ns/ext/instance-access"; _data_access_feature.URI = "http://lv2plug.in/ns/ext/data-access"; - _map_path_feature.URI = LV2_STATE_MAP_PATH_URI; _make_path_feature.URI = LV2_STATE_MAKE_PATH_URI; LilvPlugin* plugin = _impl->plugin; +#ifdef HAVE_NEW_LILV LilvNode* state_iface_uri = lilv_new_uri(_world.world, LV2_STATE_INTERFACE_URI); -#ifdef lilv_plugin_has_extension_data - _has_state_interface = lilv_plugin_has_extension_data(plugin, state_iface_uri); + LilvNode* state_uri = lilv_new_uri(_world.world, LV2_STATE_URI); + _has_state_interface = + // What plugins should have (lv2:extensionData state:Interface) + lilv_plugin_has_extension_data(plugin, state_iface_uri) + // What some outdated/incorrect ones have + || lilv_plugin_has_feature(plugin, state_uri); + lilv_node_free(state_uri); lilv_node_free(state_iface_uri); -#else - LilvNode* lv2_extensionData = lilv_new_uri(_world.world, NS_LV2 "extensionData"); - LilvNodes* extdatas = lilv_plugin_get_value(plugin, lv2_extensionData); - LILV_FOREACH(nodes, i, extdatas) { - if (lilv_node_equals(lilv_nodes_get(extdatas, i), state_iface_uri)) { - _has_state_interface = true; - break; - } - } #endif - _features = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * 8); + _features = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * 7); _features[0] = &_instance_access_feature; _features[1] = &_data_access_feature; - _features[2] = &_map_path_feature; - _features[3] = &_make_path_feature; - _features[4] = _uri_map.uri_map_feature(); - _features[5] = _uri_map.urid_map_feature(); - _features[6] = _uri_map.urid_unmap_feature(); - _features[7] = NULL; - - LV2_State_Map_Path* map_path = (LV2_State_Map_Path*)malloc( - sizeof(LV2_State_Map_Path)); - map_path->handle = this; - map_path->abstract_path = &lv2_state_abstract_path; - map_path->absolute_path = &lv2_state_absolute_path; - _map_path_feature.data = map_path; + _features[2] = &_make_path_feature; + _features[3] = _uri_map.uri_map_feature(); + _features[4] = _uri_map.urid_map_feature(); + _features[5] = _uri_map.urid_unmap_feature(); + _features[6] = NULL; LV2_State_Make_Path* make_path = (LV2_State_Make_Path*)malloc( sizeof(LV2_State_Make_Path)); @@ -467,148 +460,56 @@ LV2Plugin::c_ui_type () return (void*)_impl->ui_type; } +/** Directory for files created by the plugin (except during save). */ const std::string -LV2Plugin::state_dir() const +LV2Plugin::scratch_dir() const { - return Glib::build_filename(_session.plugins_dir(), - _insert_id.to_s()); -} - -int -LV2Plugin::lv2_state_store_callback(LV2_State_Handle handle, - uint32_t key, - const void* value, - size_t size, - uint32_t type, - uint32_t flags) -{ - DEBUG_TRACE(DEBUG::LV2, string_compose( - "state store %1 (size: %2, type: %3, flags: %4)\n", - _uri_map.id_to_uri(NULL, key), - size, - _uri_map.id_to_uri(NULL, type), - flags)); - - LV2State* state = (LV2State*)handle; - state->add_uri(key, _uri_map.id_to_uri(NULL, key)); - state->add_uri(type, _uri_map.id_to_uri(NULL, type)); - - if (type == _state_path_type) { - const LV2Plugin& me = state->plugin; - LV2_State_Map_Path* mp = (LV2_State_Map_Path*)me._map_path_feature.data; - return state->add_value( - key, - lv2_state_abstract_path(mp->handle, (const char*)value), - size, type, flags); - } else if ((flags & LV2_STATE_IS_POD) && (flags & LV2_STATE_IS_PORTABLE)) { - return state->add_value(key, value, size, type, flags); - } else { - PBD::warning << "LV2 plugin attempted to store non-portable property." << endl; - return -1; - } + return Glib::build_filename( + _session.plugins_dir(), _insert_id.to_s(), "scratch"); } -const void* -LV2Plugin::lv2_state_retrieve_callback(LV2_State_Handle host_data, - uint32_t key, - size_t* size, - uint32_t* type, - uint32_t* flags) +/** Directory for snapshots of files in the scratch directory. */ +const std::string +LV2Plugin::file_dir() const { - LV2State* state = (LV2State*)host_data; - LV2State::Values::const_iterator i = state->values.find(key); - if (i == state->values.end()) { - warning << "LV2 plugin attempted to retrieve nonexistent key: " - << _uri_map.id_to_uri(NULL, key) << endmsg; - return NULL; - } - *size = i->second.size; - *type = i->second.type; - *flags = LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE; - DEBUG_TRACE(DEBUG::LV2, string_compose( - "state retrieve %1 = %2 (size: %3, type: %4)\n", - _uri_map.id_to_uri(NULL, key), - i->second.value, *size, *type)); - if (*type == _state_path_type) { - const LV2Plugin& me = state->plugin; - LV2_State_Map_Path* mp = (LV2_State_Map_Path*)me._map_path_feature.data; - return lv2_state_absolute_path(mp->handle, (const char*)i->second.value); - } else { - return i->second.value; - } + return Glib::build_filename( + _session.plugins_dir(), _insert_id.to_s(), "files"); } -char* -LV2Plugin::lv2_state_abstract_path(LV2_State_Map_Path_Handle handle, - const char* absolute_path) -{ - LV2Plugin* me = (LV2Plugin*)handle; - if (me->_insert_id == PBD::ID("0")) { - return g_strdup(absolute_path); - } - - const std::string state_dir = me->state_dir(); - - char* ret = NULL; - if (strncmp(absolute_path, state_dir.c_str(), state_dir.length())) { - // Path outside state directory, make symbolic link - const std::string name = Glib::path_get_basename(absolute_path); - const std::string path = Glib::build_filename(state_dir, name); - Gio::File::create_for_path(path)->make_symbolic_link(absolute_path); - ret = g_strndup(path.c_str(), path.length()); - } else { - // Path inside the state directory, return relative path - 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_state_absolute_path(LV2_State_Map_Path_Handle handle, - const char* abstract_path) +/** Directory to save state snapshot version @c num into. */ +const std::string +LV2Plugin::state_dir(unsigned num) const { - LV2Plugin* me = (LV2Plugin*)handle; - 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 path = Glib::build_filename(me->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; + return Glib::build_filename(_session.plugins_dir(), + _insert_id.to_s(), + string_compose("state%1", num)); } +/** Implementation of state:makePath for files created at instantiation time. + * Note this is not used for files created at save time (Lilv deals with that). + */ char* LV2Plugin::lv2_state_make_path(LV2_State_Make_Path_Handle handle, const char* path) { LV2Plugin* me = (LV2Plugin*)handle; if (me->_insert_id == PBD::ID("0")) { + warning << string_compose( + "File path \"%1\" requested but LV2 %2 has no insert ID", + path, me->name()) << endmsg; return g_strdup(path); } - const std::string abs_path = Glib::build_filename(me->state_dir(), path); + const std::string abs_path = Glib::build_filename(me->scratch_dir(), path); const std::string dirname = Glib::path_get_dirname(abs_path); - g_mkdir_with_parents(dirname.c_str(), 0744); DEBUG_TRACE(DEBUG::LV2, string_compose("new file path %1 => %2\n", path, abs_path)); + std::cerr << "MAKE PATH " << path + << " => " << g_strndup(abs_path.c_str(), abs_path.length()) + << std::endl; return g_strndup(abs_path.c_str(), abs_path.length()); } @@ -616,9 +517,10 @@ static void remove_directory(const std::string& path) { if (!Glib::file_test(path, Glib::FILE_TEST_IS_DIR)) { + warning << string_compose("\"%1\" is not a directory", path) << endmsg; return; } - + Glib::RefPtr<Gio::File> dir = Gio::File::create_for_path(path); Glib::RefPtr<Gio::FileEnumerator> e = dir->enumerate_children(); Glib::RefPtr<Gio::FileInfo> fi; @@ -652,41 +554,57 @@ LV2Plugin::add_state(XMLNode* root) const } if (_has_state_interface) { - // Create state directory for this plugin instance - cerr << "Create statefile name from ID " << _insert_id << endl; - const std::string state_filename = _insert_id.to_s() + ".rdff"; - const std::string state_path = Glib::build_filename( - _session.plugins_dir(), state_filename); - - cout << "Saving LV2 plugin state to " << state_path << endl; - - // Get LV2 State extension data from plugin instance - LV2_State_Interface* state_iface = (LV2_State_Interface*)extension_data( - LV2_STATE_INTERFACE_URI); - if (!state_iface) { - warning << string_compose( - _("Plugin \"%1\% failed to return LV2 state interface"), - unique_id()); - return; + cout << "LV2 " << name() << " has state interface" << endl; +#ifdef HAVE_NEW_LILV + // Provisionally increment state version and create directory + const std::string new_dir = state_dir(++_state_version); + g_mkdir_with_parents(new_dir.c_str(), 0744); + + cout << "NEW DIR: " << new_dir << endl; + + LilvState* state = lilv_state_new_from_instance( + _impl->plugin, + _impl->instance, + _uri_map.urid_map(), + scratch_dir().c_str(), + file_dir().c_str(), + _session.externals_dir().c_str(), + new_dir.c_str(), + NULL, + (void*)this, + 0, + NULL); + + if (!_impl->state || !lilv_state_equals(state, _impl->state)) { + lilv_state_save(_world.world, + _uri_map.urid_unmap(), + state, + NULL, + new_dir.c_str(), + "state.ttl", + NULL); + + lilv_state_free(_impl->state); + _impl->state = state; + + cout << "Saved LV2 state to " << state_dir(_state_version) << endl; + } else { + // State is identical, decrement version and nuke directory + cout << "LV2 state identical, not saving" << endl; + lilv_state_free(state); + remove_directory(new_dir); + --_state_version; } - // Remove old state directory (FIXME: should this be preserved?) - remove_directory(state_dir()); - - // Save plugin state to state object - LV2State state(*this, _uri_map); - state_iface->save(_impl->instance->lv2_handle, - &LV2Plugin::lv2_state_store_callback, - &state, - LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE, - NULL); - - // Write state object to RDFF file - RDFF file = rdff_open(state_path.c_str(), true); - state.write(file); - rdff_close(file); + root->add_property("state-dir", string_compose("state%1", _state_version)); - root->add_property("state-file", state_filename); +#else /* !HAVE_NEW_LILV */ + warning << string_compose( + _("Plugin \"%1\" has state, but Lilv is too old to save it"), + unique_id()) << endmsg; +#endif /* HAVE_NEW_LILV */ + } else { + cout << "LV2 " << name() << " has no state interface." << endl; } } @@ -717,7 +635,7 @@ find_presets_helper(LilvWorld* world, warning << string_compose( _("Plugin \"%1\% preset \"%2%\" is missing a label\n"), lilv_node_as_string(lilv_plugin_get_uri(plugin)), - lilv_node_as_string(preset)); + lilv_node_as_string(preset)) << endmsg; } } lilv_nodes_free(presets); @@ -729,7 +647,7 @@ LV2Plugin::find_presets() LilvNode* dc_title = lilv_new_uri(_world.world, NS_DC "title"); LilvNode* oldpset_hasPreset = lilv_new_uri(_world.world, NS_OLDPSET "hasPreset"); LilvNode* pset_hasPreset = lilv_new_uri(_world.world, NS_PSET "hasPreset"); - LilvNode* rdfs_label = lilv_new_uri(_world.world, NS_RDFS "label"); + LilvNode* rdfs_label = lilv_new_uri(_world.world, LILV_NS_RDFS "label"); find_presets_helper(_world.world, _impl->plugin, _presets, oldpset_hasPreset, dc_title); @@ -748,8 +666,8 @@ LV2Plugin::load_preset(PresetRecord r) { Plugin::load_preset(r); - LilvNode* lv2_port = lilv_new_uri(_world.world, NS_LV2 "port"); - LilvNode* lv2_symbol = lilv_new_uri(_world.world, NS_LV2 "symbol"); + LilvNode* lv2_port = lilv_new_uri(_world.world, LILV_NS_LV2 "port"); + LilvNode* lv2_symbol = lilv_new_uri(_world.world, LILV_NS_LV2 "symbol"); LilvNode* oldpset_value = lilv_new_uri(_world.world, NS_OLDPSET "value"); LilvNode* preset = lilv_new_uri(_world.world, r.uri.c_str()); LilvNode* pset_value = lilv_new_uri(_world.world, NS_PSET "value"); @@ -853,31 +771,24 @@ LV2Plugin::set_state(const XMLNode& node, int version) set_parameter(port_id, atof(value)); } - if ((prop = node.property("state-file")) != 0) { - std::string state_path = Glib::build_filename(_session.plugins_dir(), - prop->value()); - - cout << "LV2 state path " << state_path << endl; - - // Get LV2 State extension data from plugin instance - LV2_State_Interface* state_iface = (LV2_State_Interface*)extension_data( - LV2_STATE_INTERFACE_URI); - if (state_iface) { - cout << "Loading LV2 state from " << state_path << endl; - RDFF file = rdff_open(state_path.c_str(), false); - LV2State state(*this, _uri_map); - state.read(file); - state_iface->restore(_impl->instance->lv2_handle, - &LV2Plugin::lv2_state_retrieve_callback, - &state, - LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE, - NULL); - rdff_close(file); - } else { - warning << string_compose( - _("Plugin \"%1\% failed to return LV2 state interface"), - unique_id()); + _state_version = 0; + if ((prop = node.property("state-dir")) != 0) { + if (sscanf(prop->value().c_str(), "state%u", &_state_version) != 1) { + error << string_compose( + "LV2: failed to parse state version from \"%1\"", + prop->value()) << endmsg; } + + std::string state_file = Glib::build_filename(_session.plugins_dir(), + _insert_id.to_s(), + prop->value(), + "state.ttl"); + + cout << "Loading LV2 state from " << state_file << endl; + LilvState* state = lilv_state_new_from_file( + _world.world, _uri_map.urid_map(), NULL, state_file.c_str()); + + lilv_state_restore(state, _impl->instance, NULL, NULL, 0, NULL); } latency_compute_run(); @@ -904,7 +815,7 @@ LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) c desc.lower *= _session.frame_rate (); desc.upper *= _session.frame_rate (); } - + desc.min_unbound = false; // TODO: LV2 extension required desc.max_unbound = false; // TODO: LV2 extension required diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 4cf14cdaf9..30590e3665 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -503,6 +503,13 @@ Session::ensure_subdirs () return -1; } + dir = externals_dir (); + + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session externals folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; + } + return 0; } @@ -2258,6 +2265,12 @@ Session::plugins_dir () const return Glib::build_filename (_path, "plugins"); } +string +Session::externals_dir () const +{ + return Glib::build_filename (_path, "externals"); +} + int Session::load_bundles (XMLNode const & node) { diff --git a/libs/ardour/wscript b/libs/ardour/wscript index b31981ffe0..dccf53735f 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -258,9 +258,12 @@ def configure(conf): if Options.options.lv2: autowaf.check_pkg(conf, 'lilv-0', uselib_store='LILV', atleast_version='0.0.0', mandatory=False) + autowaf.check_pkg(conf, 'lilv-0', uselib_store='NEW_LILV', + atleast_version='0.9.0', mandatory=False) if conf.is_defined('HAVE_LILV'): autowaf.check_pkg(conf, 'suil-0', uselib_store='SUIL', atleast_version='0.2.0', mandatory=False) + # autowaf.check_pkg(conf, 'soundtouch-1.0', uselib_store='SOUNDTOUCH', # mandatory=False) autowaf.check_pkg(conf, 'cppunit', uselib_store='CPPUNIT', @@ -635,6 +635,7 @@ const char* const ardour_config_info = "\\n\\ write_config_text('JACK session support', conf.is_defined('JACK_SESSION')) write_config_text('LV2 UI embedding', conf.is_defined('HAVE_SUIL')) write_config_text('LV2 support', conf.is_defined('LV2_SUPPORT')) + write_config_text('LV2 state support', conf.is_defined('HAVE_NEW_LILV')) write_config_text('LXVST support', conf.is_defined('LXVST_SUPPORT')) write_config_text('OGG', conf.is_defined('HAVE_OGG')) write_config_text('Phone home', conf.is_defined('PHONE_HOME')) |