(DEPRECATED) The ultimate JUCE 4.1 MultiBus Guide


#1

!!! THIS GUIDE IS DEPRECATED. PLEASE SEE THE NEW EXPERIMENTAL MULTIBUS API !!!

 
 

DISCLAIMER: If your plug-in does not require sidechain inputs or aux outputs or does not depend on the exact layout of the audio channels (i.e. your plug-in does not care if six channel audio is 5.1 surround, 6.0 surround or hexagonal audio) then it is easiest to use the PlugIn Channel Configuration field in the Introjucer. The field takes a comma-separated set list in the form {numIns, numOuts} and each pair indicates a valid plug-in configuration. For example{1, 1}, {2, 2} means that the plugin can be used either with 1 input and 1 output, or with 2 inputs and 2 outputs.

If you are using this Introjucer field then your plug-in should function as in previous versions of JUCE (if not, please, report this bug in the forums) and you do not need to read this guide. This will be the case for the majority of plug-in developers.

 

Introduction

The new multibus API of JUCE 4.1 offers a mechanism to create audio plug-ins which require multiple audio buses (such as sidechain inputs or multiple aux outputs) and/or require audio channel layout information (such as spatializer plug-ins etc).

An audio “bus” is an audio input or output of a plug-in which itself may consist of several audio channels (for example: left and right channels of a stereo bus). The main input/output of an effect plug-in is referred to as the main bus and denoted with an index of zero in JUCE. Below are three examples of plug-ins and their buses. The buses (arrows) in the image below may have different channel layouts (i.e. they may be mono, stereo, 7.1 surround etc.).

The current channel layouts of the buses is reflected in the AudioProcessor’s busArrangement member variable:

struct AudioBusArrangement
{
  Array<AuioProcessorBus> inputBuses, outputBuses;
};

AudioBusArrangement busArrangement;

which contains two arrays: busArrangement.inputBuses and busArrangement.outputBuses. Each element of the array is of type AudioProcessorBus and represents one of the buses. The first element of every array is the main bus (unless, of course, your plug-in does not have any inputs/outputs such as MIDI effect plug-ins or a synth with no inputs). The remaining elements are sidechains (input) or aux buses (output). Each bus has a name and a current channel layout channels:

struct AudioProcessorBus
{
   String name;
   AudioChannelSet channels; // <- current layout
};

For example, the value of the busArrangement member variable of a simple effect plug-in loaded on a stereo track in a DAW would have the following value:

busArrangement = {
  inputBuses = {{ “Input”, AudioChannelSet::stereo() }},
  outputBuses = {{ “Output”, AudioChannelSet::stereo() }}
};

A noise gate with a mono sidechain input loaded onto a quadraphonic surround track would have the following value:

busArrangement = {
  inputBuses =
  {
	  { “Input”,     AudioChannelSet::quadraphonic() },
	  { “Sidechain”, AudioChannelSet::mono() }
  },
  outputBuses = {{ “Output”, AudioChannelSet::quadraphonic() }}
};

Adding Sidechains and AUX buses to your plug-in

You add buses to the busArrangement.inputBuses and busArrangement.outputBuses array in the constructor of your AudioProcessor. In fact, **this is the only place **where you are allowed to add or remove any of the plug-ins buses. By default, JUCE will already add the main bus to your plug-in in the AudioProcessor’s base class constructor, so you only need to add any sidechain and aux channels (JUCE will not add a main input bus for synth plug-ins). For example, the noise gate plug-in has the following constructor:

class NoiseGate  : public AudioProcessor
{
public:
   NoiseGate()
   {
	   // add single side-chain bus
	   busArrangement.inputBuses.add (AudioProcessorBus ("Sidechain", AudioChannelSet::mono()));
   }
   .
   .
   .
};

The multi-out synth plug-in could have the following constructor:

class MultiOutSynth  : public AudioProcessor
{
public:
   MultiOutSynth()
   {
	   // Add additional aux output buses
	   for (int busNr = 1; busNr &lt; maxMidiChannel; ++busNr)
		   busArrangement.outputBuses.add (AudioProcessorBus (String ("Aux #") += String (busNr + 1), AudioChannelSet::stereo()));
	}

	.
	.
	.
};

The AudioChannelSet specified in both of these examples is the default channel layout for the plug-in (which the DAW may request to change: see below).

