CLAP: The New Audio Plug-in Standard (by U-he, Bitwig and others)

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

I have a technical question regarding sending automation from the GUI to the host.

Basically there are two options: (1) queue them on the GUI side and request_flush or (2) send them to the audio side (eg. with a wait-free queue) and let it send them in the process code.

I'm not sure if option 1 is valid when processing is active. On the other hand, I'm not sure how to reliably detect whether processing is active without taking a lock in the audio thread. Since activate/deactivate are called in the main thread, I could implement both approaches and choose based on active state, but start/stop processing is called in audio-thread, so what happens if the host stops processing, but doesn't deactivate? Is this "limbo state" where plugin remains active, but processing is suspended for extended periods of time valid?

edit: I can send messages from audio to GUI in start/stop processing to notify it about the state, but as far as I can see this just moves the race condition from one place to another... so like.. what's the recommended approach?

Post

a) No, request_flush() is not valid while processing is active. You should put pending parameter changes in a lock-free queue and then output them at the end of your process function.

b) Just use an (atomic) boolean to indicate whether your plugin is currently processing audio (start_processing()/stop_processing()) and share that between your GUi and your DSP.

Post

Urs wrote: Thu Jun 16, 2022 4:47 pm a) No, request_flush() is not valid while processing is active. You should put pending parameter changes in a lock-free queue and then output them at the end of your process function.

b) Just use an (atomic) boolean to indicate whether your plugin is currently processing audio (start_processing()/stop_processing()) and share that between your GUi and your DSP.
Atomic boolean does not work, because there is a race: if I check the atomic boolean and it says "not processing" then by the time I call request_flush processing might already have started and now we are in an invalid state.

edit: Basically it seems impossible to even make sure that request_flush is not called while processing is active unless you take a mutex in start_processing ... but even then one might end up calling request_flush while start_processing is waiting for that mutex. The header doesn't actually say anything except [thread-safe] and "must not be called in [audio-thread]" so if we go purely by the spec, then it seems valid to request_flush and if this happens when processing is active and the host is required to honor it, then the host would be required to stop_processing, then flush, then start_processing again.. but that's obviously non-sense.

This could be fixed from the plugin point of view by specifying that (1) request_flush is always valid (as long as not from audio thread) and (2) if the host calls process, then that also counts as a flush. If we assume that the host has means to respect the threading rules of flush itself without locking (not entirely sure if there's a similar issue here, but perhaps it's possible), then this seems implementable on the host side with a "need_flush" flag that's atomically set by request_flush and atomically cleared every time process is called... and then can be checked by host when it calls stop_processing to see if it needs to queue an actual flush on the main thread.
Last edited by mystran on Thu Jun 16, 2022 5:57 pm, edited 1 time in total.

Post

Statement a is a bit misworded

Request flush isn’t needed when processing is happening. But it isn’t harmful. So it’s not valid in that “you should not need to call it in regular course of business” but it’s not not valid in that “the system will crash or be in an error state”. Although you wouldn’t get flushed, is my understanding.

So that would lead to a strategy something like if atomic book says you aren’t processing stow away your changes and call request flush. Start a timeout. Next time in tour timer if you are processing and your changes are still there send them to the processing queue. Then if the request flush results in a flush you are flushed so nothing there, and if the request flush doesn’t, you use the timeout.

I’ll try and modify my little clap demo to do this to make sure I believe it myself, but i believe it to be correct. (Right now my clap demo will drop ui updates if processing is off). Sorry for the somewhat cryptic answer - on my phone and it’s a really good question so didn’t want to let it linger.

Post

