Albert Gräf, 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 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
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.

Supported Hosts

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:ctrl attributes 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=0 when compiling the C++ source; just uncomment the corresponding line in the Makefile, or invoke faust2lv2 with the -nomidicc option. (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:ctrl attribute, 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 -nometa option, if you'd rather like to provide this information yourself by editing the static manifest.

  • The global nvoices meta 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.

  • The unit attribute (e.g., [unit:Hz]) in the Faust source is translated to a corresponding LV2 unit attribute. 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 (or 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.

  • The lv2:integer attribute in the Faust source is translated to the lv2:integer LV2 port property, so that the control may be shown as an integer-only field in the host's GUI.

  • The lv2:reportsLatency attribute in the Faust source is translated to the lv2:reportsLatency LV2 port property, also adding the lv2:designation lv2:latency property 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.

  • The lv2:hidden or lv2:notOnGUI attribute maps to the LV2 notOnGUI port property, so that hosts honoring this property may suppress the display of this control in their GUI.

GUI Support

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 plugin.

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 qmake 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.

Instrument Plugins

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 Polyphony control 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 freq, gain and gate to 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.

MTS Support

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 f7 byte 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 00 to 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.)

The tt bytes specify the tuning itself, as a sequence of 12 tuning offsets for the notes C, C#, D, etc., thru B. In the one-byte encoding (subid 08), each tuning offset is a 7 bit value in the range 00..7f, with 00, 40 and 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.

Tuning Control

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 special 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 with the -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 the plugins.)

Dynamic Manifests

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.

Build Requirements

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, simply run 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 host.

By default, make will build plugins without custom plugin GUIs. Use make 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 DESTDIR=/buildroot and 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 default name faust.lv2 of the plugin directory with the bundledir 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 command:

make dyn-manifests=1

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 manifest extension.

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:

make install-faust

(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 FAUSTINC and 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:

faust2lv2 myplugin.dsp

If the plugin is supposed to become an instrument but the dsp source doesn't contain the 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

(Conversely, -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 operating the freq, gain and 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 are:

  • -dyn-manifest tells 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.

  • -gui builds 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 -qt4 or -qt5 option can be used to override the default choice (this also implies -gui). The script tries to locate qmake in some common locations. You can also set the path to the qmake executable explicitly using the QMAKE environment variable if this doesn't work.

  • The -gui option can also be combined with the -osc, -httpd and -qrcode options to add support for the built-in OSC and HTTP interfaces, as described in the Faust Quick Reference.

  • -uri-prefix URI sets 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 "" \

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, e.g.:

QMAKE=/usr/lib/qt4/bin/qmake faust2lv2 -gui myplugin.dsp

When using the -gui option, you may also want to add -osc and/or -httpd 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 -httpd 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 -osc 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.dsp and 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 (or 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 mydsp.

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

Add -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 will be used, where 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 g++ command line to change the URI according to your needs:

  • URI_PREFIX specifies the prefix of the URI to which the class name of the plugin will be appended.

  • PLUGIN_URI specifies the entire URI. This overrides the URI no matter what URI_PREFIX value and class name have been specified.

These values must be given as double-quoted strings. For instance, if the class name 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='""' \
  myplugin.cpp -o

If you really need to adjust the entire plugin URI, you can also compile the plugin with:

g++ -shared -DPLUGIN_URI='""' \
  myplugin.cpp -o

In either case, the plugin URI given here must match the URI specified in the manifest of the plugin, see "The Manifest" below.

The Manifest

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 @uri@, @name@ and @dllext@ with the URI, basename and the (OS-specific) filename extension of the plugin module. For instance:

sed -e 's?@uri@?' \
  -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: <> .
@prefix foaf: <> .
@prefix lv2:  <> .
@prefix dman: <> .

    lv2:binary <> ;
    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 something like:

@prefix doap: <> .
@prefix foaf: <> .
@prefix lv2:  <> .
@prefix rdfs: <> .

    a lv2:Plugin ;
    lv2:binary <> ;
    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".

LV2 Bundles

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 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 split-bundle make target is used, cf. "Installation".)

For instance, suppose that we have three plugins amp, chorus and 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.lv2/$plugin.ttl faust.lv2

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.

Future Work

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).