Overview
Sidechain inputs allow an audio effect to respond to an external audio source. Common uses include:- Compressors - Compress based on external signal (ducking)
- Gates - Open/close gate based on trigger signal
- Vocoders - Modulate carrier with external modulator
- Envelope Followers - Extract amplitude from external source
IPlugSideChain Template
TheIPlugSideChain example demonstrates:
- Multiple input buses (main + sidechain)
- Per-channel connection detection
- Visual feedback for connected channels
- Workarounds for Logic/Garageband sidechain bugs
Channel Configuration
config.h
Channel IO Format:
InputBuses.InputChannels-OutputChannels"2-2"= 2 channels in, 2 channels out (single bus)"1.2-2"= 1 main bus (1 channel) + sidechain bus (2 channels) → 2 out"2.2-2"= 2 main channels + 2 sidechain channels → 2 out
Basic Structure
- Header
- Initialization
- Bus Names
- Processing
IPlugSideChain.h
GetBusName()- Name input buses (Main/SideChain)IsChannelConnected()- Detect active channelsIPeakAvgSender- Meter for all channels- Track connection state for UI feedback
Creating a Sidechain Effect
cd iPlug2/Examples
python duplicate.py IPlugSideChain MySidechainCompressor MyCompany
cd MySidechainCompressor
// Stereo main + stereo sidechain → stereo out
#define PLUG_CHANNEL_IO "\
2-2 \
2.2-2"
// Or more flexible configurations:
// #define PLUG_CHANNEL_IO "\
// 1-1 \
// 1.1-1 \
// 2-2 \
// 2.2-2"
MySidechainCompressor::MySidechainCompressor(const InstanceInfo& info)
: Plugin(info, MakeConfig(kNumParams, kNumPresets))
{
#if IPLUG_DSP
SetChannelLabel(ERoute::kInput, 0, "Input L");
SetChannelLabel(ERoute::kInput, 1, "Input R");
SetChannelLabel(ERoute::kInput, 2, "SC L");
SetChannelLabel(ERoute::kInput, 3, "SC R");
SetChannelLabel(ERoute::kOutput, 0, "Output L");
SetChannelLabel(ERoute::kOutput, 1, "Output R");
#endif
}
void MySidechainCompressor::ProcessBlock(sample** inputs, sample** outputs, int nFrames)
{
const double threshold = GetParam(kThreshold)->Value();
const double ratio = GetParam(kRatio)->Value();
const double attack = GetParam(kAttack)->Value() * 0.001; // ms to seconds
const double release = GetParam(kRelease)->Value() * 0.001;
const int nChans = NOutChansConnected();
const bool scConnected = IsChannelConnected(ERoute::kInput, 2);
for (int s = 0; s < nFrames; s++) {
// Get sidechain level (or main input if SC not connected)
double scLevel = 0.0;
if (scConnected) {
// Use sidechain input (channels 2-3)
scLevel = std::max(std::abs(inputs[2][s]), std::abs(inputs[3][s]));
} else {
// Use main input (channels 0-1)
scLevel = std::max(std::abs(inputs[0][s]), std::abs(inputs[1][s]));
}
// Convert to dB
const double scLevelDB = 20.0 * std::log10(scLevel + 1e-10);
// Calculate gain reduction
double gainReduction = 0.0;
if (scLevelDB > threshold) {
const double over = scLevelDB - threshold;
gainReduction = over * (1.0 - 1.0 / ratio);
}
// Apply envelope follower
const double coeff = (gainReduction > mEnvelope) ? attack : release;
mEnvelope += (gainReduction - mEnvelope) * coeff;
// Convert to linear gain
const double gain = std::pow(10.0, -mEnvelope / 20.0);
// Apply compression to main inputs
for (int c = 0; c < nChans; c++) {
outputs[c][s] = inputs[c][s] * gain;
}
}
}
mLayoutFunc = [&](IGraphics* pGraphics) {
pGraphics->AttachPanelBackground(COLOR_GRAY);
const IRECT bounds = pGraphics->GetBounds().GetPadded(-10);
// Input meters (4 channels: Main L/R + SC L/R)
pGraphics->AttachControl(
new IVPeakAvgMeterControl<4>(
bounds.GetFromLeft(200),
"Inputs",
DEFAULT_STYLE,
EDirection::Horizontal,
{"Main L", "Main R", "SC L", "SC R"}),
kCtrlTagInputMeter);
// Output meters (2 channels)
pGraphics->AttachControl(
new IVPeakAvgMeterControl<2>(
bounds.GetFromRight(100),
"Outputs",
DEFAULT_STYLE,
EDirection::Vertical,
{"L", "R"}),
kCtrlTagOutputMeter);
// Parameter controls in center
const IRECT controlArea = bounds.GetReducedFromLeft(220).GetReducedFromRight(120);
// Add knobs/sliders here...
};
Channel Detection
Checking Connections
UI Feedback
Host-Specific Workarounds
Logic/Garageband Sidechain Bug
IPlugSideChain.cpp (lines 124-136)
Common Sidechain Effects
Sidechain Compressor (Ducking)
Sidechain Gate
Envelope Follower
Bus Configuration
Multiple Buses
iPlug2 supports multiple input/output buses:Channel Indexing
Channels are indexed sequentially across buses:Testing
Testing Sidechain:
- Load plugin in DAW
- Route audio to main input
- Route separate audio to sidechain input
- Verify meters show correct connections
- Test with no sidechain routed (should fall back gracefully)
- Test in Logic (known bugs with sidechain routing)
Next Steps
Audio Effect
Core audio processing concepts
Channel Routing
Advanced channel configuration
Metering
Visual level meters and analysis
DSP Utilities
Envelope followers and dynamics