mantaraya36's blog

Brain dump

Category Archives: Uncategorized

Update on Schema properties

My original proposal has of course changed substantially! First, instead of defining a new data structure with multiple types, I’ve started using xmmsv_t which already exists and does most of the work (IPC interchange, value serializing, memory mangement). I only needed to add support for floats. Easy, right?

It turned out to be very complicated as there is no way to serialize float values in a portable manner, because locales would interfere (if you plan to use sprintf) and because you cannot guarantee how floats will be stored in memory on all platforms (especially those which are not IEEE 754 compliant!). So I ended up with a comprimise which is using frexp to separate the float values mantissa and exponent, and encode them as two integers. This appears to give on simple testing a presicion of about 6 or 7 significant figures, which should be sufficient for XMMS2 config values. This is now available at my git repo:

https://github.com/mantaraya36/xmms2-mantaraya36/tree/config-schemas

I’ve also started work on the config properties side, and have settled on the following API for the new system:

/*
 * New config properties using schemas
 */
 xmms_config_node_t *xmms_config_node_lookup (const gchar *path);
 xmms_config_node_t *xmms_config_node_create (const gchar *name, xmmsv_type_t type);
/* Register nodes in the property tree. Callbacks are ignored for lists and dicts */
 xmms_config_node_t *xmms_config_node_register (xmms_config_node_t *node, xmms_object_handler_t cb, gpointer userdata);
gboolean xmms_config_node_unregister (xmms_config_node_t *node);
/* check node types */
 gboolean xmms_config_node_is_type (xmms_config_node_t *node, xmmsv_type_t type);
/* node query operations. Will return the first element if node is a list or array*/
 gint32 xmms_config_node_get_int (xmms_config_node_t *node, gboolean *ok); /* ok can be NULL */
 gfloat xmms_config_node_get_float (xmms_config_node_t *node, gboolean *ok);
 const gchar *xmms_config_node_get_string (xmms_config_node_t *node, gboolean *ok);
 xmms_config_node_t *xmms_config_node_get_index_node (xmms_config_node_t *parent_node, gint index);
 xmms_config_node_t *xmms_config_node_get_key_node (xmms_config_node_t *parent_node, const gchar *name);
/* node value setters. will set the vaue for the first element if node is a list or array */
 void xmms_config_node_set_int (xmms_config_node_t *node, gint value, gboolean *ok);
 void xmms_config_node_set_float (xmms_config_node_t *node, gfloat value, gboolean *ok);
 void xmms_config_node_set_string (xmms_config_node_t *node, const gchar *value, gboolean *ok);
 /* for dicts only: */
 void xmms_config_node_set_from_hash (xmms_config_node_t *parent_node, GHashTable *table);
 /* for lists only: */
 void xmms_config_node_set_from_array (xmms_config_node_t *parent_node, GArray *list);
/* element operations (for lists and dicts) */
 gint xmms_config_node_element_count (xmms_config_node_t *node);
 void xmms_config_node_element_remove_index (xmms_config_node_t *parent_node, gint index, gboolean *ok);
 void xmms_config_node_element_remove_key (xmms_config_node_t *parent_node, xmms_config_node_t *node, gboolean *ok);
/* query node elements values */
 gint xmms_config_node_get_element_int (xmms_config_node_t *parent_node, gint index, gboolean *ok);
 gfloat xmms_config_node_get_element_float (xmms_config_node_t *parent_node, gint index, gboolean *ok);
 const gchar *xmms_config_node_get_element_string (xmms_config_node_t *parent_node, gint index, gboolean *ok);
/* set node elements values */
 void xmms_config_node_set_element_int (xmms_config_node_t *parent_node, gint index, gint value, gboolean *ok);
 void xmms_config_node_set_element_float (xmms_config_node_t *parent_node, gint index, gfloat value, gboolean *ok);
 void xmms_config_node_set_element_string (xmms_config_node_t *parent_node, gint index, const gchar* value, gboolean *ok);
/* list node operations */
 void xmms_config_node_resize (xmms_config_node_t *node, gint size, gboolean *ok);
 void xmms_config_node_append (xmms_config_node_t *parent_node, xmms_config_node_t *node, gboolean *ok);
 void xmms_config_node_insert (xmms_config_node_t *parent_node, gint index, xmms_config_node_t *node, gboolean *ok);
/* callbacks (can only be set for value nodes) */
 void xmms_config_node_callback_set (xmms_config_node_t *node, xmms_object_handler_t cb, gpointer userdata);
 void xmms_config_node_callback_remove (xmms_config_node_t *node, xmms_object_handler_t cb, gpointer userdata);

Nodes will hold a copy of the value in the tree and the node’s address,, so any operations on nodes can be made thread-safe.

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.

Instantiation modes for LADSPA host

Frequently, the number of channels of the xform chain will not match the number of channels of a LADSPA plugin, e.g. the chain is stereo and the plugin is mono. Additionally, many LADSPA plugins have different number of inputs and outputs, e.g. a reverb which has mono input but stereo output. So I’ve started coding to allow automatic routing and instantiation, according to the following modes:

  • DIRECT: The xform chain and the LADSPA plugin have the same number of channels, and the number of inputs and outputs on the LADSPA plugin is the same, so there is no need to do any special routing as all channels are matched.
  • MONO: The LADSPA plugin has only one input and only one output, but the xform chain has more than 1 channel. In this case, the plugin is instantiated multiple times to match the number of channels of the chain, and each instance will independently handle a single channel from the chain. All instances of the LADSPA plugin will share the same control parameters.
  • BALANCE: For the common case where the LADSPA plugin is mono to stereo and the xform chain is stereo, a balance mode is proposed, where the plugin is instantiated twice, and each instance takes the input from the left and right channels respectively. The two stereo outputs are then added but according to the value of a balance parameter, to allow a range from complete separation of the outputs (downmixing to mono for each side) to complete mix where the left outputs are added into the left channel, and the right outputs go to the right.
  • OTHER: For any other cases, a simple circular mapping is applied to the channels, e.g. for a mono xform chain and a stereo plugin, the input will go to both plugin inputs and both plugin outputs will go to the xform output. and for a six channel chain and a stereo plugin, three instances will be created, which will take input from 1-2, 3-4 and 5-6 respectively and output in the same way.

I think this should cover the relevant cases for xmms2 (which certainly differ to the usage in a multitrack DAW). Additionally this enables the eventual addition of a plugin into the LADSPA host (in series), without having to change the current structure of inputs and outputs.

xform template done

Basic skeleton for the new xform plugin is now ready:

https://github.com/mantaraya36/xmms2-mantaraya36/commit/133838c34fb617e05e8062a97b9f0ac21188462a#commitcomment-381216

Daniel has spotted some minor issues with indentation which can checked with:

http://people.xmms2.org/~andersg/indentcheck-v5.py

and be fixed by using the proper emacs script and has pointed me to the way Changelogs should be named for XMMS2:

http://xmms2.org/wiki/ChangeLog_Format

Also set up emacs to format sources correctly from:

http://xmms2.org/wiki/Emacs_C_style

I had never added an elisp file manually before, so I followed the instructions here:

http://edward.oconnor.cx/2005/09/installing-elisp-files

Which say you can create a directory to place your elisp files:

(add-to-list 'load-path "~/elisp")

and then you can load the elisp file in the .emacs file. It seems to work…