Skip to main content

PluginExport

Trait PluginExport 

Source
pub trait PluginExport: PluginRuntime + Sized {
    type Params: Params;

    // Required methods
    fn create() -> Self;
    fn params(&self) -> &Self::Params;
    fn params_arc(&self) -> Arc<Self::Params> ;
    fn meter_store(&self) -> Arc<MeterStore> ;
    fn snapshot_slot(&self) -> Arc<SnapshotSlot> ;

    // Provided methods
    fn editor_builder(&self) -> EditorBuilder<Self::Params> { ... }
    fn param_infos_static() -> Vec<ParamInfo> { ... }
    fn has_editor_static() -> bool { ... }
}
Expand description

Unified export trait for all plugin formats.

Implement this once on your plugin type. All format wrappers (CLAP, VST3, AU, standalone) use this to construct your plugin and access its parameters.

impl PluginExport for MyPlugin {
    type Params = MyParams;
    fn create() -> Self { Self::new() }
    fn params(&self) -> &MyParams { &self.params }
    fn params_arc(&self) -> Arc<MyParams> { self.params.clone() }
}

All parameter mutation goes through the atomic-backed accessors on &Params - no &mut Params accessor is required, which keeps the trait usable while the editor holds an Arc<Params> reader.

Required Associated Types§

Required Methods§

Source

fn create() -> Self

Construct a new instance of the plugin.

Source

fn params(&self) -> &Self::Params

Immutable access to the parameter struct.

Source

fn params_arc(&self) -> Arc<Self::Params>

Get a shared Arc reference to the parameter struct.

Used by format wrappers to pass params to GUI closures without raw pointers. The Arc is cloned (cheap ref-count bump), not the params themselves.

Source

fn meter_store(&self) -> Arc<MeterStore>

Shared meter storage handle, mirroring Self::params_arc: the audio thread publishes meter values into this store from inside process() (via the shells’ meter callback), and GUI get_meter closures read it - never the plugin instance, whose &mut the audio thread holds for the whole block.

The truce::plugin! shells own the store and return their handle here; a hand-written PluginExport impl keeps one alongside its params Arc.

Source

fn snapshot_slot(&self) -> Arc<SnapshotSlot>

Shared snapshot slot for lock-free state save. The shell (audio thread) publishes the plugin’s custom state into it after each block when the plugin overrides snapshot_into; the wrapper’s save_state reads it without taking the plugin lock. A plugin that doesn’t opt in never publishes, so the slot stays empty and save_state falls back to the locked path.

The truce::plugin! shells own the slot and return their handle here; a hand-written impl keeps one alongside its params Arc.

Provided Methods§

Source

fn editor_builder(&self) -> EditorBuilder<Self::Params>

A lock-free editor builder: hand it the param store and it returns the editor (or None for a headless plugin).

Called once at instance creation - format wrappers cache the returned closure outside the plugin lock (alongside params_arc), then invoke it when the host opens the GUI, so editor construction never takes the plugin lock and never waits on an in-flight audio block. The closure binds only the lock-free param store, so a --shell build’s closure rebuilds from the reloaded dylib (GUI hot-reload survives, picked up on the next editor close+open). The truce::plugin! shells provide this; a hand-written impl returns a closure that builds its editor directly. Default: a closure that returns None.

Source

fn param_infos_static() -> Vec<ParamInfo>

Static parameter metadata for registration-time access.

Format wrappers’ register_* paths (truce-vst2, truce-vst3, truce-au, truce-aax) call this instead of the historical Self::create().params().param_infos() walk, which constructed a full plugin instance - including any allocation the constructor did (DSP buffers, FFT plans, image atlases) - just to read static metadata. On platforms where registration runs from C++ static initializers (notably AAX Describe) those allocations sit in a fragile init-order regime; avoiding them closes a class of platform-dependent registration bugs.

Default impl prefers Params::param_infos_static when it returns a non-empty vec (the #[derive(Params)] path emits an override built from compile-time metadata) and falls back to the runtime construction otherwise - so plugins with hand-written Params impls that don’t override the static path keep working unchanged.

Source

fn has_editor_static() -> bool

Static “does this plugin have an editor” predicate. AAX’s Describe path needs to know this at registration time (has_editor field on the static descriptor). Paired with Self::param_infos_static, this is the second reason every format’s registration walk constructed a plugin.

Default impl falls back to that runtime path so unannotated plugins keep working. Plugins that want to avoid the static-init plugin construction (notably for AAX hosts that run Describe very early) override with a const-style answer:

impl PluginExport for MyPlugin {
    // ...
    fn has_editor_static() -> bool { true }
}

VST2 / VST3 / AU never call this - they don’t need the answer at registration time.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§