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
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
extensionsand 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.