Ableton Live 10 crashes upon loading a saved project with a VST3 plugin that uses setLatencySamples. No call stack or any specific error in debug. VST2 is fine, other hosts seems to be fine as well.
The bug is present in both JUCE 5.4.7 develop and master tips.
Plugins compiled with JUCE 5.4.3 seem to be working instead.
A workaround is to delay the setLatencySamples call when loading for the first time. Waiting for 10-15 buffers at 512 samples seems to be enough.
Plugins compiled with JUCE 5.4.3 don’t have this issue. But, yeah, I’ll reach out to them as well.
Both macOS and Windows
I run Ableton Live in the debugger (Xcode). It just quits without showing any call stack. I’ll try to set an all exception breakpoint and see what happens.
Start debugging Live from XCode as per usual.
Detach debugger from Live (Debug -> Detach)
Reattach debugger to Live (Debug -> Attach to Process)
Not tried this myself (although seen this symptom occasionally), but was mentioned in another thread recently as a working way to get the debugger to catch such exceptions.
Does it make any difference if you leave/remove the VST2 binary of that plug-in from the path where Live searches for them? It’s a shot in the dark, but sometimes I’ve seen that playing a role
The suggested workaround works, but I use a different way: I set an uint32 global variable named paramCalled and saving a Time::getMillisecondCounter() in it.
When I have to call setLatencySamples, I check if Time::getMillisecondCounter()-paramCalled>150, then I call setLatencySamples. Lower ms values often won’t work.
This workaround helps to avoid Live 10 crashing, but I’m afraid that delaying the setLatencySamples could make offline renders on some hosts not sounding as expected.
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 Steinberg Plug-in Interfaces Documentation 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.
yes, they will. I only “reverted” that method to what it was before the commit that caused Live 10 to crash. We don’t change parameter names, so I can live without that in the meantime.
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.