Our plugin has a lot of parameters and we have the problem that changing presets with the VST3 is really slow in ableton live (macOS) and takes up a few seconds while the VST2 loads pretty fast and almost immediately. Other hosts like reaper do not have any problems.
I found out that it has to do with the setValueNotifyingHost() calls.
It helped when i check if the value did change before i notify the host. Did anyone notice this too? Is there something i can do to speed things up?
Does anyone have an idea how to avoid a blocking UI because of a lot of setValueNotifyingHost() calls? Does it make sense to offload preset loading to another thread?
I found that it loads pretty fast when I load the preset from another thread. The only problem is that the undo manager that I try to reset before loading the preset crashes now. I think this is a concurrency issue.
It looks like it’s not safe to call setValueNotifyingHost() from a different thread:
I also get crashes from time to time. What solutions are possible to avoid blocking the UI thread while loading? Dispatch the calls back to the main thread?
If the result of getCurrentProgram is different to the current program parameter value, this will send a restart request to the host with the flag kParamValuesChanged set.
Thanks for the info. Then i only call parameter.setValue() when setting the params and call updateHostDisplay() when finished to notify the host?
Will this also notify the ui components about the changes? Can i call this from another then the UI thread?
I’d strongly advise against calling any function that interacts with the plugin APIs from an arbitrary thread. The VST3 API has quite strict rules about which threads may call each function. In particular, the restartComponent call must be made on the main thread, and so JUCE’s VST3 wrapper contains a mechanism that always dispatches a restart request on the main thread.
Issuing a restart request won’t automatically notify the ui components about the changes, no. It will just tell the host that it needs to re-read all of the parameter values.
setValue() is thread save and fast. But I’m having a problem now that my plugin has parameter listeners. I tried sendValueChangedMessageToListeners() but unfortunately this isn’t thread-safe and slow when called often.
When profiling i see that i lost most of the time in ableton. Looks like still one of the listeners notifies Ableton?
Is there a way to notify only my own parameter listeners about the parameter change? Or are there other solutions out there to avoid a blocking UI while setting parameters?
Ive noticed something similar with my non JUCE vst3’s built in preset browser in Live, and I’ve been meaning to look into it. Can you tell me which of your plug-ins I can try this with?
What I do is instead of having a direct listener, have a Timer that’s comparing the parameter values to the old values, and if they changed trigger the listeners.
That’s the only reliable way I know of to send any kind of messages in the process thread to the UI thread without allocating/blocking.
It’s an unreleased new product. I see also the same problem in studio one.
After some more tests it looks like it’s safe to call sendValueChangedMessageToListeners() followed by updateHostDisplay from other threads. The UI stays responsive in this case and does not block.
Only the UndoManager is not thread-safe in this case. But i will open a new topic about that.
Edit: You also have to make sure that your parameter listeners are thread-safe.
Edit2: It looks like sendValueChangedMessageToListeners() also notifies the host. And the host should only be notified through the main thread as reuk mentioned. So this solution does not work and may have side effects.
I’m looking for a way to only notify my own listeners and the attachments.
I need a way to update all parameter listeners without notifying the host and after that notifying the host with updateHostDisplay.
This makes things a lot faster and we could still use the main thread for setting parameters. Some hosts are pretty slow when notifying a few hundred parameters when loading presets. It does not scale.
A new method like sendValueChangedMessageToListeners() that does not notify the host would be very useful!
This is the original code:
void AudioProcessorParameter::sendValueChangedMessageToListeners (float newValue)
{
ScopedLock lock (listenerLock);
for (int i = listeners.size(); --i >= 0;)
if (auto* l = listeners [i])
l->parameterValueChanged (getParameterIndex(), newValue);
if (processor != nullptr && parameterIndex >= 0)
{
// audioProcessorParameterChanged callbacks will shortly be deprecated and
// this code will be removed.
for (int i = processor->listeners.size(); --i >= 0;)
if (auto* l = processor->listeners[i])
l->audioProcessorParameterChanged (processor, getParameterIndex(), newValue);
}
}
Something like this would be useful. setValueNotifyingHost() would call the one that notifies the host:
void AudioProcessorParameter::sendValueChangedMessageToListenersAndHost (float newValue)
{
ScopedLock lock (listenerLock);
sendValueChangedMessageToListeners (float newValue)
if (processor != nullptr && parameterIndex >= 0)
{
// audioProcessorParameterChanged callbacks will shortly be deprecated and
// this code will be removed.
for (int i = processor->listeners.size(); --i >= 0;)
if (auto* l = processor->listeners[i])
l->audioProcessorParameterChanged (processor, getParameterIndex(), newValue);
}
}
void AudioProcessorParameter::sendValueChangedMessageToListeners (float newValue)
{
ScopedLock lock (listenerLock);
for (int i = listeners.size(); --i >= 0;)
if (auto* l = listeners [i])
l->parameterValueChanged (getParameterIndex(), newValue);
}
IMHO the name sendValueChangedMessageToListeners() is misleading because it does not tell that it also notifies the host. Also the comment the code will be removed looks wrong. Isn’t this code still used to notify the host?
Other ideas how this can be done are also welcome. Maybe i can overwrite something to achieve the same result?
After some research, I finally decided to reduce the number of automatable parameters and marked most of them as non-automatable.
Also because Logix X fills the parameters into a Popup and it opens slow when you have a lot of them.
Another reason was that it looks like some hosts don’t read the parameters when I request them with updateHostDisplay(). I went back to notify them with setValueNotifyHost() like usual.
It still blocks for around half a second in ableton live and no one sees the loading animations. But we may just have to accept it.