summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2012-01-28 01:45:15 +0000
committerDavid Robillard <d@drobilla.net>2012-01-28 01:45:15 +0000
commit2858d439020af4d3084d64d36776d77655eb293b (patch)
treea997682b285e8ec358fc6551bac830ee196d4f90
parent66cd3d365cd798045364493426aab11ca7421b01 (diff)
Use new Lilv state API to save LV2 plugin state.
This saves a complete history of plugin state, i.e. save is no longer destructive. However, data is shared as much as possible, and new state is only written if the plugin state has actually changed. There is exactly one link in the entire session directory to any external file, so archiving will work with minimal copying. Not sure sure about the naming of the "externals" directory, but I have nothing better... git-svn-id: svn://localhost/ardour2/branches/3.0@11372 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--libs/ardour/ardour/lv2_plugin.h53
-rw-r--r--libs/ardour/ardour/lv2_state.h147
-rw-r--r--libs/ardour/ardour/session.h7
-rw-r--r--libs/ardour/ardour/uri_map.h21
-rw-r--r--libs/ardour/lv2/lv2plug.in/ns/ext/state/state.h9
-rw-r--r--libs/ardour/lv2_plugin.cc333
-rw-r--r--libs/ardour/session_state.cc13
-rw-r--r--libs/ardour/wscript3
-rw-r--r--wscript1
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',
diff --git a/wscript b/wscript
index 4604b1d4bf..f2d226a09b 100644
--- a/wscript
+++ b/wscript
@@ -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'))