(DEPRECATED) The ultimate JUCE 4.1 MultiBus Guide

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.

Thanks for your feedback kraken!

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

JUCE’s wrapper already uses getOutputProperties/getOutputProperties to report the bus layout to the DAW and it’s similar to the sample code you quote. I don’t think this is the probem (apart from maybe the naming of the buses - but that’s a different topic).

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.

There is another aspect of VST2 which I didn’t mention in my above post. Just to make things a bit more confusing :slight_smile: . The set/getSpeakerArrangement callbacks and the number of channels reported via the numInputs/numOutputs field in the AEffect struct are two different beasts. We always report the maximum number of channels in the AEffect struct.

static i/o configuration is what we had before the multibus changes, so somehow it should be retained.

I don’t think this is correct. Look at the pre multibus code here. We’ve always allowed changing the channel configurations and channel number in VST2 and I definitely think this is sensible - at least for non-multibus plug-ins.

could you explain a bit how setBusArrangementFromTotalChannelNum works ? those iterations with index resetting everytime is a bit criptic and obscure and can’t get a grip on it.

from what i’ve tested, cubase after an ioChanged will call:

  • getSpeakerArrangement: in this case juce returns the i/o specifying the max number of channels
  • getInput/OutputProperties: if we have busses specified as AudioChannelSet::stereo(), host will call this with indexes of the first channel only, not the other, so if i have a total of 4 stereo output busses will call getOutpuProperties with index 0, 2, 4, 6 but not for 1, 3, 5, 7
  • eventually it will call setSpeakerArrangement, but i’m not sure what is the cause of having it called or not

then the output mutes (the main bus is still reported as enabled by cubase itself): checking the processReplacing after that, the synth generates audio and it is copied correctly from the vst wrapper temp buffers to the host provided buffers, so the muting should be in the host side.

it’s not clean, but this fixes it for cubase making my synth work again and keeping all the output channels enabled and functioning (not suffering the mute problem):

bool getSpeakerArrangement (VstSpeakerArrangement** pluginInput, VstSpeakerArrangement** pluginOutput) override
{
    *pluginInput = 0;
    *pluginOutput = 0;

    if (! AudioEffectX::allocateArrangement (pluginInput, busUtils.findTotalNumChannels (true)))
        return false;

    if (! AudioEffectX::allocateArrangement (pluginOutput, busUtils.findTotalNumChannels (false)))
    {
        AudioEffectX::deallocateArrangement (pluginInput);
        *pluginInput = 0;
        return false;
    }

    if (pluginHasSidechainsOrAuxs())
    {
        if ((JucePlugin_IsSynth) != 0 && PluginHostType().isCubase7orLater())
            return false;

        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
    {
        SpeakerMappings::channelSetToVstArrangement (busUtils.getChannelSet (true,  0), **pluginInput);
        SpeakerMappings::channelSetToVstArrangement (busUtils.getChannelSet (false, 0), **pluginOutput);
    }

    return true;
}

obviously this needs more testing, but it’s a starting point. food for thoughts.

This is the function that finds a supported bus arrangement from the combined total number of channels on all buses. I mentioned this in my post above:

In debug mode I additionally check that the found bus arrangement is unique. The loop is not optimal performance wise (it’s essentially the knap sack problem) and I may come back to it when I have some time.

This is only true if setSpeakerArrangement hasn’t been called before. See for example my solution suggestion #2:

Sadly this is not true for every DAW. Some DAWs also check the odd numbers.

Your fix suggestion is not a bad idea, but not sure we really need the host check. Without it, it’s identical to my solution suggestion #1:

yeah i was speaking about cubase. but in the cubase case, it is displaying “Master L (Stereo)” as output bus name… so this has to be cured

why not applying this only for cubase if it is ugly and might not work in other daws ?

Hi Fabian,

Any update on when we should expect any fixes regarding this?

Thanks,

Rail

A question regarding disabled aux channels.

I have a synth with a master stereo bus and a stereo aux, which can be disabled (setPreferredBusArrangement accepts this configuration).

In a processBlock call, if this aux bus is AudioChannelSet::disabled() (because the host says so)… how can i check it ?

I’m doing this but it’s not working in Au3 standalone using an audio device with 2 channels only, i still get the aux bus reported enabled:

AudioSampleBuffer masterBuffer = busArrangement.getBusBuffer(output, false, 0);

float* masterLeft = masterBuffer.getWritePointer(0);
float* masterRight = masterBuffer.getWritePointer(1);
float* auxLeft = nullptr;
float* auxRight = nullptr;

if (getTotalNumOutputChannels() > 2
      && busArrangement.outputBuses.size() > 1
      && busArrangement.outputBuses.getReference(1).channels != AudioChannelSet::disabled())
{
    auxLeft = output.getWritePointer(busArrangement.getChannelIndexInProcessBlockBuffer(false, 1, 0));
    auxRight = output.getWritePointer(busArrangement.getChannelIndexInProcessBlockBuffer(false, 1, 1));
}

// at this point, auxLeft and auxRight are set with garbage

Is there any other thing i miss here ?

No I don’t think you are missing something. It’s probably a bug. I’ll have a look.

In any case, it’s probably better to simply check the number of channels of a particular bus buffer:

bool isDisabled = (busArrangement.getBusBuffer (output, false, 1).getNumChannels() == 0);

Obviously, you could also cache which busses are disabled/enabled from your prepareToPlay method.

I’m curious though, which DAW currently supports multiple buses with AUv3?

now i cannot reproduce the thing anymore. anyway caching this in prepareToPlay works (i have to say this busArrangement thing is not exactly in the usual juce style, too much verbose for such a functionality):

const int totalNumOutputChannels = getTotalNumOutputChannels();
enabledOutputA = totalNumOutputChannels > 2
    && busArrangement.outputBuses.getReference(1).channels.size() == 2;
//...

PS. the host i was using is the juce standalone wrapper, which disables explicitly all input/output busses.