mantaraya36's blog

Brain dump

Category Archives: LADSPA

LADSPA Host now working. Please try it!

After some more work, I have now the LADSPA host in a nice working state. One of the harder things was allowing hot swapping of the LADSPA plugin while xmms2 was playing. And after a talk with oneman, I decided to do it the *perfect* way by allocating the new plugin and structures separately and then locking the structure only to swap the pointers, after which the old plugin, buffers and structures are freed.

The LADSPA host currently supports both S16 and float samples so it should be compatible with most (if not all) of your music. Please give it a go and report any bugs! Also any ideas or suggestions are very welcome. My git repo is here:

https://github.com/mantaraya36/xmms2-mantaraya36

So how is it used?

After building and installing the ladspahost  xform plugin, you have to add the LADSPA host to the effects chain:

nyxmms2 server config effects.order.0 ladspa

Then you need to choose the plugin you want to use from your LADSPA plugins. They are usually found on /usr/lib/ladspa. The xmms2 LADSPA host will look for plugins which don’t have an absolute path in the LADSPA_PATH environment variable, and if it’s not available in /usr/lib/ladspa, so you only need to do:

nyxmms2 server config ladspa.plugin amp.so

to load the /usr/lib/ladspa/amp.so plugin.

LADSPA plugin libraries can actually contain more than one plugin. If you don’t specify which one you want, the first plugin in the library is loaded. You can find out about the contents of particular plugins by using the LADSPA analyseplugin tool, like this:

analyseplugin amp.so

which will produce:

Plugin Name: "Mono Amplifier"
Plugin Label: "amp_mono"
Plugin Unique ID: 1048
Maker: "Richard Furse (LADSPA example plugins)"
Copyright: "None"
Must Run Real-Time: No
Has activate() Function: No
Has deativate() Function: No
Has run_adding() Function: No
Environment: Normal or Hard Real-Time
Ports:    "Gain" input, control, 0 to ..., default 1, logarithmic
    "Input" input, audio
    "Output" output, audio

Plugin Name: "Stereo Amplifier"
Plugin Label: "amp_stereo"
Plugin Unique ID: 1049
Maker: "Richard Furse (LADSPA example plugins)"
Copyright: "None"
Must Run Real-Time: No
Has activate() Function: No
Has deativate() Function: No
Has run_adding() Function: No
Environment: Normal or Hard Real-Time
Ports:    "Gain" input, control, 0 to ..., default 1, logarithmic
    "Input (Left)" input, audio
    "Output (Left)" output, audio
    "Input (Right)" input, audio
    "Output (Right)" output, audio

As you can see, this plugin library has two plugins inside. To specify which plugin you want to use, you need to add the plugin name, plugin label or unique ID (any one of these will work) after the library name with a colon, like this:

nyxmms2 server config ladspa.plugin amp.so:1049

Once you have loaded a plugin, you can see the available controls by checking the ladspa properties, which will have adjusted to the plugin, an easy way is:

nyxmms2 server config | grep ladspa

which can show something like:

effect.order.0 = ladspa
ladspa.control.0 = 7000
ladspa.control.1 = -90
ladspa.control.2 = 30
ladspa.control.3 = 1
ladspa.control.4 = 1.000000
ladspa.control.5 = 1.000000
ladspa.control.6 = 1.000000
ladspa.control.7 = 0.000000
ladspa.controlname.0 = Decay [ms]
ladspa.controlname.1 = Dry Level [dB]
ladspa.controlname.2 = Wet Level [dB]
ladspa.controlname.3 = Comb Filters
ladspa.controlname.4 = Allpass Filters
ladspa.controlname.5 = Bandpass Filter
ladspa.controlname.6 = Enhanced Stereo
ladspa.controlname.7 = Reverb Type
ladspa.enabled = 1
ladspa.plugin = tap_reverb.so
ladspa.priority.audio/pcm = 50

You can change the control values for a plugin like you would any other xmms2 property:

nyxmms2 server config ladspa.control.3 0

Properties will be retained and used across songs and across server reboots, but will be lost if you change the plugin.

Current limitations and future work

  • Since configuration parameters can’t be removed, if the previous plugin you loaded had more controls, there will be some useless controls (which you can identify because they will have no name). Additionally since config parameters live on, you will always tend to have more controls than the plugin actually supports. When arrays are implemented, it is likely that this problem will be resolved as configuration keys that are arrays should resize automatically to the number of elements in the array.
  • Although the internal data structures are designed to support plugin chains, this is not yet exposed to the user, as I’m waiting to implement the schema system for properties, which will allow doing this simply (See my previous posts).
Advertisements

More on LADSPA modes

