Writing Plugins
From Lv2
This document shows how to create a single bundle containing a single plugin (see the Bundle Definition) using only the core LV2 specification. The reader is assumed to have some experience with basic GNU/Linux development tools for C programming (GCC, GNU Make).
The example plugin used in this document is available here.
[edit] Writing the RDF files
The one file that must exist in every LV2 bundle is manifest.ttl. It should list the URIs for the plugins available in the bundle, in this case http://lv2plug.in/plugins/example_amp. For every plugin there must be a RDF triple that says that its URI refers to a LV2 plugin. The triple looks like this:
<http://lv2plug.in/plugins/example_amp> a lv2:Plugin
If there are additional files containing RDF data for this plugin they need to be referenced in manifest.ttl using a triple that looks like this:
<http://lv2plug.in/plugins/example_amp> rdfs:seeAlso <amp.ttl>
assuming that rdfs: is defined as a prefix for http://www.w3.org/2000/01/rdf-schema#.
You can see the complete manifest.ttl file for the plugin here.
A plugin needs to provide more information in its RDF data. The Amp plugin has this information in amp.ttl, which looks like this:
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix doap: <http://usefulinc.com/ns/doap#> .
<http://lv2plug.in/plugins/example_amp> a lv2:Plugin ;
doap:name "Simple amplifier" ;
doap:licence <http://usefulinc.com/doap/licenses/gpl> ;
lv2:optionalFeature lv2:hardRtCapable ;
lv2:port [
a lv2:InputPort ;
a lv2:ControlPort ;
lv2:index 0 ;
lv2:symbol "gain" ;
lv2:name "gain" ;
lv2:default 0.0 ;
lv2:minimum -90.0 ;
lv2:maximum 24.0 ;
] , [
a lv2:AudioPort ;
a lv2:InputPort ;
lv2:index 1 ;
lv2:symbol "in" ;
lv2:name "in" ;
] , [
a lv2:AudioPort ;
a lv2:OutputPort ;
lv2:index 2 ;
lv2:symbol "out" ;
lv2:name "out" ;
]
.
The line <http://lv2plug.in/plugins/example_amp> a lv2:Plugin is not strictly needed since this file will be read together with manifest.ttl which already contains that triple, but it doesn't hurt. The following 3 lines say that the plugin is known under the name Simple amplifier, is released under the GNU GPL and is capable of running in a realtime safe host (see the RDF Schema Description for more information about Features). doap:name and doap:license are mandatory properties for an LV2 plugin - if a plugin does not have both of them a host might not load it. The RDF data of a plugin should also have a reference to the shared library that implements the plugin so the host knows where to find it. In this plugin that information is in manifest.ttl in the form of the triple <http://lv2plug.in/plugins/example_amp> lv2:binary <amp.so>, it does not have to be repeated in amp.ttl.
The rest of the file describes the plugin's input and output ports. This plugin has one control input port, one audio input port and one audio output port. Every port must have the properties lv2:index (a nonnegative integer), lv2:symbol (a string that is a valid identifier in the C programming language) and lv2:name (a string that can be displayed in the host's user interface). Every port symbol must be unique, and the indices must start at 0 and be contiguous - you can't have a port with index 4 unless you also have ports with indices 3, 2, 1 and 0. The indices will be used in the shared library to identify the ports.
Each port must also have at least one type. Most of the time a port will have two types, one that says what type of data the port will handle (lv2:AudioPort and lv2:ControlPort are the only such types defined in the core LV2 spec) and one that says if the data should be read or written by the plugin (lv2:InputPort and lv2:OutputPort).
[edit] Writing the code
The full source code for the shared library part of the plugin is available in amp.c. Most of it consists of implementations of the mandatory plugin callbacks (see the C API documentation). The last function is lv2_descriptor(), a function that must be present in all LV2 plugin libraries.
LV2_SYMBOL_EXPORT
const LV2_Descriptor *lv2_descriptor(uint32_t index)
{
if (!ampDescriptor) init();
switch (index) {
case 0:
return ampDescriptor;
default:
return NULL;
}
}
LV2_SYMBOL_EXPORT is used to make sure that the function is available to runtime linkers on all platforms. The index parameter is an index into a conceptual array of LV2 descriptors, each containing pointers to the callbacks that implement that plugin. In this plugin library there is only one LV2 descriptor so the function returns that descriptor when index is 0 and NULL otherwise. If the library had contained 2 descriptors it should have returned one of them when index was 0, the other when index was 1, and NULL for any larger values.
[edit] Creating the bundle
The files manifest.ttl, amp.ttl and the shared library amp.so built from amp.c should be placed in an LV2 bundle. However, an LV2 bundle is just a directory with a name ending with .lv2 and containing a manifest.ttl, so we can just build the shared library in the current directory and use that as the bundle. There is a Makefile in the directory that does this, just type make and Amp-example.lv2 will be a complete LV2 bundle containing one plugin.

