Building Plugins with CORE¶
Tutorial paso a paso para crear tu primer plugin usando 04_CORE
🎯 Lo Que Vas a Construir¶
Un simple gain plugin funcional que demuestra:
- Herencia de AudioProcessor
- Uso de Parameter para controls
- Processing RT-safe
- Lifecycle completo
Tiempo estimado: 30 minutos
📋 Prerequisitos¶
# 1. CORE debe estar compilado
cd C:/AudioDev/audio-lab/build
cmake --build . --target audiolab_core
# 2. Familiarízate con:
- C++17 básico
- Conceptos de audio (sample rate, buffer size)
🚀 Paso 1: Crear Estructura del Proyecto¶
# Crear directorio
mkdir -p 05_MODULES/MY_PLUGINS/simple_gain
cd 05_MODULES/MY_PLUGINS/simple_gain
# Archivos a crear:
# - gain_plugin.hpp # Plugin class
# - CMakeLists.txt # Build config
# - README.md # Documentation
📝 Paso 2: Implementar el Plugin¶
gain_plugin.hpp¶
#pragma once
#include "../../../04_CORE/04_10_audio_processor/audio_processor.hpp"
#include "../../../04_CORE/04_08_parameter_system/parameter.hpp"
namespace audiolab {
namespace myplugins {
class SimpleGainPlugin : public core::AudioProcessor {
public:
SimpleGainPlugin()
: AudioProcessor(core::ProcessorInfo{
"Simple Gain",
"My Plugins",
"com.myplugins.gain",
"1.0.0"
})
, gainDb_("Gain dB", -60.0f, 20.0f, 0.0f)
{}
protected:
// Called once when DAW prepares
void on_prepare(const core::ProcessorConfig& config) override {
// Nothing to allocate for simple gain
sampleRate_ = config.sample_rate;
}
// RT-SAFE: Main DSP (called every buffer)
void on_process(
const float* const* inputs,
float** outputs,
uint32_t num_samples
) noexcept override {
// Get gain in dB, convert to linear
const float gainDb = gainDb_.get();
const float gain = std::pow(10.0f, gainDb / 20.0f);
// Process each channel
for (uint32_t ch = 0; ch < config().num_output_channels; ++ch) {
for (uint32_t i = 0; i < num_samples; ++i) {
outputs[ch][i] = inputs[ch][i] * gain;
}
}
}
void on_release() override {
// Nothing to release
}
public:
core::Parameter<float>& getGainParameter() {
return gainDb_;
}
private:
core::Parameter<float> gainDb_;
double sampleRate_ = 48000.0;
};
} // namespace myplugins
} // namespace audiolab
🔧 Paso 3: Configurar Build¶
CMakeLists.txt¶
cmake_minimum_required(VERSION 3.20)
project(simple_gain VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 17)
# Create library (or executable for testing)
add_library(simple_gain STATIC
gain_plugin.hpp
)
# Link with CORE
target_link_libraries(simple_gain
PRIVATE
# audiolab_core # Uncomment when CORE builds library
)
# Include paths
target_include_directories(simple_gain
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../../../
)
🧪 Paso 4: Testing¶
test_gain.cpp (opcional)¶
#include "gain_plugin.hpp"
#include <iostream>
#include <vector>
int main() {
using namespace audiolab::myplugins;
SimpleGainPlugin plugin;
// Prepare
plugin.prepareToPlay(48000.0, 512, 2);
plugin.activate();
// Set gain to +6dB
plugin.getGainParameter().set(6.0f);
// Create test buffer
std::vector<float> input(512, 1.0f); // 0 dB
std::vector<float> output(512);
const float* inputs[] = {input.data()};
float* outputs[] = {output.data()};
// Process
plugin.processBlock(inputs, outputs, 512);
// Check output
float expectedGain = std::pow(10.0f, 6.0f / 20.0f); // ~2.0
std::cout << "Input: 1.0, Gain: +6dB, Output: "
<< output[0] << " (expected ~" << expectedGain << ")\n";
plugin.deactivate();
plugin.releaseResources();
return 0;
}
🏗️ Paso 5: Build y Run¶
# Build
cd build
cmake ..
cmake --build .
# Run test
./simple_gain_test
# Expected output:
# Input: 1.0, Gain: +6dB, Output: 1.995 (expected ~1.995)
🎨 Paso 6: Añadir Más Features¶
Parámetros Adicionales¶
private:
core::Parameter<float> gainDb_;
core::Parameter<bool> mute_; // NEW: Mute switch
core::Parameter<float> panPos_; // NEW: Pan position
public:
SimpleGainPlugin()
: gainDb_("Gain", -60, 20, 0)
, mute_("Mute", false) // NEW
, panPos_("Pan", -1, 1, 0) // NEW: -1=L, 0=C, 1=R
{}
protected:
void on_process(...) noexcept override {
if (mute_.get()) {
// Output silence
for (uint32_t ch = 0; ch < num_channels; ++ch) {
std::memset(outputs[ch], 0, num_samples * sizeof(float));
}
return;
}
const float gain = db_to_linear(gainDb_.get());
const float pan = panPos_.get();
// Stereo pan law
const float gainL = gain * std::cos(M_PI * (pan + 1.0f) / 4.0f);
const float gainR = gain * std::sin(M_PI * (pan + 1.0f) / 4.0f);
for (uint32_t i = 0; i < num_samples; ++i) {
outputs[0][i] = inputs[0][i] * gainL; // Left
outputs[1][i] = inputs[1][i] * gainR; // Right
}
}
🔄 Patrones Comunes¶
1. Pre-calcular en prepare()¶
void on_prepare(const ProcessorConfig& config) override {
sampleRate_ = config.sample_rate;
// Pre-calculate lookup table
for (int i = 0; i < 1024; ++i) {
float x = (float)i / 1024.0f;
lookupTable_[i] = std::sin(2.0f * M_PI * x);
}
}
2. Usar RingBuffer para Delay¶
#include "04_CORE/04_03_memory_management/ring_buffer.hpp"
private:
core::containers::RingBuffer<float> delayLine_;
void on_prepare(const ProcessorConfig& config) override {
delayLine_.resize(config.sample_rate * 0.5); // 500ms max
}
void on_process(...) noexcept override {
for (uint32_t i = 0; i < num_samples; ++i) {
float input = inputs[0][i];
float delayed = delayLine_.read(delaySamples);
outputs[0][i] = input + 0.5f * delayed;
delayLine_.write(input);
}
}
3. GUI↔Audio Events¶
#include "04_CORE/04_07_event_dispatcher/event_dispatcher.hpp"
// In GUI thread
void onButtonClick() {
MyEvent event;
core::EventDispatcher::instance().enqueue_deferred(event);
}
// In audio thread (process)
core::EventDispatcher::instance().process_deferred_events();
✅ Checklist¶
Antes de considerar tu plugin completo:
- Hereda de
AudioProcessor - Implementa
on_prepare()(allocate resources) - Implementa
on_process()noexcept (DSP) - Implementa
on_release()(free resources) - Parámetros expuestos via
getXXXParameter() - CMakeLists.txt enlaza con
audiolab_core - README.md documenta uso
- Tests verifican funcionalidad básica
🐛 Troubleshooting¶
Error: "audiolab_core not found"¶
# Solución: Asegurar que CORE está en scope
# Opción 1: Build como parte del proyecto principal
add_subdirectory(04_CORE)
add_subdirectory(05_MODULES/MY_PLUGINS/simple_gain)
# Opción 2: Usar find_package
find_package(AudioLabCore REQUIRED)
Error: "Cannot allocate in on_process"¶
// ❌ WRONG
void on_process(...) noexcept override {
std::vector<float> temp(num_samples); // ALLOCATION!
}
// ✅ CORRECT
void on_prepare(...) override {
tempBuffer_.resize(config.max_block_size);
}
void on_process(...) noexcept override {
// tempBuffer_ already allocated
}
🔗 Recursos¶
Ejemplos para Estudiar¶
- Delay Example - Delay con feedback
- 04_CORE Examples - Ejemplos de cada subsistema
Documentación de CORE¶
🚀 Próximos Pasos¶
- Añade complejidad gradualmente:
- Empieza simple (gain)
- Añade delay
- Añade modulación (LFO)
-
Añade filtros
-
Estudia plugins existentes:
05_MODULES/05_15_REFERENCE_IMPLEMENTATIONS/-
05_MODULES/05_07_ATOMS_L1/ -
Integra con frameworks:
- JUCE (AudioProcessor → juce::AudioProcessor wrapper)
- VST3 (vst::IAudioProcessor)
- AAX (AAX_CEffectParameters)
¡Ahora sabes cómo construir plugins con CORE! 🎵
Siguiente: Advanced Patterns (coming soon)