Skip to main content
Multi-bus support allows plugins to handle multiple audio streams independently - essential for multi-output instruments, sidechain compression, and complex routing scenarios.

Overview

By default, plugins have a single stereo input and output bus. Multi-bus configurations enable:
  • Multi-output instruments - Separate drum kit pieces to individual mixer tracks
  • Sidechain inputs - Key a compressor from an external source
  • Stem outputs - Export multiple mix stems from a single plugin
  • Multi-channel processors - Process surround sound or immersive audio

Configuration

Define bus layout in config.h using the channel I/O string:
config.h - Multi-Output Example
// Format: "inputs outputs"
#define PLUG_CHANNEL_IO "0-2 2-2 2-2 2-2" // Instrument with 4 stereo outputs

Format Breakdown

"2-2" // 2 in, 2 out (standard stereo effect)

IPlugDrumSynth Example

The IPlugDrumSynth example demonstrates a 4-drum instrument with individual outputs:
Examples/IPlugDrumSynth/config.h
#if !defined PLUG_CHANNEL_IO
  // Multi-out when param is enabled: 4 stereo buses
  #define PLUG_CHANNEL_IO "0-2 2-2 2-2 2-2"
#endif

Naming Buses

Override GetBusName() to provide descriptive names:
IPlugDrumSynth.cpp:105
void IPlugDrumSynth::GetBusName(ERoute direction, int busIdx, int nBuses, 
                                WDL_String& str) const
{
  if (direction == ERoute::kOutput)
  {
    if(busIdx == 0)
      str.Set("Drum1");
    else if(busIdx == 1)
      str.Set("Drum2");
    else if(busIdx == 2)
      str.Set("Drum3");
    else if(busIdx == 3)
      str.Set("Drum4");
    
    return;
  }
  
  str.Set("");
}

Routing Audio

The DSP code routes each drum to its bus:
Simplified from IPlugDrumSynth_DSP.h
void ProcessBlock(sample** outputs, int nFrames)
{
  const int nOuts = mMultiOut ? kNumDrums * 2 : 2;
  
  for(int i = 0; i < kNumDrums; i++)
  {
    Voice& voice = mVoices[i];
    
    for(int s = 0; s < nFrames; s++)
    {
      sample drumSample = voice.Process();
      
      if(mMultiOut)
      {
        // Route to individual outputs
        // Bus i: outputs[i*2] (left), outputs[i*2+1] (right)
        outputs[i * 2][s] += drumSample;
        outputs[i * 2 + 1][s] += drumSample;
      }
      else
      {
        // Mix all drums to main output
        outputs[0][s] += drumSample;
        outputs[1][s] += drumSample;
      }
    }
  }
}

Querying Connected Channels

Use these methods to check active channels at runtime:
Channel Query Methods
void ProcessBlock(sample** inputs, sample** outputs, int nFrames) override
{
  const int nInChans = NInChansConnected();   // Actually connected inputs
  const int nOutChans = NOutChansConnected(); // Actually connected outputs
  
  // Process only connected channels
  for(int s = 0; s < nFrames; s++)
  {
    for(int c = 0; c < nOutChans; c++)
    {
      if(c < nInChans)
        outputs[c][s] = inputs[c][s];
      else
        outputs[c][s] = 0.0;
    }
  }
}
Hosts may not connect all available buses. Always check NInChansConnected() and NOutChansConnected() to avoid processing unused channels.

Sidechain Example

Typical sidechain compressor configuration:
Sidechain Compressor Setup
// config.h
#define PLUG_CHANNEL_IO "2-2 1-0" // Stereo + mono sidechain

// ProcessBlock
void ProcessBlock(sample** inputs, sample** outputs, int nFrames) override
{
  const int nChans = NInChansConnected();
  
  // Main input: inputs[0], inputs[1]
  // Sidechain: inputs[2] (if connected)
  
  for(int s = 0; s < nFrames; s++)
  {
    // Get sidechain signal (or use main if not connected)
    sample scInput = (nChans > 2) ? inputs[2][s] : inputs[0][s];
    
    // Calculate gain reduction from sidechain
    sample gainReduction = ComputeGainReduction(scInput);
    
    // Apply to main signal
    outputs[0][s] = inputs[0][s] * gainReduction;
    outputs[1][s] = inputs[1][s] * gainReduction;
  }
}

Bus Configuration Patterns

#define PLUG_CHANNEL_IO "2-2"
Standard stereo in/out

Channel Indexing

For multi-bus configurations, channels are indexed sequentially:
// Config: "0-2 2-2 2-2" (3 stereo buses)
// outputs[0] = Bus 0 Left
// outputs[1] = Bus 0 Right
// outputs[2] = Bus 1 Left
// outputs[3] = Bus 1 Right
// outputs[4] = Bus 2 Left
// outputs[5] = Bus 2 Right

Dynamic Bus Activation

Some hosts allow dynamic bus activation. Query this at runtime:
IPlugDrumSynth.cpp:126
void ProcessBlock(sample** inputs, sample** outputs, int nFrames) override
{
  const int nOutChans = NOutChansConnected();
  
  // Only process buses the host has activated
  const int nBuses = (nOutChans + 1) / 2; // Each bus = 2 channels
  
  for(int bus = 0; bus < nBuses; bus++)
  {
    // Process this bus...
  }
}

Host Compatibility

Multi-bus support varies by host:
  • Full support: Reaper, Cubase, Studio One, Logic Pro
  • Limited: Some hosts only support single bus
  • AAX: Limited to specific I/O configurations
Test your plugin in target hosts to verify bus routing.

Best Practices

1

Validate Channel Counts

Always check NInChansConnected() and NOutChansConnected() - hosts may activate fewer buses than configured
2

Provide Fallback

Support both multi-out and summed output modes for maximum compatibility
3

Name Your Buses

Implement GetBusName() to help users identify buses in the host
4

Clear Unused Outputs

Zero out channels that aren’t being used to avoid noise

See Also

Changing PLUG_CHANNEL_IO may invalidate saved projects in hosts. Consider this a compile-time constant for released plugins.