After an IRC chat with vdust, he suggested the automatic modes might work most of the time, but that it might be good to allow more flexible connections for other type of streams like 5.1. Thinking about that later, I realized this might be very useful to do time delay compensation, and individual loudspeaker eq. I’ve thought about this, and I’ve come up with an idea, which could serve the purpose and builds on the work that’s already done.

Currently, the LADSPA host I wrote creates LADSPA plugin nodes which can actually hold several instances of a plugin (e.g. for MONO mode), but the plugin parameters are shared between all the instances, as well as any intermediate buffers. But for flexibility, it might be desirable to allow arbitrary routing of inputs and outputs, as well as independent control of plugin parameters. I think this can be accomplished in a sensible way through a new structure called stage that can wrap nodes and create complex chains.

Three separate configuration options have to be set. One is the list of plugins, one will determine routing and another will control the parameters.

Plugins and routing

To create a chain, you would pass a list like:

[ "pluglib1.so:plugmono_name", "pluglib2.so:plugstereo_name2" ]

This will create a chain that looks like:

input => pluglib1.so:plugmono_name => pluglib2.so:plugstereo_name2 =>output

Notice that both the plugin library and the plugin name are given in the string, and they are separated by a colon.

On the routing config option, the inputs and outputs will be specified. Each element of the plugins list is a stage, so you need to declare the inputs to each of the stages and lastly the way the outputs from the last stage are mapped to the outputs, for example:

[ [1,2], [3, 4], [5,6] ]

Would mean that the first stage takes as input channels 1 and 2 (if channels don’t match the automatic modes for channel assignment are used), the second stage takes channels 3 and 4 from the previous stage, and finally outputs 5 and 6 from the second stage are passed to the two channels of the chain. This will give great flexibility in routing. One thing that is not supported is mixing channels from the config. A special LADSPA plugin downmixer would need to be used for this. Also note that to determine the number of channels for each stage, it must look both at input and output and use the largest number.

A more complex chain can look like:

[ ["plug1.so:plug1",  "plug2.so:plug2"], "plug3.so:plug3" ]

The first stage will have plug1 and plug2 in parallel, which then feed plug3. You can specify routings like:

[ [ [1] , [2] ], [1, 2, 3, 4], [1, 4] ]

This means that the first plugin of stage 1 takes input channel 1, and the second takes 2. Assuming the plugins are mono to stereo, this means that although two channels enter the stage, 4 will come out. These will be numbered sequentially, for each consecutive plugin, so in this case, the second stage will take the output from plug1 on inputs 1 and 2 and the output of plug2 on inputs 3 and 4. Finally, the second stage will send outputs 1 and 4 out the xform chain.

Parameters

A third config option deals with plugin parameters. These will use a JSON style schema notation, and will look like:

[ [  {"stage1param1":value , "stage1param2":value}, {"stage1param1":value, "stage1param2":value} ], {"stage2param1":value} ]

A dictionary will represent the parameters for each node within each stage. If a parameter name is not listed, it should take its default value.

Fallback

When a single node is the whole stage, there is no need to put the node’s configuration options with an array of a single element. In this case just using the element directly is preferred.

When arrays are not used (i.e. only the pluginlib name is given not as part of an array), the LADSPA host will fall back to using a single node with automatic modes.

This proposal will wait implementation until the next stage of GSoC dealing with the implementation of schemas is done.

LADSPA host proof of concept

Proof of concept code for LADSPA hosting is now up in github. This code is still very inflexible, as you can’t even select the plugin you want to use (it uses a simple gain plugin amp.so, sorry oneman!), but it shows a working LADSPA host, which can take parameters from the server config. If you try it out, you will be able to modify the gain by using:

nyxmms2 server config ladspa.parameter0 1.0

Where the last number is the gain value for the LADSPA plugin.

This xform plugin accepts both S16 and float samples, and creates internal buffers to pass to the LADSPA plugin. If you have a look, there are still many TODOs, but most infrastructure is there, even to support multiple LADSPA plugins within the same xform.

I had done a LADSPA host before, so I tried first to write the host from a quick look at the LADSPA header. This turned out to be a bad idea, as there were many small details and problems which I could’ve avoided by reading some documentation like:

http://gdam.ffem.org/ladspa-doc/ladspa-4.html

In particular it is important in LADSPA to remember to allocate buffers for all ports (both audio and control), even the ones you are not going to use.

One of the challenges now is deciding how to handle different channel counts between the chain and plugins, and try to get input from the xmms2 devs regarding a way to define config properties from the init function rather than the setup function. Most xforms are OK with setting their properties in setup, but a LADSPA host definitely needs to register the config properties in the init function where the plugins are initialized. It should also be able to dynamically create and delete properties in case the plugin is changed in the middle of a song.