Loading Plugins

From Lv2

Jump to: navigation, search

This page describes how a 'host' process can load an LV2 plugin. Note that there are libraries available to do this for you, so you shouldn't have to implement a complete plugin loader yourself unless you have some other reason to do so.


Contents

[edit] Step 1: Finding available plugins

Every LV2 plugin has a unique URI that should be used to identify it. In order to use a plugin you need to know its URI. It could be entered directly by the user, or your host program could present a list of all installed LV2 plugins and let the user choose which one to use.

In order to present such a list, you need to know where in the filesystem the LV2 plugins can be found. The recommended method is to look at the environment variable LV2_PATH, which (if it is defined) should consist of a list of directory paths, separated by ':'. These directories are where the host should look for LV2 plugin bundles. If LV2_PATH is not defined, a good default value (on UNIX systems) is "$HOME/.lv2:/usr/local/lib/lv2:/usr/lib/lv2" (with $HOME replaced by the content of the environment variable HOME).

Plugin bundles are directories containing a file called manifest.ttl (see the Bundle Definition). If the bundle contains information about the plugin with URI http://my.plugin, its manifest.ttl file will contain the triple

<http://my.plugin> <http://www.w3.org/2000/01/rdf-schema#type> <http://lv2plug.in/ns/lv2core#Plugin>

So in order to find the URIs for all installed plugins you need to parse the manifest files in all plugin bundles and extract all plugin URIs from such triples. The data for a plugin may be distributed over multiple bundles (for example the plugin may be in one bundle while a GUI for it is in another bundle), so each URI may appear in more than one manifest file. This does not necessarily mean that there are multiple copies of that plugin installed, but rather that you will need to read data from multiple bundles in order to load the plugin.

[edit] Step 2: Parsing the RDF data

Once you know the URI of the plugin you want to load, you need to query the RDF data for that plugin in order to find out how to use it. The RDF data for the plugin consists of the manifest files that contain the triple above, as well as any additional RDF files referenced using rdfs:seeAlso (see the Bundle Definition).

[edit] Checking compatibility

Since LV2 allows extensions to the core specification, you may not be able to use a plugin (because you don't support the extensions that it needs). You need to check that

  • The plugin does not require any features that you don't support
  • There are no ports of classes that you don't support that must be connected

[edit] Features

A feature is an extension to the core LV2 spec that is identified by an URI (see LV2_Feature). If a plugin makes use of a host feature, its RDF data will contain one of the triples

<http://my.plugin> lv2:optionalFeature <http://my.feature>.
<http://my.plugin> lv2:requiredFeature <http://my.feature>.

If it uses lv2:optionalFeature you can safely ignore it - that just means that the plugin can use that feature if it's available, but can work without it. But if there is a lv2:requiredFeature the plugin needs that feature in order to work at all, and you must not load the plugin (unless you support that particular feature).

A plugin can have any number of required and optional features.

[edit] Port classes

In LV2, every port has a class. The class specify what type of data the plugin expects the host to write to the port (if it's an input port) or what it writes to the port itself (if it's an output port). The core spec defines a small number of port classes, but there may be other ones defined as extensions. Thus you must check the class of each port in a plugin before you load it, and make sure that you know how to handle it. The class of a port is identified by an URI and specified in the following way:

<http://my.plugin> lv2:port [ <http://www.w3.org/2000/01/rdf-schema#type> <http://my.port.class>; ... ].

If you do not support the class of one of the plugin's ports you must not load the plugin, unless that port is also marked as optional. This would look like this:

<http://my.plugin> lv2:port [ <http://www.w3.org/2000/01/rdf-schema#type> <http://my.port.class>;
                              lv2:portProperty lv2:connectionOptional; ... ].

In that case you may load the plugin, but you must not connect any data buffer to that port (you must set it to a NULL pointer using connect_port()).

[edit] Checking ports

The RDF data (not the shared object file) contains all the information about the plugin's ports. In order to use a plugin you will need to know the number of ports and their directions as well as their classes.

Ports are specified in the RDF data using the lv2:port predicate, and each port has an index that is specified using lv2:index. The RDF may look like this:

<http://my.plugin> lv2:port [ lv2:index 0; a lv2:AudioPort; a lv2:InputPort; lv2:name "Input"; ... ],
                            [ lv2:index 1; a lv2:AudioPort; a lv2:OutputPort; lv2:name "Output"; ...],
                            [ lv2:index 2; a lv2:ControlPort; a lv2:InputPort; lv2:name "Gain"; ...].

The indices must be contiguous and start at 0 - if you find 3 ports for a plugin there must be one with index 0, one with index 1, and one with index 2. These indices are used to connect data buffers to the ports using connect_port(). If the port indices for a plugin are not contiguous or do not start at 0 that means that the plugin is broken and you should not attempt to load it.

As you can see a port may have multiple classes - lv2:InputPort and lv2:OutputPort denote the direction of the port, and lv2:AudioPort and lv2:ControlPort denote the data type (an array of floats vs a single float). These classes are the only ones that are defined in the core specification. Each port also has a name (specified using lv2:name) that may be internationalised.

[edit] Finding the binary

Finally, in order to load the plugin you need to know where to find the shared object file that contains the implementation of the plugin. This is given in the RDF data using the triple

<http//my.plugin> lv2:binary <myplugin.so>

In this case the implementation can be found in the shared object file myplugin.so in the bundle that contains the file with this triple.

[edit] Step 3: Loading and running the plugin

Once you have read all the necessary information from the RDF files, you can load the shared object file using dlopen() or equivalent, and then use dlsym() (or equivalent) to get a pointer to the function in the shared object file with the name lv2_descriptor. This function can be assumed to have the type LV2_Descriptor_Function. It takes a single uint32_t parameter and returns a pointer to a LV2_Descriptor.

A single shared object file may contain implementations for multiple plugins, so you need to check that you get the correct one. The way to do that is to first call the lv2_descriptor() function with the parameter 0, compare the URI member of the returned LV2_Descriptor to the URI for the plugin you want to load, then call the function again with parameter 1, then again with parameter 2 and so on until you either get the correct plugin implementation or the function returns NULL. If the function returns NULL before you find the right plugin implementation, although its RDF data says that the implementation should be in this shared object file, the plugin is broken and you should not attempt to load it.

Once you have a pointer to the LV2_Descriptor object for the plugin, you need to create an instance of it, activate it (if it has an activation function), connect its ports to data buffers (at least all of them that do not have the lv2:connectionOptional hint), and finally run the plugin. When you are done running it you need to deactivate the plugin (if it has a deactivation function) and destroy the plugin instance, which frees all resources it uses internally. To do all these things you use the function pointer members of the LV2_Descriptor object - see their documentation for more details:

Also, see the threading rules to learn how you are allowed to use these functions in a multi-threaded program.