Difference between revisions of "Understanding LV2"

From MOD Wiki
Jump to navigation Jump to search
Line 21: Line 21:
 
Before installing the Mod toolchain there are some dependencies that need to be installed in order to get the toolchain to work. Run the following command:
 
Before installing the Mod toolchain there are some dependencies that need to be installed in order to get the toolchain to work. Run the following command:
  
''# install dependencies (note multi-line command)
+
''# install dependencies (note multi-line command)''
sudo apt install acl bc curl cvs git mercurial rsync subversion wget \
+
''sudo apt install acl bc curl cvs git mercurial rsync subversion wget \''
bison bzip2 flex gawk gperf gzip help2man nano perl patch tar texinfo unzip \
+
''bison bzip2 flex gawk gperf gzip help2man nano perl patch tar texinfo unzip \''
automake binutils build-essential cpio libtool libncurses-dev pkg-config libtool-bin''
+
''automake binutils build-essential cpio libtool libncurses-dev pkg-config libtool-bin''
  
 
N.B. if this throws an error try installing them separately, also check if python is installed since these commands make that assumption. Next is the plugin builder and bootstrapping:
 
N.B. if this throws an error try installing them separately, also check if python is installed since these commands make that assumption. Next is the plugin builder and bootstrapping:
  
''# clone MPB
+
''# clone MPB''
git clone https://github.com/moddevices/mod-plugin-builder.git
+
''git clone https://github.com/moddevices/mod-plugin-builder.git''
cd mod-plugin-builder
+
''cd mod-plugin-builder''
  
# bootstrap the cross-compiler
+
''# bootstrap the cross-compiler''
./bootstrap.sh modduox minimal''
+
''./bootstrap.sh modduox minimal''
  
 
N.B. modduo can be replaced with moddwarf or modduo.
 
N.B. modduo can be replaced with moddwarf or modduo.
Line 53: Line 53:
 
To log values when programming an lv2 plugin use the lv2_logger. When programming using C there are examples on how to do so in the lv2 book. However, when using C++ you will be forced to wrap it in a “C-wrapper”. This complicates the logging process as you can only log features within the wrapping implementation’s portion of C-code. The easiest way I found throughout my testing is to add a logger and map to the cpp header file, log messages throughout functions as they are implemented in the C-wrapper and instantiate the logger and map as you would when you are programming using C using the appropriate headers from the lv2 book examples. N.B. you cannot log messages from the implementation, not even when you write a “extern “C”” wrapped log function with a local logger that’s properly instantiated.
 
To log values when programming an lv2 plugin use the lv2_logger. When programming using C there are examples on how to do so in the lv2 book. However, when using C++ you will be forced to wrap it in a “C-wrapper”. This complicates the logging process as you can only log features within the wrapping implementation’s portion of C-code. The easiest way I found throughout my testing is to add a logger and map to the cpp header file, log messages throughout functions as they are implemented in the C-wrapper and instantiate the logger and map as you would when you are programming using C using the appropriate headers from the lv2 book examples. N.B. you cannot log messages from the implementation, not even when you write a “extern “C”” wrapped log function with a local logger that’s properly instantiated.
  
''//log feature in the header
+
''//log feature in the header''
LV2_URID_MAP* map;
+
''LV2_URID_MAP* map;''
LV2_Log_Logger logger;
+
''LV2_Log_Logger logger;''
  
//instantiation in the C-wrapper instantiation function, <effect> is a PLUGIN_CLASS
+
''//instantiation in the C-wrapper instantiation function, <effect> is a PLUGIN_CLASS''
const char* missing = lv2_features_query(
+
''const char* missing = lv2_features_query(''
features,
+
'' features,''
LV2_LOG__log, &<effect>->logger.log, false,
+
'' LV2_LOG__log, &<effect>->logger.log, false,''
LV2_URID__map, &<effect>->map, true,
+
'' LV2_URID__map, &<effect>->map, true,''
NULL);
+
'' NULL);''
  
lv2_log_logger_set_map(&<effect>->logger, <effect>->map);
+
''lv2_log_logger_set_map(&<effect>->logger, <effect>->map);''
  
