(DEPRECATED) The ultimate JUCE 4.1 MultiBus Guide

We are working on it. We want to give it proper testing before releasing. 

+1 - I'm also waiting for the Graph to support Bus layout... that would be great!

Hi Fabian, is your experimental JUCE fork available somewhere in case we want to help testing your new multibus fixes?

1 Like

HomeopaticChannelSet class. providing inconsistent bus channels in every daw since 1999

Are the new multibus fixes available in JUCE 4.2?

Hi @myhrman,

all the fixes should be in this release apart from things involving the AudioProcessorGraph or hosting multi-bus plug-ins. Also, we haven’t touched the API in this release - but we will rename a few things going forward to avoid confusion. This will also get rid of the macros being using in AudioProcessor.cpp.

In addition, this release offers more control for VST-2 plug-ins if you use the setBusArrangement API. See examples/PluginSamples/GainPlugin for this. For example, if JUCE detects that you are using the setBusArrangement API, then the layout set in the constructor of your plug-in will be the default VST-2 plug-in layout. You can also indirectly specify the maximum number of supported channels in the setPreferredBusArrangement callback. See the example for more details.

Kraken, have you tested 4.2 regarding that “vst with no inputs busses” issue?

yes, i will do a detailed report tomorrow

I believe I hit the exact same problem (“VST with no input buses”) with 4.2. The crash happens after setLatencySamples() has been called, or more specifically after the ioChanged() function has been called asynchronously.

bool getSpeakerArrangement (VstSpeakerArrangement** pluginInput, VstSpeakerArrangement** pluginOutput) override
{
    ...

    if (pluginHasSidechainsOrAuxs())
    {
        int numIns  = busUtils.findTotalNumChannels (true);
        int numOuts = busUtils.findTotalNumChannels (false);

        AudioChannelSet layout = AudioChannelSet::canonicalChannelSet (numIns);
        SpeakerMappings::channelSetToVstArrangement (layout,  **pluginInput);

        layout = AudioChannelSet::canonicalChannelSet (numOuts);
        SpeakerMappings::channelSetToVstArrangement (layout,  **pluginOutput);
    }
    else
    {
        // CRASHES ON THIS LINE:
        SpeakerMappings::channelSetToVstArrangement (busUtils.getChannelSet (true,  0), **pluginInput);
        SpeakerMappings::channelSetToVstArrangement (busUtils.getChannelSet (false, 0), **pluginOutput);
    }

    return true;
}

The crash above only happens if the synth has:

  • No individual output buses (i.e. only a main output bus).
  • No sidechain inputs

So, as soon as there is only a main output bus, I have to add also a sidechain input bus, or else the plugin crashes as soon as anything triggers the asyncUpdate in VST wrapper and ends up in ioChanged().

This is reproducible in the MultiOutSynth example, if you simply set the maxMidiChannel = 1 instead of 16 (in which case it will only get a main output bus). And then call setLatencySamples(1000) from prepareToPlay(). Crash!

I also have no inputs and Juce4.2 works fine for me for any number of output channels (but I have no midi).
That’s what I do in the constructor:

	busArrangement.inputBuses.clear();
	busArrangement.outputBuses.clear();
	busArrangement.outputBuses.add(AudioProcessorBus("my_name", AudioChannelSet::discreteChannels(numChannels)));

(and then of course only allow this number in setPreferredBusArrangement)

mmmh this could be a temporary workaround if it works in all hosts. have you tried if it’s working ok in VST2 on cubase8 with any number of channels > 2 ?

still with this you cannot report to the host which channels pairs are to be treated as stereo, as you are reporting you want a plain list of 10 channels, also you will miss naming individual busses.

i’m sorry but this does not work, they are reported as N individual mono outputs, and since in most hosts by default only the first bus is enabled and mapped to the mixer, you will get only the first channel outputting audio, resulting in mono output even if the synth generate a stereo pair.

Sorry, you are right, this only works with one Stereo or n Mono channels.
For my purpose n Mono channels are sufficient…

Even after the latest changes, Cubase8 with VST2 seems to be still affected of muting outputs after a call of ioChanged().

Here it is my config, it’s working ok in all hosts (VST2 & 3, AU2) i threw it apart the St3!nb0rg one with VST2:

