anklang icon indicating copy to clipboard operation
anklang copied to clipboard

LV2: preset loading stalls DSP thread

Open swesterfeld opened this issue 5 months ago • 6 comments

Before I describe the actual problem, let me give you some context. LV2 has a preset mechanism which allows sharing presets between applications. Some plugins also ship with presets. The idea is that a LV2 plugin state is stored in a .ttl file, either in the /usr/lib/lv2 or ~/.lv2 directory. Presets generated by one application (and stored in ~/.lv2) can be loaded by another application. Ardour UI provides a menu with all available presets above the custom UI and a big "+" button to store a new preset. Preset store/load is done by liblilv, so that the actual preset I/O code is compatible between all LV2 hosts.

preset

When I started to implement LV2 support, I wanted to have preset support mainly to test LV2 plugins which have filename properties (such as the liquidsfz or sfizz LV2 plugins). Since we have no string properties, but with preset loading support I could change the filename for the plugin by changing the preset. However, I did not want to mess with the Anklang UI here, so I decided to simply create a choice property and put a list of all presets into the choice. Whenever the choice property is changed by the user, the new preset is now loaded in my LV2 code. Note that this does not cover saving presets (in a way that they could be used by other plugins) at all. So in the Anklang UI, the same preset menu looks like this (which is probably not the final form, but still acceptable from a usability point of view):

anklang

Now to the actual problem: whenever the choice value is changed by the user, a new preset needs to be loaded. Right now, this is implemented by directly switching into the Gtk thread from the DSP thread, and waiting until the preset has been loaded. This is also because we are never allowed to change state while the DSP thread is processing audio.

https://lv2plug.in/c/html/group__state.html documents the restore function (which needs to be called for loading presets) as

"This function (restore) is in the "Instantiation" threading class as defined by LV2. This means it MUST NOT be called concurrently with any other function on the same plugin instance."

Ok, since we obviously do not want to stall the audio thread during preset loading, the way we deal with preset loading needs to be changed. Again here are options.

Option 1

Since presets will be - at some point - implemented properly in Anklang, with the correct UI support - we could for now disable the preset loading code, merge the stuff without it. LV2 support without load/save presets is probably a lot better than no LV2 support.

Option 2

Since we do not want to stall the DSP thread for loading a preset, we could do the following changes:

  • once the preset is changed in the DSP thread, we send a message to the Anklang thread
  • at the same time we set a flag on the LV2 device to avoid calling run again until the new preset has been loaded; if the render method on the LV2 device is called while a preset is loaded the render method would have to fill all output buffers with zeros
  • in the Anklang thread we can then load the preset while the plugin is essentially disabled and reset the flag
  • we can stall the Anklang thread while the preset is being loaded to ensure that no other LV2 functions run at the same time
  • as soon as the flag is no longer set, the DSP thread can resume calling the LV2 processing function during render

Since option 2 is quite a bit of coding work, I'd like to have a second opinion here. Should we do this? Should we simply postpone the problem to a later point in time (option 1)? Is there a third way I haven't thought of yet?

swesterfeld avatar Jan 29 '24 10:01 swesterfeld