After reading my last post, and watching a few old talks around LV2 and so on,
I got to thinking about the extension mess problem I mentioned, and it occured
to me that there might be some commonality here with the "staging" or "contrib"
area question as well.
This is all based on some ideas that have been bouncing around in my head for
ages, but that I haven't really developed and certainly not written down, so
I'm going to try and sketch out a proposal for how to handle these things
without breaking anything.
Concretely, there are two problems here: one is that the spec is just a mess.
For example, the Data
Access and Instance
Access
extensions are really just parts of the same thing and should live together,
nobody cares about Morph and it's
not in a state that really belongs in the "recommended standard" list (sorry,
flagrant abuse of power on my part there), and so on.
The other problem is that there are sometimes contributions which solve a
problem, and are a reasonable enough pragmatic step, but also not really up to
par. Maybe they aren't portable, aren't defined well enough, could do more
harm than good if they're presented as recommendations, and so on. People, for
whatever reason, want them "in LV2". Yet, nobody has the time to spend to
develop them into a more proper specification yet, and nobody is happy when
things don't get merged.
It seems there is a common factor to these problems, and it's moving things
without breaking anything. To clean up the current mess, we can move
extensions to the contrib area. When a previously half-baked contribution is
developed further, we can move it from the contrib area. This is an obvious
coarse-grained use case; I think there is also a case for finer-grained URI
migration, but I'll focus on the easy and most useful case for now.
How might we do this? Though moving instance-access to contrib is not a goal,
it's about as simple as an extension gets, so I'll pretend we want to do that
for the sake of a simple example. At the very least, it will be a nice little
fantasy for me to pretend that the curse of crappy plugin UIs that mess with
DSP guts has finally been vanquished for good :) This is just about the
mechanism, what we should actually do to clean things up is a question for
another time.
So, what's instance-access? It's a handful of URIs, and a feature. The
feature is extremely simple, the payload is just some pointer. Can those URIs
be moved without breaking anything? For at least this simple case, I think so:
-
Lilv, on loading data, aggressively maps everything to the new location.
If it says http://lv2plug.in/ns/ext/instance-access in a data file, then
it gets loaded into memory as
http://lv2plug.in/ns/contrib/instance-access. In this case, that means
that, as far as the host can tell, the UI has lv2:optionalFeature
http://lv2plug.in/ns/contrib/instance-access. This can be done pretty
easily just above the parser level so that it's universally true.
-
When the UI is instantiated, the (old) host passes the
http://lv2plug.in/ns/ext/instance-access feature to
lilv_plugin_instantiate()
. Internally, lilv duplicates this, and passes
both the old and new features to the UI with the same data.
-
The plugin is either old, and looks for the old feature URI, or new, and
looks for the new feature URI, and either way, finds it.
I can't think of a reason this wouldn't work, and it doesn't even require any
host changes. It's a bit bloated, but not in a way that matters, and would
need a significant (but not too bad) amount of code specifically to deal with
this in lilv, but such is my lot in life.
In the more general case, there is also the issue of URID mappings. Let's
pretend that http://lv2plug.in/ns/ext/instance-access is mapped to a URID
both by the host and the plugin, and that URID is sent between them. Though
this isn't really an intended use-case for this particular extension, it's a
perfectly valid thing to do:
- The host URID-map maps both the old and new URIs to the same URID.
... that's it, actually. Regardless of which "version" either host or plugin
know about, the URID is identical. This requires hosts to actually implement
something though, or for a URI map to be added to lilv, so it's not as easy.
It can't just be done in LV2 and would take some time to get established.
There is one remaining snag: extension_data
. This one is a bit trickier,
because we need to assume the hosts uses lilv_instance_get_extension_data
which is just a trivial wrapper, and probably not used by everyone. That's an
easy enough fix to make, though. Then, lilv just needs to call the plugin
method for the new URI, return that if it isn't NULL, and fallback to calling
it with the old URI.
All of this requires a map between old and new to exist, of course, but this
would be written down in the specs themselves and it's easy enough to load such
a thing inside lilv.
I'm sure there are other places where URIs as strings are used in the API that
would need thinking about, and I'll have to scan through the spec to see, but I
suspect the above is at least 90% of what matters.
So... am I missing something? Do send me (or lv2-dev) an email if so, but now
that I write it down this seems more viable than I assumed it would be. There
will definitely be corner cases, since plugins and hosts can use these strings
for anything everywhere, but as far as the actual interface is concerned it
seems possible to make this happen without too much pain. What could we do
with this?
-
Merge data-access and instance-access
-
Merge buf-size and resize-port
-
Put all the "official" extensions in the same namespace ("directory"), and
get rid of the annoying inconsistency of ext
and extensions
and so on
(which doesn't really matter, except in the soft sense that ugliness
matters). The header includes already look like this and it's so much
nicer.
-
We could put the deprecated extensions in a special namespace so they really
stand out, but this doesn't seem to really matter (though it should be done
visually on the spec page regardless).
-
Move presets into lv2core itself? This isn't an extension-level move like
the above, but why not? One less prefix to bother with, and in retrospect,
a plugin spec without any kind of presets at all is pretty silly. Perhaps
the same for port-groups.
-
Do... something with port-properties, and maybe parameters. Let's say
combine them into a "control" extension that generally has all the
definition of control related stuff.
-
Move morph to contrib.
-
Maybe move dyn-manifest to contrib. This is a bit more contentious, but
it's a pretty ugly solution, and the caveats of using it currently aren't
very clear.
That would leave a specification list like this (assuming parameters and
port-properties move to "control"):
- Atom: A generic value container and several data types
- Buf Size: Access to, and restrictions on, block and buffer sizes
- Instance Access: Provides access to a plugin instance
- Log: A feature for writing log messages
- LV2: An open and extensible audio plugin standard
- MIDI: A normalised definition of raw MIDI
- Options: Instantiation time options
- Control: Common properties and parameters for audio processing
- Patch: Messages for accessing and manipulating properties with events
- State: An interface for LV2 plugins to save and restore state
- Time: Properties for describing time
- UI: LV2 plugin UIs of any type
- Units: Meaningful units for values
- URID: Features for mapping URIs to and from integers
- Worker: Support for doing non-realtime work in plugins
Not everything left is immaculate, and from a user-facing documentation point
of view other things like putting the data-only vocabularies in a separate
section might help even more, but I think this would be a big improvement.
More importantly, it would of course give us an attic to put slightly more
sketchy things. Looking at LV2 as a Specification™, that feels wrong, but
looking at it as a project, it seems really necessary.