Rust Audio

How to implement Extension Data support?

The only feature that’s still missing from the current version of the lv2 crate is support for extension data.

First of, what is “extension data”? Some specifications require new callback functions from the plugin; A prime example is the State specification, the standard way to safe and restore the state of a plugin. To support this specification, a plugin has to implement a “save” function and a “restore” function that, obviously, save and restore the state of the plugin. Somehow, pointers to these functions have to get to the host and this is where extension data becomes important:

From a host’s point of view, every plugin implements a function called “extension_data” which receives a pointer to a URI and returns a pointer. In the case of the State specification, this function should return a struct containing pointers to the save and restore functions when “extension_data” is called with the appropriate URI. However, matching the strings, creating the pointer struct and returning it involves a lot of boiler-plate code and is therefore a candidate for abstractions. This is the thread where we can discuss these abstractions.

I guess, from the user’s/plugin programmer’s point of view, it would be ideal to only implement a trait and not to do much more. Although I think that founding this feature on trait implementations, this is far from enough to do the job: Somehow, the “extension_data” method has to know of these traits as well as their implementations and export them.

How could we do this? I would personally prefer “normal” methods over solutions involving macros, but if macros provide the most convenient design, I would agree to them too.

1 Like

Pretty much agree with everything you said here. :slight_smile:

In my mind implementing an LV2 extension should be as straightforward as:

/// This is not what the real `State` trait will look like, ofc
impl State for Amp {
    fn save (&mut self) { /* */ }
    fn restore (&mut self) { /* */ }
}

However, we need to have a list of all the extensions (traits) at runtime to provide to the host later, which is not something we can have out of the box with Rust.

So I was thinking we could do something like this maybe? :

impl Plugin for Amp {
    type Ports = AmpPorts;
    type Features = ();

    // This can be omitted if no extensions are present
    const EXTENSIONS: &'static [&ExtensionSomething] = lv2_extensions![State, Extension1, Extension2];

    /* The other method implementations … */
}

The macro here would be needed to expand to the needed type-fu to “store” traits and resolve to their respective URLs and pointer tables (not exactly sure how this could be done, but I think it is possible, I can work on this).
However, I think this is simple enough to be implemented as a “simple” macro, not a procedural one.

If we want to reduce boilerplate to a minimum and/or hide some implementation details, we could also do just this instead:

impl Plugin for Amp {
    type Ports = AmpPorts;
    type Features = ();

    // This can be omitted if no extensions are present
    lv2_plugin_extensions![State, Extension1, Extension2];
}

The macro would still expand to the const declaration above, but this way it reduces boilerplate and hides the implementation detail of what extensions are actually stored as.

I did an initial implementation here, please give your feedback over there! :slight_smile:

I’ve done my own take, and it fits into a gist!

First of all, I started by writing the code that should be hand-written or generated by a macro. Then, I tried to reduce it as much as possible, and this is what I ended up with.

I don’t want to take to much control away from the user; using macros should generate code that would have been written by the programmer anyway. Also, the standard doesn’t explicitly state that the extension data has to be a struct containing function pointers; It only say that this is typically the case. Therefore, the plugin should be able to return any extension data, literally Any extension data! :grin:

Another problem was to control common elements in extensions, in my case the function to return the interface URI and the interface itself. You’ve solved that by introducing and implementing a trait for extension traits. However, I was not able to replicate that with my approach and therefore I needed to do something else, a macro. :neutral_face:

The rest is more or less the same, but somehow, my approach looks shorter, can you point out why? What do you think to about my approach?