AudioLab Plugin Architecture¶
Plugin registration, factory pattern, and VST3 wrapper infrastructure for AudioLab processors.
Overview¶
This module provides the infrastructure to: - Register plugins dynamically using factory pattern - Create processors by ID at runtime - Expose metadata without instantiation - Wrap AudioLab processors as VST3 plugins (skeleton for Phase 2)
Components¶
1. PluginDescriptor (plugin_descriptor.hpp)¶
Static metadata about a plugin, including: - Identification (ID, name, vendor, version, UID) - Capabilities (category, channels, MIDI, sidechain) - Parameters (count, IDs for preset compatibility) - Resources (factory function, icon, manual URL)
Features: - ✅ Validation (checks completeness) - ✅ JSON export (for plugin scanning) - ✅ Deterministic UID generation (hash from string ID) - ⏸️ JSON import (stub in Phase 1, full in Phase 2)
Usage:
PluginDescriptor desc{
.id = "audiolab.my_plugin",
.name = "My Plugin",
.vendor = "AudioLab",
.version = "1.0.0",
.category = PluginCategory::Effect,
.num_inputs = 2,
.num_outputs = 2,
.factory_function = &create_my_plugin
};
2. ProcessorFactory (processor_factory.hpp)¶
Registry for creating AudioProcessors dynamically.
Design Pattern: Meyers Singleton + Function Registry - Thread-safe initialization (C++11 magic statics) - O(1) lookup by plugin ID - Lock-free reads after static initialization
API:
// Register (called at static init time)
ProcessorFactory::register_processor(descriptor);
// Create instance
auto processor = ProcessorFactory::create("audiolab.my_plugin");
// Query
auto all = ProcessorFactory::all_descriptors();
const auto* desc = ProcessorFactory::find_descriptor("audiolab.my_plugin");
bool exists = ProcessorFactory::is_registered("audiolab.my_plugin");
Registration Macro:
// In plugin implementation file (.cpp)
PluginDescriptor my_plugin_desc = { /* ... */ };
AUDIOLAB_REGISTER_PLUGIN(my_plugin_desc);
3. VST3 Adapter (vst3/vst3_adapter.hpp)¶
⚠️ Phase 1: Skeleton Only
Documents the VST3 integration requirements without SDK dependency. All methods are stubbed with clear TODOs for Phase 2 implementation.
Design Pattern: Adapter - Wraps AudioLab AudioProcessor - Translates VST3 API ↔ AudioLab API - Maps parameters, buses, state
Phase 1 Status:
- ✅ Interface documented
- ✅ All VST3 methods identified
- ✅ Clear TODOs for implementation
- ❌ No VST3 SDK dependency yet
- ❌ Methods return kNotImplemented
Phase 2 Requirements:
- Link VST3 SDK (vcpkg: vst3sdk)
- Inherit from Steinberg::Vst::IComponent
- Inherit from Steinberg::Vst::IAudioProcessor
- Implement all stubbed methods
- Pass VST3 validator
4. VST3 Types (vst3/vst3_types.hpp)¶
Placeholder type definitions without VST3 SDK. These will be replaced by actual Steinberg types in Phase 2.
Defines: - Basic types (UID, ParamID, ParamValue, Result) - Bus types (Direction, Type, MediaType) - Processing types (ProcessSetup, ProcessData, AudioBusBuffers) - Interface documentation (IComponent, IAudioProcessor methods)
Examples¶
Complete Registration Example¶
See examples/register_plugin.cpp:
// 1. Define processor
class SimpleGain : public AudioProcessor {
// ... implementation ...
};
// 2. Factory function
std::unique_ptr<AudioProcessor> create_simple_gain() {
return std::make_unique<SimpleGain>();
}
// 3. Descriptor
PluginDescriptor simple_gain_desc{
.id = "audiolab.simple_gain",
.name = "Simple Gain",
.factory_function = &create_simple_gain,
// ... other fields ...
};
// 4. Register
AUDIOLAB_REGISTER_PLUGIN(simple_gain_desc);
// 5. In main(): create dynamically
int main() {
auto processor = ProcessorFactory::create("audiolab.simple_gain");
// Use processor...
}
Run example:
cmake --build build --target register_plugin_example
.\build\04_CORE\04_09_plugin_lifecycle\examples\Release\register_plugin_example.exe
Tests¶
Factory Tests (tests/test_factory.cpp)¶
- ✅ Register and create
- ✅ Query descriptors
- ✅ Unknown plugin returns nullptr
- ✅ Duplicate registration (last wins)
- ✅ Invalid descriptor not registered
Descriptor Tests (tests/test_descriptor.cpp)¶
- ✅ Validation (valid/invalid cases)
- ✅ JSON serialization roundtrip
- ✅ UID generation is deterministic
- ✅ UID uniqueness for different IDs
Run tests:
Future Roadmap¶
Phase 2: VST3 Implementation (2-3 weeks)¶
Goals: 1. Integrate VST3 SDK (vcpkg) 2. Implement VST3Adapter fully 3. Parameter automation mapping 4. State serialization (presets) 5. MIDI handling 6. Pass VST3 validator
Tasks: - Replace placeholder types with Steinberg types - Implement IComponent interface - Implement IAudioProcessor interface - Convert ProcessData ↔ AudioBuffer - Handle parameter queues (IParamValueQueue) - Serialize/deserialize state (IBStream) - Build as VST3 module (.vst3 bundle)
Phase 3: Multi-Format Support (3-4 weeks per format)¶
Formats: 1. Audio Unit (AU) for macOS 2. AAX for Pro Tools 3. CLAP (future open standard)
Architecture: - Single AudioProcessor implementation - Multiple format wrappers (VST3, AU, AAX) - Format-agnostic descriptor - Cross-format preset conversion
Phase 4: Plugin Scanning¶
Features: - Multi-threaded plugin scanner - JSON cache for fast loading - Blacklist for crashed plugins - Sandboxed validation
Architecture Decisions¶
Why Factory Pattern?¶
- Decouples creation from usage: Host doesn't need to know processor types
- Runtime registration: Plugins self-register at static init
- Type-safe: Returns
unique_ptr<AudioProcessor> - Extensible: New plugins don't modify host code
Why Meyers Singleton?¶
- Thread-safe: C++11 guarantees initialization
- No order issues: Static initialization order is safe
- Lazy initialization: Registry created on first access
- Lifetime management: Lives until program end
Why Skeleton Before SDK?¶
- Validates architecture before SDK complexity
- Documents requirements clearly with TODOs
- Allows testing factory pattern independently
- Faster iteration without SDK build times
- Clear scope for Phase 2 work
Dependencies¶
Phase 1 (Current): - AudioProcessor (04_10_audio_processor) - Standard library only - No external dependencies
Phase 2 (Future): - VST3 SDK (vcpkg: vst3sdk) - Parameter system (04_08_parameter_system)
Build Configuration¶
Header-Only Library:
add_library(audiolab_plugin_architecture INTERFACE)
target_link_libraries(my_target PRIVATE audiolab_plugin_architecture)
Example Executable:
add_executable(register_plugin_example examples/register_plugin.cpp)
target_link_libraries(register_plugin_example PRIVATE audiolab_plugin_architecture)
Reference Implementations¶
Studied for architecture patterns (no code copied):
- JUCE Plugin Client
modules/juce_audio_plugin_client/-
Single processor, multiple wrappers (VST3/AU/AAX)
-
iPlug2 Wrapper
IPlug/VST3/IPlugVST3.cpp-
Parameter mapping strategies
-
VST3 SDK Examples
public.sdk/samples/vst/- Process method, bus handling
License¶
AudioLab Project - See root LICENSE file
Contact¶
For questions about plugin architecture: - See 04_CORE README - Phase 2 VST3 integration: TBD