Note that some people on the forum are confused about the meaning of the busArrangement member variable. It is not an array of all possible layouts that your plug-in supports. It is a snapshot of the current layout of your plug-in and the constructor of your plug-in should initialize your plug-in to a default state.

In some cases you may also want to change the default layout (stereo) or name (“Input”/”Output”) for the main buses. To do this you can simply clear all the buses from the busArrangement.inputBuses and busArrangement.outputBuses arrays before adding the main buses again. For example a spatializer plug-in (a plug-in which places a mono signal into a 3D space of microphones: see image below) will probably want to have a surround channel layout as it’s default output:

class Spatializer  : public AudioProcessor
{
public:
	Spatializer()
	{
	   // clear the default buses added by the base class constructor
	   busArrangement.inputBuses. clear();
	   busArrangement.outputBuses.clear();

	   busArrangement.inputBuses. add (AudioProcessorBus ("Input", AudioChannelSet::mono()));
	   busArrangement.outputBuses.add (AudioProcessorBus ("Output",AudioChannelSet::createLCRS()));
	 }

  .
  .
  .
};

The number of buses and their names will never change throughout the lifetime of a plug-in. Number of buses in the previous sentence should not be confused with the number of channels (stereo, mono, quadraphonic, …) of each individual bus. The latter can be changed during the plug-in’s lifetime via a DAW callback (see sections below). In addition, it is also possible to disable/enable buses as long as they are not the main bus. You can also disable a bus by default. This is achieved by a special AudioChannelSet called disabled:

class MultiOutSynth  : public AudioProcessor
{
public:
	MultiOutSynth()
   {

	   // Add additional aux output buses
	   // but disable them by default

	   for (int busNr = 1; busNr &lt; maxMidiChannel; ++busNr)
		   busArrangement.outputBuses.add (AudioProcessorBus (String ("Aux #") += String (busNr + 1), AudioChannelSet::disabled()));
	}

	.
	.
	.
};

Responding to channel layout change requests

Sometimes the DAW will want to change the channel layout of a particular bus. Your AudioProcessor will be notified via the setPreferredBusArrangement callback. The default implementation of setPreferredBusArrangement will accept any layout the DAW requests and automatically update the busArrangement member variable to reflect the new layout.

However, you may not want to accept a requested layout. Let’s say that a plug-in only wants to accept mono and stereo channels. To do this you simply override the setPreferredBusArrangement callback:

