aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ext/persist.lv2/persist.h50
-rw-r--r--ext/persist.lv2/persist.ttl123
2 files changed, 120 insertions, 53 deletions
diff --git a/ext/persist.lv2/persist.h b/ext/persist.lv2/persist.h
index 928a297..2ac4880 100644
--- a/ext/persist.lv2/persist.h
+++ b/ext/persist.lv2/persist.h
@@ -29,26 +29,31 @@ extern "C" {
#define LV2_PERSIST_URI "http://lv2plug.in/ns/ext/persist"
-/** Causes the host to store a value under a given key.
- *
- * This callback is passed by the host to LV2_Persist.save().
+/** 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 (RDF predicate) under which the value is to be stored.
- * @param value Pointer to the value (RDF object) to be stored.
+ * @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
+ * 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).
- * If only the key is of interest, store the empty string (which has size 1).
+ *
+ * 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,
@@ -57,19 +62,23 @@ typedef void (*LV2_Persist_Store_Function)(
size_t size,
uint32_t type);
-/** Causes the host to retrieve a value under a given key.
- *
- * This callback is passed by the host to LV2_Persist.restore().
+/** 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 (RDF predicate) under which a value has been stored.
+ * @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 (RDF object), or NULL if no value
+ * @return A pointer to the restored value (object), or NULL if no value
* has been stored under @a key.
*
- * The returned value MUST remain valid until LV2_Persist.restore() returns. The plugin
- * MUST NOT attempt to access a returned pointer outside of the LV2_Persist.restore()
- * context (it MUST make a copy in order to do so).
+ * 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.
*/
typedef const void* (*LV2_Persist_Retrieve_Function)(
void* callback_data,
@@ -98,8 +107,8 @@ typedef const void* (*LV2_Persist_Retrieve_Function)(
* with meaningful types to avoid such compatibility issues in the future.
*/
typedef struct _LV2_Persist {
- /** Causes the plugin to save state data using a host-provided
- * @a store callback.
+
+ /** 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.
@@ -131,18 +140,17 @@ typedef struct _LV2_Persist {
* 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 point in time. The simplest way to do this is to modify 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 would be more appropriate
+ * a purely functional map data structure may be more appropriate
* since a complete copy is not necessary).
*/
void (*save)(LV2_Handle instance,
LV2_Persist_Store_Function store,
void* callback_data);
- /** Causes the plugin to restore state data using a host-provided
- * @a retrieve callback.
+ /** 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.
diff --git a/ext/persist.lv2/persist.ttl b/ext/persist.lv2/persist.ttl
index 1cbbdb7..490e2c1 100644
--- a/ext/persist.lv2/persist.ttl
+++ b/ext/persist.lv2/persist.ttl
@@ -47,38 +47,97 @@ This extension provides a mechanism for plugins to save and restore state
across instances, allowing hosts to save configuration/state/data with a
project or fully clone a plugin instance (including internal state).
-The motivating idea behind this extension is that a plugin instance's state
-is entirely represented by port values and a single key/value dictionary.
-This makes state well-defined and easily manageable by hosts. Keys are URIs,
-avoiding conflicts and allowing the same dictionary to be used to store plugin
-state in any context. Values are typed tagged (by URI mapped integers),
-but otherwise are simple binary blobs.
-
-This extension defines plugin instance state and provides a mechanism
-for saving/restoring it, but otherwise makes no restrictions on how a
-plugin works with state. For example, other extensions may define dynamic
-ways to control plugin state at runtime. The idea is that <em>all</em>
-plugin state can be represented with a single (conceptual) dictionary.
-This state representation is tried-and-true, universal, and works well with
-many existing technologies. Accordingly, plugins/extensions that deal with
-instance state in any way SHOULD represent it in a way compatible with this
-extension, i.e. URI keys with URI-typed values. Similarly, plugins SHOULD NOT
-use any other mechanism to store/restore state; this <strong>will</strong>
-cause serious problems, don't do it! Note that you can store values of any
-format whatsoever, so if you have an existing state representation to use
-(e.g. XML), simply store it as a single value under some key.
-
-Files may be persisted using this extension in conjunction with the
-<a href="http://lv2plug.in/ns/ext/files">LV2 Files</a> extension.
-
-Instance state as defined by this extension is RDF compatible, allowing for
-simple and seamless integration with existing technology (LV2 or otherwise).
-An obvious benefit of this is that plugin state can be elegantly described
-in Turtle files; the persist:instanceState predicate is provided for this
-purpose. RDF compatibility is also convenient since LV2 hosts are likely
-to already have mechanisms for working with RDF-style data. Note, however,
-that hosts may store state in any way, and are not required to use any
-specific technology or file format to support this extension.
+Unlike ports, this extension allows plugins to save private state data.
+The motivating ideal behind this extension is for the state of a plugin
+instance to be entirely described by port values (as with all LV2 plugins) and
+a key/value dictionary as defined by this extension. This mechanism is simple,
+yet sufficiently powerful to describe the state of very advanced plugins.
+
+The "state" described by this extension is conceptually a single key/value
+dictionary. Keys are URIs, and values are typed-tagged blobs of any type.
+The plugin provides a save and restore method for saving and restoring state.
+To initiate a save or restore, the host calls these methods, passing a callback
+to be used for saving or restoring a single key/value pair. In this way, the
+actual mechanism of saving and restoring state is completely abstract from the
+plugin's perspective.
+
+Because the state is a simple dictionary, hosts and plugins can work with state
+easily (virtually all programming languages have an appropriate dictionary
+type available). Additionally, this format is simple and terse to serialise
+in many formats (e.g. any RDF syntax, JSON, XML, key/value databases such as
+BDB, etc.). In particular, state can be elegantly described in a plugin's
+Turtle description, which is useful for presets (among other things).
+Note that these are all simply possibilities enabled by this simple data
+model. This extension defines only a few function prototypes and does not
+impose any requirement to use a particular syntax, data structure, library,
+or other implementation detail. Hosts are free to work with plugin state
+in whatever way is most appropriate for that host.
+
+This extension makes it possible for plugins to save private data, but state is
+not necessarily private, e.g. a plugin could have a public interface via ports
+for manipulating internal state, which would be saved using this extension.
+Plugins are strongly encouraged to represent all state change as modifications
+of such key/value variables, to minimize implementation burden and enable
+the many benefits of having a universal model for describing plugin state.
+The use of URI keys prevents conflict and allows unrelated plugins to
+meaningfully describe state changes. Future extensions will describe a
+dynamic mechanism for manipulating plugin state, as well as define various
+keys likely to be useful to a wide range of plugins.
+
+In pseudo code, a typical use case in a plugin is:
+<pre>
+static const char* const KEY_GREETING = "http://example.org/greeting";
+
+void my_save(LV2_Handle instance,
+ LV2_Persist_Store_Function store,
+ void* callback_data)
+{
+ MyPlugin* plugin = (MyPlugin*)instance;
+ const char* greeting = plugin->state->greeting;
+
+ store(callback_data, KEY_GREETING,
+ greeting, strlen(greeting) + 1,
+ lv2_uri_map("http://lv2plug.in/ns/ext/atom#String"));
+}
+
+void my_restore(LV2_Handle instance,
+ LV2_Persist_Retrieve_Function retrieve,
+ void* callback_data)
+{
+ MyPlugin* plugin = (MyPlugin*)instance;
+
+ size_t size;
+ uint32_t type;
+ const char* greeting = retrieve(callback_data, KEY_GREETING, &size, &type);
+
+ if (greeting)
+ plugin->state->greeting = greeting;
+ else
+ plugin->state->greeting = "Hello";
+
+}
+</pre>
+
+Similarly, a typical use case in a host is:
+<pre>
+void store_callback(void* callback_data,
+ const char* key,
+ const void* value,
+ size_t size,
+ uint32_t type)
+{
+ Map* state_map = (Map*)callback_data;
+ state_map->insert(key, Value(value, size, type));
+}
+
+Map get_plugin_state(LV2_Handle instance)
+{
+ LV2_Persist* persist = instance.extension_data("http://lv2plug.in/ns/ext/persist");
+ Map state_map;
+ persist.save(instance, store_callback, &state_map);
+ return state_map;
+}
+</pre>
""" .
persist:InstanceState