From 8de9f07305a074c158f6af33cc46b9fd0dc31363 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Mon, 18 Mar 2019 18:10:47 +0100 Subject: Clean up WIP book --- doc/lv2_plugin_guide/guide.adoc | 130 ++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 80 deletions(-) diff --git a/doc/lv2_plugin_guide/guide.adoc b/doc/lv2_plugin_guide/guide.adoc index eb64657..c59f346 100644 --- a/doc/lv2_plugin_guide/guide.adoc +++ b/doc/lv2_plugin_guide/guide.adoc @@ -2,7 +2,7 @@ Programming LV2 Plugins ======================= :toc: :source-highlighter: pygments - +:pygments-style: friendly Introduction ------------ @@ -26,7 +26,7 @@ provides basic .ttl file information A MIDI gate:: Shows midi processing, introduces LV2 atoms, introduces LV2 extension API Fifths:: A more complex MIDI processor, explains ??? LV2 concept -Metromome:: Explores timining information, explains ??? LV2 concept +Metronome:: Explores timining information, explains ??? LV2 concept A trivial sampler:: Provides a MIDI waveform sampler, introduces the LV2 worker extension and extensions in general A GUI oscilloscope:: Discusses user interfaces in the context of the LV2 @@ -36,7 +36,7 @@ This manual encourages users to jump around, but recommends reading the LV2 asides in each chapter in order to best understand how the LV2 specific concepts relate to each other. -Building a basic amplifier +Building a Basic Amplifier -------------------------- In this example we're going to build a simple amplifier plugin. @@ -64,12 +64,11 @@ Let's start off by including the core LV2 header. [source,c] -------------------------------------------------------------------------------- -#include +#include -------------------------------------------------------------------------------- -The include path of LV2 headers reflect the URI of the specification they are a part of by design. -This makes it easy to find detailed information about the API. -This header is part of the "core" LV2 specification: http://lv2plug.in/ns/lv2core. +LV2 has many "extensions" each with their own headers. +This simple plugin uses only `lv2.h` from the "core" LV2 specification, http://lv2plug.in/ns/lv2core. LV2 plugins are are responsible for keeping track of what ports they have and what host-controlled data structures they're attached to. In our simple amplifier, these are the only variables stored in the plugin structure: @@ -87,27 +86,12 @@ include::../../plugins/eg-amp.lv2/amp.c[tags=PortIndex] ---- With the data for our amplifier set up we need to implement the functions needed -for the host to communicate with our plugin. -These functions are summarized in the link:http://lv2plug.in/doc/html/group__lv2core.html#structLV2__Descriptor[LV2_Descriptor] which is passed to a -host and it includes: - -//More details in later examples -// Actually the LV2 spec for these should have a bunch more detail on each, -// though perhaps it should still be integrated - -instantiate:: Allocate and initialize the plugin. -connect_port:: Connect a port to a host-provided buffer. -activate:: Reset the plugin to a default state and prepare it to run. -run:: Process data for a given number of samples. -deactivate:: Stop the plugin from running (counterpart to activate). -cleanup:: Free plugin (counterpart to instantiate). -extension_data:: Get data defined by LV2 extensions. +for the host to communicate with it. +These functions will end up in the link:http://lv2plug.in/doc/html/group__lv2core.html#structLV2__Descriptor[LV2_Descriptor] +which is accessed by the host. This plugin does not need the `activate`, `deactivate`, or `extension_data` functions, which are optional and do not need to be defined if they are not needed. - -NOTE:: For this simple plugin we don't really need to care about when each one -of these callbacks can be called. For more detailed information about what -guarantees you have for when each callback may be invoked see XXX +That leaves `instantiate`, `connect_port`, `run`, and `cleanup`. Since this plugin contains minimal state, the `instantiate` and `cleanup` functions simply allocate and free the `Amp` structure: @@ -121,96 +105,82 @@ include::../../plugins/eg-amp.lv2/amp.c[tags=instantiate] include::../../plugins/eg-amp.lv2/amp.c[tags=cleanup] -------------------------------------------------------------------------------- -Connecting ports provided by the LV2 host is somewhat more complex. -Each call from the host will provide a pointer to a buffer for one of the ports. -The plugin will need to store that pointer and either read from it or write to -it depending upon if it's an input or an output. +Connecting ports provided by the host is a bit more involved. +The host will provide a pointer to a buffer for one of the ports by calling `connect_port`. +The plugin needs to store that pointer for use while it is running. [source,c] ---- include::../../plugins/eg-amp.lv2/amp.c[tags=connect_port] ---- -After the host has connected data to each one of the ports and activated the -plugin then the amplfier LV2 plugin can run for a number of samples. +After the host has connected data to each one of the ports and activated the plugin, +the plugin is ready to process audio. +This is done by the `run()` function, which is called repeatedly for blocks of samples: [source,c] ---- include::../../plugins/eg-amp.lv2/amp.c[tags=run] ---- -All that's left is providing the host with a descriptor containing pointers to -all of these functions. -Since multiple LV2 plugins can be contained in the same shared library this is -done through the exported lv2_descriptor(link) function: +Now that we've implemented the core of our plugin, +all that's left is to provide a descriptor to the host so that it can use the plugin. +For this, we define an +link:http://lv2plug.in/doc/html/group__lv2core.html#structLV2__Descriptor[LV2_Descriptor]: [source,c] ---- include::../../plugins/eg-amp.lv2/amp.c[tags=descriptor] ---- -Part 2: The specification -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now that we have an amplifier plugin setup, you might expect that a host could -load it and be ready to run. -The code however doesn't define all of the details. -For instance the code doesn't provide a list of the inputs and outputs of the -plugin, so how does the host know about them? - -The answer is that LV2 uses data files to store this information. - +Then, expose it to the host with the `lv2_descriptor()` function, +which is the entry point to our library: -LV2 plugins are defined in two parts: code and data. -The code is written in C, or any C compatible language such as C++. -Static data is described separately in the human and machine friendly http://www.w3.org/TeamSubmission/turtle/[Turtle] syntax. - -Generally, the goal is to keep code minimal, -and describe as much as possible in the static data. -There are several advantages to this approach: - - * Hosts can discover and inspect plugins without loading or executing any plugin code. - * Plugin data can be used from a wide range of generic tools like scripting languages and command line utilities. - * The standard data model allows the use of existing vocabularies to describe plugins and related information. - * The language is extensible, so authors may describe any data without requiring changes to the LV2 specification. - * Labels and documentation are translatable, and available to hosts for display in user interfaces. +[source,c] +---- +include::../../plugins/eg-amp.lv2/amp.c[tags=lv2_descriptor] +---- +Part 2: The data +~~~~~~~~~~~~~~~~ -A .ttl file maps a collection of properties to values using a collection of -URLs. -Now let's build one for the amplifier called 'amp.ttl' +Now that we have an amplifier plugin written, you might expect that a host could run it. +However, the code doesn't include all the necessary details. +For example, the code doesn't provide a list of the inputs and outputs of the plugin, +so how does the host know about them? -We can start out by saying that the AMP_URL which we previously defined is 'a' -plugin: +For various reasons, this information is provided in a separate data file which describes the plugin. +Plugins, along with other things in LV2, are described in the human-friendly but machine-readable http://www.w3.org/TeamSubmission/turtle/[Turtle] syntax. -[source, turtle] --------------------------------------------------------------------------------- - - a http://lv2plug.in/ns/lv2core#Plugin . --------------------------------------------------------------------------------- +A Turtle file describes things as sets of properties. +If you are familiar with JSON, the basic idea is similar, +but property keys are URIs rather than arbitrary strings. -Since we're going to define a number of properties in the LV2 namspace, let's -define a few macros to condense the file and since we're defining multiple -properties on our plugin let's use the continuation ';' rather than the property -terminator '.'. +First we will define some prefixes for the namespaces we use, +to keep things nice and readable later on: [source, turtle] -------------------------------------------------------------------------------- include::../../plugins/eg-amp.lv2/amp.ttl[tags=prefixes] -------------------------------------------------------------------------------- -Now we can begin describing our plugin, by first stating that it is 'a' LV2 plugin: + +To describe our plugin, +we can start by saying that our plugin is `a` link:http://lv2plug.in/ns/lv2core#Plugin[Plugin]: +footnote:[`a` here is a special shorthand for `rdf:type`] [source, turtle] -------------------------------------------------------------------------------- - a lv2:Plugin ; + a lv2:Plugin ; -------------------------------------------------------------------------------- -IMPORTANT: The URI used to identify the plugin in the data must match the one in the code, `AMP_URI` in this example. +IMPORTANT: The URI used to identify the plugin must match the one in the code, +`AMP_URI` in this example. + +From here we can provide all kinds of information about our plugin, +such as an associated project, display name, license, and supported features: -From here we can provide information to what project the plugin is associated -with, a display name, a license, and even specify features of the plugin: [source, turtle] -------------------------------------------------------------------------------- lv2:project ; @@ -523,7 +493,7 @@ typedef struct { Now in instantiate we can find the value of the MIDI event URID: - +[source,c] -------------------------------------------------------------------------------- static LV2_Handle instantiate(const LV2_Descriptor* descriptor, -- cgit v1.2.1