Hosting Plugins in Pulp¶
Pulp can both be a plugin and host plugins. The hosting APIs live in
pulp::host and let you load VST3 / AU / CLAP / LV2 binaries, wire them
into a DAG, and process audio through the chain.
Status today: CLAP loads and processes audio; VST3 / AU / LV2 loaders are stubbed and log a warning. The signal-graph topology (DAG + topological sort) is implemented; execution along the topology is in progress.
Quick start¶
#include <pulp/host/plugin_slot.hpp>
pulp::host::PluginInfo info;
info.name = "MyGain";
info.path = "/path/to/MyGain.clap";
info.format = pulp::host::PluginFormat::CLAP;
auto slot = pulp::host::PluginSlot::load(info);
if (!slot) {
// Loader not available for this format yet, or the bundle failed to open.
return 1;
}
slot->prepare(48000.0, 512);
slot->process(out_buffer, in_buffer, midi_in, midi_out, 512);
slot->release();
PluginSlot::load() dispatches by info.format. Each format backend lives
in its own translation unit (plugin_slot_clap.cpp, …) and is compiled in
only when the matching SDK is available. The symbol a backend exposes is a
plain function — load_clap_plugin(info), load_vst3_plugin(info), … —
that the dispatcher forwards to. No dynamic registry; the dispatcher knows
the formats at compile time.
Scanning¶
PluginScanner walks the standard plug-in directories for each format and
returns PluginInfo descriptors.
auto scanner = pulp::host::PluginScanner{};
for (auto& info : scanner.scan(pulp::host::PluginFormat::CLAP)) {
// Each info is ready to pass to PluginSlot::load().
}
Format-specific scanners (e.g. scanner_clap.cpp) read bundle metadata so
name / vendor / version / unique id come back populated.
Signal graph¶
SignalGraph is a DAG of nodes: InputNode, OutputNode, GainNode,
MidiInputNode, MidiOutputNode, and PluginNode. You add nodes and
connect output ports to input ports. The graph runs topological sort to
produce a deterministic processing order.
pulp::host::SignalGraph graph;
auto in = graph.add_input_node(2, "in");
auto plug = graph.add_plugin_node(std::move(slot), "gain");
auto out = graph.add_output_node(2, "out");
graph.connect(in, 0, plug, 0);
graph.connect(plug, 0, out, 0);
Execution walks graph.processing_order() each block and invokes
PluginSlot::process() on each plugin node.
Delay compensation (PDC)¶
PluginSlot::latency_samples() reports per-node latency. The graph sums
latencies along each path and inserts delay lines on the shorter paths so
signals stay phase-aligned where they recombine. Feedback loops need an
explicit delay (there's no acausal solver).
CLI¶
Two commands surface hosting in the CLI:
pulp scan [--format clap|vst3|au|lv2] # list discoverable plugins
pulp host <path> # load and briefly run a plugin
These exist to smoke-test the loaders outside a full DAW context.
Limits¶
- CLAP events (note / param changes) are stubbed at empty streams — the plugin still processes, but parameter automation isn't routed yet.
- Only one factory descriptor per
.clapis selected (first one, or one matchinginfo.unique_id). - Editor view creation (
create_editor_view()) returnsnullptr; hosting UIs ships after ViewBridge lands.