Update for latest LV2 persist extension.

git-svn-id: svn://localhost/ardour2/branches/3.0@9225 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2011-03-29 08:56:22 +00:00
parent 02d551d183
commit 9bfc43bf66
5 changed files with 142 additions and 85 deletions

View file

@ -150,17 +150,19 @@ class LV2Plugin : public ARDOUR::Plugin
static uint32_t _midi_event_type;
static int lv2_persist_store_callback (void* callback_data,
uint32_t subject,
uint32_t key,
const void* value,
size_t size,
uint32_t type,
bool pod);
uint32_t flags);
static const void* lv2_persist_retrieve_callback (void* callback_data,
uint32_t subject,
uint32_t key,
size_t* size,
uint32_t* type,
bool* pod);
uint32_t* flags);
void init (LV2World& world, SLV2Plugin plugin, framecnt_t rate);
void run (pframes_t nsamples);

View file

@ -297,15 +297,15 @@ LV2Plugin::nth_parameter(uint32_t n, bool& ok) const
}
struct PersistValue {
inline PersistValue(uint32_t k, const void* v, size_t s, uint32_t t, bool p)
: key(k), value(v), size(s), type(t), pod(p)
inline PersistValue(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 bool pod;
const bool flags;
};
struct PersistState {
@ -329,11 +329,12 @@ struct PersistState {
return 0;
}
int add_value(uint32_t file_key,
int add_value(uint32_t file_subject,
uint32_t file_key,
const void* value,
size_t size,
uint32_t file_type,
bool pod) {
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) {
@ -349,7 +350,7 @@ struct PersistState {
memcpy(value_copy, value, size); // FIXME: leak
values.insert(
make_pair(key,
PersistValue(key, value_copy, size, type, pod)));
PersistValue(key, value_copy, size, type, flags)));
return 0;
}
}
@ -361,11 +362,12 @@ struct PersistState {
int
LV2Plugin::lv2_persist_store_callback(void* callback_data,
uint32_t subject,
uint32_t key,
const void* value,
size_t size,
uint32_t type,
bool pod)
uint32_t flags)
{
DEBUG_TRACE(DEBUG::LV2, string_compose("persist store %1\n",
_uri_map.id_to_uri(NULL, key)));
@ -373,19 +375,17 @@ LV2Plugin::lv2_persist_store_callback(void* callback_data,
PersistState* state = (PersistState*)callback_data;
state->add_uri(key, _uri_map.id_to_uri(NULL, key));
state->add_uri(type, _uri_map.id_to_uri(NULL, type));
return state->add_value(key, value, size, type, pod);
return state->add_value(subject, key, value, size, type, flags);
}
const void*
LV2Plugin::lv2_persist_retrieve_callback(void* callback_data,
uint32_t subject,
uint32_t key,
size_t* size,
uint32_t* type,
bool* pod)
uint32_t* flags)
{
DEBUG_TRACE(DEBUG::LV2, string_compose("persist retrieve %1\n",
_uri_map.id_to_uri(NULL, key)));
PersistState* state = (PersistState*)callback_data;
PersistState::Values::const_iterator i = state->values.find(key);
if (i == state->values.end()) {
@ -395,7 +395,10 @@ LV2Plugin::lv2_persist_retrieve_callback(void* callback_data,
}
*size = i->second.size;
*type = i->second.type;
*pod = true; // FIXME
*flags = LV2_PERSIST_IS_POD | LV2_PERSIST_IS_PORTABLE; // FIXME
DEBUG_TRACE(DEBUG::LV2, string_compose(
"persist retrieve %1 = %s (size: %u, type: %u)\n",
_uri_map.id_to_uri(NULL, key), i->second.value, *size, *type));
return i->second.value;
}
@ -418,7 +421,7 @@ LV2Plugin::add_state(XMLNode* root) const
if (_supports_persist) {
// Create state directory for this plugin instance
const std::string state_filename = _id.to_s() + ".lv2f";
const std::string state_filename = _id.to_s() + ".rdff";
const std::string state_path = Glib::build_filename(
_session.plugins_dir(), state_filename);
@ -641,20 +644,21 @@ LV2Plugin::set_state(const XMLNode& node, int version)
RDFFChunk* chunk = (RDFFChunk*)malloc(sizeof(RDFFChunk));
chunk->size = 0;
while (!rdff_read_chunk(file, &chunk)) {
if (!strncmp(chunk->type, "URID", 4)) {
if (rdff_chunk_is_uri(chunk)) {
RDFFURIChunk* body = (RDFFURIChunk*)chunk->data;
printf("READ URI %u: %s\n", body->id, body->uri);
state.add_uri(body->id, body->uri);
} else if (!strncmp(chunk->type, "KVAL", 4)) {
} else if (rdff_chunk_is_triple(chunk)) {
RDFFTripleChunk* body = (RDFFTripleChunk*)chunk->data;
printf("READ VAL %u = %s (size: %u type: %u)\n",
body->predicate, body->object,
body->object_size, body->object_type);
state.add_value(body->predicate,
state.add_value(body->subject,
body->predicate,
body->object,
body->object_size,
body->object_type,
true);
LV2_PERSIST_IS_POD | LV2_PERSIST_IS_PORTABLE);
}
}
free(chunk);

View file

@ -35,34 +35,59 @@ extern "C" {
#define LV2_PERSIST_URI "http://lv2plug.in/ns/ext/persist"
/**
A host-provided function to store a value under a given key.
Flags describing value characteristics.
These flags are used along with the value's type URI to determine how to
(de-)serialise the value data, or whether it is even possible to do so.
*/
typedef enum {
/**
Plain Old Data.
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
(e.g. the same endianness and alignment).
Implementations MUST NOT attempt to copy or serialise a non-POD value if
they do not understand its type (and thus know how to correctly do so).
*/
LV2_PERSIST_IS_POD = 1,
/**
Portable (architecture independent) data.
Values with this flag are in a format that is usable on any
architecture, i.e. if the value is saved on one machine it can safely be
restored on another machine regardless of endianness, alignment, etc.
*/
LV2_PERSIST_IS_PORTABLE = 1 << 1
} LV2_Persist_Flags;
/**
A host-provided function to store a property.
@param callback_data Must be the callback_data passed to LV2_Persist.save().
@param subject The subject of this property (URI), or 0 for plugin instance.
@param key The key (predicate) to store @a value under (URI mapped integer).
@param value Pointer to the value (object) to be stored.
@param size The size of the data at @a value in bytes.
@param type The type of @a value (URI mapped integer).
@param pod True iff @a value is POD.
@param type The type of @a value (URI).
@param flags LV2_Persist_Flags for @a value.
@return 0 on success, otherwise a non-zero error code.
The host passes a callback of this type to LV2_Persist.save().
This callback is called repeatedly by the plugin within
LV2_Persist.save() to store all the key/value records that describe
its current state.
The host passes a callback of this type to LV2_Persist.save(). This callback
is called repeatedly by the plugin within LV2_Persist.save() to store all
the statements that describe its current state.
If @a pod is true, @a value is guaranteed to be architecture-independent POD
(i.e. a region of memory that does not contain pointers or references to
non-persistent resources and can safely be copied and stored with a simple
memcpy). Note that this definition of POD is more strict than exclusively
in-memory definitions since the value MUST be architecture independent;
e.g. endianness must be considered (so basic numeric types are typically NOT
POD). Hosts MAY fail to store the value, particularly if it is
non-POD. Plugins MUST gracefully handle this situation, even though state
may not be fully restored. Hosts SHOULD support any POD value, even if the
host does not know anything about its type. Plugins SHOULD express their
state entirely with POD values whenever possible, and use non-POD values
only where necessary. Plugins SHOULD use common RDF types and/or types from
the Atom extension <http://lv2plug.in/ns/ext/atom> whenever possible since
hosts are likely to already contain the necessary implementation.
The host MAY fail to store a statement if the type is not understood and is
not LV2_PERSIST_IS_POD and/or LV2_PERSIST_IS_PORTABLE. Implementations are
encouraged to use POD and portable values (e.g. string literals) wherever
possible, and use common types (e.g. types from
http://lv2plug.in/ns/ext/atom) regardless, since hosts are likely to already
contain the necessary implementation.
Note that @a size MUST be > 0, and @a value MUST point to a valid region of
memory @a size bytes long (this is required to make restore unambiguous).
@ -71,56 +96,60 @@ extern "C" {
LV2_Persist.restore() context.
*/
typedef int (*LV2_Persist_Store_Function)(
void* callback_data,
const uint32_t key,
const void* value,
size_t size,
uint32_t type,
bool pod);
void* callback_data,
uint32_t subject,
uint32_t key,
const void* value,
size_t size,
uint32_t type,
uint32_t flags);
/**
A host-provided function to retrieve a value under a given key.
A host-provided function to retrieve a property.
@param callback_data Must be the callback_data passed to LV2_Persist.restore().
@param key The key (predicate) of the value to retrieve (URI mapped integer).
@param subject The subject of the property (URI), or 0 for plugin instance.
@param key The key (predicate) of the property to retrieve (URI).
@param size (Output) If non-NULL, set to the size of the restored value.
@param type (Output) If non-NULL, set to the type of the restored value.
@param pod (Output) If non-NULL, set to true iff @a value is POD.
@param flags (Output) If non-NULL, set to the LV2_Persist_Flags for
the returned value.
@return A pointer to the restored value (object), or NULL if no value
has been stored under @a key.
A callback of this type is passed by the host to LV2_Persist.restore(). This
callback is called repeatedly by the plugin within LV2_Persist.restore() to
retrieve the values of any keys it requires to restore its state.
retrieve any properties it requires to restore its state.
The returned value MUST remain valid until LV2_Persist.restore() returns.
The plugin MUST NOT attempt to use this function, or any value returned from
it, outside of the LV2_Persist.restore() context. Returned values MAY be
copied for later use if necessary, assuming the plugin knows how to
correctly do so (e.g. the value is POD, or the plugin understands the type).
copied for later use if necessary, assuming the plugin knows how to do so
correctly (e.g. the value is POD, or the plugin understands the type).
*/
typedef const void* (*LV2_Persist_Retrieve_Function)(
void* callback_data,
uint32_t subject,
uint32_t key,
size_t* size,
uint32_t* type,
bool* pod);
uint32_t* flags);
/**
Persist Extension Data.
When the plugin's extension_data is called with argument LV2_PERSIST_URI,
the plugin MUST return an LV2_Persist structure, which remains valid for
the lifetime of the plugin.
the plugin MUST return an LV2_Persist structure, which remains valid for the
lifetime of the plugin.
The host can use the contained function pointers to save and restore the
state of a plugin instance at any time (provided the threading restrictions
for the given function are met).
The typical use case is to save the plugin's state when a project is
saved, and to restore the state when a project has been loaded. Other
uses are possible (e.g. cloning plugin instances or taking a snapshot
of plugin state).
The typical use case is to save the plugin's state when a project is saved,
and to restore the state when a project has been loaded. Other uses are
possible (e.g. cloning plugin instances or taking a snapshot of plugin
state).
Stored data is only guaranteed to be compatible between instances of plugins
with the same URI (i.e. if a change to a plugin would cause a fatal error
@ -141,30 +170,27 @@ typedef struct _LV2_Persist {
this MUST be passed as its callback_data parameter.
The plugin is expected to store everything necessary to completely
restore its state later (possibly much later, in a different
process, on a completely different machine, etc.)
restore its state later (possibly much later, in a different process, on
a completely different machine, etc.)
The @a callback_data pointer and @a store function MUST NOT be
used beyond the scope of save().
The @a callback_data pointer and @a store function MUST NOT be used
beyond the scope of save().
This function has its own special threading class: it may not be
called concurrently with any "Instantiation" function, but it
may be called concurrently with functions in any other class,
unless the definition of that class prohibits it (e.g. it may
not be called concurrently with a "Discovery" function, but it
may be called concurrently with an "Audio" function. The plugin
is responsible for any locking or lock-free techniques necessary
to make this possible.
This function has its own special threading class: it may not be called
concurrently with any "Instantiation" function, but it may be called
concurrently with functions in any other class, unless the definition of
that class prohibits it (e.g. it may not be called concurrently with a
"Discovery" function, but it may be called concurrently with an "Audio"
function. The plugin is responsible for any locking or lock-free
techniques necessary to make this possible.
Note that in the simple case where state is only modified by
restore(), there are no synchronization issues since save() is
never called concurrently with restore() (though run() may read
it during a save).
Note that in the simple case where state is only modified by restore(),
there are no synchronization issues since save() is never called
concurrently with restore() (though run() may read it during a save).
Plugins that dynamically modify state while running, however,
must take care to do so in such a way that a concurrent call to
save() will save a consistent representation of plugin state for a
single instant in time.
Plugins that dynamically modify state while running, however, must take
care to do so in such a way that a concurrent call to save() will save a
consistent representation of plugin state for a single instant in time.
*/
void (*save)(LV2_Handle instance,
LV2_Persist_Store_Function store,
@ -182,15 +208,15 @@ typedef struct _LV2_Persist {
The plugin MAY assume a restored value was set by a previous call to
LV2_Persist.save() by a plugin with the same URI.
The plugin MUST gracefully fall back to a default value when a
value can not be retrieved. This allows the host to reset the
plugin state with an empty map.
The plugin MUST gracefully fall back to a default value when a value can
not be retrieved. This allows the host to reset the plugin state with an
empty map.
The @a callback_data pointer and @a store function MUST NOT be used
beyond the scope of restore().
This function is in the "Instantiation" threading class as defined
by LV2. This means it MUST NOT be called concurrently with any other
This function is in the "Instantiation" threading class as defined by
LV2. This means it MUST NOT be called concurrently with any other
function on the same plugin instance.
*/
void (*restore)(LV2_Handle instance,

View file

@ -163,6 +163,19 @@ rdff_read_chunk(RDFF file,
return RDFF_STATUS_OK;
}
bool
rdff_chunk_is_uri(RDFFChunk* chunk)
{
return !strncmp(chunk->type, CHUNK_URID, CHUNK_ID_LEN);
}
bool
rdff_chunk_is_triple(RDFFChunk* chunk)
{
return !strncmp(chunk->type, CHUNK_TRIP, CHUNK_ID_LEN);
}
void
rdff_close(RDFF file)
{

View file

@ -121,6 +121,18 @@ RDFFStatus
rdff_read_chunk(RDFF file,
RDFFChunk** buf);
/**
Return true iff @a chunk is a URI chunk.
*/
bool
rdff_chunk_is_uri(RDFFChunk* chunk);
/**
Return true iff @a chunk is a Triple chunk.
*/
bool
rdff_chunk_is_triple(RDFFChunk* chunk);
/**
Close @a file.
After this call, @a file is invalid.