[LV2] Parameters, groups, and dynamicism
d at drobilla.net
Sat Sep 17 09:58:11 PDT 2016
I have been working on implementing parameters (event-based controls)
more completely in Jalv, with an eye towards the general effort of
bringing this up to a/the first-class way to control LV2 plugins,
possibly dragging support code up into Lilv to make it as simple as
possible for hosts, and so on.
I've come pretty far, but one thing is conspicuously missing from the
spec: grouping. For ports, we implemented grouping by necessity by
adding a pg:group property to ports. This was necessary because the way
of describing ports was already to put them directly on the plugin
description, and we needed to add groups without breaking backwards
compatibility. It is a bit tedious, though.
For parameters, it is more problematic to do it this way, because the
idea is that a parameter is described somewhere and that description
should apply wherever. For example:
a lv2:Parameter ;
rdfs:range atom:Float ;
lv2:default 0.0 ;
lv2:minimum -20.0 ;
lv2:maximum 20.0 ;
units:unit units:db ;
rdfs:label "gain" ;
rdfs:comment "Gain in decibels." .
If we used the same scheme for grouping as for ports, then you would
need a different description of the gain parameter *for every group*
solely to describe the group:
pg:group :ampControls .
pg:group :filterControls .
This sucks. We could use inheritance to eliminate most of the redundant
description, but it still sucks, because part of the idea with parameter
descriptions is that host (or UI library, or...) can understand commonly
used parameters and implement them specially.
So, we need to do something different, and I'm thinking describing the
groups on the plugin directly is the way. This is particularly nice
because you can re-use entire group descriptions. For example, we have
params:EnvelopeControls which describe the controls for a DAHDSR
envelope. If a plugin could do something like:
a param:EnvelopeControls ;
lv2:symbol "amp_env" ;
Then you can describe an entire set of established controls with just
that description, and re-use the same group description for many
controls (e.g. for plugins with many envelopes).
We would need rules to make the plugin-unique symbol for groups
parameters, probably "groupsymbol_paramsymbol", so this plugin would
e.g. have a control amp_env_attack.
* This is not compatible with the current way of using patch:readable
and patch:writable to discover parameters. I don't think this is
terribly important since this isn't well-established anyway. Perhaps we
should even move to parameters being *always* groups with some "default
* Not sure how this works with the general concepts of
readable/writable. Can a single group have parameters that are
differently readable/writable? The current group description can not
* We need symbols to support the general LV2 design constraint that all
controls have a unique symbol. These are not useful at run-time,
though, so it might be better to enforce URIs for parameter groups (like
we probably should have for ports, even though it makes the ttl
description a bit uglier), like:
a param:EnvelopeControls ;
lv2:symbol "amp_env" .
foo:parameterGroup :ampEnvControls .
Which lets you get a URID for ampEnvControls. Setting grouped
parameters would use the subject property of patch messages, so:
a patch:Set ;
patch:subject :ampEnvControls ;
patch:property params:attack ;
patch:value 0.15 .
This is a bit worrying, however, because claiming patch:subject means it
is impossible to tinker grouped parameters on different "subjects" than
the plugin instance. We could mandate some things about what the
*instance* URI is to get around this and make rules that the group URI
must be a fragment of it and so on, though mandating URIs syntactically
is generally frowned upon in some circles...
(Sidenote: reason #8347289 URID probably should have been a general
symbol interning interface instead)
* Dynamicism would be achieved by emitting a description much like the
one above about new parameters showing up. To make this realistic,
probably something like lilv_controls_message_is_relevant(atom_msg),
which if returns true, the hosts should ringbuffer it to somewhere and
later call lilv_controls_update(controls, atom_msg) which will update
the (hypothetical) LilvControls which emits callbacks to notify the host
of new stuff and so on. This is pretty nice because you can do things
like describe an entire new set of envelope controls in a terse message
without enumerating all the specifics.
* Should we provide an index mechanism? URIDs let you do it, but
frankly the requirement to map a ton of URIDs is already pretty
burdensome, and having to do it for all your parameters seems like it
could be the straw that breaks plugin developer's backs...
This is a bit of a meandering rant about this stuff and I'm not sure
what I'm asking concretely, but since I know others are interested in
finally realizing controls that don't suck like ControlPort, here's
where I'm at right now. Opinions welcome.
More information about the Devel