Difference between revisions of "Understanding LV2"

From MOD Wiki
Jump to navigation Jump to search
Line 148: Line 148:
  
 
''$(eval $(generic-package))''
 
''$(eval $(generic-package))''
 +
 +
=== Automatic file generation ===
 +
 +
This is the design document for the file generation to build MOD plugins. The file generation is meant as a way to generate a skeleton of files to make audio/MIDI plugins (like JUCE).
 +
 +
==== Requirements, constraints and preferences: ====
 +
'''Requirements:'''
 +
* Generation of a skeleton of files to make plugins:
 +
* makefile (w/ MOD build targets)
 +
* header and implementation of the plugin’s DSP portion
 +
* turtle files for the parameter portion
 +
* Files should be well commented and have written out function names (i.e. minimize abbreviations to keep things as clear as possible)
 +
 +
'''Constraints:'''
 +
* The skeleton should use the Distrho Plugin Framework
 +
* The plugins have to be lv2 bundles
 +
* The plugins have to be buildable for the MOD platform
 +
 +
'''Preferences:'''
 +
* Have a UI to make choosing options for file generation easy
 +
* Have licenses/notices added to files
 +
* README generation
 +
* Automatic pushing to a connected MOD device after compiling
 +
* Adaptive file generation based on options for the PluginInfo-header:
 +
* MIDI i/o
 +
* Audio i/o
 +
* is a synth
 +
* has programs/states
 +
* Have the user specify the number of parameters:
 +
* name
 +
* type
 +
* set-/getParameter implementation
 +
* UI generation
 +
* Simple DSP headers for new developers to get started:
 +
* biquad filter
 +
* delay line
 +
* LFO
 +
* envelope
 +
* oscillator (w/ waveform interpolation)
 +
* noise/random generator
 +
 +
==== File structuring: ====
 +
The generation tool should make a set of files, this part will outline the structure of each of these:
 +
'''makefile'''
 +
Should follow the DPF example makefile structure, add MOD specific build commands and fill in the file names of the generated files in the correct places.
 +
DistrhoPlugin.hpp
 +
Should be the same as the DPF example and fill in the options (i. e. name, URI, num_inputs, etc.) based on user input. This should include the warnings and explanations from the example.
 +
'''<name>.hpp'''
 +
General header file structure where all the information getter functions are directly implemented because they are simple enough and would clutter the implementation. Functions generally speak for themselves through their name, except for the activate and run functions, they should get a small explanation. Run function should have appropriate inputs based on the number of inputs, outputs and MIDI i/o. All parameters, if automatically generated, should also be added to private members along with the explanation that those were generated by user input. Preset structs should be specified, in case of user input not wanting presets, should be commented out.
 +
'''<name>.cpp'''
 +
General implementation file structure. Functions should have a simple explanation either before their implementation as to what their purpose is or within the function brackets to give the user an inclination as to what idiosyncratic or complex processing should/could go on within the function (e. g. MIDI processing). Parameter setter and getter functions should be implemented based on user input/user specified parameters.
 +
'''manifest.ttl'''
 +
General manifest file based on user input. Should have most URIs explained.
 +
'''<name>.ttl'''
 +
General file structure based on user input. Should have most URIs explained. Should link to the lv2 documentation to explain additional parameter specifications such as exponential param ranges, units, scale points etc.
 +
'''readme.md'''
 +
Should either be a general readme file, telling the user to input a small explanation about their plugin, or could be used to explain the structure and purposes of each of the generated files.

Revision as of 20:40, 17 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 mod-host on the device for data processing and accessible in the browser on a remote client for the GUI through mod-ui.

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, process 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. modduox 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))

Automatic file generation

This is the design document for the file generation to build MOD plugins. The file generation is meant as a way to generate a skeleton of files to make audio/MIDI plugins (like JUCE).

Requirements, constraints and preferences:

Requirements:

  • Generation of a skeleton of files to make plugins:
  • makefile (w/ MOD build targets)
  • header and implementation of the plugin’s DSP portion
  • turtle files for the parameter portion
  • Files should be well commented and have written out function names (i.e. minimize abbreviations to keep things as clear as possible)

Constraints:

  • The skeleton should use the Distrho Plugin Framework
  • The plugins have to be lv2 bundles
  • The plugins have to be buildable for the MOD platform

Preferences:

  • Have a UI to make choosing options for file generation easy
  • Have licenses/notices added to files
  • README generation
  • Automatic pushing to a connected MOD device after compiling
  • Adaptive file generation based on options for the PluginInfo-header:
  • MIDI i/o
  • Audio i/o
  • is a synth
  • has programs/states
  • Have the user specify the number of parameters:
  • name
  • type
  • set-/getParameter implementation
  • UI generation
  • Simple DSP headers for new developers to get started:
  • biquad filter
  • delay line
  • LFO
  • envelope
  • oscillator (w/ waveform interpolation)
  • noise/random generator

File structuring:

The generation tool should make a set of files, this part will outline the structure of each of these: makefile Should follow the DPF example makefile structure, add MOD specific build commands and fill in the file names of the generated files in the correct places. DistrhoPlugin.hpp Should be the same as the DPF example and fill in the options (i. e. name, URI, num_inputs, etc.) based on user input. This should include the warnings and explanations from the example. <name>.hpp General header file structure where all the information getter functions are directly implemented because they are simple enough and would clutter the implementation. Functions generally speak for themselves through their name, except for the activate and run functions, they should get a small explanation. Run function should have appropriate inputs based on the number of inputs, outputs and MIDI i/o. All parameters, if automatically generated, should also be added to private members along with the explanation that those were generated by user input. Preset structs should be specified, in case of user input not wanting presets, should be commented out. <name>.cpp General implementation file structure. Functions should have a simple explanation either before their implementation as to what their purpose is or within the function brackets to give the user an inclination as to what idiosyncratic or complex processing should/could go on within the function (e. g. MIDI processing). Parameter setter and getter functions should be implemented based on user input/user specified parameters. manifest.ttl General manifest file based on user input. Should have most URIs explained. <name>.ttl General file structure based on user input. Should have most URIs explained. Should link to the lv2 documentation to explain additional parameter specifications such as exponential param ranges, units, scale points etc. readme.md Should either be a general readme file, telling the user to input a small explanation about their plugin, or could be used to explain the structure and purposes of each of the generated files.