summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2011-03-28 23:54:08 +0000
committerDavid Robillard <d@drobilla.net>2011-03-28 23:54:08 +0000
commit2c0cd4d430f8766e224d04e1ebcc07a91b9eef92 (patch)
tree39b65528b96fca6ba1c68b73fdb22329e60c3cab /libs
parent8d86a71f0f25a0a6d73a203077d21bcd955b2bb7 (diff)
Implement most recent LV2 persist extension.
Plugin state data is saved to a simple RIFF-based binary file. Cross-endianness and non-POD data not yet implemented. git-svn-id: svn://localhost/ardour2/branches/3.0@9220 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/lv2_plugin.h22
-rw-r--r--libs/ardour/lv2_pfile.c233
-rw-r--r--libs/ardour/lv2_pfile.h115
-rw-r--r--libs/ardour/lv2_plugin.cc170
-rw-r--r--libs/ardour/lv2ext/lv2_persist.h337
5 files changed, 574 insertions, 303 deletions
diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h
index a1791661bf..71d9bfa624 100644
--- a/libs/ardour/ardour/lv2_plugin.h
+++ b/libs/ardour/ardour/lv2_plugin.h
@@ -149,16 +149,18 @@ class LV2Plugin : public ARDOUR::Plugin
static URIMap _uri_map;
static uint32_t _midi_event_type;
- static void lv2_persist_store_callback (void* callback_data,
- const char* key,
- const void* value,
- size_t size,
- uint32_t type);
-
- static const void* lv2_persist_retrieve_callback (void* callback_data,
- const char* key,
- size_t* size,
- uint32_t* type);
+ static int lv2_persist_store_callback (void* callback_data,
+ uint32_t key,
+ const void* value,
+ size_t size,
+ uint32_t type,
+ bool pod);
+
+ static const void* lv2_persist_retrieve_callback (void* callback_data,
+ uint32_t key,
+ size_t* size,
+ uint32_t* type,
+ bool* pod);
void init (LV2World& world, SLV2Plugin plugin, framecnt_t rate);
void run (pframes_t nsamples);
diff --git a/libs/ardour/lv2_pfile.c b/libs/ardour/lv2_pfile.c
index f5c1c02a78..cdf14b2ed2 100644
--- a/libs/ardour/lv2_pfile.c
+++ b/libs/ardour/lv2_pfile.c
@@ -1,21 +1,24 @@
-/* Portable file-based implementation of LV2 Persist.
- * See <http://lv2plug.in/ns/ext/persist> for details.
- * Copyright (C) 2010 David Robillard <http://drobilla.net>
- *
- * This file 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 file 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 file; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+/*
+ Portable file-based LV2 Persist implementation.
+ See <http://lv2plug.in/ns/ext/persist> for details.
+
+ Copyright 2011 David Robillard <http://drobilla.net>
+
+ This 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 software 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 sofware; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
#include <assert.h>
#include <errno.h>
@@ -25,8 +28,16 @@
#include "lv2_pfile.h"
+#define CHUNK_ID_LEN 4
+
+static const char FILE_TYPE[CHUNK_ID_LEN] = "LV2F"; /* LV2 RIFF File */
+static const char CHUNK_KVAL[CHUNK_ID_LEN] = "KVAL"; /* Key/Value Chunk */
+static const char CHUNK_URID[CHUNK_ID_LEN] = "URID"; /* URI ID Chunk */
+
struct _LV2PFile {
- FILE* fd;
+ FILE* fd;
+ uint32_t size;
+ bool write;
};
LV2PFile
@@ -38,88 +49,125 @@ lv2_pfile_open(const char* path, bool write)
return NULL;
}
- static const char* const magic = "LV2PFILE";
- static const size_t magic_len = 8;
+ uint32_t size = 0;
+
if (write) {
- fwrite(magic, magic_len, 1, fd);
+ fwrite("RIFF", CHUNK_ID_LEN, 1, fd); /* RIFF chunk ID */
+ fwrite(&size, sizeof(size), 1, fd); /* RIFF chunk size */
+ fwrite(FILE_TYPE, CHUNK_ID_LEN, 1, fd); /* LV2 RIFF file type */
} else {
- char file_magic[magic_len];
- if (fread(file_magic, magic_len, 1, fd) != 1
- || strncmp(file_magic, magic, magic_len)) {
+ char magic[CHUNK_ID_LEN];
+ if (fread(magic, CHUNK_ID_LEN, 1, fd) != 1
+ || strncmp(magic, "RIFF", CHUNK_ID_LEN)) {
+ fclose(fd);
+ fprintf(stderr, "%s: error: not a RIFF file\n", path);
+ return NULL;
+ }
+
+ if (fread(&size, sizeof(size), 1, fd) != 1) {
+ fclose(fd);
+ fprintf(stderr, "%s: error: missing RIFF chunk size\n", path);
+ return NULL;
+ }
+
+ if (fread(magic, CHUNK_ID_LEN, 1, fd) != 1
+ || strncmp(magic, FILE_TYPE, CHUNK_ID_LEN)) {
fclose(fd);
+ fprintf(stderr, "%s: error: not an LV2 RIFF file\n", path);
return NULL;
}
}
- LV2PFile ret = (LV2PFile)malloc(sizeof(LV2PFile));
- ret->fd = fd;
+ LV2PFile ret = (LV2PFile)malloc(sizeof(struct _LV2PFile));
+ ret->fd = fd;
+ ret->size = size;
+ ret->write = write;
return ret;
}
-LV2PFileStatus
-lv2_pfile_write(LV2PFile file,
- const char* key,
- const void* value,
- uint64_t size,
- const char* type)
-{
#define WRITE(ptr, size, nmemb, stream) \
if (fwrite(ptr, size, nmemb, stream) != nmemb) { \
return LV2_PFILE_UNKNOWN_ERROR; \
}
- const uint32_t key_len = strlen(key);
- WRITE(&key_len, sizeof(key_len), 1, file->fd);
- WRITE(key, key_len + 1, 1, file->fd);
-
- const uint32_t type_len = strlen(type);
- WRITE(&type_len, sizeof(type_len), 1, file->fd);
- WRITE(type, type_len + 1, 1, file->fd);
-
- WRITE(&size, sizeof(size), 1, file->fd);
- WRITE(value, size, 1, file->fd);
+LV2PFileStatus
+lv2_pfile_write_uri(LV2PFile file,
+ uint32_t id,
+ const char* uri,
+ uint32_t len)
+{
+ const uint32_t chunk_size = sizeof(id) + len + 1;
+ WRITE(CHUNK_URID, CHUNK_ID_LEN, 1, file->fd);
+ WRITE(&chunk_size, sizeof(chunk_size), 1, file->fd);
+ WRITE(&id, sizeof(id), 1, file->fd);
+ WRITE(uri, len + 1, 1, file->fd);
+ if ((chunk_size % 2)) {
+ WRITE("", 1, 1, file->fd); /* pad */
+ }
+ file->size += 8 + chunk_size;
+ return LV2_PFILE_OK;
+}
+LV2PFileStatus
+lv2_pfile_write_value(LV2PFile file,
+ uint32_t key,
+ const void* value,
+ uint32_t size,
+ uint32_t type)
+{
+ const uint32_t chunk_size = sizeof(key) + sizeof(type) + sizeof(size) + size;
+ WRITE(CHUNK_KVAL, CHUNK_ID_LEN, 1, file->fd);
+ WRITE(&chunk_size, sizeof(chunk_size), 1, file->fd);
+ WRITE(&key, sizeof(key), 1, file->fd);
+ WRITE(&type, sizeof(type), 1, file->fd);
+ WRITE(&size, sizeof(size), 1, file->fd);
+ WRITE(value, size, 1, file->fd);
+ if ((size % 2)) {
+ WRITE("", 1, 1, file->fd); /* write pad */
+ }
+ file->size += 8 + chunk_size;
return LV2_PFILE_OK;
}
LV2PFileStatus
-lv2_pfile_read(LV2PFile file,
- char** key,
- uint32_t* key_len,
- char** type,
- uint32_t* type_len,
- void** value,
- uint64_t* size)
+lv2_pfile_read_chunk(LV2PFile file,
+ LV2PFileChunkHeader** buf)
{
if (feof(file->fd))
return LV2_PFILE_EOF;
#define READ(ptr, size, nmemb, stream) \
if (fread(ptr, size, nmemb, stream) != nmemb) { \
- assert(false); \
return LV2_PFILE_CORRUPT; \
}
- READ(key_len, sizeof(*key_len), 1, file->fd);
- *key = (char*)malloc(*key_len + 1);
- READ(*key, *key_len + 1, 1, file->fd);
-
- READ(type_len, sizeof(*type_len), 1, file->fd);
- *type = (char*)malloc(*type_len + 1);
- READ(*type, *type_len + 1, 1, file->fd);
-
- READ(size, sizeof(*size), 1, file->fd);
- *value = malloc(*size);
- READ(*value, *size, 1, file->fd);
+ const uint32_t alloc_size = (*buf)->size;
+ READ((*buf)->type, sizeof((*buf)->type), 1, file->fd);
+ READ(&(*buf)->size, sizeof((*buf)->size), 1, file->fd);
+ if ((*buf)->size > alloc_size) {
+ *buf = realloc(*buf, sizeof(LV2PFileChunkHeader) + (*buf)->size);
+ }
+ READ((*buf)->data, (*buf)->size, 1, file->fd);
+ if (((*buf)->size % 2)) {
+ char pad;
+ READ(&pad, 1, 1, file->fd); /* skip pad */
+ }
return LV2_PFILE_OK;
}
void
lv2_pfile_close(LV2PFile file)
{
- if (file)
+ if (file) {
+ if (file->write) {
+ fseek(file->fd, 4, SEEK_SET);
+ if (fwrite(&file->size, sizeof(file->size), 1, file->fd) != 1) {
+ fprintf(stderr, "failed to write RIFF header size\n");
+ }
+ }
fclose(file->fd);
+ }
free(file);
}
@@ -140,14 +188,23 @@ main(int argc, char** argv)
if (!file)
goto fail;
- char wkey[6];
- char wval[6];
- const char* wtype = "http://example.org/type";
-#define NUM_RECORDS 32
- for (int i = 0; i < NUM_RECORDS; ++i) {
- snprintf(wkey, sizeof(wkey), "KEY%02d", i);
- snprintf(wval, sizeof(wval), "VAL%02d", i);
- lv2_pfile_write(file, wkey, wval, strlen(wval) + 1, wtype);
+ static const int N_URIS = 16;
+ static const int N_RECORDS = 16;
+
+ char uri[64];
+ for (int i = 0; i < N_URIS; ++i) {
+ snprintf(uri, sizeof(uri), "http://example.org/uri%02d", i + 1);
+ lv2_pfile_write_uri(file, i + 1, uri, strlen(uri) + 1);
+ }
+
+ char val[6];
+ for (int i = 0; i < N_RECORDS; ++i) {
+ snprintf(val, sizeof(val), "VAL%02d", i);
+ lv2_pfile_write_value(file,
+ rand() % N_URIS,
+ val,
+ sizeof(val),
+ 0);
}
lv2_pfile_close(file);
@@ -156,23 +213,31 @@ main(int argc, char** argv)
if (!file)
goto fail;
- char* rkey;
- uint32_t rkey_len;
- char* rtype;
- uint32_t rtype_len;
- uint64_t rsize;
- void* rval;
- for (int i = 0; i < NUM_RECORDS; ++i) {
- if (lv2_pfile_read(file, &rkey, &rkey_len, &rtype, &rtype_len, &rval, &rsize))
+ LV2PFileChunkHeader* chunk = malloc(sizeof(LV2PFileChunkHeader));
+ chunk->size = 0;
+ for (int i = 0; i < N_URIS; ++i) {
+ if (lv2_pfile_read_chunk(file, &chunk)
+ || strncmp(chunk->type, "URID", 4)) {
+ fprintf(stderr, "error: expected URID chunk\n");
goto fail;
+ }
+ LV2PFileURIChunk* body = (LV2PFileURIChunk*)chunk->data;
+ printf("URI: %s\n", body->uri);
+ }
- printf("%s = %s :: %s\n", rkey, (char*)rval, rtype);
- free(rkey);
- free(rtype);
- free(rval);
+ for (int i = 0; i < N_RECORDS; ++i) {
+ if (lv2_pfile_read_chunk(file, &chunk)
+ || strncmp(chunk->type, "KVAL", 4)) {
+ fprintf(stderr, "error: expected KVAL chunk\n");
+ goto fail;
+ }
+ LV2PFileValueChunk* body = (LV2PFileValueChunk*)chunk->data;
+ printf("KEY %d = %s\n", body->key, body->value);
}
+ free(chunk);
lv2_pfile_close(file);
+
return 0;
fail:
diff --git a/libs/ardour/lv2_pfile.h b/libs/ardour/lv2_pfile.h
index be4611b87e..8cc97875db 100644
--- a/libs/ardour/lv2_pfile.h
+++ b/libs/ardour/lv2_pfile.h
@@ -1,24 +1,37 @@
-/* Portable file-based implementation of LV2 Persist.
- * See <http://lv2plug.in/ns/ext/persist> for details.
- *
- * This file 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 file 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 file; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+/*
+ Portable file-based LV2 Persist implementation.
+ See <http://lv2plug.in/ns/ext/persist> for details.
+
+ Copyright 2011 David Robillard <http://drobilla.net>
+
+ This 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 software 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 sofware; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+*/
+
+#ifndef LV2PFILE_H
+#define LV2PFILE_H
#include <stdbool.h>
#include <stdint.h>
+#ifdef __GNUC__
+# define PACKED __attribute__((__packed__))
+#else
+# define PACKED
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -33,21 +46,57 @@ typedef enum {
LV2_PFILE_CORRUPT = 3
} LV2PFileStatus;
-/** Open/Create a new persist file. */
+typedef struct {
+ char type[4];
+ uint32_t size;
+ char data[];
+} PACKED LV2PFileChunkHeader;
+
+typedef struct {
+ uint32_t id;
+ char uri[];
+} PACKED LV2PFileURIChunk;
+
+typedef struct {
+ uint32_t key;
+ uint32_t type;
+ uint32_t size;
+ char value[];
+} PACKED LV2PFileValueChunk;
+
+/**
+ Open/Create a new persist file.
+*/
LV2PFile
lv2_pfile_open(const char* path, bool write);
-/** Write a record to a persist file. */
+/**
+ Write a URI ID to @a file.
+*/
LV2PFileStatus
-lv2_pfile_write(LV2PFile file,
- const char* key,
- const void* value,
- uint64_t size,
- const char* type);
-
-/** Read a record from a persist file.
- * @a key and @a value are allocated with malloc and must be freed by caller.
- */
+lv2_pfile_write_uri(LV2PFile file,
+ uint32_t id,
+ const char* uri,
+ uint32_t size);
+
+/**
+ Write a key/value record to @a file.
+*/
+LV2PFileStatus
+lv2_pfile_write_value(LV2PFile file,
+ uint32_t key,
+ const void* value,
+ uint32_t size,
+ uint32_t type);
+LV2PFileStatus
+lv2_pfile_read_chunk(LV2PFile file,
+ LV2PFileChunkHeader** buf);
+
+/**
+ Read a record from a persist file.
+ @a key and @a value are allocated with malloc and must be freed by caller.
+*/
+#if 0
LV2PFileStatus
lv2_pfile_read(LV2PFile file,
char** key,
@@ -56,13 +105,17 @@ lv2_pfile_read(LV2PFile file,
uint32_t* type_len,
void** value,
uint64_t* size);
+#endif
-/** Close a persist file.
- * After this call, @a file is invalid.
- */
+/**
+ Close @a file.
+ After this call, @a file is invalid.
+*/
void
lv2_pfile_close(LV2PFile file);
#ifdef __cplusplus
} /* extern "C" */
#endif
+
+#endif /* LV2PFILE_H */
diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc
index f3675bce12..f2016e6340 100644
--- a/libs/ardour/lv2_plugin.cc
+++ b/libs/ardour/lv2_plugin.cc
@@ -57,7 +57,7 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
-URIMap LV2Plugin:: _uri_map;
+URIMap LV2Plugin::_uri_map;
uint32_t LV2Plugin::_midi_event_type = _uri_map.uri_to_id(
"http://lv2plug.in/ns/ext/event",
"http://lv2plug.in/ns/ext/midi#MidiEvent");
@@ -296,30 +296,108 @@ LV2Plugin::nth_parameter(uint32_t n, bool& ok) const
return 0;
}
-void
+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)
+ {}
+
+ const uint32_t key;
+ const void* value;
+ const size_t size;
+ const uint32_t type;
+ const bool pod;
+};
+
+struct PersistState {
+ PersistState(URIMap& map) : uri_map(map) {}
+
+ typedef std::map<uint32_t, std::string> URIs;
+ typedef std::map<uint32_t, PersistValue> 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()) {
+ 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(make_pair(file_id, str));
+ return 0;
+ }
+
+ int add_value(uint32_t file_key,
+ const void* value,
+ size_t size,
+ uint32_t file_type,
+ bool pod) {
+ 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) {
+ return 1;
+ }
+
+ Values::const_iterator i = values.find(key);
+ if (i != values.end()) {
+ error << "LV2 state contains duplicate keys" << endmsg;
+ return 1;
+ } else {
+ void* value_copy = malloc(size);
+ memcpy(value_copy, value, size); // FIXME: leak
+ values.insert(
+ make_pair(key,
+ PersistValue(key, value_copy, size, type, pod)));
+ return 0;
+ }
+ }
+
+ URIMap& uri_map;
+ URIs uris;
+ Values values;
+};
+
+int
LV2Plugin::lv2_persist_store_callback(void* callback_data,
- const char* key,
+ uint32_t key,
const void* value,
size_t size,
- uint32_t type)
+ uint32_t type,
+ bool pod)
{
- LV2PFile file = (LV2PFile)callback_data;
-
- // FIXME: assumes URIs are mapped in the default context (or not event, at least)
- const char* type_uri = LV2Plugin::_uri_map.id_to_uri(NULL, type);
- cout << "LV2 PERSIST STORE " << key << " = " << value << " :: " << type_uri << endl;
- lv2_pfile_write(file, key, value, size, type_uri);
+ cout << "LV2 PERSIST STORE " << key
+ << " = " << value
+ << " :: " << type
+ << " POD: " << pod << endl;
+
+ 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);
}
const void*
-LV2Plugin::lv2_persist_retrieve_callback(void* callback_data,
- const char* key,
- size_t* size,
- uint32_t* type)
+LV2Plugin::lv2_persist_retrieve_callback(void* callback_data,
+ uint32_t key,
+ size_t* size,
+ uint32_t* type,
+ bool* pod)
{
- //LV2PFile file = (LV2PFile)callback_data;
- cout << "LV2 PERSIST RETRIEVE " << key << endl;
- return NULL;
+ cout << "LV2 PERSIST RETRIEVE " << _uri_map.id_to_uri(NULL, key) << endl;
+
+ PersistState* state = (PersistState*)callback_data;
+ PersistState::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;
+ *pod = true; // FIXME
+ return i->second.value;
}
void
@@ -341,7 +419,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() + ".lv2pfile";
+ const std::string state_filename = _id.to_s() + ".lv2f";
const std::string state_path = Glib::build_filename(
_session.plugins_dir(), state_filename);
@@ -357,8 +435,35 @@ LV2Plugin::add_state(XMLNode* root) const
return;
}
+ // Save plugin state to state object
+ PersistState state(_uri_map);
+ persist->save(_instance->lv2_handle,
+ &LV2Plugin::lv2_persist_store_callback,
+ &state);
+
+ // Open state file
LV2PFile file = lv2_pfile_open(state_path.c_str(), true);
- persist->save(_instance->lv2_handle, &LV2Plugin::lv2_persist_store_callback, file);
+
+ // Write all referenced URIs to state file
+ for (PersistState::URIs::const_iterator i = state.uris.begin();
+ i != state.uris.end(); ++i) {
+ lv2_pfile_write_uri(file, i->first,
+ i->second.c_str(), i->second.length() + 1);
+ }
+
+ // Write all values to state file
+ for (PersistState::Values::const_iterator i = state.values.begin();
+ i != state.values.end(); ++i) {
+ const uint32_t key = i->first;
+ const PersistValue& val = i->second;
+ lv2_pfile_write_value(file,
+ key,
+ val.value,
+ val.size,
+ val.type);
+ }
+
+ // Close state file
lv2_pfile_close(file);
root->add_property("state-file", state_filename);
@@ -527,9 +632,34 @@ LV2Plugin::set_state(const XMLNode& node, int version)
if (persist) {
cout << "Loading LV2 state from " << state_path << endl;
LV2PFile file = lv2_pfile_open(state_path.c_str(), false);
+
+ PersistState state(_uri_map);
+
+ // Load file into state object
+ LV2PFileChunkHeader* chunk = (LV2PFileChunkHeader*)malloc(
+ sizeof(LV2PFileChunkHeader));
+ chunk->size = 0;
+ while (!lv2_pfile_read_chunk(file, &chunk)) {
+ if (!strncmp(chunk->type, "URID", 4)) {
+ LV2PFileURIChunk* body = (LV2PFileURIChunk*)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)) {
+ LV2PFileValueChunk* body = (LV2PFileValueChunk*)chunk->data;
+ printf("READ VAL %u = %s (size: %u type: %u)\n",
+ body->key, body->value, body->size, body->type);
+ state.add_value(body->key,
+ body->value,
+ body->size,
+ body->type,
+ true);
+ }
+ }
+ free(chunk);
+
persist->restore(_instance->lv2_handle,
&LV2Plugin::lv2_persist_retrieve_callback,
- file);
+ &state);
lv2_pfile_close(file);
} else {
warning << string_compose(
diff --git a/libs/ardour/lv2ext/lv2_persist.h b/libs/ardour/lv2ext/lv2_persist.h
index 2ac4880154..5ed6c45030 100644
--- a/libs/ardour/lv2ext/lv2_persist.h
+++ b/libs/ardour/lv2ext/lv2_persist.h
@@ -1,177 +1,198 @@
-/* lv2_persist.h - C header file for the LV2 Persist extension.
- * Copyright (C) 2010 Leonard Ritter <paniq@paniq.org>
- *
- * This header is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This header 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 Lesser General Public
- * License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this header; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA
- */
-
-/** @file
- * C header for the LV2 Persist extension <http://lv2plug.in/ns/ext/persist>.
- */
+/*
+ Copyright (C) 2010-2011 David Robillard <http://drobilla.net>
+ Copyright (C) 2010 Leonard Ritter <paniq@paniq.org>
+
+ This header is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This header 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this header; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA
+*/
+
+/**
+ @file
+ C API for the LV2 Persist extension <http://lv2plug.in/ns/ext/persist>.
+*/
#ifndef LV2_PERSIST_H
#define LV2_PERSIST_H
+#include <stdbool.h>
+#include <stdint.h>
+
#ifdef __cplusplus
extern "C" {
#endif
#define LV2_PERSIST_URI "http://lv2plug.in/ns/ext/persist"
-/** A host-provided function to store a value under a given key.
- * @param callback_data Must be the callback_data passed to LV2_Persist.save().
- * @param key The URI key (predicate) under which the value is to be stored.
- * @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, as a URI mapped to an integer.
- *
- * 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.
- *
- * Unless @a type is 0, @a value is guaranteed to be POD (i.e. a region
- * of memory that does not contain pointers and can safely be copied
- * and persisted indefinitely with a simple memcpy). If @a type is 0,
- * then @a value is a reference, as defined by the LV2 Atom extension
- * <http://lv2plug.in/ns/ext/atom/>. Hosts are not required to support
- * references: a plugin MUST NOT expect a host to persist references unless
- * the host supports the feature <http://lv2plug.in/ns/ext/atom#blobSupport>.
- * Plugins SHOULD express their state entirely with POD values.
- *
- * 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).
- *
- * The plugin MUST NOT attempt to use this function outside of the
- * LV2_Persist.restore() context.
- */
-typedef void (*LV2_Persist_Store_Function)(
- void* callback_data,
- const char* key,
- const void* value,
- size_t size,
- uint32_t type);
-
-/** A host-provided function to retrieve a value under a given key.
- * @param callback_data Must be the callback_data passed to LV2_Persist.restore().
- * @param key The URI key (predicate) under which a value has been stored.
- * @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.
- * @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.
- *
- * 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.
- */
+/**
+ A host-provided function to store a value under a given key.
+ @param callback_data Must be the callback_data passed to LV2_Persist.save().
+ @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.
+ @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.
+
+ 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.
+
+ 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).
+
+ The plugin MUST NOT attempt to use this function outside of the
+ 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);
+
+/**
+ A host-provided function to retrieve a value under a given key.
+ @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 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.
+ @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.
+
+ 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).
+*/
typedef const void* (*LV2_Persist_Retrieve_Function)(
- void* callback_data,
- const char* key,
- size_t* size,
- uint32_t* type);
-
-/** 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 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).
- *
- * 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
- * when restoring state saved by a previous version of that plugin, the plugin
- * URI must change just as it must when a plugin's ports change). Plugin
- * authors should consider this possibility, and always store sensible data
- * with meaningful types to avoid such compatibility issues in the future.
- */
+ void* callback_data,
+ uint32_t key,
+ size_t* size,
+ uint32_t* type,
+ bool* pod);
+
+/**
+ 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 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).
+
+ 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
+ when restoring state saved by a previous version of that plugin, the plugin
+ URI MUST change just as it must when ports change incompatibly). Plugin
+ authors should consider this possibility, and always store sensible data
+ with meaningful types to avoid such compatibility issues in the future.
+*/
typedef struct _LV2_Persist {
-
- /** Save plugin state using a host-provided @a store callback.
- *
- * @param instance The instance handle of the plugin.
- * @param store The host-provided store callback.
- * @param callback_data An opaque pointer to host data, e.g. the map or
- * file where the values are to be stored. If @a store is called,
- * 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.)
- *
- * 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.
- *
- * 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. The simplest way to do this is to modify a
- * copy of the state map and atomically swap a pointer to the entire
- * map once the changes are complete (for very large state maps,
- * a purely functional map data structure may be more appropriate
- * since a complete copy is not necessary).
- */
+
+ /**
+ Save plugin state using a host-provided @a store callback.
+
+ @param instance The instance handle of the plugin.
+ @param store The host-provided store callback.
+ @param callback_data An opaque pointer to host data, e.g. the map or
+ file where the values are to be stored. If @a store is called,
+ 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.)
+
+ 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.
+
+ 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.
+ */
void (*save)(LV2_Handle instance,
LV2_Persist_Store_Function store,
void* callback_data);
- /** Restore plugin state using a host-provided @a retrieve callback.
- *
- * @param instance The instance handle of the plugin.
- * @param retrieve The host-provided retrieve callback.
- * @param callback_data An opaque pointer to host data, e.g. the map or
- * file from which the values are to be restored. If @a retrieve is
- * called, this MUST be passed as its callback_data parameter.
- *
- * 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 @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
- * function on the same plugin instance.
- */
+ /**
+ Restore plugin state using a host-provided @a retrieve callback.
+
+ @param instance The instance handle of the plugin.
+ @param retrieve The host-provided retrieve callback.
+ @param callback_data An opaque pointer to host data, e.g. the map or
+ file from which the values are to be restored. If @a retrieve is
+ called, this MUST be passed as its callback_data parameter.
+
+ 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 @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
+ function on the same plugin instance.
+ */
void (*restore)(LV2_Handle instance,
LV2_Persist_Retrieve_Function retrieve,
void* callback_data);