busArrangement.inputBuses.clear();
busArrangement.outputBuses.clear();
busArrangement.outputBuses.add(AudioProcessorBus("Master", AudioChannelSet::stereo()));
busArrangement.outputBuses.add(AudioProcessorBus("Aux A", AudioChannelSet::stereo()));
busArrangement.outputBuses.add(AudioProcessorBus("Aux B", AudioChannelSet::stereo()));
busArrangement.outputBuses.add(AudioProcessorBus("Aux C", AudioChannelSet::stereo()));
busArrangement.outputBuses.add(AudioProcessorBus("Aux D", AudioChannelSet::stereo()));

bool setPreferredBusArrangement(bool isInputBus, int busIndex, const AudioChannelSet& preferredLayout)
{
    const int numChannels = preferredLayout.size();

    // we only accept the first 5 stereo output busses
    if (isInputBus || numChannels != 2 || (busIndex > 4 && preferredLayout != AudioChannelSet::disabled()))
        return false;

    // when accepting a layout, always fall through to the base class
    return AudioProcessor::setPreferredBusArrangement(isInputBus, busIndex, preferredLayout);
}

Any idea on this ?

Too bad, for our plugins this is one of the biggest remaining issues now. The quite drastic workaround is to expose only the main output to Cubase users, and hide all aux output buses.

Wondering if the crash (“VST with no input buses”) is still there. Didn’t have time to test yet. Anyone?

We should extract these issues into separate forum threads if they remain. It’s difficult to monitor activity.

Yes. I am aware of this bug and have several ideas on how to fix it (see below). I also didn’t want to fix this on a tagged release.

I’d also like to discuss the nature of the bug with you guys before I push this on a develop branch. Maybe you have a better solution. It’s also good if people here get an understanding on how the VST2 wrapper works, as many of you have a lot of experience on how different DAWs handle VST2 plug-ins.

See here’s the general problem with a multi-aux synth plug-in in Cubase:

Most DAWs call getSpeakerArrangement before calling any processing methods. However, Cubase will only call this method after an ioChange callback AND only if the plug-in is a synth. As far as I can tell, Cubase 8 will always mute all outputs no matter which speaker arrangement JUCE returns from the getSpeakerArrangement callback. The only solution is to return false to indicate that you do not support the getSpeakerArrangement callback. This will work fine with the MultiSynth plug-in, for example.

I can see some logic in this: essentially getSpeakerArrangement was obviously not really meant for multi-bus plug-ins (in fact all of VST2 wasn’t really designed with multibus plug-ins in mind) and there is no authoritative documentation on what getSpeakerArrangement should do in a multibus setting. It should essentially return the speaker arrangement (or AudioChannelSet in JUCE speak) of the only existing bus - but the plug-in has several buses - so what to do?. It sort of makes sense to indicate that the plug-in does not support getSpeakerArrangement by returning false if the plug-in has more than one bus.

One solution could be that JUCE always returns false if the plug-in has sidechains or aux buses. However, this breaks the use of sidechain effect plug-ins in both Cubase and many other DAWs. The problem is that Cubase doesn’t really support multibus VST2 effect plug-ins at all. The workaround is to create a quadrophonic group fx track - for example - and use the the surround channels as sidechain channels.

When you load the effect plug-in onto the quadrophonic track, Cubase 8 will call setSpeakerArrangement/getSpeakerArrangement to change the layout of the plug-in to quadrophonic.

For getSpeakerArrangement to be consistent, JUCE will simply return a canonical layout with the same number of channels as the combined total of number of channels of all input/output buses together - if, and only if, the plug-in is a multi-bus plug-in. If it only has a main bus the setSpeakerArrangment/getSpeakerArrangement simply passes through the arrangement to the plug-in’s setPreferredBusArrangement call.

A related problem, is that a JUCE plug-in can support an infinite combination of layouts on it’s buses - this is really hard to get working correctly with VST2. Take, for example a NoiseGate plug-in. Let’s say that the developer of this plug-in wants the sidechain to always only be mono, but the main bus, could really be any layout - for the sake of argument, let’s say mono, stereo and LCR. I now need to find a unique mapping between the layout supplied by setSpeakerArrangment to the set of layouts on each bus.

For example, the combined total number of input channels for the above mentioned NoiseGate plug-in is anything between 2 and 4. If the plug-in gets a setSpeakerArrangement callback with a quadrophonic input layout, then I know that the only possible bus configuration of the plug-in must be “main-bus: LCR + sidechain: mono = 4 (quadraphonic)”. If the arrangement supplied to setSpeakerArrangement is LCR, then the only possible bus configuration of the plug-in is “main-bus: stereo + sidechain: mono = 3 (LCR)”.