baconpaul wrote: Thu Jun 16, 2022 5:41 pm Request flush isn’t needed when processing is happening. But it isn’t harmful. So it’s not valid in that “you should not need to call it in regular course of business” but it’s not not valid in that “the system will crash or be in an error state”. Although you wouldn’t get flushed, is my understanding.
Ok, so as I proposed in the edit that I wrote when you were writing this, it seems reasonable that request_flush simply gets ignored when process is about to be called anyway. In this case the host can set an atomic flag when request_flush is called and clear it right before it calls process and then only worry about looking at the flag and potentially calling flush when it knows that processing is suspended.
So that would lead to a strategy something like if atomic book says you aren’t processing stow away your changes and call request flush. Start a timeout. Next time in tour timer if you are processing and your changes are still there send them to the processing queue. Then if the request flush results in a flush you are flushed so nothing there, and if the request flush doesn’t, you use the timeout.
Well.. sure.. but if unnecessary request_flush gets ignored anyway, then wouldn't an equally valid strategy be just always calling request_flush from the GUI thread no matter what?

Post

I mean the strategy that seems fairly reasonable to me and seemingly valid would be to always put the events into the same wait-free queue and request_flush ... and then just empty the queue from both process() and flush() which the host is not allowed to call at the same time, so no need to worry about locking ... and the original question was basically whether this strategy is actually valid.

Post

That is an excellent question which I can answer when I’m at my computer next. This particular convo may be better suited as a GitHub discussion rather than a kvr one if you have a GitHub Id? If so let me know your id and I can tag you into a discussion (or just start one at free-audio/clap)

Post

baconpaul wrote: Thu Jun 16, 2022 6:13 pm That is an excellent question which I can answer when I’m at my computer next. This particular convo may be better suited as a GitHub discussion rather than a kvr one if you have a GitHub Id? If so let me know your id and I can tag you into a discussion (or just start one at free-audio/clap)
https://github.com/signaldust :)

Post

https://twitter.com/wrl/status/1537125901917372417
This just popped up in my feed. Dude's doing a Twitch stream of him porting his plugin framework to Clap.
https://www.twitch.tv/wrlight

Post

I .. have a problem ... namely no host to test ... tried Multitrack Studio but it seems to crash (on M1) somewhere after fetching the plugin descriptor (ie. just the struct with a bunch of strings) so .. not helpful for testing.

edit: oh nevermind, silly me copied the plugin-template without thinking and forgot to 0 terminate the feature list (even if it would have been obvious that it's missing termination if I'd bothered turning my brain on).. now I get a more useful crash ^^

edit2: actually not really more helpful.. crashes on load, doesn't automatically break in lldb on signal (think MtS is doing something to catch them) and ... if I break in debugger when the plugin is initialized, then it loads .. so seems like a timing sensitive crash .. and I guess the only option is to wait for a host that plays nicer with a debugger (come on Reaper please)

Post

(yeah, a nice debuggable host would be wonders now...)

Post

Waiting for a host to start testing. Not that I have anything against Bitwig, but I'd rather not have to get familiar with the nuances of a new host at this time.

Post

Is the CLAP documentation in the works? So far, it seems to only be the comments in the source at github.

Post

camsr wrote: Thu Jun 16, 2022 10:17 pm Is the CLAP documentation in the works? So far, it seems to only be the comments in the source at github.
The headers are really well commented and organized. Just download the whole thing and open in some editor with a directory view on a sidebar (eg. VSCode would work) or add them to the project (if you'we using something like Visual Studio proper) and it's really quite easy to use them as a reference.

Post

Urs wrote: Thu Jun 16, 2022 10:11 pm (yeah, a nice debuggable host would be wonders now...)
So I've done quite a few CLAP ports in the last month. Bootstrapping was really the trick. To make my life easier I wrote a little command line host that anyone else can use to get started which is at https://github.com/surge-synthesizer/clap-info - that will load, scan params, print port configuration, etc... and if you want it to do more we are happy for PRs

I also write a prior version of it that actually sent midi to your plug and sent sound out using rtaudio. That's a little rockier but if you want it DM me.

There's also the demo clap host at https://github.com/free-audio/clap-host which shows the GUI and interacts with midi also.

So there's quite a few ways to get started.

My experience so far has been: if clap-info can print out stuff about your plugin that looks right, bitwig can at least get to the point of making your process loop crash in a way you can debug.

Post Reply

Return to “DSP and Plugin Development”