if (missing) {
+
''if (missing) {''
lv2_log_error(&<effect>->logger, “Missing feature <%s>\n”, missing);
+
'' lv2_log_error(&<effect>->logger, “Missing feature <%s>\n”, missing);''
free(<effect>);
+
'' free(<effect>);''
return NULL;
+
'' return NULL;''
}
+
''}''
  
//logging example, for example on parameter change
+
''//logging example, for example on parameter change''
lv2_log_note(&<effect>->logger, “printed parameter: %f”, <effect>->getParameter());''
+
''lv2_log_note(&<effect>->logger, “printed parameter: %f”, <effect>->getParameter());''
  
 
=== Starting from scratch: ===
 
=== Starting from scratch: ===
Line 91: Line 91:
 
Within the makefile there are a couple of constants and commands to be declared. Similarly to the folder and file names these also need to adhere to naming conventions in order for the build script to execute the makefile correctly. This means all upper case and ‘_’ to denote spaces so <our-name> becomes <OUR_NAME>. The constants and commands are as follows:
 
Within the makefile there are a couple of constants and commands to be declared. Similarly to the folder and file names these also need to adhere to naming conventions in order for the build script to execute the makefile correctly. This means all upper case and ‘_’ to denote spaces so <our-name> becomes <OUR_NAME>. The constants and commands are as follows:
  
''<OUR_NAME>_SITE_METHOD
+
''<OUR_NAME>_SITE_METHOD''
<OUR_NAME>_SITE
+
''<OUR_NAME>_SITE''
<OUR_NAME>_VERSION
+
''<OUR_NAME>_VERSION''
<OUR_NAME>_DEPENDENCIES
+
''<OUR_NAME>_DEPENDENCIES''
<OUR_NAME>_BUNDLES
+
''<OUR_NAME>_BUNDLES''
<OUR_NAME>_TARGET_WAF/OUR_NAME_TARGET_MAKE
+
''<OUR_NAME>_TARGET_WAF/OUR_NAME_TARGET_MAKE''
<OUR_NAME>_CONFIGURE_CMDS
+
''<OUR_NAME>_CONFIGURE_CMDS''
<OUR_NAME>_BUILD_CMDS
+
''<OUR_NAME>_BUILD_CMDS''
<OUR_NAME>_INSTALL_TARGET_CMDS
+
''<OUR_NAME>_INSTALL_TARGET_CMDS''
$(eval $(generic-package))''
+
''$(eval $(generic-package))''

Revision as of 11:58, 12 January 2022

This page describes basic LV2 related concepts for those unfamiliar with the subject.

LV2 Basics