bool MyPlugIn::setPreferredBusArrangement (bool isInputBus, int busIdx, const AudioChannelSet&amp; preferredLayout)
{
   const int numChannels = preferredLayout.size();

   // we only accept mono/stereo
   if (numChannels &gt; 2) return false;

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

It is evident that the above implementation will reject any layouts which have more than two channels by simply returning false. On the other hand, when accepting a layout, you must always call through to the base class’ setPreferredBusArrangement as only this function is allowed to modify the current busArrangement member variable. This is done in the last line of the above function. As a rule of thumb (you will find a summary of rules at the end of this post), your implementation of setPreferredBusArrangement should never return true directly - it must always call base class’ setPreferredBusArrangement.

Most plug-ins will not allow you to arbitrarily change the channel layouts of all buses independently. For example, a simple effect plug-in will usually require the same channel layout on the main input and output bus. A simple noise gate may only allow a mono side-chain input bus but any layout on the main buses as long as they are the same.

To accommodate such plug-ins, your implementation of setPreferredBusArrangement may change the layout of other buses in response to a layout change request by the DAW. For example, the implementation of setPreferredBusArrangement for a simple effect plug-in would make sure that the main output bus has the same channel layout as the main input bus and vice versa:

bool MyPlugIn::setPreferredBusArrangement (bool isInputBus, int busIdx, const AudioChannelSet&amp; preferredLayout)
{
   const int numChannels = preferredLayout.size();

   // we only accept mono/stereo
   if (numChannels &gt; 2) return false;


   // make sure that the bus in the opposite direction has the same layout
   if (! AudioProcessor::setPreferredBusArrangement (! isInputBus, busIdx, preferredLayout))
	   return false;

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

Again, you are not allowed to change the busArrangement variable directly, but rely on calling the base’ class version of setPreferredBusArrangement as is done above. As a rule of thumb, you must accept a layout change to a particular bus if it is somehow possible to accommodate that layout change no matter how much you need to change the layouts of other buses.

Let’s talk about a more complicated example: a plug-in which should accept the following layouts: {1,1},{2,2},{1,2} (see the disclaimer at the top of this post if you are not familiar with this notation). Note that the layout {2,1} is missing. A naive implementation would be the following (can you spot the error?);

bool setPreferredBusArrangement (bool isInputBus, int busIndex,
								const AudioChannelSet&amp; preferred) override
{
   const int numChannels = preferred.size();
   if (numChannels &gt; 2)
	   return false;

   // if the input is stereo then the output must be stereo
   if (isInputBus  &amp;&amp; numChannels == 2)
   {
		if (! AudioProcessor::setPreferredBusArrangement (! isInputBus, 0, preferred))
			 return false;
   }

   // when accepting the layout we must fall through to the base class!
   return AudioProcessor::setPreferredBusArrangement (isInputBus, busIndex, preferred);
}

The mistake is that the above function does not account for the situation when the DAW requests a layout change of the output from stereo to mono. In this case we must also ensure that the input bus is mono as the layout {2,1} is an invalid layout. The correct version of the above function is as follows:

bool setPreferredBusArrangement (bool isInputBus, int busIndex,
								const AudioChannelSet&amp; preferred) override
{
   const int numChannels = preferred.size();
   if (numChannels &gt; 2)
	   return false;


   if ( (isInputBus  &amp;&amp; numChannels == 2)  // if the input is stereo then the output must be stereo
  || ((! isInputBus) &amp;&amp; numChannels == 1)) // if the output is mono then the input must be mono
   {
		if (! AudioProcessor::setPreferredBusArrangement (! isInputBus, 0, preferred))
			 return false;
   }

   // when accepting the layout we must fall through to the base class!
   return AudioProcessor::setPreferredBusArrangement (isInputBus, busIndex, preferred);
}

Audio processing in multi bus plug-ins

The signature of the processBlock callback hasn’t changed in JUCE 4.1. In a multibus plug-in the buffer will simply be a multi-channel buffer with all the channels of every bus combined. The busArrangement member variable has the utility method getBusBuffer to help you access the individual buffers. For example, the NoiseGate example project (in JUCE/examples/PlugInSamples) has the following processBlock method:

void processBlock (AudioSampleBuffer&amp; buffer, MidiBuffer&amp;) override
{
   for (int i = getTotalNumInputChannels(); i &lt; getTotalNumOutputChannels(); ++i)
	   buffer.clear (i, 0, buffer.getNumSamples());


   AudioSampleBuffer mainInputOutput = busArrangement.getBusBuffer (buffer, true, 0);
   AudioSampleBuffer sideChainInput  = busArrangement.getBusBuffer (buffer, true, 1);


   float alphaCopy = *alpha;

   float thresholdCopy = *threshold;

   for (int j = 0; j &lt; buffer.getNumSamples(); ++j)
   {
	   float mixedSamples = 0.0f;
	   for (int i = 0; i &lt; sideChainInput.getNumChannels(); ++i)
		   mixedSamples += sideChainInput.getReadPointer (i) [j];

	   mixedSamples /= static_cast&lt;float&gt; (sideChainInput.getNumChannels());
	   lowPassCoeff = (alphaCopy * lowPassCoeff) + ((1.0f - alphaCopy) * mixedSamples);

	   if (lowPassCoeff &gt;= thresholdCopy)
		   sampleCountDown = (int) getSampleRate();

	   // very in-effective way of doing this
	   for (int i = 0; i &lt; mainInputOutput.getNumChannels(); ++i)
		   *mainInputOutput.getWritePointer (i, j) = sampleCountDown &gt; 0 ? *mainInputOutput.getReadPointer (i, j) : 0.0f;

	   if (sampleCountDown &gt; 0)
		   --sampleCountDown;
   }
}

Enabling/Disabling Buses

As mentioned above, some DAWs may occasionally request to disable/enable certain sidechain/aux buses (the main buses cannot be disabled). If the DAW wishes to disable a bus it will invoke the setPreferredBusArrangement callback with a special channel layout: AudioChannelSet::disabled(). It can enable a disabled bus by changing the layout of that bus to anything other than AudioChannelSet::disabled(). If you do not want to allow the DAW to disable a particular bus, you can just return false in the setPreferredBusArrangement callback:

bool MyPlugIn::setPreferredBusArrangement (bool isInputBus, int busIdx, const AudioChannelSet&amp; preferredLayout)
{
	// do not allow the host to disable any buses
	if (preferredLayout == AudioChannetSet::disabled())
	   return false;

	.
	.
	.
}

Surround Plug-Ins

In the examples above, we were only interested in the number of channels on a particular bus - and not  in the exact layout of the channels. This was done by extracting the number of channels from the AudioChannelSet:

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

	  .
	  .
	  .
}

Surround or spatializer plug-ins, however, are interested in the exact channel layout of a particular bus. For example, a surround plug-in may be compatible with 5.1 and 6.0 surround but not with hexagonal surround audio (all of which have 6 audio channels). In previous versions of JUCE it was not possible to distinguish between these layouts. This is now simple in JUCE 4.1:

bool setPreferredBusArrangement (bool isInputBus, int busIndex,
								const AudioChannelSet&amp; preferred) override
{
	if (preferred == AudioChannelSet::create5point1()
	 || preferred == AUdioChannelSet::create6point0())
	{
		// accept the layout change
		return AudioProcessor::setPreferredBusArrangement (isInputBus, busIndex, preferred);
	 }

	 return false;
}

RULES

  1. You may only add/remove/modify buses directly in the constructor of your AudioProcessor or indirectly by calling through to the base class’ version of setPreferredBusArrangment (see rule #2)
  2. You may only call the base class’ version of setPreferredBusArrangment in your plug-in classes’ implementation of setPreferredBusArrangment
  3. There are **only two ways **to exit the setPreferredBusArrangment callback: either by returning false, or by calling through to the base class: return AudioProcessor::setPreferredBusArrangment (isInputBus, busIdx, preferredLayout);
  4. If there is a way for your plug-in to accept a requested channel layout, then you must accept this request even if it involves changing the channel layout of other buses. This must always be done indirectly by calling the base class version of setPreferredBusArrangment. (see rule #1)
  5. You may never disable the main input or output buses
  6. Your setPreferredBusArrangment should not do any heavy processing or memory allocations. setPreferredBusArrangment is typically invoked hundreds of times during plug-in initialization so that JUCE can deduce the layout structure of your plug-in. Do any allocations or pre-calculations in the prepareToPlay callback instead.

Gotchas

  1. You must save your project using the newest Introjucer or Projucer for multibus to work properly. People often update their JUCE repository but forget to re-build the Introjucer resulting in undefined behaviour with the multibus code. Don’t forget to re-save your introjucer project file after re-building the Introjucer!
  2. Update to the latest git tip of JUCE. We are constantly fixing bugs while we test the various plug-in layouts, DAWs and plug-in format combinations
  3. Some DAWs require you to **re-scan plug-ins **if you change any buses or the layouts you accept. In particular, Logic Pro requires you to re-scan the plug-ins in the Plug-In Manager. If you forget to do this then you will have undefined behaviour.
  4. If you are overriding the setPreferredBusArrangment callback or are adding any extra buses to your plug-in, then the “Plug-In Channel Configuration” field in the Introjucer (see disclaimer at the top of this post) must be empty
  5. See known limitations below

Known Limitations

Logic Pro

  1. Most DAWs only show a subset of possible plug-in configurations. For example, Logic Pro will typically only show the following configurations on a plug-in which supports 16 output buses (of which the last 15 can be disabled). This is not a JUCE limitation: 
  2. There is a bug in Logic Pro which will result in the audio of first aux output channel of the plug-in to be incorrectly routed to the second aux channel (the first aux channel remains silent). This can be reproduced with Apple’s own plug-ins (for example the AUSampler). A bug report has been filed. This bug seems to be fixed in Logic 10.2.1 ​
  3. WARNING: If you select “None” in a side-chain AU plug-in (see image below) in Logic then this can randomly result in very loud feedback. This bug is now fixed on the latest JUCE tip. Please update! 

AULab

  1. JUCE will automatically detect if your plug-in is interested in the exact channel layout information (spatializer or surround plug-ins) or if your plug-in only takes the number of channels of a layout into account (most plug-ins). If the former is the case, then JUCE enables certain AudioUnit callbacks related to the channel layout (GetAudioChannelLayout, GetChannelLayoutTags, etc.). These callbacks seem to crash AULab when loading a project containing your plug-in. We strongly believe this is an AULab bug.

VST 2.x

We strongly advise against the use of VST 2.x for any Plug-Ins requiring sidechain or aux outputs. Please use VST 3 instead. Note that Plug-Ins without sidechains or aux outputs should work fine. If you still choose to use VST 2.x for multibus plug-ins then there are the following limitations:

  1. The channel layout of any sidechains or aux channels needs to be either fixed (i.e. always mono, stereo, etc. regardless of the layout on the main buses) or always identical to the channel layout of the main bus in the same direction. VST2.x does not support any other use cases which is supported by all major DAWs
  2. Some DAWs don’t like it if there are more total channels on the input side than on the output side. This can happen, for example, with sidechain plug-ins. To avoid issues, it is recommended to pass-through all the sidechain inputs to sidechain outputs. This way the total channel count on input and output will be the same.

Cubase

  1. Routing sidechain VST2.x plug-ins is quite complicated in Cubase. I followed this guide.

Ableton Live

  1. Routing sidechain VST2.x plug-ins is quite complicated in Ableton. I followed this guide.

ProTools AAX

  1. AAX only supports one mono sidechain input. This is an AAX limitation.
  2. If your plug-in has more than one sidechain input, then it must be possible to disable the other sidechain inputs
  3. There can only be a single aux output channel layout for every channel layout pair on the main buses. For example, you cannot have an AAX plug-in which can change the format on an aux bus without also changing the format of either the input or the output (or both) on the main bus.

FAQ

Why didn’t JUCE choose to use a channel configuration string approach as was used in previous versions of JUCE?
A string based approach cannot cover the many use cases of multi-bus plug-ins. For example, there is no good way to distinguish between different surround channel layouts which have the same number of channels. Furthermore, some Plug-Ins with a large number of buses (such as synths with 16 output buses) can have a near infinite combination of channel layouts which cannot be neatly expressed in a string.

AudioUnits require the plug-in to report all the layouts that it supports via AudioChannelLayout property. How does JUCE know this?
JUCE queries your plug-in by calling setPreferredBusArrengement several hundred times to understand what the logic behind the supported layouts of your plug-in are. JUCE does a best effort to detect this and we are confident that our JUCE code can detect all reasonable use cases.


How to add 15 channel to Audio plug in
Status of side chain support
Simple channels for simple synth
#2

wow thanks!


#3

Hi,

what I am really missing in the AudioChannelSet class is the actual speaker arrangement.

Avid AAX for instance uses the following speaker arrangement for AAX_eStemFormat_5_1: "L C R Ls Rs LFE".

As far as I can see this information is simply lost as AudioChannelSet only retains which channels are used but not how they are arranged. So this will be mapped to "L R C LFE Ls Rs".

 

Am I missing something or is this information really lost?

With the old Juce versions I could access this information via getInputSpeakerArrangement()...

 

 


#4

hey, you've got some html in your example code in the Introduction section.

 


#5

Thanks for spotting.


#6

Thank you (and others) for pointing this out. We are currently discussing how to best fix this issue.


#7

I thought this information would be in the AudioChannelSet::getAudioTypes() array... (as soon as the actual stems are mapped...)

That's even nicer than the string in getInputSpeakerArrangement(), which needs parsing from my side (and who says that the format in that string never changes...?)


#8

Hi Fabian,

thanks so much for implementing. This is great news. This kind of explanations should be on a seperate part of the website, so it's easier to find. What do you think?


#9

Why not putting it here: http://www.juce.com/doc/structAudioProcessor_1_1AudioBusArrangement#details ?

And add a link from AudioProcessor details.

I also would like to have a "reader" of the concepts, but if this place is going through ROLIs webteam, we might wait for the information forever, as the webteam seems to be a little overstrained, as far as I can see from outside... ;-)


#10

Yes. This will migrated to a seperate tutorial soon.


#11

This is now fixed on the latest tip. JUCE will now re-map the channels in a way that the always have the same order in JUCE code. The order will now always be the same as the channel order in the AudioChannelSet::getAudioTypes() array. So you can simply iterate through this array.


#12

 

This is broken with the latest tip and VST2.x

I created a blank new project (with the latest Introjucer). I set both the basic configurations as your example screenshot ({1, 1}, {2, 2}) and added a debug in the processBlock that prints the value of totalNumInputChannels and totalNumOutputChannels. Everything is ok with AU, but with VST2 I get mono where it should be stereo. Tried in Ableton Live 9.


#13

Sorry about the breaking change. This is fixed now on the latest tip.


#14

Excellent tutorial!


#15

I found that by not calling the base class in setPreferredBusArrangement as shown in this example, I was able to get Logic to properly sort out the busses I was specifying in the constructor.  When I did call the base class I was getting all sorts of bus configurations including mono (which I didn't want) and also the plugin was failing to launch in logic.

from your example, the last line where the base class is called, I removed this from my code and just returned true, this fixed my problem with Logic not getting the correct layouts.  I don't know why and haven't dug deeper, but just reporting how I got Logic to properly deal with my plugin.  this also works in VST

Here's my example

.....

//in the constructor I want 7 additional stereo aux outputs, there are no inputs in my case, I did this:

    busArrangement.outputBuses.add(AudioProcessorBus( String("Channel 3/4"), AudioChannelSet::stereo()));

    busArrangement.outputBuses.add(AudioProcessorBus( String("Channel 5/6"), AudioChannelSet::stereo()));

    busArrangement.outputBuses.add(AudioProcessorBus( String("Channel 7/8"), AudioChannelSet::stereo()));

    busArrangement.outputBuses.add(AudioProcessorBus( String("Channel 9/10"), AudioChannelSet::stereo()));

    busArrangement.outputBuses.add(AudioProcessorBus( String("Channel 11/12"), AudioChannelSet::stereo()));

    busArrangement.outputBuses.add(AudioProcessorBus( String("Channel 13/14"), AudioChannelSet::stereo()));

    busArrangement.outputBuses.add(AudioProcessorBus( String("Channel 15/16"), AudioChannelSet::stereo()));

....

 

Here's my setPreferredBusArrangement

 

 

bool LiquidRhythmAudioProcessor::setPreferredBusArrangement (bool isInputBus, int busIdx, const AudioChannelSet& preferredLayout)

{

    const int numChannels = preferredLayout.size();

    if(isInputBus || numChannels != 2)

        return false;

 

    return true;

}

 

 


 

 


#16

I can't seem to reproduce this: I get the same configurations in logic with or without calling through to the base class. I simply copied your code and put it into the MultiOutSynth demo (I also changed maxMidiChannel to 8 to avoid a crash). You really need to call the base class version of setPreferredBusArrangement as your channel counts (as returned by getTotalNumInputChannels()/getTotalNumOutputChannels()) will be wrong if you do not call the base class.


#17

"Look into the source Luke" ;-)

In the base class bool AudioProcessor::setPreferredBusArrangement (bool isInput, int busIndex, const AudioChannelSet& preferredSet) you see, what's NOT happening, if you don't call the base class to accept:

bus.channels = preferredSet;

if (oldNumInputs != getTotalNumInputChannels() || oldNumOutputs != getTotalNumOutputChannels())
{
    updateSpeakerFormatStrings();
    numChannelsChanged();
}

You see, the channels used in the AudioProcessor::AudioBusArrangement are not set and the speaker format strings are not called as well as some other update mechanisms behind numChannelsIsChanged.

Probably the reason why it worked in your case was, that the constructor is initialized with stereo bus, which is not surprising. But if you try to set another preferred channel set, you end up still using what the constructor set (and if you're lucky it never changes, because nobody said, it is supposed to be stereo, it's just luck). So better stick to Fabians guide and set the preferred set by using the AudioProcessor::setPreferredBusArrangement

What happened in the first place, what made you change it, I can't tell, I used similar code and had no problems, especially in AAX.


#18

Okay – this doesn’t seem to be working with my plugin which uses an AudioProcessorGraph.

Was this tested using a Synth plugin with an AudioProcessorGraph?

I’ll modify the Multiout example to demonstrate the problem in the next day or so… but it would help to know if this should work.

Thanks,

Rail


AudioProcessorGraph with VST plugins - issue getting started
AudioGraphIOProcessor with the new bus system
#19

Okay – I modified the MultiOutSynth to use an AudioProcessorGraph which shows the issue…

I’m not using the AudioProcessorGraph in the processBlock in this example… the issues lie outside there.

Rail


Very Basic Usage of AudioProcessorGraph
#20

Thank you, I'll have a look!