Wavelab Crashes VST2 plugins with Sidechain


#1

I checked and this is occurring with the latest on develop, using the NoiseGateDemoPlugin. It happens on both Windows and macOS.

There seems to be a bug with Wavelab 9.5 and JUCE VST2 plugins with a sidechain (I haven’t been able to check non-JUCE plugins, yet). Wavelab will instantiate the plugin with a sidechain input, but then passes nullptr for that buffer. So, if you try to access that buffer…crash.

This seems to be a bug with Wavelab, and can obviously be worked around inside processBlock. But, it probably makes more sense to check for this case and fix in the VST2 wrapper.


#2

Ok, I should revise my findings. It appears that in the VST wrapper’s internalProcessReplacing(), Wavelab is only passing buffers for the main input and output buses, and NOT any buffers for additional sidechain input or aux output buses. Essentially, Wavelab is ignoring any buses beyond the main bus. If you break in internalProcessReplacing() and examine the vstEffect member struct of JuceVSTWrapper, the number of input and output channels set in that struct match only the main input/output buses. This also means that the VstTempBuffer passed is incorrectly sized.


#3

After needing to spend some time on other tasks, I had a chance to really dig into this. Perhaps @fabian (or someone with knowledge of multibus and how it relates to VST2) can provide some more insight.

The steps run before processing occurs in VST2 in Wavelab seem to be thus:

  1. Plugin is constructed. The plugin provides the number of input channels and output channels it wants by filling the vstEffect struct (line 290-291 in Juce_VST_Wrapper.cpp).

    vstEffect.numInputChannels = maxNumInChannels;
    vstEffect.numOutputChannels = maxNumOutChannels;

    Let’s say here that the plugin is stereo-in stereo-out and has a stereo sidechain (4 ins 2 outs).

  2. handleSetSpeakerConfiguration() is called by Wavelab, with 2 inputs and 2 outputs, should Wavelab be processing a stereo file. This function sets the main bus layout of the plugin to match this configuration (line 1897), but doesn’t do anything about the sidechain bus (i.e. leaves it in the initial format).

  3. Upon playback, Wavelab calls resume() and these lines are run (line 549):

    auto numInAndOutChannels = static_cast<size_t> (vstEffect.numInputChannels + vstEffect.numOutputChannels);
    floatTempBuffers .channels.calloc (numInAndOutChannels);

    The problem is, if you break here, vstEffect.numInputChannels and vstEffect.numOutputChannels are both now equal to 2 (Wavelab changed them!), and floatTempBuffers is sized accordingly. The plugin, however, is still expecting 4 in channels and 2 out channels, because the bus configuration hasn’t been updated.

When processing is run (internalProcessReplacing()), the channel counts for iterating the passed inputs and outputs buffers are determined using the plugin’s bus layout (line 398):

 const int numIn  = processor->getTotalNumInputChannels();
 const int numOut = processor->getTotalNumOutputChannels();

So when we iterate over any additional inputs (line 455):

for (; i < numIn; ++i)
    tmpBuffers.channels[i] = inputs[i];

tmpBuffers.channels[i] (which is a reference to floatTempBuffers) will read out of bounds, because it is only sized for 2 ins and 2 outs, and numIn is 4!

Now, HERE’S THE QUESTION: I’m not sure if this is a bug in Wavelab, or a bug in the JUCE code. It seems that the JUCE code assumes the sidechain will just always be after the main bus inputs in the passed inputs buffer, but maybe that’s not an assumption that can always be made? Or is Wavelab changing vstEffect.numInputChannels and vstEffect.numOutputChannels when it shouldn’t be? Should a VST2 host always pass buffers to internalProcessReplacing() that are sized according to the number of channels initially reported by the plugin, or only what was set in handleSetSpeakerConfiguration()?

I’ve reached out to Steinberg for more information about this, but would appreciate any further input about what the expected behavior should be with additional input and output buses (e.g. sidechain or aux output) in VST2. It would also be good to perhaps safeguard against this, maybe by checking for vstEffect.numInputChannels and vstEffect.numOutputChannels being different than the bus channel counts in resume(), and disabling sidechain/aux buses in the processor their if need be?


#4

Ok, likely talking to myself here, but for future reference:

I’ve been in touch with the developer of Wavelab, and he is aware of the issue, but it is not high priority given that sidechain works correctly in VST3. So it seems like the issue is in Wavelab, not the JUCE wrapper. Hopefully it will be fixed in a future update.

For now, I have fixed this problem by disabling aux inputs/outputs if running in Wavelab, but that only seems like a band-aid of a fix.