Skip to main content

Overview

Surround plugins process multi-channel audio formats beyond stereo. Common formats include:
  • 5.1 - 6 channels (L, R, C, LFE, Ls, Rs)
  • 7.1 - 8 channels (L, R, C, LFE, Ls, Rs, Lrs, Rrs)
  • 7.1.2 - 10 channels (7.1 + height pair)
  • 7.1.4 - 12 channels (7.1 + 4 height channels) [Atmos]

IPlugSurroundEffect Template

The IPlugSurroundEffect example demonstrates:
  • Multiple channel configurations (1, 2, 6, 8, 10, 12 channels)
  • Platform-specific speaker arrangements
  • 12-channel metering
  • Custom bus type handling for VST3/AU/AAX

Channel Configuration

config.h
#define PLUG_CHANNEL_IO "1-1 2-2 6-6 8-8 10-10 12-12"

#define PLUG_TYPE 0  // Audio Effect

#define VST3_SUBCATEGORY "Fx|Spatial"
#define AAX_PLUG_CATEGORY_STR "SoundField"
Supported Configurations:
  • 1-1 = Mono
  • 2-2 = Stereo
  • 6-6 = 5.1 surround
  • 8-8 = 7.1 surround
  • 10-10 = 7.1.2 (Atmos bed)
  • 12-12 = 7.1.4 (Atmos full)

Basic Structure

Creating a Surround Plugin

1
Duplicate Template
2
cd iPlug2/Examples
python duplicate.py IPlugSurroundEffect MySurroundReverb MyCompany
cd MySurroundReverb
3
Configure Channels
4
Edit config.h to support desired formats:
5
// Support stereo and surround formats
#define PLUG_CHANNEL_IO "2-2 6-6 8-8"

// Or support everything including Atmos:
// #define PLUG_CHANNEL_IO "1-1 2-2 6-6 8-8 10-10 12-12"

#define VST3_SUBCATEGORY "Fx|Spatial"
#define AAX_PLUG_CATEGORY_STR "SoundField"
6
Implement Bus Type Mapping
7
Copy the GetAPIBusTypeForChannelIOConfig function from IPlugSurroundEffect to map channel counts to platform formats.
8
Process Channels
9
void MySurroundReverb::ProcessBlock(sample** inputs, sample** outputs, int nFrames)
{
  const int nChans = NOutChansConnected();
  
  // Detect current format
  switch (nChans) {
    case 1:  // Mono
      ProcessMono(inputs, outputs, nFrames);
      break;
      
    case 2:  // Stereo
      ProcessStereo(inputs, outputs, nFrames);
      break;
      
    case 6:  // 5.1
      Process51(inputs, outputs, nFrames);
      break;
      
    case 8:  // 7.1
      Process71(inputs, outputs, nFrames);
      break;
      
    case 10:  // 7.1.2
      Process712(inputs, outputs, nFrames);
      break;
      
    case 12:  // 7.1.4 (Atmos)
      Process714(inputs, outputs, nFrames);
      break;
  }
}
10
Add Channel Meters
11
mLayoutFunc = [&](IGraphics* pGraphics) {
  pGraphics->AttachPanelBackground(COLOR_GRAY);
  
  const IRECT bounds = pGraphics->GetBounds().GetPadded(-10);
  const IRECT meterArea = bounds.GetPadded(-50);
  
  const IVStyle meterStyle = DEFAULT_STYLE.WithColor(kFG, COLOR_WHITE.WithOpacity(0.3f));
  
  // Input meters (12 channels max)
  pGraphics->AttachControl(
    new IVPeakAvgMeterControl<12>(
      meterArea.FracRectVertical(0.5, true), 
      "Inputs",
      meterStyle,
      EDirection::Vertical,
      {"L", "R", "C", "LFE", "Ls", "Rs", "Lrs", "Rrs", "Ltf", "Rtf", "Ltr", "Rtr"}),
    kCtrlTagInputMeter);
  
  // Output meters
  pGraphics->AttachControl(
    new IVPeakAvgMeterControl<12>(
      meterArea.FracRectVertical(0.5, false), 
      "Outputs",
      meterStyle,
      EDirection::Vertical,
      {"L", "R", "C", "LFE", "Ls", "Rs", "Lrs", "Rrs", "Ltf", "Rtf", "Ltr", "Rtr"}),
    kCtrlTagOutputMeter);
};

Channel Order

Standard Speaker Layouts

Channel Index  |  Speaker Position
---------------|-------------------
     0         |  L   (Left)
     1         |  R   (Right)
     2         |  C   (Center)
     3         |  LFE (Low Frequency)
     4         |  Ls  (Left Surround)
     5         |  Rs  (Right Surround)
Platform Differences: Channel order can vary between plugin formats and hosts. Always test in your target environment.

Processing Examples

5.1 Surround Reverb

