Just in case -I think latency can only be reported from the message thread. From here:
Q: How report to the host that the Plug-in latency has changed?
The Plug-in should call from the editController its component handler with flag kLatencyChanged: componentHandler->restartComponent (kLatencyChanged);
virtual tresult restartComponent (int32 flags)
Instructs host to restart the component.
This must be called in the UI-Thread context!
I guess each host may tolerate a non UI thread call differently. I’ve had different responses from different versions of Cubase, but no problems with Live 10 calling from the message thread. Maybe restartComponent was called asynchronously in 5.4.6, and synchronously now?
Really? I remember a few examples where it was suggested to use it in the processBlock (and in processBlockBypassed). I’ve used it there for years without issues. I also remember I had issues when setLatencySamples was only placed in prepareToPlay (some hosts didn’t like that), especially when you have a dynamic latency that can change with different parameters/presets.
Just in case -I think latency can only be reported from the message thread.
There is a workflow diagram in the root docs (can’t get a link to it -> go to https://steinbergmedia.github.io/vst3_doc/ and click the “workflow diagrams” on the left hand side near the bottom) showing the call sequence for reporting a latency change from the audio thread.
What’s supposed to happen is the audio processor notifies the host, which then restarts the component, suspends audio processing and waits for the latency to be changed on the audio thread. I’m a little unsure of what interface you’re supposed to call on the host from the plugin to send that message though.
This also seems related to this other thread where I complained about parameter name changes triggering kLatencyChanged component restarts. The whole latency change and updateHostDisplay() mechanism is currently not done well in the Juce vst3 wrapper.
Right now we have crashes from changing latency plus processing gaps from changing parameter names in some hosts. The wrapper really should be much more clever when it comes to calling restartComponent and only send the flags that are really needed!
I totally agree. In the meantime I’ll comment those lines in the wrapper, to avoid our plugins to crash in Ableton. Since most of our products are non-linear processors, it’s very common to our users to save a project having the oversampling enabled.
There’s an example in the VST3 SDK (public.sdk/samples/vst/hostchecker) where this is done -the processor calls sendMessage() with id “Latency”, then the controller receives the message with the same id through notify(), and calls restartComponent. This is underdocumented to say the least. Anyway, Juce is not doing it -setLatencySamples calls updateHostDisplay, audioProcessorChanged, restartComponent.
Right, I’ll do that asap myself… on the other thread I ended up disabling parameter name changes for vst3 anyway because it caused skips in the audio… and now even crashes. Would be nice if this stuff would work, but I guess Ableton needs to fix this crash and the JUCE team should have another look at the vst3 wrapper and restartComponent().
I dived a bit in the SDK. sendMessage/notify is just a connection mechanism between component/processor and controller, the “host” is the PlugProvider which sets up a pair of connection proxies to pass messages from one side to the other. When this is used, messages not coming from the UI thread (specifically, from the thread that created the connections) are just dropped -there’s a threadChecker for that, and a comment: “TODO we should test if we are in UI main thread else postpone the message”. Juce doesn’t need this because the wrapper owns both component and controller, so it makes sense to call restartComponent directly, but it seems clear which thread it should be called from. It makes sense in the same way that it makes sense to call prepareToPlay from the UI thread. As the diagram shows, VST expects the processor to hold the actual change of latency until the next call to prepareToPlay (changeAlgo inside setActive(true)). Whether a host tolerates a non-UI thread call is another thing. In Cubase 5/6, it makes a very long pause and sometimes freezes, versus a short pause when called from the UI thread. In Cubase 9/10, it seems to be indifferent. It’s possible that they’re postponing the call in the host to deal with non-compliant plugins.
Still, Juce jumbling all flags in a single call is a mess in any case.