[LV2] Parameters, groups, and dynamicism

Hanspeter Portner ventosus at airpost.net
Thu Sep 29 01:15:07 PDT 2016


On 26.09.2016 19:49, David Robillard wrote:
> On Mon, 2016-09-26 at 11:32 +0200, Hanspeter Portner wrote:
> [...]
>>> param:ampGain
>>> 	...
>>> 	pg:group :ampControls .
>>>
>>> param:filterGain
>>> 	...
>>> 	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.
>>
>> I feel your pain, indeed. It is really cumbersome to define each parameter
>> individually.
>>
>> Out of curiosity. How would such an inheritance scheme look like?
>
> You can do property inheritance with rdfs:subPropertyOf in essentially
> the same way you do for classes (just say it is a rdfs:subPropertyOf
> some:superProperty).
>
> In our case, this would require hosts (or lilv, or whatever) to actually
> follow that chain to find information, but that's not too hard.
>
>>> 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:
>>>
>>> :myplug
>>> 	...
>>> 	foo:parameterGroup [
>>> 		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).
>>
>> Sounds reasonable to me.
>>
>>> 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.
>>>
>>> So, issues:
>>>
>>> * 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
>>> group" defined.
>>
>> Mandatory groups would be a nice thing to have as it would tremendously improve
>> representation in automatic/generic UIs. Plugins that define port groups are
>> much nicer to interact with in automatic UIs than those without.
>>
>>> * 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
>>> support this.
>>
>> I can think of a single group having both readable and writable parameters, yes.
>
> Such as?
>
> This is trouble because the way "standard" groups currently define their
> elements has no such context and can be applied in either an input or
> output context.  Having groups with both read-only and write-only
> parameters diverges things from the way audio/control ports work.
> Probably doable, but it'd be nice if we didn't have to...

E.g.
https://github.com/OpenMusicKontrollers/orbit.lv2/blob/master/orbit.ttl#L421

It would be nice if I could group all the orbit:beatbox_bar_* and all the
orbit:beatbox_beat_* parameters.

Grouping the two readable parameters is sensible, too, though. So you don't
really need to bother for my exotic example :)

>>> * 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:
>>>
>>> :ampEnvControls
>>> 	a param:EnvelopeControls ;
>>> 	lv2:symbol "amp_env" .
>>>
>>> :myplug
>>> 	...
>>> 	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 .
>>
>> I like that.
>>
>>> 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...
>>
>> I've run into the same issue with my current way of dynamic properties (see
below).
>>
>> What about referencing the target plugin via a separate property instead of a
>> naming rule?
>>
>> []
>> 	a patch:Set ;
>> 	patch:plugin :myplug ;
>> 	patch:subject :ampEnvControls ;
>> 	patch:prperty params:attack ;
>> 	patch:value 0.15 .
>
> This would work, though the lack of genericism ala everything else rubs
> me the wrong way.  Maybe that's just a terminology thing though.
>
> "Context", perhaps...

Sure, the more generic, the better.

>>> (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.
>>
>> Yes, I'd like to have an 'official' way for dynamicism.
>>
>> I'm doing my own experiments with that already.
>>
>> It's roughly the same thing as you're proposing, instead of custom parameter
>> groups, I use patch:writable and patch:readable as the only available parameter
>> groups.
>>
>> Followingly the jalv dump of registering a single dynamic parameter
>> <urn:moony:bender#bendrange> to the UI.
>>
>> ## UI => Plugin (32 bytes) ##
>> []
>> 	a patch:Get ;
>> 	patch:sequenceNumber "0"^^xsd:int .
>>
>> ## Plugin => UI (104 bytes) ##
>> []
>> 	a patch:Patch ;
>> 	patch:remove [
>> 		patch:writable patch:wildcard ;
>> 		patch:readable patch:wildcard
>> 	] ;
>> 	patch:add [
>> 		
>> 	] .
>>
>>
>> ## Plugin => UI (104 bytes) ##
>> []
>> 	a patch:Patch ;
>> 	patch:remove [
>> 		patch:writable <urn:moony:bender#bendrange>
>> 	] ;
>> 	patch:add [
>> 		patch:writable <urn:moony:bender#bendrange>
>> 	] .
>>
>>
>> ## Plugin => UI (448 bytes) ##
>> []
>> 	a patch:Patch ;
>> 	patch:subject <urn:moony:bender#bendrange> ;
>> 	patch:remove [
>> 		<http://www.w3.org/2000/01/rdf-schema#label> patch:wildcard ;
>> 		<http://www.w3.org/2000/01/rdf-schema#range> patch:wildcard ;
>> 		<http://www.w3.org/2000/01/rdf-schema#comment> patch:wildcard ;
>> 		<http://lv2plug.in/ns/lv2core#minimum> patch:wildcard ;
>> 		<http://lv2plug.in/ns/lv2core#maximum> patch:wildcard ;
>> 		<http://lv2plug.in/ns/extensions/units#unit> patch:wildcard ;
>> 		<http://lv2plug.in/ns/lv2core#scalePoint> patch:wildcard
>> 	] ;
>> 	patch:add [
>> 		<http://www.w3.org/2000/01/rdf-schema#label> "Pitch Bend Range" ;
>> 		<http://www.w3.org/2000/01/rdf-schema#range>
<http://lv2plug.in/ns/ext/atom#Int> ;
>> 		<http://www.w3.org/2000/01/rdf-schema#comment> "set pitch bend range of
>> receiving instrument" ;
>> 		<http://lv2plug.in/ns/lv2core#minimum> "0"^^xsd:int ;
>> 		<http://lv2plug.in/ns/lv2core#maximum> "9600"^^xsd:int ;
>> 		<http://lv2plug.in/ns/extensions/units#unit>
>> <http://lv2plug.in/ns/extensions/units#cent>
>> 	] .
>>
>>
>> ## UI => Plugin (56 bytes) ##
>> []
>> 	a patch:Get ;
>> 	patch:property <urn:moony:bender#bendrange> ;
>> 	patch:sequenceNumber "0"^^xsd:int .
>>
>>
>> ## Plugin => UI (56 bytes) ##
>> []
>> 	a patch:Set ;
>> 	patch:property <urn:moony:bender#bendrange> ;
>> 	patch:value "200"^^xsd:int .
>
> Cool.  Note you can use patch:Set for more terse messages that avoid all
> the wildcards if the set of keys is the same anyway (not important, just
> nicer, thought I'd mention it).

That would be nicer, indeed.

>>> * 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...
>>
>> A lot of URIDs can be burdensome, indeed. Still, I'd prefer URIDs over indexes.
>>
>> Some 'official' utility functions to (un)map URIDS to/from enums would be nice,
>> though.
>
> True.  I think maybe eliminating the burden of URIs for things defined
> in the standard spec would be good (more on that in a separate thread),
> but having to "register" your parameters in some way seems pretty
> reasonable.

Sounds great.


More information about the Devel mailing list