Missing notification about a sample rate change in VST

I encountered an issue where a sample rate change won't be reported to a plugin when used as VST.

It happens in Audacity with an audio file that has a sample rate other than 44.1kHz, e.g. 96kHz. The plugin only gets a sample rate of 44.1kHz from the JuceVSTWrapper via prepareToPlay(). I don't understand the VST API and it could be that Audacity is using it in strange ways. That said, it appears to me to be a Juce issue, according to the following insight.

 

A breakpoint in AudioEffect::setSampleRate() gets hit multiple times:

When the plugin gets loaded:
sampleRate = 48000
sampleRate = 48000
sampleRate = 44100
-> this gets forwarded by the JuceVSTWrapper to the plugin by calling its prepareToPlay().

When the offline processing gets started by clicking the 'Apply' button in Audacity:
sampleRate = 96000, with the corresponding call stack

#0    0x11614339 in AudioEffect::setSampleRate(float) at .../VST3 SDK/public.sdk/source/vst2.x/audioeffect.h:67
#1    0x11610f51 in AudioEffect::dispatcher(int, int, int, void*, float) at .../VST3 SDK/public.sdk/source/vst2.x/audioeffect.cpp:187
#2    0x11610ce7 in AudioEffectX::dispatcher(int, int, int, void*, float) at .../VST3 SDK/public.sdk/source/vst2.x/audioeffectx.cpp:318
#3    0x116164bc in JuceVSTWrapper::dispatcher(int, int, int, void*, float) at .../juce/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp:1474
#4    0x11612ecb in AudioEffect::dispatchEffectClass(AEffect*, int, int, int, void*, float) at .../VST3 SDK/public.sdk/source/vst2.x/audioeffect.cpp:54
#5    0x0049aecf in VSTEffect::callDispatcher(int, int, long, void*, float) ()

Sadly, this sampleRate wont be forwarded to the plugin.
The AudioEffect::dispatcher (opCode, index, value, ptr, opt) is called with the arguments
opCode = effSetSampleRate
opt = 96000

but this information wont be passed to the plugin.

 

Audacity v.2.1.2
Mac OS X 10.11.2
Juce 28a322138d (2016 02 03)

So you mean it calls setSampleRate but then there isn't a subsequent prepareToPlay call?

Exactly

Reaktor is also suffering from getting a wrong sample rate, so this is clearly an issue that needs to be resolved by Audacity (I created a bug report).

I noticed in JuceVSTWrapper::resume() :

double rate = getSampleRate();
jassert (rate > 0.0);
if (rate <= 0.0)
    rate = 44100.0;
...
filter->prepareToPlay (rate, currentBlockSize);

But in the juce_AU_Wrapper.mm, such a precaution is not implemented (not even an assertion is in place) and in Audacity a sample rate of 0 is actually passed to the plugins prepareToPlay(). I suggest to copy the above code over to JuceAU::prepareToPlay() as well.

Hmm. This seems to be more of a host failure than anything that should be fixed in juce..

If it's changing the sample rate, a host should stop and restart the plugin - it'd be very optimistic to expect all plugins to correctly handle random rate changes in mid-stream.

And if the host passes 0 as a sample rate, it's again a host bug. Even the workaround I added to the VST version isn't really very good, since 0 and 44100.0 are equally wrong! TBH as a plugin writer I'd rather be given a wrong value of 0 so that at least then you know that the host is messing up, rather than just playing at a different rate without knowing anything is wrong.

I'd actually prefer if the VST wrapper would pass a sample rate of 0 as well.

At the moment, in a faulty host, the AU wrapper will return 0 whereas the VST wrapper will return 44100. May I request to remove the part in the VST wrapper that changes the sample rate from 0 to 44100? For the sake of consistency in the wrappers.

TBH as a plugin writer I'd rather be given a wrong value of 0 so that at least then you know that the host is messing up, rather than just playing at a different rate without knowing anything is wrong.

Yeah, already happened to me in the past...

Agreed - I'm going to change the VST version so it also doesn't use a fallback value.