summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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'))