Cubase hangs on call to AudioEffectX::ioChanged


I'm seeing a strange problem in Cubase 7.0/7.5 and Nuendo 6 on both Mac and Windows, 32 and 64-bit.  When I load my plug-in Cubase will hang for a few seconds (and once in a while permanently) and audio will stop for the entire session.  Removing the plug-in does not fix the problem.  The only way to get audio back is to restart Cubase or reactivate the session.  Strangely the problem does not occur in Cubase 6 or any other host that I've tried.


I tracked the problem down to Cubase hanging on the AudioEffectX::ioChanged callback when I set the plugin latency (setLatencySamples).  If I comment out the call to ioChanged (and AudioEffectX::updateDisplay) in audioProcessorChanged then the problem doesn't occur.  Below I've included the call stack when Cubase was hung.


I'm calling setLatencySamples from prepareToPlay which seems like a reasonable place.  I was not able to reproduce the bug when adding setLatencySamples to the Demo PlugIn, but with one of my plug-ins the problem only occurs once in a while, so potentially I didn't try enough times.  I'm not using the Juce tip, but it doesn't look like this code has changed recently, so I wouldn't expect that to make a difference.  


Anyhow, do you guys have any ideas or suggestions for working around this issue?  I could just remove the callbacks from audioProcessorChanged, since I don't plan on updating the latency dynamically.


Thanks,
Chris
 


Thread 1, Queue : com.apple.main-thread
#0 0x94d4a822 in semaphore_timedwait_trap ()
#1 0x019fa9c2 in 0x019fa9c2 ()
#2 0x019fa87c in <????> ()
#3 0x00e77aeb in <????> ()
#4 0x00e77d4e in <????> ()
#5 0x00e71814 in <????> ()
#6 0x01602ee1 in <????> ()
#7 0x019fb6d9 in <????> ()
#8 0x019fb88a in <????> ()
#9 0x019eb25d in <????> ()
#10 0x00f26e88 in <????> ()
#11 0x01602ee1 in <????> ()
#12 0x019fb6d9 in <????> ()
#13 0x019fb88a in <????> ()
#14 0x019eb25d in <????> ()
#15 0x00f96bb6 in <????> ()
#16 0x00fb0586 in <????> ()
#17 0x00fb079d in <????> ()
#18 0x0fd0bd23 in gpus_io_data ()
#19 0x0fd0f181 in gpus_io_data ()
#20 0x2b6fc33b in AudioEffectX::ioChanged at /VST2/public.sdk/source/vst2.x/audioeffectx.cpp:449
#21 0x2b702c54 in JuceVSTWrapper::audioProcessorChanged(juce::AudioProcessor*) ()
#22 0x2b702ef9 in non-virtual thunk to JuceVSTWrapper::audioProcessorChanged(juce::AudioProcessor*) ()
#23 0x2b2df3df in juce::AudioProcessor::updateHostDisplay() ()
#24 0x2b2df365 in juce::AudioProcessor::setLatencySamples(int) ()
#25 0x2b1e85d1 in MyPlugInAudioProcessor::prepareToPlay(double, int) ()
#26 0x2b1e8620 in virtual thunk to MyPlugInAudioProcessor::prepareToPlay(double, int) ()
#27 0x2b700ce3 in JuceVSTWrapper::resume() ()
#28 0x2b6fbd27 in AudioEffect::dispatcher(int, int, int, void*, float) ()
#29 0x2b6fba77 in AudioEffectX::dispatcher(int, int, int, void*, float) ()
#30 0x2b700993 in JuceVSTWrapper::dispatcher(int, int, int, void*, float) ()
#31 0x2b6fdd6b in AudioEffect::dispatchEffectClass(AEffect*, int, int, int, void*, float) ()

 

I'm thinking the simplest option is to just add a flag to setLatencySamples specifying if the host should be notified of updates.  If setLatencySamples is called during initialization (e.g., prepareToPlay) then the correct latency value should be passed to the host even if the flag is set to off.  I think the only time where it would be appropriate to do the host notification is if the plug-in latency changes during processing, although I imagine hosts will not necesssarily handle that in a consistent way.

Jules, what do you think about adding this, or something similar, to the codebase?

 

modules\juce_audio_processors\processors\juce_AudioProcessor.h

void setLatencySamples (int newLatency, bool doUpdateHost=true);

 

modules\juce_audio_processors\processors\juce_AudioProcessor.cpp

void AudioProcessor::setLatencySamples (const int newLatency, bool doUpdateHost)
{
    if (latencySamples != newLatency)
    {
        latencySamples = newLatency;
        if(doUpdateHost)
            updateHostDisplay();
    }
}
 

I think that if you won’t notify the host you don’t need to call setLatencySamples.

I remember Cubase being picky about calling ioChanged from the processing thread (i.e. calling it from within processBlock(), maybe even from other threads than the message thread) but you don’t seem to do that. Nonetheless, as it seems to work in other JUCE plugins it probably is something in your code.

Edit: setInitialDelay is called in the VSTWrapper’s resume, so the first sentence is wrong and can be ignored.

Yes, it looks like VSTWrapper resume sets the VST latency after PrepareToPlay, so the updateHostDisplay should be redundent at that point.  I'm just calling setLatencySamples at initialization.  I'm definitely NOT calling it from the processing thread, so that should be an issue.