mirror of
https://github.com/Ardour/ardour.git
synced 2026-01-08 14:45:43 +01:00
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
This commit is contained in:
parent
66cd3d365c
commit
2858d43902
9 changed files with 179 additions and 412 deletions
|
|
@ -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;
|
||||
const std::string scratch_dir () const;
|
||||
const std::string file_dir () const;
|
||||
const std::string state_dir (unsigned num) 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);
|
||||
static char* lv2_state_make_path (void* host_data,
|
||||
const char* path);
|
||||
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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(), "scratch");
|
||||
}
|
||||
|
||||
/** Directory for snapshots of files in the scratch directory. */
|
||||
const std::string
|
||||
LV2Plugin::file_dir() const
|
||||
{
|
||||
return Glib::build_filename(
|
||||
_session.plugins_dir(), _insert_id.to_s(), "files");
|
||||
}
|
||||
|
||||
/** Directory to save state snapshot version @c num into. */
|
||||
const std::string
|
||||
LV2Plugin::state_dir(unsigned num) 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;
|
||||
}
|
||||
}
|
||||
|
||||
const void*
|
||||
LV2Plugin::lv2_state_retrieve_callback(LV2_State_Handle host_data,
|
||||
uint32_t key,
|
||||
size_t* size,
|
||||
uint32_t* type,
|
||||
uint32_t* flags)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
_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 << "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 << "Saving LV2 plugin state to " << state_path << endl;
|
||||
cout << "NEW DIR: " << new_dir << 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;
|
||||
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());
|
||||
root->add_property("state-dir", string_compose("state%1", _state_version));
|
||||
|
||||
// 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-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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
1
wscript
1
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'))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue