Building LV2 Plugins with JUCE and CMake

As a programmer who develops open source audio plugins, I suppose it was only a matter of time…

Let’s say that you’re an audio plugin developer, and one day you decide that you would like your plugins to support Linux. These days, that decision makes a lot of sense, given the growing number of music producers using Linux, and the high-quality DAWs that the Linux users of today can choose from, including Reaper, Bitwig, and Ardour.

Popular plugin formats like Apple’s Audio Unit (AU) and Avid’s Audio Extensions (AAX) are obviously out of the question, as the former is restricted to Apple devices, and the latter only work with other Avid software (namely Pro Tools). The next logical plugin format for many is Steinberg’s Virtual Studio Technology (VST). Steinberg started adding Linux support for VST3 plugins in 2017, and does support the development of open-source VST3 plugins (under the GPLv3), but not all Linux plugin hosts support the VST3 format.

So if you’re like me, and you have an open-source audio plugin out there on GitHub supporting some the plugin formats listed above, at some point you’ll probably get GitHub issue looking something like this:

For the uninitiated, LV2 is short for LADSPA Version 2 (the successor to the Linux Audio Developer’s Simple Plugin API (LADSPA)), and has been developed by David Robillard and others as an open-source audio plugin API. Many audio software on Linux support LV2s including Ardour, Carla, and recently Reaper as well, so it makes sense that music-makers who use Linux (and there are more and more of them every year!) would like it if their favorite plugins were available as LV2s.

What Can We Do

As a plugin developer, there are a lot of choices we make that determine how easy it is to support the LV2 format. If you’re using the DISTRHO Plugin Framework (DPF), you probably already support LV2, and don’t really need to be reading this! LV2 support is one of the great reasons to choose DPF as a plugin framework in the first place.

However, a very popular choice of plugin frameworks is JUCE, a more general and fully-featured framework that I’ve discussed previously in this article. As I mention there, JUCE has not yet added LV2 support, largely due to the fact that it scored rather low in their last developer survey. Unfortunately, that meant that adding LV2 support to a JUCE-based plugin used to be a pretty painful process. I was fortunate to be guided through this process by GitHub users @falkTX and @kottV, two wonderful folks with a deep knowledge of the Linux audio world. Even still, I only maintained LV2 support for one of my plugins because it was so difficult to get it working.

One of my plugins, ChowTapeModel, running as an LV2 in Carla.

Enter CMake

With the release of JUCE 6, JUCE now supports creating plugins with the CMake build system, which offers much more flexibility than the JUCE’s Projucer of old. Working with the GitHub users mentioned above, there is now a fork of JUCE that does support LV2 builds “out-of-the-box” via the CMake interface. So if you’re a plugin developer looking to add LV2 support to your plugin, here are some steps you can follow:

  1. Set up your plugin to build with the JUCE CMake API.
  2. Add JUCE as a submodule to your repository. This isn’t strictly necessary, but I like to do it so that I always know which version of JUCE my plugin requires. Be sure to checkout the lv2 branch on the JUCE fork.
mkdir modules && cd modules
git submodule add https://github.com/lv2-porting-project/JUCE
cd JUCE
git checkout lv2

3. In your CMakeLists.txt, add LV2 to your plugin formats when building on Linux. Note that it should be possible to build LV2 plugins on Mac and Windows as well, I just haven’t tested it yet.

4. Create a URI for your plugin and add that to the CMake configuration as part of the juce_add_plugin command, with the argument LV2_URI.

5. If the name of your plugin contains spaces, you will also need to define a name with no spaces for the shared library file that will contain your plugin. This value will be defined in juce_add_plugin as well, with the argument LV2_SHARED_LIBRARY_NAME. Note that the LV2 spec has no problem with having spaces in the library name, it’s just a limitation of the CMake build system that has not yet been resolved, leading to this solution as a workaround.

6. Build the plugin as follows:

sudo apt-get install lv2-dev 
cmake -Bbuild
cmake --build build

As an example, here’s a snippet of the CMakeLists for my ChowMatrix plugin. Note the LV2_URI, and LV2_SHARED_LIBRARY_NAME defined near the bottom.

# default plugin formats
set(JUCE_FORMATS AU VST3 Standalone)
# Build LV2 only on Linux
if(UNIX AND NOT APPLE)
message(STATUS "Building LV2 plugin format")
list(APPEND JUCE_FORMATS LV2)
endif()
juce_add_plugin(ChowMatrix
COMPANY_NAME chowdsp
PLUGIN_MANUFACTURER_CODE Chow
PLUGIN_CODE spg3
FORMATS ${JUCE_FORMATS}
ProductName "ChowMatrix"
ICON_BIG res/logo.png
MICROPHONE_PERMISSION_ENABLED TRUE
LV2_URI https://github.com/Chowdhury-DSP/ChowMatrix
LV2_SHARED_LIBRARY_NAME ChowMatrix
)

Wrapping Up

Anyway, I’m hoping that this post will encourage more folks who are making open-source plugins with JUCE to take a look at supporting the LV2 format. I’m happy to say that all of my last three plugin releases have included LV2 support! And finally, I’d like to thank @falkTX, @kottV, and @mxmilkiib on GitHub for encouraging me to add LV2 support for my plugins, and for giving me the necessary guidance and assistance to make it possible. If you’re looking to port your JUCE plugin to LV2 and are running into issues, feel free to reach out to me via email or GitHub issues!

Jatin Chowdhury is a student.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store