Skip to main content

Core Components

iPlug2 is split into two main architectural layers that work together to create audio plugins:

IPlug

Audio processing, parameters, and plugin API abstraction

IGraphics

Cross-platform UI toolkit with multiple rendering backends

IPlug Core

The IPlug layer provides the audio plugin abstraction, handling the differences between plugin formats (VST3, AU, AAX, CLAP, WAM) so your code works everywhere.

Class Hierarchy

IPlugProcessor          (audio processing, I/O, timing)

IPluginBase            (parameters, presets, state)

IPlugAPIBase           (editor delegate, host communication)

YourPlugin             (your implementation)
The architecture separates concerns: IPlugProcessor handles real-time audio, IPluginBase manages state, and IPlugAPIBase handles host integration.

Key Responsibilities

Real-time audio processing (IPlugProcessor.h)
  • Audio I/O management and channel routing
  • MIDI processing (note on/off, CC, SysEx)
  • Sample rate and block size handling
  • Latency and tail size reporting
  • Transport and timing information
void ProcessBlock(sample** inputs, sample** outputs, int nFrames);
void ProcessMidiMsg(const IMidiMsg& msg);
void ProcessSysEx(const ISysEx& msg);
void OnReset(); // Called when sample rate changes
void OnActivate(bool active); // Called when track is enabled/disabled

IGraphics

IGraphics is an optional UI toolkit for building plugin interfaces. You can also use SwiftUI, WebView, or other UI frameworks.

Rendering Backends

NanoVG

Lightweight and fast
  • OpenGL-based vector rendering
  • No external dependencies needed
  • Default choice for most plugins
  • Good performance on older hardware

Skia

High quality rendering
  • Same engine as Chrome/Android
  • Better text and effects quality
  • Requires Skia dependency download
  • Used by Google, Flutter

IGraphics Architecture

IGraphics                    (base graphics context)

IGraphicsNanoVG/Skia        (drawing implementation)

IGraphicsMac/Win/Linux      (platform-specific event handling)
IGraphics is only instantiated when the plugin UI is opened by the host. Audio processing continues independently even without a visible UI.

UI Construction Pattern

IGraphics UIs are typically built in a lambda function:
mMakeGraphicsFunc = [&]() {
  return MakeGraphics(*this, PLUG_WIDTH, PLUG_HEIGHT, PLUG_FPS, 
                      GetScaleForScreen(PLUG_WIDTH, PLUG_HEIGHT));
};

mLayoutFunc = [&](IGraphics* pGraphics) {
  pGraphics->AttachPanelBackground(COLOR_GRAY);
  pGraphics->LoadFont("Roboto-Regular", ROBOTO_FN);
  
  // Controls automatically link to parameters by index
  pGraphics->AttachControl(new IVKnobControl(bounds, kGain));
};

Plugin Lifecycle

Understanding the plugin lifecycle is crucial for proper initialization and cleanup.
1

Construction

Your plugin constructor is called when the host loads the plugin.
IPlugEffect::IPlugEffect(const InstanceInfo& info)
: Plugin(info, MakeConfig(kNumParams, kNumPresets))
{
  // Initialize parameters
  GetParam(kGain)->InitDouble("Gain", 0., 0., 100., 0.01, "%");
  
  // Setup graphics (if IPLUG_EDITOR is defined)
  mMakeGraphicsFunc = [&]() { /* ... */ };
}
Don’t allocate large buffers in the constructor. Wait for OnReset() when you know the sample rate.
2

OnReset()

Called when:
  • Plugin is first activated
  • Sample rate changes
  • I/O configuration changes
void OnReset() override {
  // Allocate DSP buffers based on GetSampleRate()
  mDelay.SetDelayTime(GetSampleRate() * 0.5); // 500ms
}
3

OnActivate()

Called when the plugin is enabled/disabled on a track.
void OnActivate(bool active) override {
  if (active) {
    // Clear buffers, reset state
    mPhase = 0.0;
  }
}
Not all hosts support this reliably. Use OnReset() for critical initialization.
4

ProcessBlock()

Called repeatedly on the audio thread for real-time processing.
void ProcessBlock(sample** inputs, sample** outputs, int nFrames) {
  const double gain = GetParam(kGain)->Value() / 100.;
  const int nChans = NOutChansConnected();
  
  for (int s = 0; s < nFrames; s++) {
    for (int c = 0; c < nChans; c++) {
      outputs[c][s] = inputs[c][s] * gain;
    }
  }
}
5

Destruction

Your plugin destructor cleans up resources when the host unloads the plugin.
~IPlugEffect() {
  // Cleanup happens automatically for smart pointers
  // Manual cleanup if needed
}

Threading Model

iPlug2 uses a clear threading model to prevent audio dropouts and race conditions.

Thread Types

High priority real-time threadMethods called on audio thread:
  • ProcessBlock()
  • ProcessMidiMsg()
  • ProcessSysEx()
Real-time safety rules:
NEVER do these on the audio thread:
  • Memory allocation/deallocation
  • File I/O or network operations
  • Locking mutexes (use lock-free queues)
  • System calls that can block
  • TRACE or logging in release builds
void ProcessBlock(sample** inputs, sample** outputs, int nFrames) {
  // ✅ Good: direct parameter access (atomic)
  double gain = GetParam(kGain)->Value();
  
  // ❌ Bad: allocation
  // std::vector<double> buffer(nFrames); // NO!
  
  // ❌ Bad: file I/O
  // fwrite(outputs[0], sizeof(sample), nFrames, file); // NO!
  
  // ❌ Bad: mutex lock
  // mMutex.lock(); // NO!
}

Communication Patterns

// UI → Audio Thread (via parameters)
GetParam(kGain)->Set(newValue);  // Atomic, thread-safe

// Audio Thread → UI (via ISender)
mSender.PushData({kTagScope}, {buffer, nFrames});  // Lock-free queue

// Host → Plugin (parameter automation)
// Handled automatically by API layer

// Plugin → Host (MIDI output)
SendMidiMsg(msg);  // Called from ProcessBlock or UI

API-Specific Implementations

Each plugin format has its own API class that inherits from the base classes:
class IPlugVST3 : public IPlugAPIBase,
                  public IPlugVST3ProcessorBase,
                  public IPlugVST3ControllerBase,
                  public Steinberg::Vst::SingleComponentEffect
You never directly interact with these API classes in your plugin code. The abstraction handles format-specific details automatically.

Best Practices

Separation of Concerns

Keep audio processing in ProcessBlock(), UI in layout functions, and state management in parameters.

Real-time Safety

Never allocate, lock, or do I/O in ProcessBlock(). Pre-allocate buffers in OnReset().

Parameter Design

Use non-normalized values (e.g., 0-100 for gain) - they’re easier to work with in DSP code.

Smart Initialization

Initialize UI lazily (in lambda), allocate DSP buffers in OnReset() when sample rate is known.

Next Steps

Plugin Formats

Learn about VST3, AU, AAX, CLAP, and WAM specifics

Parameters

Master parameter types, automation, and groups

Audio Processing

Deep dive into ProcessBlock and real-time audio

Project Structure

Understand config.h, Plugin.h, and Plugin.cpp