As a result, JUCE’s VST-2 implementation requires that multibus plug-ins mustn’t allow two bus configurations that have the same combined total number of channels on all buses. In fact, JUCE will assert if this is the case.

I personally see three options to solve the Cubase 8/VST2/muting problem:

  1. Always return false if getSpeakerArrangement is called in a synth with more than one output bus.
    This would probably work for Cubase, but I feel this is a bit ugly and might not work in other DAWs.

  2. Return false from a getSpeakerArrangement call if setSpeakerArrangement was never called before AND the plug-in has aux and/or sidechains. If the DAW never called setSpeakerArrangement arrangement then the plug-in will have the maximum number of total channels enabled by default (this is a VST2 requirement). Therefore, the plug-in will have all aux outputs enabled. If setSpeakerArrangement is called then we have to return the same layout from a getSpeakerArrangement to be consistent.

  3. Reject both the getSpeakerArrangement/setSpeakerArrangement calls in all plug-ins which have aux and/or sidechains. This would get rid of a lot of complexity of the current VST2 wrapper. However, it would mean that VST2 plug-ins with aux and/or sidechains could not change their bus layouts, i.e the layouts on each bus would be static, as there is no way for the DAW to switch between layouts. With this, we would need to limit the above NoiseGate example to a single configuration: mono sidechain and stereo main bus, for example. Effect plug-ins with a single main in/out bus would not be affected and could have multiple layouts.

I prefer either 2 or 3 where 3 would probably remove much of the complex, potentially buggy code. However, it does come with a big down side: sidechain/aux plug-ins can only support a single bus configuration. Any ideas? Comments?

1 Like

Wow, thank you for sharing, looks complicated. Processing… :hourglass_flowing_sand:

yes, vst2 has no bus concept, and imho should always ‘fuse’ all the i/o pins and report to the daw the maximum number of channels, which will be always enabled. static i/o configuration is what we had before the multibus changes, so somehow it should be retained.
in a multibus plugin wrapped as vst2 we could try rejecting the set/getSpeakerArrangement calls at all while relying on the sum of all channels plus using getInput/getOutputProperties to configure the channel ‘partecipation’ to a particular speaker layout. tomorrow will give it a spin.

these are examples from vst2sdk documentation.

Example 1

bool MyPlug::getOutputProperties (VstInt32 index, VstPinProperties* properties)
{
    bool returnCode = false;
    if (index < kNumOutputs)
    {
        sprintf (properties->label, "My %1d Out", index + 1);
        properties->flags = kVstPinIsStereo | kVstPinIsActive;
        returnCode = true;
    }
    return (returnCode);
}

Example 2 : plug-in with 1 mono, 1 stereo and one 5.1 outputs (kNumOutputs = 9):

bool MyPlug::getOutputProperties (VstInt32 index, VstPinProperties* properties)
{
	bool returnCode = false;
	if (index >= 0 && index < kNumOutputs)
	{
		properties->flags = kVstPinIsActive;
		if (index == 0) // mono
		{
			strcpy (properties->label, "Mono Out");
			properties->arrangementType = kSpeakerArrMono;
		}
		else if (index == 1) // stereo (1 -> 2)
		{
			strcpy (properties->label, "Stereo Out");
			properties->flags |= kVstPinIsStereo;
			properties->arrangementType = kSpeakerArrStereo;
		}
		else if (index >= 3) // 5.1 (3 -> 8)
		{
			strcpy (properties->label, "5.1 Out");
			properties->flags |= kVstPinUseSpeaker;
			properties->arrangementType = kSpeakerArr51; // for old VST Host < 2.3, make 5.1 to stereo/mono/mono/stereo (L R C Lfe Ls Rs)
			if (index == 3 || index == 7)
				properties->flags |= kVstPinIsStereo;
			if (index == 5)
				strcpy (properties->label, "Center");	
			else if (index == 6)
				strcpy (properties->label, "Lfe");	
			else if (index == 7) // (7 -> 8)
				strcpy (properties->label, "Stereo Back");
		}
		returnCode = true;
	}
	return returnCode;
}

so definately it could be possible to reflect the AudioProcessor busArrangement to the static vst2 input output pins.

obviously these are only hints for the host how the consider the individual channels, if they are part of an arrangement or not, and cannot be switched at runtime.