diff options
-rw-r--r-- | lv2/lv2plug.in/ns/ext/state/manifest.ttl | 2 | ||||
-rw-r--r-- | lv2/lv2plug.in/ns/ext/state/state.h | 34 | ||||
-rw-r--r-- | lv2/lv2plug.in/ns/ext/state/state.ttl | 151 | ||||
-rw-r--r-- | lv2/lv2plug.in/ns/ext/worker/worker.h | 4 | ||||
-rw-r--r-- | lv2/lv2plug.in/ns/ext/worker/worker.ttl | 9 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/sampler.c | 12 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/sampler.ttl | 26 |
7 files changed, 133 insertions, 105 deletions
diff --git a/lv2/lv2plug.in/ns/ext/state/manifest.ttl b/lv2/lv2plug.in/ns/ext/state/manifest.ttl index 84d1af0..94de219 100644 --- a/lv2/lv2plug.in/ns/ext/state/manifest.ttl +++ b/lv2/lv2plug.in/ns/ext/state/manifest.ttl @@ -4,5 +4,5 @@ <http://lv2plug.in/ns/ext/state> a lv2:Specification ; lv2:minorVersion 0 ; - lv2:microVersion 6 ; + lv2:microVersion 8 ; rdfs:seeAlso <state.ttl> . diff --git a/lv2/lv2plug.in/ns/ext/state/state.h b/lv2/lv2plug.in/ns/ext/state/state.h index 2f5af22..44e19cb 100644 --- a/lv2/lv2plug.in/ns/ext/state/state.h +++ b/lv2/lv2plug.in/ns/ext/state/state.h @@ -31,8 +31,8 @@ #define LV2_STATE_URI "http://lv2plug.in/ns/ext/state" #define LV2_STATE_PREFIX LV2_STATE_URI "#" -#define LV2_STATE__Interface LV2_STATE_PREFIX "Interface" #define LV2_STATE__State LV2_STATE_PREFIX "State" +#define LV2_STATE__interface LV2_STATE_PREFIX "interface" #define LV2_STATE__makePath LV2_STATE_PREFIX "makePath" #define LV2_STATE__mapPath LV2_STATE_PREFIX "mapPath" #define LV2_STATE__state LV2_STATE_PREFIX "state" @@ -95,10 +95,12 @@ typedef enum { /** A status code for state functions. */ typedef enum { - LV2_STATE_SUCCESS = 0, /**< Completed successfully. */ - LV2_STATE_ERR_UNKNOWN = 1, /**< Unknown error. */ - LV2_STATE_ERR_BAD_TYPE = 2, /**< Failed due to unsupported type. */ - LV2_STATE_ERR_BAD_FLAGS = 3 /**< Failed due to unsupported flags. */ + LV2_STATE_SUCCESS = 0, /**< Completed successfully. */ + LV2_STATE_ERR_UNKNOWN = 1, /**< Unknown error. */ + LV2_STATE_ERR_BAD_TYPE = 2, /**< Failed due to unsupported type. */ + LV2_STATE_ERR_BAD_FLAGS = 3, /**< Failed due to unsupported flags. */ + LV2_STATE_ERR_NO_FEATURE = 4, /**< Failed due to missing features. */ + LV2_STATE_ERR_NO_PROPERTY = 5 /**< Failed due to missing property. */ } LV2_State_Status; /** @@ -171,7 +173,7 @@ typedef const void* (*LV2_State_Retrieve_Function)( LV2 Plugin State Interface. When the plugin's extension_data is called with argument - LV2_STATE__Interface, the plugin MUST return an LV2_State_Interface + LV2_STATE__interface, the plugin MUST return an LV2_State_Interface structure, which remains valid for the lifetime of the plugin. The host can use the contained function pointers to save and restore the @@ -223,11 +225,11 @@ typedef struct _LV2_State_Interface { 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_State_Store_Function store, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature *const * features); + LV2_State_Status (*save)(LV2_Handle instance, + LV2_State_Store_Function store, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features); /** Restore plugin state using a host-provided @p retrieve callback. @@ -254,11 +256,11 @@ typedef struct _LV2_State_Interface { LV2. This means it MUST NOT be called concurrently with any other function on the same plugin instance. */ - void (*restore)(LV2_Handle instance, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature *const * features); + LV2_State_Status (*restore)(LV2_Handle instance, + LV2_State_Retrieve_Function retrieve, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features); } LV2_State_Interface; diff --git a/lv2/lv2plug.in/ns/ext/state/state.ttl b/lv2/lv2plug.in/ns/ext/state/state.ttl index 6bc884b..2d12d08 100644 --- a/lv2/lv2plug.in/ns/ext/state/state.ttl +++ b/lv2/lv2plug.in/ns/ext/state/state.ttl @@ -30,8 +30,8 @@ doap:shortdesc "An interface for LV2 plugins to save and restore state." ; doap:license <http://opensource.org/licenses/isc> ; doap:release [ - doap:revision "0.6" ; - doap:created "2012-03-03" ; + doap:revision "0.8" ; + doap:created "2012-03-30" ; dcs:blame <http://drobilla.net/drobilla#me> ] ; doap:developer <http://lv2plug.in/ns/people#paniq> , @@ -68,7 +68,7 @@ key:value pair is called a <q>property</q>.</p> dynamic dictionary data structure available.</li> </ul> -<p>To implement state, the plugin provides a state:Interface to the host. To +<p>To implement state, the plugin provides a state:interface to the host. 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 property. The host is free to implement property storage and retrieval @@ -85,15 +85,15 @@ is simply a C string.</p> <p>Plugins may need to refer to existing files (e.g. loaded samples) in their state. This is done by storing the file's path as a property just like any other value. However, there are some rules which MUST be followed when storing -paths, see <a href="#mapPath">state:mapPath</a> for details. Plugins MUST use -the type <a href="http://lv2plug.in/ns/ext/atom#Path">atom:Path</a> for all -paths in their state.</p> +paths, see state:mapPath for details. Plugins MUST use the type <a +href="http://lv2plug.in/ns/ext/atom#Path">atom:Path</a> for all paths in their +state.</p> <p>Plugins are strongly encouraged to avoid creating files, instead storing all state as properties. However, occasionally the ability to create files is -necessary. To make this possible, the host can provide the feature <a -href="#makePath">state:makePath</a> which allocates paths for plugin-created -files. Plugins MUST NOT create files in any other locations.</p> +necessary. To make this possible, the host can provide the feature +state:makePath which allocates paths for plugin-created files. Plugins MUST +NOT create files in any other locations.</p> <h3>Plugin Code Example</h3> @@ -104,20 +104,24 @@ files. Plugins MUST NOT create files in any other locations.</p> */ #define NS_MY "http://example.org/myplugin/schema#" -LV2_Handle my_instantiate(...) +#define DEFAULT_GREETING "Hello" + +LV2_Handle +my_instantiate(...) { MyPlugin* plugin = ...; plugin->uris.atom_String = map_uri(LV2_ATOM__String); plugin->uris.my_greeting = map_uri(NS_MY "greeting"); - plugin->state.greeting = strdup("Hello"); // Default value + plugin->state.greeting = strdup(DEFAULT_GREETING); return plugin; } -void my_save(LV2_Handle instance, - LV2_State_Store_Function store, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature *const * features) +LV2_State_Status +my_save(LV2_Handle instance, + LV2_State_Store_Function store, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features) { MyPlugin* plugin = (MyPlugin*)instance; const char* greeting = plugin->state.greeting; @@ -128,13 +132,16 @@ void my_save(LV2_Handle instance, strlen(greeting) + 1, // Careful! Need space for terminator plugin->uris.atom_String, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); + + return LV2_STATUS_SUCCESS; } -void my_restore(LV2_Handle instance, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - uint32_t flags, - const LV2_Feature *const * features) +LV2_State_Status +my_restore(LV2_Handle instance, + LV2_State_Retrieve_Function retrieve, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features) { MyPlugin* plugin = (MyPlugin*)instance; @@ -148,14 +155,17 @@ void my_restore(LV2_Handle instance, free(plugin->state->greeting); plugin->state->greeting = strdup(greeting); } else { - plugin->state->greeting = strdup("Hello"); + plugin->state->greeting = strdup(DEFAULT_GREETING); } + + return LV2_STATUS_SUCCESS; } -const void* my_extension_data(const char* uri) +const void* +my_extension_data(const char* uri) { static const LV2_State_Interface state_iface = { my_save, my_restore }; - if (!strcmp(uri, LV2_STATE_INTERFACE_URI)) { + if (!strcmp(uri, LV2_STATE__interface)) { return &state_iface; } } @@ -164,12 +174,13 @@ const void* my_extension_data(const char* uri) <h3>Host Code Example</h3> <pre class="c-code"> -int store_callback(LV2_State_Handle handle, - uint32_t key, - const void* value, - size_t size, - uint32_t type, - uint32_t flags) +LV2_State_Status +store_callback(LV2_State_Handle handle, + uint32_t key, + const void* value, + size_t size, + uint32_t type, + uint32_t flags) { if ((flags & LV2_STATE_IS_POD)) { /* We only care about POD since we're keeping state in memory only. @@ -183,9 +194,10 @@ int store_callback(LV2_State_Handle handle, } } -Map get_plugin_state(LV2_Handle instance) +Map +get_plugin_state(LV2_Handle instance) { - LV2_State* state = instance.extension_data(LV2_STATE__Interface); + LV2_State* state = instance.extension_data(LV2_STATE__interface); Map state_map; /** Request a fast/native/POD save, since we're just copying in memory */ state.save(instance, store_callback, &state_map, @@ -209,26 +221,36 @@ instance, at least well enough that saving and restoring will yield an plugin instance.</li> </ul> -<p>The recommended way to add new functionality that may affect state is to -simply define it in terms of properties. For example, instead of defining a -new set of commands, define properties whose values can be set appropriately. -This provides persistence <q>for free</q>, since rather than having to define a -set of commands <em>and</em> a set of properties for storing their effects, -only the properties need to be defined.</p> - -<p>This <q>property principle</q> is summed up in the phrase: <q>Don't -stop; set playing to false</q>.</p> +<h3>The <q>Property Principle</q></h3> + +<p>The main benefit of this meaningful state model is that it can double as a +plugin control/query mechanism. For plugins that require more advanced control +than simple control ports, instead of defining a set of commands, define +properties whose values can be set appropriately. This provides both a way to +control and save that state <q>for free</q>, since there is no need to define +commands <em>and</em> a set of properties for storing their effects. In +particular, this is a good way for UIs to achieve more advanced control of +plugins.</p> + +<p>This <q>property principle</q> is summed up in the phrase: +<q>Don't stop; set playing to false</q>.</p> + +<p>This extension does not define a dynamic mechanism for state access and +manipulation. The <a href="http://lv2plug.in/ns/ext/patch">LV2 Patch</a> +extension defines a generic set of messages which can be used to access or +manipulate properties, and the <a href="http://lv2plug.in/ns/ext/atom">LV2 +Atom</a> extension defines a port type and data container capable of +transmitting those messages.</p> """ . -state:Interface - a rdfs:Class ; - rdfs:subClassOf lv2:ExtensionData ; +state:interface + a lv2:ExtensionData ; lv2:documentation """ <p>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 LV2_Descriptor::extension_data() when it is called with URI -LV2_STATE__Interface.</p> +LV2_STATE__interface.</p> <p>The plugin data file should describe this like so:</p> <pre class="turtle-code"> @@ -236,7 +258,7 @@ LV2_STATE__Interface.</p> <plugin> a lv2:Plugin ; - lv2:extensionData state:Interface . + lv2:extensionData state:interface . </pre> """ . @@ -245,9 +267,9 @@ state:State rdfs:label "Plugin Instance State" ; lv2:documentation """ <p>A state dictionary. This type should be used wherever instance state is -described in RDF, or any other context that uses type URIs. The properties of -a resource with this type correspond directly to the properties of the state -dictionary (except the property that states it has this type).</p> +described. The properties of a resource with this type correspond directly to +the properties of the state dictionary (except the property that states it has +this type).</p> """ . state:state @@ -255,13 +277,13 @@ state:state rdfs:label "State" ; rdfs:range state:State ; lv2:documentation """ -<p>The state of this plugin instance, or similar resource. This property may -be used anywhere a state needs to be described, for example:</p> +<p>The state of this instance. This property may be used anywhere a state +needs to be described, for example:</p> <pre class="turtle-code"> @prefix eg: <http://example.org/> . -<plugininstance> +<plugin-instance> state:state [ eg:somekey "some value" ; eg:someotherkey "some other value" ; @@ -313,24 +335,23 @@ state:makePath lv2:documentation """ <p>This feature allows plugins to create new files and/or directories. To support this feature the host passes an LV2_Feature with URI -LV2_STATE__makePath and data pointed to an LV2_State_Make_Path to the -plugin. The host may make this feature available only during save by passing -it to LV2_State_Interface::save(), or available any time by passing it to +LV2_STATE__makePath and data pointed to an LV2_State_Make_Path to the plugin. +The host may make this feature available only during save by passing it to +LV2_State_Interface::save(), or available any time by passing it to LV2_Descriptor::instantiate(). If passed to LV2_State_Interface::save(), the feature MUST NOT be used beyond the scope of that call.</p> -<p>The plugin is guaranteed a hierarchial namespace unique to that plugin +<p>The plugin is guaranteed a hierarchical namespace unique to that plugin instance, and may expect the returned path to have the requested path as a suffix. There is <em>one</em> such namespace, even if the feature is passed to -both LV2_Descriptor::instantiate() <em>and</em> -LV2_State_Interface::save(). Beyond this, the plugin MUST NOT make any -assumptions about the returned paths.</p> +both LV2_Descriptor::instantiate() <em>and</em> LV2_State_Interface::save(). +Beyond this, the plugin MUST NOT make any assumptions about the returned +paths.</p> -<p>Like any other paths, the plugin MUST map these paths using <a -href="#mapPath">state:mapPath</a> before storing them in state. The plugin -MUST NOT assume these paths will be available across a save/restore otherwise, -i.e. only mapped paths saved to state are persistent, any other created paths -are temporary.</p> +<p>Like any other paths, the plugin MUST map these paths using state:mapPath +before storing them in state. The plugin MUST NOT assume these paths will be +available across a save/restore otherwise, i.e. only mapped paths saved to +state are persistent, any other created paths are temporary.</p> <p>For example, a plugin may create a file in a subdirectory like so:</p> @@ -339,7 +360,7 @@ char* 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"); + fprintf(myfile, "I am some data"); fclose(myfile); return path; } diff --git a/lv2/lv2plug.in/ns/ext/worker/worker.h b/lv2/lv2plug.in/ns/ext/worker/worker.h index b0d87ec..74ac45b 100644 --- a/lv2/lv2plug.in/ns/ext/worker/worker.h +++ b/lv2/lv2plug.in/ns/ext/worker/worker.h @@ -29,7 +29,7 @@ #define LV2_WORKER_URI "http://lv2plug.in/ns/ext/worker" #define LV2_WORKER_PREFIX LV2_WORKER_URI "#" -#define LV2_WORKER__Interface LV2_WORKER_PREFIX "Interface" +#define LV2_WORKER__interface LV2_WORKER_PREFIX "interface" #define LV2_WORKER__schedule LV2_WORKER_PREFIX "schedule" /** @@ -60,7 +60,7 @@ typedef LV2_Worker_Status (*LV2_Worker_Respond_Function)( This is the interface provided by the plugin to implement a worker method. The plugin's extension_data() method should return an LV2_Worker_Interface - when called with LV2_WORKER__Interface as its argument. + when called with LV2_WORKER__interface as its argument. */ typedef struct _LV2_Worker_Interface { /** diff --git a/lv2/lv2plug.in/ns/ext/worker/worker.ttl b/lv2/lv2plug.in/ns/ext/worker/worker.ttl index 517aabb..09f7249 100644 --- a/lv2/lv2plug.in/ns/ext/worker/worker.ttl +++ b/lv2/lv2plug.in/ns/ext/worker/worker.ttl @@ -49,14 +49,13 @@ offline rendering, or in real-time with non-real-time work taking place in a separate thread.</p> """ . -work:Interface - a rdfs:Class ; - rdfs:subClassOf lv2:ExtensionData ; +work:interface + a lv2:ExtensionData ; lv2:documentation """ <p>The interface provided by the plugin to implement a worker. To implement this extension, the plugin must return a valid LV2_Worker_Interface from LV2_Descriptor::extension_data() when it is called with URI -LV2_WORKER__Interface.</p> +LV2_WORKER__interface.</p> <p>The plugin data file should describe this like so:</p> <pre class="turtle-code"> @@ -64,7 +63,7 @@ LV2_WORKER__Interface.</p> <plugin> a lv2:Plugin ; - lv2:extensionData work:Interface . + lv2:extensionData work:interface . </pre> """ . diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c index baae81a..fa6adbe 100644 --- a/plugins/eg-sampler.lv2/sampler.c +++ b/plugins/eg-sampler.lv2/sampler.c @@ -367,7 +367,7 @@ run(LV2_Handle instance, } } -static void +static LV2_State_Status save(LV2_Handle instance, LV2_State_Store_Function store, LV2_State_Handle handle, @@ -393,9 +393,11 @@ save(LV2_Handle instance, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); free(apath); + + return LV2_STATE_SUCCESS; } -static void +static LV2_State_Status restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, @@ -419,6 +421,8 @@ restore(LV2_Handle instance, free_sample(plugin->sample); plugin->sample = load_sample(plugin, path); } + + return LV2_STATE_SUCCESS; } static const void* @@ -426,9 +430,9 @@ extension_data(const char* uri) { static const LV2_State_Interface state = { save, restore }; static const LV2_Worker_Interface worker = { work, work_response, NULL }; - if (!strcmp(uri, LV2_STATE__Interface)) { + if (!strcmp(uri, LV2_STATE__interface)) { return &state; - } else if (!strcmp(uri, LV2_WORKER__Interface)) { + } else if (!strcmp(uri, LV2_WORKER__interface)) { return &worker; } return NULL; diff --git a/plugins/eg-sampler.lv2/sampler.ttl b/plugins/eg-sampler.lv2/sampler.ttl index e6bd816..140992e 100644 --- a/plugins/eg-sampler.lv2/sampler.ttl +++ b/plugins/eg-sampler.lv2/sampler.ttl @@ -15,13 +15,14 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -@prefix atom: <http://lv2plug.in/ns/ext/atom#> . -@prefix doap: <http://usefulinc.com/ns/doap#> . -@prefix foaf: <http://xmlns.com/foaf/0.1/> . -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . -@prefix urid: <http://lv2plug.in/ns/ext/urid#> . -@prefix work: <http://lv2plug.in/ns/ext/worker#> . +@prefix atom: <http://lv2plug.in/ns/ext/atom#> . +@prefix doap: <http://usefulinc.com/ns/doap#> . +@prefix foaf: <http://xmlns.com/foaf/0.1/> . +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix state: <http://lv2plug.in/ns/ext/state#> . +@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . +@prefix urid: <http://lv2plug.in/ns/ext/urid#> . +@prefix work: <http://lv2plug.in/ns/ext/worker#> . <http://lv2plug.in/plugins/eg-sampler> a lv2:Plugin ; @@ -30,7 +31,8 @@ lv2:requiredFeature urid:map , work:schedule ; lv2:optionalFeature lv2:hardRTCapable ; - lv2:extensionData <http://lv2plug.in/ns/ext/state#Interface> ; + lv2:extensionData state:interface , + work:interface ; ui:ui <http://lv2plug.in/plugins/eg-sampler#ui> ; lv2:port [ a lv2:InputPort , @@ -40,7 +42,7 @@ <http://lv2plug.in/ns/ext/patch#Message> ; lv2:index 0 ; lv2:symbol "control" ; - lv2:name "Control" ; + lv2:name "Control" ] , [ a lv2:OutputPort , atom:AtomPort ; @@ -48,13 +50,13 @@ atom:supports <http://lv2plug.in/ns/ext/patch#Message> ; lv2:index 1 ; lv2:symbol "notify" ; - lv2:name "Notify" ; + lv2:name "Notify" ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 2 ; lv2:symbol "out" ; - lv2:name "Out" ; + lv2:name "Out" ] . <http://lv2plug.in/plugins/eg-sampler#ui> @@ -63,5 +65,5 @@ ui:portNotification [ ui:plugin <http://lv2plug.in/plugins/eg-sampler> ; lv2:symbol "notify" ; - ui:notifyType atom:Blank ; + ui:notifyType atom:Blank ] . |