Hosting LV2s

I noticed that lilv the library that a few DAW’s use to load lv2 plugins has python api bindings

Instead of using jalv can we get zynthian to just shove them in using python-lilv?

Yes, we could get it to work, and certainly it would improve things, but i’m not sure it’s “the way” because all MIDI-learning/CC-routing/automation would be still done in python.

I’m finishing the merge of riban’s “chainification refact” and after this, i would like to focus in improving sequencer. One of the many interesting features we would like to add is “controller-tracks”, so we could automate volume-fades in/out, ping-pong panning or “kind-of-LFO” on any engine parameter or mixer fader, etc.
With the current state of things, all this massive stream of control messages is going to pass thru the sloooow python MIDI dispatcher function (and worst! from there, they will go thru the CLI interfaces of (mostly) Jalv LV2 engines!!). I’m afraid this is going to be a real bottleneck when we start adding “automation” on every sequence. And we want to do it, of course.

@riban, i know you understand me :sweat_smile:

All the best!


Hi @guys!

You seem to have had a long and interesting chat.

Well, indeed zynthian dependency of WiringPi is not so big. Most complex code is user-space i2c stuff for managing MCP23017. We could move to kernel’s i2c-dev library quite easily. For the native GPIO stuff, moving to libgpiod seems to be the way.

Yes. I also found some of this LV2 plugins and i suspect the number will go up, so yes, we need to support this “LV2-parameters”. Do you know, @riban, if the “grouping” mechanism is also implemented for “LV2-parameters”? I couldn’t found any info about it. Well, indeed, the documentation about “parameters” is also scarce and too-cryptic. As someone told me, LV2 docs are clear after you understand the subject of the documentation :wink:

The best!

1 Like

But if you replace jalv with something else surely you have the same problem, if you use python-lilv wont it all be in python. Python becomes the lv2 host.

But python is slow for processing MIDI in real time. I would like to implement all the MIDI processing in a faster language, and this would include managing MIDI-learning of engine parameters.

Anyway, we need to put numbers to this and really know how many MIDI CC messages can we manage with python without being a bottle neck or consuming too many CPU resources.


I pulled these posts out of the ZynthClub topic as they are developing into a discussion of a specific subject.

We already use Python lilv bindings for interacting with ttl (LV2 configuration files). Some operations can be painfully slow, like the iteration required to search for plugins and configs due to the tree like structure of LV2 config - it has to pull detail from each branch which is both slow and memory comsuming. (I think there is a bug / limitation of lilv or the Python bindings.)

It may be possible to launch the plugins using pylilv and control and monitor them directly in Python. This might not even have significant impact on performance because we are already filtering MIDI CC through Python. We want to improve this part in the future which may see us move this message processing to a lower level language at which point anything we may have done with hosting LV2 could go with it. Of course a change like that proposed by @Baggypants could involve subatantial effort which might be better targetted elsewhere. It may be more appropriate to wait until the refactor of CC processing occurs to consider such a change. It kinda depends on who is feeling like doing what and when…


I would ideally want to replace jalv with a LV2 host that can:

  • Instantiate plugins without running their UI
  • Present control ports and parameters for monitoring and control
  • Allow the naitive UI to be launched and stopped independently

This may be available in existing code or with a little modification otherwise it is a daunting task to write a LV2 host from scratch!

LV2 Parameters

The basic LV2 mechanism for controls is lv2:ControlPort, inherited from LADSPA. Control ports are problematic because they are not sample accurate, support only one type (float), and require that plugins poll to know when a control has changed.

Parameters can be used instead to address these issues. Parameters can be thought of as properties of a plugin instance; they are identified by URI and have a value of any type. This deliberately meshes with the concept of plugin state defined by the LV2 state extension. The state extension allows plugins to save and restore their parameters (along with other internal state information, if necessary).

Parameters are accessed and manipulated using messages sent via a sequence port. The LV2 patch extension defines the standard messages for working with parameters. Typically, only two are used for simple plugins: patch:Set sets a parameter to some value, and patch:Get requests that the plugin send a description of its parameters.

Taking JC303 as an example…

The dsp.ttl file defines a set of parameter types, e.g.

        a lv2:Parameter ;
        rdfs:label "Cutoff" ;
        rdfs:range atom:Float ;
        lv2:default 0 ;
        lv2:minimum 0 ;
        lv2:maximum 1 .

It then defines instances of these types as writable (a parameter that the host can set) and readable (a parameter that the host can read that may be changed internally), e.g.

                plug:waveform ,
                plug:tuning ,
                plug:cutoff ,
                plug:resonance ,
                plug:envmod ,
                plug:decay ,
                plug:accent ,
                plug:volume ;
                plug:waveform ,
                plug:tuning ,
                plug:cutoff ,
                plug:resonance ,
                plug:envmod ,
                plug:decay ,
                plug:accent ,
                plug:volume ;

A relatively simple implementation might be to detect writable parameters in our lv2 module and jalv engine alongside control ports then present these in the UI.

The lilv library does not expose parameters as obviously as it does control ports. We need to dig into the data model further. I am yet to find example code for this so may need to look at jalv source (which does seem to find the parameters and present them as controls with names prefixed by underscore, e.g.

_accent = 0.000000
_cutoff = 0.000000
_decay = 0.000000
_envmod = 0.000000
_resonance = 0.000000
_tuning = 0.000000
_volume = 0.000000
_waveform = 0.000000

:star2: :+1: That would greatly improve the sequencer’s expressiveness, and allow for interesting multi-synth layered patches, like polyrhythmic arps with cross-fading components and articulated or sequenced pads/strings/brass.

1 Like

I will add, “managing MIDI learning and automation”, so we have sample-accurate control of parameters, allowing precise automation and avoiding to overload python code with “real-time” tasks.

Also, “preset management”, that is already implemented (and we extended!) in jalv.


1 Like

I was reading Jalv code and trying to find a “fast way” to expose these parameters to zynthian, but it’s more complex than i thought. Anyway, it’s a “low-enough hanging fruit” that we should collect for having “parameters” working with zynthian in the next weeks/months.


1 Like
import lilv
world = lilv.World()
plugins = world.get_all_plugins()
plugin = plugins.get_by_uri("")

writable_uri = world.new_uri("")
readable_uri = world.new_uri("")
lv2_properties = world.find_nodes(plugin.get_uri(), writable_uri, None)
for lv2_property in lv2_properties:
    world.ask(plugin.get_uri(), writable_uri, lv2_property)
    world.ask(plugin.get_uri(), readable_uri, lv2_property)

This gets the properties but then controls need to be created based on each properties’ parameters, e.g. rdfs_label. I am stuck there!

1 Like

Well I guess this just serves us right.