From 73137d533a3e853aa13064cb76d040bcab16dceb Mon Sep 17 00:00:00 2001
From: David Robillard This extension provides a mechanism for plugins to save and restore state
across instances, allowing hosts to save, restore, clone, or take a snapshot of
a plugin instance's state at any point in time. The intention is for a plugin
instance's state to be completely described by port values (as with all
LV2 plugins) and a simple dictionary. The The Because state is a simple dictionary, hosts and plugins can work with it
-easily from many languages and protocols. Keys are URIDs for performance
-reasons as well as RDF compatibility, which makes it simple to serialise state
-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 e.g. presets or default state.
-Specific keys may be described in Turtle on the fly or in extensions,
-allowing plugins to use common well-defined keys. This extension defines a conceptual model of state and a mechanism for
-saving and restoring it, but no interface for manipulating it dynamically.
-While no such mechanism is defined here, dynamic control of plugins SHOULD be
-achieved by generic manipulations of the same conceptual state dictionary used
-by this extension (e.g. In pseudo code, a typical use case in a plugin is:state
described by this extension is conceptually a single
-key/value dictionary, where keys are URIDs and values are type-tagged blobs of
-any type. The plugin provides an LV2_State_Interface for working with this
-state. To save or restore, the host calls LV2_State_Interface::save() or
+state
described by this extension is conceptually a simple
+key:value dictionary, where keys are URIDs (URIs mapped to integers) and values
+are type-tagged blobs of any type. A single key:value pair is called a
+property
. The plugin provides an LV2_State_Interface for working with
+this state. To save or restore, the host calls LV2_State_Interface::save() or
LV2_State_Interface::restore(), passing a callback to be used for handling a
-single key/value pair. The host is free to implement saving and restoring in
-any way; the actual mechanism is completely abstract from the plugin's
-perspective.plugin->set(key, value)
). Accordingly,
-plugins SHOULD use meaningful and well-defined keys wherever possible.
This state model is simple yet has many benefits:
+mapor
dictionarydata + structure.
This extension defines a conceptual state model and a mechanism for saving
+and restoring it, but no interface for manipulating it dynamically. However,
+any such mechanism SHOULD work with the same properties used in this extension
+to avoid complicating the concept of plugin state. For example, an extension
+to the example plugin below could be to support a message like
+set(eg:greeting, "Bonjour")
, which could be sent by the host, UIs,
+or other plugins (via the host) to dynamically control the plugin's state.
+Accordingly, plugins SHOULD use meaningful and well-defined keys wherever
+possible.
#define NS_EG "http://example.org/" #define NS_ATOM "http://lv2plug.in/ns/ext/atom#" @@ -92,7 +106,6 @@ void my_save(LV2_Handle instance, void* handle, uint32_t flags, const LV2_Feature *const * features) - { MyPlugin* plugin = (MyPlugin*)instance; const char* greeting = plugin->state.greeting; @@ -109,18 +122,15 @@ void my_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, void* handle, uint32_t flags, - const LV2_Feature *const * features) + const LV2_Feature *const * features) { MyPlugin* plugin = (MyPlugin*)instance; size_t size; uint32_t type; uint32_t flags; - const char* greeting = retrieve(handle, - plugin->uris.eg_greeting, - &size, - &type, - &flags); + const char* greeting = retrieve( + handle, plugin->uris.eg_greeting, &size, &type, &flags); if (greeting) { free(plugin->state->greeting); @@ -139,7 +149,8 @@ const void* my_extension_data(const char* uri) }-
Similarly, a typical use case in a host is:
+int store_callback(void* handle, uint32_t key, @@ -171,12 +182,33 @@ Map get_plugin_state(LV2_Handle instance) return state_map; }+ +
This extension deliberately avoids imposing any file formats or file system +dependencies on implementations. However, some plugins need to refer to +files in their state. This is done by storing the file's path as a property +just like any other value.
+ +Plugins MUST use the type state:Path for all paths in their state. This +allows hosts to know about all such files, which is necessary for making +session export and archival possible (among other things). Hosts are free to +map paths in any way, and plugins MUST NOT assume the restored path will be +identical to the saved path.
+ +New implementations and basic plugins are strongly encouraged to simply +store all state as properties using this API. However, some plugins have an +existing file or directory formats for state. Plugins MAY create new files +or directories by using the state:newPath feature, if it is provided by the +host.
""" . state:Interface - a rdfs:Class ; - rdfs:subClassOf lv2:ExtensionData ; - lv2:documentation """ + a rdfs:Class ; + rdfs:subClassOf lv2:ExtensionData ; + lv2:documentation """A structure (LV2_State_Interface) which contains functions to be called by the host to save and restore state. In order to support this extension, the plugin must return a valid LV2_State_Interface from @@ -194,23 +226,23 @@ LV2_STATE_INTERFACE_URI.
""" . state:InstanceState - a rdfs:Class ; - rdfs:label "Plugin Instance State" ; - rdfs:comment """ -This class is used to express a plugin instance's state in RDF. The key/value + a rdfs:Class ; + rdfs:label "Plugin Instance State" ; + lv2:documentation """ +This class is used to express a plugin instance's state in RDF. The key/value properties of the instance form the predicate/object (respectively) of triples with a state:InstanceState as the subject (see state:instanceState for an example). This may be used wherever it is useful to express a plugin instance's state in RDF (e.g. for serialisation, storing in a model, or transmitting over a network). Note that this class is provided because it may be useful for hosts, plugins, or extensions that work with instance state, but its use is not -required to support the LV2 State extension. +required to support the LV2 State extension.
""" . state:instanceState - a rdf:Property ; - rdfs:range state:InstanceState ; - lv2:documentation """ + a rdf:Property ; + rdfs:range state:InstanceState ; + lv2:documentation """Predicate to relate a plugin instance to an InstanceState. This may be used wherever the state of a particular plugin instance needs to be represented. Note that the domain of this property is unspecified, since LV2 does not define @@ -219,13 +251,94 @@ sense to do so, e.g.:
@prefix eg: <http://example.org/> . -<plugininstance> state:instanceState [ - eg:somekey "some value" ; - eg:someotherkey "some other value" ; - eg:favourite-number 2 -] . +<plugininstance> + state:instanceState [ + eg:somekey "some value" ; + eg:someotherkey "some other value" ; + eg:favourite-number 2 + ] .
Note that this property is provided because it may be useful for hosts, plugins, or extensions that work with instance state, but its use is not -required to support the LV2 State extension.
+required to support this extension. +""" . + +state:mapPath + a lv2:Feature ; + rdfs:label "Support for storing paths in files" ; + lv2:documentation """ +This feature allows plugins to store paths inside files stored in its state +(if, for example, a plugin saves to an existing file format which contains +paths). To support this feature a host must pass an LV2_Feature with URI +LV2_STATE_MAP_PATH_URI and data pointed to an LV2_State_Map_Path to the +plugin's LV2_State_Interface methods.
+ +The plugin MUST use the provided functions to map all paths stored +in any files it creates (using state:makePath) and stores in the state. This +is necessary to enable host to handle file system references correctly, e.g. +for session export or archival.
+ +For example, a plugin may write a path to a state file like so:
+ ++void write_path(LV2_State_Map_Path* map_path, FILE* myfile, const char* path) +{ + char* abstract_path = map_path->abstract_path(map_path->handle, path); + fprintf(myfile, "%s", abstract_path); + free(abstract_path); +} ++ +
Then, later reload the path like so:
+ ++char* read_path(LV2_State_Map_Path* map_path, FILE* myfile) +{ + /* Obviously this is not production quality code! */ + char abstract_path[1024]; + fscanf(myfile, "%s", abstract_path); + return map_path->absolute_path(map_path->handle, abstract_path); +} ++""" . + +state:makePath + a lv2:Feature ; + rdfs:label "Support for creating new files and directories" ; + lv2:documentation """ + +
This feature allows plugins to create new files or directories. To support +this feature the host passes an LV2_Feature with URI LV2_STATE_MAKE_PATH_URI +and data pointed to an LV2_State_Make_Path to the plugin. The host may provide +this feature during state saving only or at any time, by passing the feature to +LV2_State_Interface::save() or LV2_Descriptor::instantiate(), respectively.
+ +For example, a plugin may create a file in a subdirectory like so:
+ ++void save_myfile(LV2_State_Make_Path* make_path) +{ + char* path = make_path->path(make_path->handle, "foo/bar/myfile.txt"); + FILE* myfile = fopen(path, 'w'); + fprintf(myfile, "Hello"); + fclose(myfile); +} ++""" . + +state:Path + a rdfs:Class ; + rdfs:label "Path" ; + lv2:documentation """ +
A path to a file or directory.
+ +The format of a state:Path is a C string escaped or otherwise restricted in
+a system-specific manner. This URI (LV2_STATE_PATH_URI), mapped to an integer,
+MUST be used as the type
parameter for any files passed to the
+LV2_State_Store_Function; and will likewise be returned by the corresponding
+call to the LV2_State_Retrieve_Function.
When storing and retrieving a path, the plugin MUST NOT assume the same path +will be restored. However, the restored path will refer to a file with +equivalent contents to the original.
""" . -- cgit v1.2.1