LV2 plugins are audio plugins, this means they process and/or generate audio, MIDI and/or control data. Just like other audio plugins they require a host, in case of MOD they are hosted in JACK on the device for data processing and in the browser on a remote client for the GUI. For programming LV2 plugins, experience with C/C++ is required. (The LV2 developers have a handy webpage with examples at https://lv2plug.in/book/)

The LV2 plugin contains two parts: code and data. The code part contains C or C compatible language (e.g. C++) files which are responsible for processes like instantiation and port connection (this means where the plugin can find the locations of the data to read and analyse and write output to) as well as the DSP components of the plugin. The data part contains Turtle files which are used to link all files together in a “bundle” such that the host can quickly scan the plugin for critical information (in the manifest.ttl) and loads more detailed meta-data (in the pluginname.ttl) once the plugin is analysed. This means a host can quickly scan for manifests to discover plugins, proces the other Turtle files to generate a UI and use the code portion of the bundle to process audio data once loaded.

Set-up to develop Mod plugins on windows

In a broad overview we will need to set up a virtual machine running linux, installing necessary packages, installing the Mod toolchain and building, testing and deploying an example plugin.

Setting up a virtual machine:

To set up the virtual machine (VM) we are going to use VirtualBox. This allows the user to run another computer on their system. Make a new machine, choose Linux/Ubuntu 64 and make sure to allocate at minimum 2gb ram and 50gb of memory. It’s smart to choose a drive that has a quick connection to your computer, a VM on an SSD is going to run much quicker than a VM on an external drive using an old USB connection.

Next we need an installation image to use in the VM. Download any Ubuntu image from the internet and install Ubuntu. After installing make sure to save the state of the VM as restarting the VM will make you reinstall the image, throw an error saying you installed it already and make things harder. Once installed make sure to run an update check through the system settings.

The Mod toolchain and necessary packages:

Before installing the Mod toolchain there are some dependencies that need to be installed in order to get the toolchain to work. Run the following command:

# install dependencies (note multi-line command) sudo apt install acl bc curl cvs git mercurial rsync subversion wget \ bison bzip2 flex gawk gperf gzip help2man nano perl patch tar texinfo unzip \ automake binutils build-essential cpio libtool libncurses-dev pkg-config libtool-bin

N.B. if this throws an error try installing them separately, also check if python is installed since these commands make that assumption. Next is the plugin builder and bootstrapping:

# clone MPB git clone https://github.com/moddevices/mod-plugin-builder.git cd mod-plugin-builder

# bootstrap the cross-compiler ./bootstrap.sh modduox minimal

N.B. modduo can be replaced with moddwarf or modduo.

Building and testing:

To build and test a plugin navigate to the mod-plugin-builder folder. Follow the instructions from the README.md to build a plugin. As a test let’s build the pitchshifter by typing:

./build modduox mod-pitchshifter

If everything works,it should have built the plugin in ../mod-workdir/modduox/. To now push it to the unit, navigate to folder containing the .lv2 directory and with the device connected through USB type:

tar cz <pluginname>.lv2 | base64 | curl -F 'package=@-' http://192.168.51.1/sdk/install

N.B. change the pluginname in the command. Once you connect to the web UI, the plugin should show up (showing a ‘local’-tag in the components window).

Logging values:

To log values when programming an lv2 plugin use the lv2_logger. When programming using C there are examples on how to do so in the lv2 book. However, when using C++ you will be forced to wrap it in a “C-wrapper”. This complicates the logging process as you can only log features within the wrapping implementation’s portion of C-code. The easiest way I found throughout my testing is to add a logger and map to the cpp header file, log messages throughout functions as they are implemented in the C-wrapper and instantiate the logger and map as you would when you are programming using C using the appropriate headers from the lv2 book examples. N.B. you cannot log messages from the implementation, not even when you write a “extern “C”” wrapped log function with a local logger that’s properly instantiated.

//log feature in the header LV2_URID_MAP* map; LV2_Log_Logger logger;

//instantiation in the C-wrapper instantiation function, <effect> is a PLUGIN_CLASS const char* missing = lv2_features_query( features, LV2_LOG__log, &<effect>->logger.log, false, LV2_URID__map, &<effect>->map, true, NULL);

lv2_log_logger_set_map(&<effect>->logger, <effect>->map);

if (missing) { lv2_log_error(&<effect>->logger, “Missing feature <%s>\n”, missing); free(<effect>); return NULL; }

//logging example, for example on parameter change lv2_log_note(&<effect>->logger, “printed parameter: %f”, <effect>->getParameter());

Starting from scratch:

To start writing a new plugin navigate to the /mod-plugin-builder/ folder. From there let’s navigate to the /plugin/package/ folder and make a new folder among the list of others.

mkdir <our-name>

N.B. all lower case characters and ‘-’ to denote spaces. Navigate to the folder.

cd <our-name>

From here it expects a makefile with the same name as our folder, so let’s make that.

touch <our-name>.mk

Within the makefile there are a couple of constants and commands to be declared. Similarly to the folder and file names these also need to adhere to naming conventions in order for the build script to execute the makefile correctly. This means all upper case and ‘_’ to denote spaces so <our-name> becomes <OUR_NAME>. The constants and commands are as follows:

<OUR_NAME>_SITE_METHOD <OUR_NAME>_SITE <OUR_NAME>_VERSION <OUR_NAME>_DEPENDENCIES <OUR_NAME>_BUNDLES <OUR_NAME>_TARGET_WAF/OUR_NAME_TARGET_MAKE <OUR_NAME>_CONFIGURE_CMDS <OUR_NAME>_BUILD_CMDS <OUR_NAME>_INSTALL_TARGET_CMDS $(eval $(generic-package))