Webview extension
This draft lets CLAP plugins straightforwardly use a web-page for their UI.
Messages are exchanged in both directions as opaque blocks of bytes on the main thread. The webpage receives and sends messages using standard web APIs.
One thing that I'm wondering is if the plugin needs to be able to specify a minimum requirement for html, css, javascript ? Or this is better done within the page that will be loaded?
@abique I think the requirements-check is best done from inside the page. Websites manage this OK (outside of ancient enterprise workplace tools which don't work in Firefox or whatever 😄), and modern web tech is pretty stable.
@abique For the starting URI length, can we somehow fix the capacity at CLAP_PATH_SIZE? 1kB seems long enough, gives more consistency from hosts, and avoids an extra method in the API.
@abique For the starting URI length, can we somehow fix the capacity at
CLAP_PATH_SIZE? That seems long enough, gives more consistency from hosts, and avoids an extra method in the API.
For the draft we can, for the final version I'd need to think more about it.
Do the web browser have a max url size?
We could also define a CLAP_URI_SIZE if we want more space.
Ah you said that chrome has a max. Then we could go this way I suppose.
I think it'd be good to set a fixed size, because what is a plugin meant to do if the host gives it a smaller capacity? The maximum length varies by browser/webview, but none of them are below 1024.
For context, this URL is 509 bytes - less than half the 1024 of CLAP_PATH_SIZE:
http://localhost:8000/tmp/web-audio/?url=/tmp/dynamap/web-audio/DynaMap.js&ui=/tmp/dynamap/web/#dynamap?preset=vVLBSsRADP2XnKNMlYr2qCx48aQ32UNsx93QdqY0abGU-XfZaWV3sUUQ9JSQeXnvJZMRetsKewfZDQI5qgZhgWw81q8QGtuyL55inXLl3kJmEHqqOgtZagICqVJe3nsvuowyl2lAqLwvaW9plWyGsSuX35MQEIrBUc35uc8E4W1oSNaIA0LetYfiSZNBaDw7FcheR_iA7OL6DmGYow5N7NweRLUlJ2wjdjxb20-yUnFuJ4XTtta-H7aQItRT2BFPc6h3E4nzLFNmC9ZnJf1mv-50QoivfEy-7JgQcFExmSWTf9RMzTym-QvNLYJ0opF3-WoQxDbUkka-RUy8vJnmJTpb-9Bo95F3-4du5dpvjTFHts2vhgqf&audio=audio%2Fexample.mp3
Local paths are relative to the plugin bundle, so length shouldn't be a problem, and an absolute URL of 1024 bytes is long enough to hold quite a lot of config/auth-tokens/SHA-sums/etc.
The max URL size isn't set in stone in browser.
The approach I suggested is maybe a bit boring to impl and use, but it has the advantage that we don't have to think about it in the future and we won't be sorry in the future because it is constrained to a small size.
OK - can the length query be a separate method (rather than calling stuff with NULL)?
// Expected length of the URI. For compatibility, this should not exceed 2000 bytes
uint32_t (CLAP_ABI *get_uri_length)(const clap_plugin_t *plugin);
// ... existing docs
// + "capacity must be at least that returned by get_uri_length()"
bool (CLAP_ABI *get_uri)(const clap_plugin_t *plugin, char *uri, uint32_t uri_capacity);
If you have just one method
int32_t (CLAP_ABI *get_uri)(const clap_plugin_t *plugin, char *uri, uint32_t uri_capacity);
Which always returns the URI length, then you're good. If the returned value is <= 0, then you consider that there was an error and the uri couldn't be computed by the plugin.
You can call it with NULL, but event if you pass a buffer that is too big or too small you still get a null terminated string and can figure out if it is truncated by comparing the returned sized and your capacity.
In the host code base or any code base that would use this approach, you can have utility code that will compute a std::string out of this interface. If you have two functions: one for the length and one for the content, you'll have to provide two function pointers instead of one to the utility. And two function pointers means a mismatch risk like asking for the size of A while getting the content of B.
Vulkan is doing something similar to read dynamic size array and once you wire your utility code, it is easy to work with.
So if the host provides a buffer that's too small, should the plugin return the size we wants the URI, not the actual size it's returning?
So if the host provides a buffer that's too small, should the plugin return the size we wants the URI, not the actual size it's returning?
Yes. URI: 64 bytes get_uri(capacity: 24) -> returns 64.
OK - and should uri be allowed to be null?
We could demand that it's always not null, but allow a capacity of 0 for a length query. This would match discussions about audio_outputs where the conclusion (as I understood it) was that the array cannot be a null pointer, even if audio_outputs_count == 0.
std::string get_dyn_str(std::function<int32_t (char *, uint32_t)> f)
{
const auto sz = f(nullptr, 0);
std::string s;
s.resize(sz);
f(s.data(), sz);
return s;
}
auto uri = get_dyn_str([&] (char *s, uint32_t c) -> int32_t { return webview->get_uri(plugin, s, c); });
This is more or less what I have in mind on how to use this. Once you wire it well, it is easy to work with.
OK, got it! A null uri is unexpected enough (for me) that I've add a note in the comments, but otherwise the API should match what you were suggesting.
OK - and should
uribe allowed to be null?
I'd say that if uri is null then capacity must be 0. And if capacity is 0, then uri can't be used (so its value doesn't matter).
We could demand that it's always not null, but allow a capacity of 0 for a length query. This would match discussions about
audio_outputswhere the conclusion (as I understood it) was that the array cannot be a null pointer, even ifaudio_outputs_count == 0.
If you're talking about https://github.com/free-audio/clap/blob/main/include/clap/process.h#L51 Then if the port count is 0, then it doesn't make a difference if the pointer is null or not because you can't use it.
Oh sorry, I've forgotten to merge!
@abique I've added a comment saying that if get_uri() returns a file: URI, the host is allowed to rewrite the URL to some other scheme/prefix, as long as the filename and all relative paths are preserved.
This has two functions: in a native environment, file: URLs have some limitations about the requests they can make. Additionally, this lets non-bundled WCLAPs write their resources to some directory (using the WASI virtual filesystem) and then point to it, the same way as platforms like Windows.
In the case of bundles (which aren't supported across all OSes), I've said that relative paths should be resolved against a file: URL for the bundle's resource directory, which I think is nice and clear.
@abique I've added a comment saying that if get_uri() returns a file: URI, the host is allowed to rewrite the URL to some other scheme/prefix, as long as the filename and all relative paths are preserved.
This has two functions: in a native environment, file: URLs have some limitations about the requests they can make. Additionally, this lets _non-_bundled WCLAPs write their resources to some directory (using the WASI virtual filesystem) and then point to it, the same way as platforms like Windows.
In the case of bundles (which aren't supported across all OSes) relative paths should be resolved against a file: URL for the bundle's resource directory.
Thank you very much! :-)
This is awesome, thanks so much.
I don't know if you saw, but WCLAPs got a small shout-out in one of the ADC'25 talk proposals (about the future of audio plugins on the web).
This is awesome, thanks so much.
I don't know if you saw, but WCLAPs got a small shout-out in one of the ADC'25 talk proposals (about the future of audio plugins on the web).
I didn't see it but that's great :-)