void Process51(sample** inputs, sample** outputs, int nFrames)
{
  // Extract channels
  sample* L = inputs[0];
  sample* R = inputs[1];
  sample* C = inputs[2];
  sample* LFE = inputs[3];
  sample* Ls = inputs[4];
  sample* Rs = inputs[5];
  
  for (int s = 0; s < nFrames; s++) {
    // Process front channels (L, R, C) with reverb
    const double frontMono = (L[s] + R[s] + C[s]) / 3.0;
    const double reverbOut = mReverb.Process(frontMono);
    
    // Apply reverb to output
    outputs[0][s] = L[s] + reverbOut * 0.3;   // L
    outputs[1][s] = R[s] + reverbOut * 0.3;   // R
    outputs[2][s] = C[s] + reverbOut * 0.2;   // C
    outputs[3][s] = LFE[s];                    // LFE (no reverb)
    outputs[4][s] = Ls[s] + reverbOut * 0.5;  // Ls (more reverb)
    outputs[5][s] = Rs[s] + reverbOut * 0.5;  // Rs (more reverb)
  }
}

Upmixing Stereo to 5.1

void UpmixStereoTo51(sample** stereoIn, sample** surroundOut, int nFrames)
{
  for (int s = 0; s < nFrames; s++) {
    const double left = stereoIn[0][s];
    const double right = stereoIn[1][s];
    const double mono = (left + right) * 0.5;
    
    surroundOut[0][s] = left;                    // L
    surroundOut[1][s] = right;                   // R
    surroundOut[2][s] = mono * 0.7;              // C (phantom center)
    surroundOut[3][s] = 0.0;                     // LFE
    surroundOut[4][s] = left * 0.3;              // Ls (ambience)
    surroundOut[5][s] = right * 0.3;             // Rs (ambience)
  }
}

Spatial Panner

void SpatialPanner::ProcessBlock(sample** inputs, sample** outputs, int nFrames)
{
  const double azimuth = GetParam(kAzimuth)->Value();    // 0-360 degrees
  const double elevation = GetParam(kElevation)->Value(); // -90 to +90
  
  const int nChans = NOutChansConnected();
  const double inputMono = inputs[0][0];  // Assume mono input
  
  // Calculate speaker gains based on position
  double gains[12] = {0};
  CalculateSpeakerGains(azimuth, elevation, nChans, gains);
  
  for (int s = 0; s < nFrames; s++) {
    const double input = inputs[0][s];
    
    for (int c = 0; c < nChans; c++) {
      outputs[c][s] = input * gains[c];
    }
  }
}

LFE Channel

LFE Handling:
  • LFE is typically at index 3
  • Low-pass filter below 120 Hz
  • Don’t apply reverb or spatial effects
  • Some hosts expect +10dB gain compensation
void ProcessLFE(sample** inputs, sample** outputs, int nFrames)
{
  sample* lfe = inputs[3];
  
  for (int s = 0; s < nFrames; s++) {
    // Low-pass filter
    double filtered = mLFEFilter.Process(lfe[s]);
    
    // Apply gain compensation (optional, host-dependent)
    outputs[3][s] = filtered * 1.0;  // No gain comp
    // outputs[3][s] = filtered * 3.16;  // +10dB gain comp
  }
}

Atmos Height Channels

For 7.1.2 and 7.1.4 formats:
void Process714(sample** inputs, sample** outputs, int nFrames)
{
  // Bed channels (7.1)
  for (int c = 0; c < 8; c++) {
    ProcessChannel(inputs[c], outputs[c], nFrames);
  }
  
  // Height channels (top speakers)
  sample* ltf = inputs[8];   // Left Top Front
  sample* rtf = inputs[9];   // Right Top Front
  sample* ltr = inputs[10];  // Left Top Rear
  sample* rtr = inputs[11];  // Right Top Rear
  
  for (int s = 0; s < nFrames; s++) {
    // Process height with different reverb
    const double heightMono = (ltf[s] + rtf[s] + ltr[s] + rtr[s]) / 4.0;
    const double heightReverb = mHeightReverb.Process(heightMono);
    
    outputs[8][s] = ltf[s] + heightReverb;
    outputs[9][s] = rtf[s] + heightReverb;
    outputs[10][s] = ltr[s] + heightReverb;
    outputs[11][s] = rtr[s] + heightReverb;
  }
}

Testing

Testing Surround:
  1. Use a DAW with surround support (Pro Tools, Nuendo, Logic)
  2. Create a surround session (5.1, 7.1, etc.)
  3. Insert plugin on surround track
  4. Verify correct channel mapping with tone generators
  5. Test channel meters show correct activity
  6. Check LFE handling (some hosts are picky)

Test Signals

// Generate test tones for each channel
void GenerateTestTones(sample** outputs, int nChans, int nFrames)
{
  const double frequencies[] = {
    220.0,  // L   (A3)
    246.9,  // R   (B3)
    261.6,  // C   (C4)
    110.0,  // LFE (A2)
    293.7,  // Ls  (D4)
    329.6,  // Rs  (E4)
    349.2,  // Lrs (F4)
    392.0,  // Rrs (G4)
  };
  
  for (int c = 0; c < nChans; c++) {
    for (int s = 0; s < nFrames; s++) {
      outputs[c][s] = std::sin(mPhase[c]);
      mPhase[c] += 2.0 * M_PI * frequencies[c] / GetSampleRate();
    }
  }
}

Next Steps

Channel Routing

Advanced channel configuration options

Spatial Audio

Ambisonics and 3D audio processing

Metering

Multi-channel meters and analysis

Testing

Validate surround plugins in DAWs

Build docs developers (and LLMs) love