Albert Gräf email@example.com, 2017-01-01
This project provides an LV2 plugin architecture for the Faust programming language. The package contains the Faust architecture and templates for the needed LV2 manifest (ttl) files, a collection of sample plugins written in Faust, and a generic GNU Makefile as well as a shell script to compile plugins using the architecture.
faust-lv2 is free and open source software. The latest source can be found in the git repository at https://bitbucket.org/agraef/faust-lv2. In the downloads section of this site you can also find released tarballs along with my faust-lv2 paper for the 2013 Linux Audio Conference. Please use the issue tracker for bug reports and requests. You're also invited to join one of Grame's Faust mailing lists to discuss faust-lv2 or Faust in general.
Note: Various versions of the faust-lv2 architecture have been included in
the official Faust distribution since Faust 0.9.58. If you want to make sure
that you have the latest faust-lv2 installed, you can also add the
architecture and the faust2lv2 compilation script to an existing Faust
installation by running
make install-faust from this package, see
"Installation" below for details. Moreover, faust-lv2 is also available as a
target architecture in the Faust online compiler, so you can compile your
Faust dsps as LV2 plugins even without installing any additional
software. Please head over to the Faust website to learn more.
Copyright (c) 2012-2017 by Albert Gräf firstname.lastname@example.org
Copyright (c) 2015-2016 of the Faust/Qt support by Roman Svidler
This document is available under the GNU FDL (Free Documentation License).
faust-lv2 is distributed under the GNU LGPL (Lesser General Public License) v3+. Please see the included COPYING and COPYING.LESSER files for details.
The aim of this project is to provide a full-featured LV2 implementation of Faust plugins which supports both effect and instrument plugins. Faust is Grame's functional signal processing language. LV2 is the new open-source audio and MIDI plugin standard for Linux and other Unix-like systems, successor of the venerable LADSPA standard.
faust-lv2 is the first (and, at the time of this writing, still the only) LV2 implementation for Faust. It was also the first Faust architecture to provide full support for both effect processors and polyphonic synths with automatic voice allocation, which makes it really easy to create great working LV2 plugins ready to be used in popular LV2 hosts such as Ardour and Qtractor.
faust-lv2 has also been ported to Steinberg's VST plugin standard which, at the time of this writing, is still the prevalent cross-platform plugin architecture for commercial DAW software. Please check the author's faust-vst project for details. Both faust-lv2 and faust-vst provide exactly the same set of features, so a simple recompile suffices to port your plugins from LV2 to VST and vice versa. This makes it possible to use the same Faust plugins with a variety of both open-source and commercial DAW software on both Linux and Mac OS X. (Windows support is still on the TODO list at this time, but porting should be a piece of cake and contributions are welcome!)
The main items in the faust-lv2 package are the Faust architecture lv2.cpp and the faust2lv2 compilation script which are used to compile LV2 plugins with the Faust compiler. Custom Faust-generated Qt GUIs are implemented by the lv2ui.cpp architecture and the accompanying lv2qtgui.h header file. All these items should be included in recent Faust versions, but you can also install them from the faust-lv2 package if necessary. The package also includes some sample Faust dsps, as well as a generic GNU Makefile showing how to automatize the build process.
LV2 is fully supported by the popular open-source DAWs Ardour (on both Linux and Mac OS X) and Qtractor (Linux only). LV2 plugins can also be run in Miller Puckette's Pd through the author's lv2plugin~ external. Stand-alone hosts such as Jalv and Carla can be used to interface with other software which doesn't support LV2 out of the box. Note that Carla, which runs on both Linux and Mac OS X, is also available as a VST plugin which can be used to host LV2 plugins in commercial DAWs which only support VST plugins natively.
The current release has been tested and is known to work with Ardour, Qtractor, Carla, Jalv and lv2plugin~. We generally recommend using hosts based on Dave Robillard's LV2 host library lilv, which should be readily available in most Linux distributions. All plugin hosts mentioned above (except Carla) will work with this library if it is available on your system.
All plugins created with lv2.cpp provide the following basic features:
Audio inputs and outputs of the Faust dsp are made available as LV2 audio input and output ports.
Faust controls are made available as LV2 control ports with the proper label, initial value, range and (if supported by the host) step size. Both "active" (input) and "passive" (output) Faust controls are supported and mapped to the corresponding LV2 input and output ports. (The control outputs a.k.a. passive Faust controls seem to work with most LV2 hosts nowadays; Qtractor is the only notable exception.)
If the dsp defines any controls with corresponding MIDI mappings (
midi:ctrlattributes in the Faust source), the plugin also provides an LV2 MIDI input port and interprets incoming MIDI controller messages accordingly. This feature can also be disabled by setting
FAUST_MIDICC=0when compiling the C++ source; just uncomment the corresponding line in the Makefile, or invoke faust2lv2 with the
-nomidiccoption. (Disabling this option may occasionally be useful if the MIDI cc processing gets in the way of a host's own MIDI mapping and automation facilities. But normally you shouldn't have to worry about this, since it is always up to the LV2 host to decide whether it sends MIDI controller data to the plugin or not.) Also note that for instrument plugins certain MIDI controllers have a predefined meaning and hence cannot be assigned using the
midi:ctrlattribute, see below.
Plugin name, version, description, author and license information provided as meta data in the Faust source is translated to the corresponding fields in the LV2 manifest of the plugin. This can also be disabled with the corresponding macro (
FAUST_META=0) or with faust2lv2's
-nometaoption, if you'd rather like to provide this information yourself by editing the static manifest.
nvoicesmeta data key can be used to indicate that the plugin is actually an instrument (synthesizer) rather than an audio (effect) plugin. It also specifies the maximum number of "voices" (i.e., the amount of polyphony) of the synth. See "Instrument Plugins" below for details.
The architecture also recognizes the following Faust control meta data and
sets up the LV2 control port properties accordingly. Note that some of these
properties rely on extensions which may not be supported by all LV2 hosts.
Also note that all of these can be disabled with
FAUST_META=0 or the
-nometa option. Please refer to the LV2 documentation for a closer
description of the corresponding LV2 properties.
[unit:Hz]) in the Faust source is translated to a corresponding LV2
unitattribute. The host may then display this information in its GUI rendering of the plugin controls.
LV2 scale points can be set with the
lv2:scalepoint) attribute on the Faust side. The value of this attribute in the Faust source takes the form of a list of pairs of descriptive labels and corresponding values, for instance:
toggle = button("trigger [lv2:scalepoint on 1 off 0]");
The host may then display the given scale points with descriptive labels in its GUI, or in the form of a dropdown list, menu, radio control or some similar widget.
lv2:integerattribute in the Faust source is translated to the
lv2:integerLV2 port property, so that the control may be shown as an integer-only field in the host's GUI.
lv2:reportsLatencyattribute in the Faust source is translated to the
lv2:reportsLatencyLV2 port property, also adding the
lv2:designation lv2:latencyproperty to the port, so that the control may be used by the host to compensate for the latency of a plugin (if the host supports this). Note that this only works with passive (output) Faust controls, and that it is the responsibility of the programmer to specify the proper values for the control.
lv2:notOnGUIattribute maps to the LV2
notOnGUIport property, so that hosts honoring this property may suppress the display of this control in their GUI.
Many LV2 hosts offer an option to display a generic GUI for the control elements of an LV2 plugin, which will also work with all plugins created with the lv2.cpp architecture. Ardour, Carla and Qtractor all do a reasonably good job at this. The hierarchical layout of GUI controls prescribed by the Faust source is lost in the generic plugin GUIs, but the GUI elements should still be shown in the right order at least. Moreover, as described above, faust-lv2 supports a few LV2 GUI hints as Faust meta data, which can be used to "style" GUI elements in various ways (such as proper rendering of integer and radio- or menu-style controls). This will make the generic GUIs work pretty well in most host applications.
However, as of version 1.0, faust-lv2 also includes experimental support for
custom plugin GUIs, which are generated using the generic Qt GUI support
implemented by the Faust library (faustqt.h et al, included in recent Faust
versions). These will respect the hierarchical group layout of GUI controls
prescribed by the Faust source, as well as various
style hints in the
control meta data (please check the Faust Quick Reference for details on
this). The custom Qt GUIs are compiled as separate modules created from the
lv2ui.cpp architecture. They also employ special manifest templates which
include the required data describing the plugin GUI and linking it to its
To improve cross-platform compatibility, custom GUIs are not built by
default in the present version, but it's easy to enable this by using the
gui=1 setting with the Makefile or by invoking the faust2lv2 script with the
-gui option. In addition, you may have to pick the desired Qt version by
specifying the corresponding qmake executable, using either the
variable of the Makefile, or by setting the
QMAKE environment variable with
the faust2lv2 script. (Please check the "Installation" and "Using the Build
Script" sections below for examples.)
Custom GUIs also optionally offer the same kind of built-in OSC and HTTP support as the stand-alone Faust architectures, which allows a plugin to be controlled via the OSC (Open Sound Control) and HTTP protocols (i.e., through an OSC device or a web browser). This is enabled with the appropriate options in the Makefile and the faust2lv2 script. (Note that the OSC and web servers of a plugin are currently tied to its Qt GUI and will thus only be available as long as the GUI remains open in the host application.) Please check the sections "OSC support" and "HTTP support" in the Faust Quick Reference manual for details.
The custom GUIs require an LV2 host which supports the LV2 UI extension and is capable of managing Qt4 or Qt5 plugin GUIs. At the time of this writing, Ardour still requires Qt4, while recent versions of Qtractor support Qt5. Carla will work with either Qt version. The custom Faust GUIs work fine with all of these on Linux. On Mac OS X, only Ardour and Carla support LV2 at present, and neither seem to work with the custom Faust GUIs yet, so you'll have to go with the generic host-generated GUIs for now.
The lv2.cpp architecture can also be used to create instrument plugins, which provide the necessary logic to drive a polyphonic synth with automatic voice allocation. The easiest way to specify this is to include the following kind of meta data in the Faust source:
declare nvoices "16";
The given number specifies the desired maximum number of voices. (This value
can also be configured manually by setting the
NVOICES macro when compiling
the C++ source of the plugin, but usually it will be much more convenient to
have this information in the dsp source.) The plugin will manage that many
instances of the Faust dsp. The actual number of voices can then be changed
dynamically from 1 to the
nvoices value with a special
provided by the plugin. In the current implementation, this control defaults
to half the total number of voices available.
NOTE: To make this work, the Faust dsp must be able to function as a
monophonic synth which provides controls labeled
set the pitch (frequency in Hz), velocity (normalized to the 0-1 range) and
gate (0/1 signal used to trigger the envelop) of a note, respectively. Voice
allocation is then handled automatically by the architecture. The controls can
be under any path in the dsp's group hierachy. But note that only a single
instance of each of these control variables will be used to control each
voice, so it's best to have all of these in a single group (often the toplevel
group of a dsp) in order to avoid confusion. Please check the Faust sources in
the examples folder for sample instruments (.dsp files containing the
nvoices declaration) which show how to implement such synth units in Faust;
examples/organ.dsp is a good one for starters since it is quite simple.
Instrument plugins always provide a MIDI input port and interpret incoming MIDI note and pitch bend messages, as well as a number of General MIDI standard controller and sysex messages, as detailed below. By default, the synth units have a pitch bend range of +/- 2 semitones (General MIDI default) and are tuned in equal temperament with A4 at 440 Hz. These defaults can be adjusted as needed using some of the controller and sysex messages described below.
The "all notes off" (123) and "all sounds off" (120) MIDI controllers stop sounding notes on the corresponding MIDI channel.
The "all controllers off" (121) MIDI controller resets the current RPN and data entry controllers on the corresponding MIDI channel (see below).
The registered parameters (RPNs) 0 (pitch bend range), 1 (channel fine tuning) and 2 (channel coarse tuning) can be used to set the pitch bend range and fine/coarse master tuning on the corresponding MIDI channel in the usual way, employing a combination of the RPN (101, 100) and data entry controller pairs (6 and 38, as well as 96 and 97). Please check Jeff Glatt's MIDI specification for details.
Universal realtime and non-realtime scale/octave tuning messages following the MIDI Tuning Standard (MTS) can be used to set the synth to a given octave-based tuning specified as cent offsets relative to equal temperament, which is repeated in every octave of the MIDI note range 0..127. Please check "MTS Support" below for further details.
These special features may seem a little arcane, but they are utilized by some DAW and MIDI player software (especially the "all notes off" and "all sounds off" controllers). Also, the tuning capabilities come in handy when experimenting with historical temperaments and microtonality.
The general format of the supported MTS messages is as follows (using hexadecimal notation):
f0 7f/7e <device-id> 08 08/09 bb bb bb tt ... tt f7
Note that the
f0 7f and
f0 7e headers are used to denote a universal
realtime and non-realtime sysex message, respectively, and the final
terminates the message. Both types of messages will take effect immediately,
but the realtime form will also change the frequencies of already sounding
notes. The device id can be any 7-bit value from
7f and will be
ignored, so that the unit will always respond to these messages, no matter
which device id is specified. The following
08 id denotes an MTS message,
followed either by the
08 subid to denote 1-byte, or the
09 subid to
denote 2-byte encoding (see below).
The lv2.cpp architecture keeps track of separate tunings for different MIDI
channels. The three
bb bytes together specify the bitmask of MIDI channels
the message applies to, most significant byte first; the bitmask
03 7f 7f
thus sets the tuning for all MIDI channels, while the bitmask
00 00 01 only
affects the tuning of the first MIDI channel. (Note that bits 3..7 of the most
significant byte aren't used right now, but are reserved by MTS for future
use, so you shouldn't set these unless you know what you're doing.)
tt bytes specify the tuning itself, as a sequence of 12 tuning offsets
for the notes
D, etc., thru
B. In the one-byte encoding (subid
08), each tuning offset is a 7 bit value in the range
7f denoting -64, 0 and +63 cents, respectively. Thus equal
temperament is specified using twelve
40 bytes, and a quarter comma meantone
tuning could be denoted, e.g., as
4a 32 43 55 3d 4e 36 47 2f 40 51 39. The
two-byte encoding (subid
09) works in a similar fashion, but provides both
an extended range and better resolution. Here each tuning offset is specified
as a 14 bit value encoded as two data bytes (most significant byte first),
mapping the range 0..16384 to -100..+100 cents with the center value 8192
40 00) denoting 0 cents. Please check the MMA's MIDI Tuning Standard
document for details.
Note that in order to utilize MTS sysex messages, your LV2 host needs to be
able to receive and/or store sysex messages and pass them on to LV2 plugins.
Not all LV2 hosts implement this feature at this time, so the lv2.cpp
architecture also provides an alternative way to select a tuning through a
Tuning control which allows you to choose a tuning from a collection
of MTS sysex files determined at load time. (This feature can also be disabled
-DFAUST_MTS=0 option in the Makefile, or the
-notuning option of
the faust2lv2 script.)
You then just need to drop some MTS sysex (.syx) files into the
~/.faust/tuning folder. If this folder is present and contains some MTS sysex
files in the right format, then the
Tuning control becomes available on all
faust-lv2 instrument plugins which have been compiled with this option. The
control usually takes the form of a slider which shows the current tuning
number along with the basename of the corresponding sysex file. The tunings
are numbered in alphabetic order starting at 1; a slider value of 0 denotes
the default tuning (equal temperament). Changing the slider value in the
control GUI adjusts the tuning in real-time.
NOTE: Instead of ~/.faust you can also name a different "home" folder by setting the FAUST_HOME environment variable accordingly. In addition, on Mac OS X the ~/Library/Faust/Tuning folder will also be searched for tunings if ~/.faust/tuning doesn't exist or contains no valid sysex files.
This controller-based method for changing the tuning will of course become rather unwieldy if you need to work with a large number of different tunings. On the other hand, it also offers the advantage that the tuning becomes an automatable parameter which can be recorded and played back by hosts which provide control automation (your favorite DAW probably does). The amount of data in the octave-based tunings is rather small and the data is stored in main memory at load time. Thus changing tunings is not an expensive operation, and perfectly feasible also in real-time operation.
Another gotcha is that the tuning control will work best with dynamic
manifests (see below), in which case the tuning control is configured
dynamically at load time. Otherwise the control will be configured at compile
time, i.e., when the plugin's static manifest is created, so a recompile of
all instrument plugins will be in order any time the contents of the tuning
folder changes. (In this case it's probably better to employ the
-DFAUST_MTS=0 option to completely disable the tuning control when compiling
Plugins created with lv2.cpp also support the dynamic manifest extension, so that all requisite information about the plugin's name, author, ports, etc. can be included in the plugin module itself. To provide better compatibility with current LV2 hosts, which often don't have this extension enabled, this feature isn't used by default in the provided Makefile. But you can select it by uncommenting the corresponding option in the Makefile, see the corresponding remarks under "Static and Dynamic Manifests" below for details.
To compile this package (or any Faust source using the included LV2 architecture), you'll need GNU make, the GNU C++ compiler and the Boost headers, as well as recent versions of the Faust compiler and LV2. You'll also need Qt version 4 or 5 to build the custom plugin GUIs (this is optional).
Note that the examples still use the "old" a.k.a. "legacy" Faust library modules, so they should work out of the box with both "old" Faust versions (up to 0.9.85) and later ones featuring the "new" Faust library (anything after 0.9.85, including current git sources).
Please review the build requirements above and check that you have all the
necessary third-party software installed. To compile the sample Faust plugins,
make in the source directory. The compiled LV2 plugins will end
up in the lv2/faust.lv2 directory, ready to be run with your favourite LV2
make will build plugins without custom plugin GUIs. Use
gui=1 if you want these. This requires Qt, and you may also have to set
the path to the qmake executable of the Qt version that you want to use. Qt5
is usually the default on recent Linux systems. If you want to use Qt4 instead
(this is required to make the GUIs work with Ardour, for instance) then
something like the following should do the trick:
make gui=1 qmake=/usr/lib/qt4/bin/qmake
You can also run
make install to install the the sample plugins in the
system-wide LV2 directory (/usr/local/lib/lv2 by default). You need to do this
as root, e.g., via
sudo, if you're installing into system directories.
Package maintainers may want to add something like
prefix=/usr to the
make install command to install into a staging
directory and change the installation prefix.
NOTE: The installation step is optional. If you just want to try the plugins with your favourite LV2 host after compilation, it should be enough to set your LV2_PATH environment variable (or configure your LV2 host) so that the host includes the generated faust-lv2/lv2 directory in its LV2 plugin search path.
There are a number of compilation options which can be changed by setting the
appropriate variables in the Makefile, please check the Makefile for
details. In particular, the installation prefix can be changed with the
prefix variable, and you can also use the
lv2libdir variable to set the
LV2 installation path in a direct fashion. In addition, you can change the
faust.lv2 of the plugin directory with the
variable. Moreover, it is possible to build and install the plugins in
separate bundles (one per plugin) by running make with the following targets:
make split-bundles make install-split
The plugins will then be found in separate directories named after the plugin
amp.lv2 for the
amp.dsp plugin etc.) so that you can also cherry-pick
the plugins that you want.
Static and Dynamic Manifests
By default, faust-lv2 uses static "manifests" (ttl files generated at compile time, cf. "The Manifest" below) for the plugins; this offers the best compatibility with existing LV2 hosts.
It is also possible to compile the plugins for LV2's dynamic manifest
extension which allows most of the manifest to be included in the plugin
module itself. This is selected by uncommenting the corresponding line reading
dyn-manifests = 1 in the Makefile, or by adding this setting to the make
This option considerably speeds up the build process, and is also recommended
when making use of dynamic features such as the MTS tuning control. But it
requires that the LV2 host supports the dynamic manifest extension. Many LV2
hosts do not support this out of the box, hence this isn't the default right
now. If you want to go that route, we recommend using hosts based on the
lilv library, such as Ardour and Qtractor. You'll also have to make sure
that lilv was configured with
--dyn-manifest in order to enable the dynamic
Installing the Build Script and the Architectures
Finally, to compile your own plugins you may want to have the latest architecture files and faust2lv2 build script installed. If you have a recent Faust version from git on your system, then most likely you already have these. Otherwise, you can add them to your existing Faust installation with:
(You may want to specify the
prefix variable to point the Makefile to the
Faust installation prefix, usually either /usr or /usr/local. But this isn't
mandatory. The faust2lv2 script will try to locate Faust and the faust-lv2
architecture files in some common locations, and you can also set the
FAUSTLIB environment variables to point faust2lv2 to the
proper locations if needed.)
The faust2lv2 script doesn't offer all the options you have when using the Makefile or compiling plugins manually, but it covers most common use cases and in any case it's currently the easiest way to create a self-contained LV2 bundle from a single Faust source. Please check the following section for detailed usage instructions.
Using the Build Script
The distribution includes the faust2lv2 script which runs the Faust compiler for you and handles all steps necessary to create a working LV2 bundle in an automatic fashion. You can create an LV2 bundle for a plugin in the Faust source file myplugin.dsp simply as follows:
If the plugin is supposed to become an instrument but the dsp source doesn't
nvoices declaration (or if you want to override the value given
there), just add the
-nvoices option to the command line, e.g.:
faust2lv2 -nvoices 16 myplugin.dsp
-nvoices 0 can be used to turn an instrument into an ordinary
effect, so that you can run the synth in a manual fashion by explicitly
gate controls through the GUI or automation
facilities provided by the LV2 host.)
The resulting LV2 bundle myplugin.lv2 will be created in the current working
directory, ready to be moved to your LV2 plugin directory. The script also
recognizes a few additional options which let you configure some aspects of
the resulting LV2 bundle. Please run
faust2lv2 -h or
faust2lv2 -help for a
brief summary of all supported options. Currently the most important of these
-dyn-manifesttells the script to create a plugin with dynamic manifests; the default is to use static manifests. See "Static and Dynamic Manifests" above for details on this option and when you may want to use it.
-guibuilds a custom GUI along with the plugin and also adjusts the manifest accordingly. This requires Qt version 4 or 5 (you'll typically use Qt4 for Ardour, Qt5 for the other hosts). By default, Qt5 will be preferred if it is found; the
-qt5option can be used to override the default choice (this also implies
-gui). The script tries to locate
qmakein some common locations. You can also set the path to the
qmakeexecutable explicitly using the
QMAKEenvironment variable if this doesn't work.
-guioption can also be combined with the
-qrcodeoptions to add support for the built-in OSC and HTTP interfaces, as described in the Faust Quick Reference.
-uri-prefix URIsets the prefix of the plugin URI to the given option value. The plugin URI is used by LV2 plugin hosts to uniquely identify a plugin, see the LV2 documentation or "Compiling The C++ Source" below for details.
For instance, the following options enable dynamic manifests and set the URI prefix of the compiled plugin:
faust2lv2 -dyn-manifest -uri-prefix "http://somewhere.org/plugins" \ myplugin.dsp
And here is how you invoke faust2lv2 in order to add a custom GUI:
faust2lv2 -gui myplugin.dsp
The script will look for a suitable
qmake executable in some common
locations. Exactly which
qmake will be chosen is displayed as the default
value of the
QMAKE environment variable if you run
faust2lv2 -h. If
qmake cannot be found then you'll have to set this variable accordingly,
QMAKE=/usr/lib/qt4/bin/qmake faust2lv2 -gui myplugin.dsp
When using the
-gui option, you may also want to add
for Faust's built-in OSC and HTTP support:
faust2lv2 -gui -osc -httpd myplugin.dsp
This will let you control the plugin with OSC devices and/or through a web
browser, as described in the Faust Quick Reference manual. The
option can also be combined with
-qrcode to have the plugin GUI pop up a
window with the plugin's URL and the corresponding QR code which is
convenient, e.g., to open the plugin's web interface on your smartphone. Note
that in the current implementation each plugin instance gets its own
dynamically assigned URL, which is active only as long as the plugin GUI is
open. The same also holds for the OSC port assigned when using the
option to compile a plugin. Please check the corresponding sections of the
Faust Quick Reference manual for details.
Please note that the faust2lv2 script only lets you create an individual plugin bundle from a single Faust source at present. If that is all you need then you can stop reading now. But if you want to create plugin libraries as single bundles or if you need more control over the build process then you'll have to use the provided Makefile or compile the plugins manually. To these ends, the next section breaks down the compilation process into different steps and discusses each of these in turn. (Exactly the same steps are also invoked automatically in the Makefile and the faust2lv2 script.)
Using the LV2 Architecture in your own Faust Programs
The options you have to build LV2 plugins from your own Faust sources are the following, from easiest to hardest:
Use the Faust online compiler. Dead-easy. You don't even need to install Faust, just grab the compiled plugin and run with it. You need a working Internet connection, however, and the online compiler needs to be up and running.
Use the faust2lv2 script. See "Using the Build Script" above. Basic usage is very easy, just run
faust2lv2 myplugin.dspand drop the resulting myplugin.lv2 folder into your LV2 plugin directory. (The same script is also used by the Faust online compiler.)
Drop your Faust sources into the examples folder and rerun
make split-bundles). Then move the resulting faust.lv2 (or myplugin.lv2) folder into your LV2 plugin directory.
Create your own build system based on the generic Makefile. This may require some hand-editing of the variables in the Makefile (URI prefixes and such), but is usually quite straightforward. You might want to do this if you'd like to distribute your own plugin bundle in source form.
Roll your own build system or script. This is not for the faint of heart and requires some detailed knowledge about the build process (which will be provided in the remainder of this section, starting at "Compiling the Faust Source").
Compiling the Faust Source
Basic usage of the architecture file is as with any other Faust architecture,
i.e., you simply specify the architecture with Faust's
-a option. E.g.:
faust -a lv2.cpp -cn myplugin myplugin.dsp -o myplugin.cpp
Note the use of Faust's
-cn (class name) option. This isn't strictly
necessary, but it sets a reasonable default for the plugin URI which is used
to identify the plugin (see below for a more detailed explanation of this). If
you do not specify this option, Faust will use the default class name
You should now have the C++ source of the plugin in the current directory. To create an actual LV2 plugin from the C++ source, you'll have to go through the following steps:
- compile the plugin source to a shared module
- create an LV2 "manifest" for the plugin
- create an LV2 "bundle" containing the plugin module and its manifest
We now take a closer look at these steps so that you get a good understanding of the build process. This will enable you to handle more complex use cases, diagnose problems, customize scripts and Makefile, and even come up with your own build system if the need arises.
Compiling the C++ Source
After running the dsp source through the Faust compiler, the resulting C++ source file for the plugin must be compiled to a shared module, using your favourite C++ compiler. For instance, using gcc under Linux:
g++ -shared myplugin.cpp -o myplugin.so
-fPIC and other compilation options such as
-O3 as needed. To make
this work, you'll need to have the LV2 framework installed, so that the
required LV2 header files are available. The instrument part of the
architecture also needs some of the header files of the Boost C++ library,
so you should have at least the headers from that project installed as well.
To create a working plugin, you may also have to modify the URI of the plugin. As already mentioned, the URI ("uniform resource identifier", cf. RFC3986 and RFC1630) serves as a global name which is used to identify the plugin and differentiate it from other installed LV2 plugins, hence this name must be unique. For instance, the URI might be the actual URL of the website where the plugin may be downloaded, or it may just be an abstract name using one of the available URI schemes.
In order to support dynamic manifests, the URI needs to be included in the
plugin source. The LV2 architecture thus contains a definition for the plugin
URI. By default the URI
https://faustlv2.bitbucket.io/myplugin will be used,
myplugin is the actual class name of the plugin as set with Faust's
-cn option discussed above. Of course, you will want to change this when
compiling your own plugins.
There are some macro definitions which can be adjusted on the
line to change the URI according to your needs:
URI_PREFIXspecifies the prefix of the URI to which the class name of the plugin will be appended.
PLUGIN_URIspecifies the entire URI. This overrides the URI no matter what
URI_PREFIXvalue and class name have been specified.
These values must be given as double-quoted strings. For instance, if the
myplugin has been set with the Faust
-cn option, then it's
usually sufficient to just change the URI prefix, e.g., as follows:
g++ -shared -DURI_PREFIX='"http://somewhere.org/plugins"' \ myplugin.cpp -o myplugin.so
If you really need to adjust the entire plugin URI, you can also compile the plugin with:
g++ -shared -DPLUGIN_URI='"http://somewhere.org/plugins/myplugin"' \ myplugin.cpp -o myplugin.so
In either case, the plugin URI given here must match the URI specified in the manifest of the plugin, see "The Manifest" below.
With the steps described in the previous subsections the compilation of the plugin is now essentially complete. However, LV2 also requires that you prepare a manifest.ttl file for the plugin, as explained in the LV2 documentation. The manifest is read by LV2 hosts to discover which plugins are available and how they can be loaded. You could prepare the manifest files by hand, but for non-trivial plugins this will be very error-prone and thus is not recommended. Therefore faust-lv2 comes with some templates which can be used to create the manifest files in a more or less automatic fashion, as explained below.
A minimal manifest must at least include the URI of the plugin and the name of the binary (shared module) from which the plugin can be loaded. If your LV2 host supports the dynamic manifest extension (see the corresponding remarks under "Static and Dynamic Manifests"), this is in fact all that is needed, since the remaining information (human-readable name, author and license of the plugin, port information, etc.) can be provided by the plugin module itself.
Manifest files are so-called RDF files written in Turtle syntax, which
customarily have the .ttl filename extension. You can find a template for a
minimal manifest in the lv2-manifest-template.ttl file which is included in
the faust-lv2 distribution. This file can be edited by hand or, e.g., with the
sed program to replace the placeholders
the URI, basename and the (OS-specific) filename extension of the plugin
module. For instance:
sed -e 's?@uri@?http://somewhere.org/plugins/myplugin?g' \ -e 's?@name@?myplugin?g' -e 's?@dllext@?.so?g' \ < lv2-manifest-template.ttl > manifest.ttl
The plugin URI must match what was set when compiling the plugin, see "Compiling the C++ Source" above. Here's how the created manifest might look like:
@prefix doap: <http://usefulinc.com/ns/doap#> . @prefix foaf: <http://xmlns.com/foaf/0.1/> . @prefix lv2: <http://lv2plug.in/ns/lv2core#> . @prefix dman: <http://lv2plug.in/ns/ext/dynmanifest#> . <http://somewhere.org/plugins/myplugin/manifest> lv2:binary <myplugin.so> ; a dman:DynManifest .
This is all that's needed for the dynamic manifest case. If your LV2 host does
not support the dynamic manifest extension (many LV2 hosts currently
don't, at least not out of the box), a static manifest will be needed in the
form of a ttl file which includes all the requisite information about the
plugin. Instead of lv2-manifest-template.ttl you should use
lv2-manifest-static-template.ttl in this case. Replacing the
@uri@ et al
placeholders in this file works the same as above. This will give you
@prefix doap: <http://usefulinc.com/ns/doap#> . @prefix foaf: <http://xmlns.com/foaf/0.1/> . @prefix lv2: <http://lv2plug.in/ns/lv2core#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . <http://somewhere.org/plugins/myplugin> a lv2:Plugin ; lv2:binary <myplugin.so> ; rdfs:seeAlso <myplugin.ttl> .
Note that the static manifest refers to an additional .ttl file with the same basename as the plugin module which contains all the additional information about plugin name, ports etc. You can create this file from the same C++ source as the plugin module itself, by compiling the source to an executable which, when run, simply prints the dynamic manifest of the plugin to standard output, from where it can be redirected into the ttl file. After creating the ttl file, the executable is no longer needed and can be deleted. For instance:
g++ myplugin.cpp -o myplugin ./myplugin > myplugin.ttl rm myplugin
You should add to the
g++ command all options that may be needed to compile
the plugin. In particular, the same symbol definitions which set the plugin
URI will be needed as described under "Compiling the C++ Source".
Finally, shared module and manifest are packaged together as an LV2 bundle, a directory which customarily has the .lv2 extension. For dynamic manifests, this can be done as follows:
mkdir myplugin.lv2 cp myplugin.so manifest.ttl myplugin.lv2
If you use a static manifest, as described in the previous subsection, you must also copy the generated ttl file to this directory:
cp myplugin.ttl myplugin.lv2
You can then set up LV2_PATH to point to the parent directory of the myplugin.lv2 folder, or copy the folder to some directory on your LV2_PATH to have the plugin recognized by your LV2 host.
Bundling of Plugin Libraries
If you have more than one Faust plugin, you can simply repeat the steps
described above for each plugin to be compiled. However, in this case you also
have the option to create a single bundle from the entire plugin collection.
To these ends, just concatenate all the manifest.ttl files of the individual
plugins to one big manifest.ttl file. Then place this file along with all the
plugin modules (and, in the case of static manifests, the corresponding .ttl
file for each plugin) into a single folder. (This is also the way that the
distributed Makefile packages the plugins unless the
target is used, cf. "Installation".)
For instance, suppose that we have three plugins
freeverb in corresponding .lv2 subdirectories, then we can bundle them
together in a single folder, say,
faust.lv2, as follows:
mkdir faust.lv2 for plugin in amp chorus freeverb; do cat $plugin.lv2/manifest.ttl >> faust.lv2/manifest.ttl cp $plugin.lv2/$plugin.so $plugin.lv2/$plugin.ttl faust.lv2 done
Creating a Plugin GUI
To keep things simple, we've only discussed how to create plugins without custom GUIs. Adding a plugin GUI involves translating the Faust source with the lv2ui.cpp architecture in lieu of lv2.cpp and compiling the resulting C++ source to a separate GUI module using the Qt build tool (qmake). You'll also have to adjust the manifest so that the LV2 host knows about the plugin GUI and links it to the proper plugin. The precise steps are all rather straightforward, but involve a few technicalities, because qmake needs to be used in order to support Qt's signal/slot mechanism. The gory details can be found in the provided Makefile or the faust2lv2 script.
Automatizing the Build Process
The above steps needed to build and install a working LV2 plugin or an entire plugin collection from Faust source can of course be automatized, using any build tool of your choice. In fact the entire faust-lv2 package is just one big example which illustrates how to do this with GNU make or with the included build script. Using the explanation of the manual build process above you should now be able to customize the provided Makefile and the faust2lv2 script for your own purposes.
The LV2 plugin implementation of the Faust LV2 architecture is fully functional and reasonably complete already. But of course there are ways in which it can be further improved. Some useful and interesting directions for future research and development are pointed out below.
Add improvements for smoother playback. In particular, the polyphony control of instruments is fairly disruptive right now, as it simply resets all voices each time the control changes. Another idea would be to do fully dynamic voice allocation so that the polyphony control wouldn't be needed at all. Also, it would be useful to implement crossfading to prevent clicks when a plugin is activated or deactivated.
(As suggested on the Faust mailing list:) Add support for alternative voice allocation and control models such as MPE (Multidimensional Polyphonic Expression). MPE is used by some of the latest MIDI hardware and also has support in some DAWs already.
Add support for the LV2 Time extension. This extension provides transport information such as current position, tempo and meter to a plugin, which is useful for plugins which need to synchronize their beats and/or tempo with the host.
Implement MIDI mapping for passive Faust controls. This data is already available in the control output ports, but now that most DAWs fully support MIDI routing with plugins, it might be handy to also have the data as MIDI output for some use cases.
Port the architecture to other targets (Audio Units, Pd and Max come to mind) and GUI toolkits. The existing architecture already has an abstract plugin class at its core which gets rid of most of the plugin API specifics. In the end, it should even be possible to unify all these to a single architecture which works with all these plugin APIs (this is a long-term project, though, so don't hold your breath just yet).