Skip to main content

PluginLogic64

Trait PluginLogic64 

Source
pub trait PluginLogic64: Send + 'static {
    type Params: Params;

    // Required methods
    fn reset(&mut self, sample_rate: f64, max_block_size: usize);
    fn process(
        &mut self,
        buffer: &mut AudioBuffer<'_, f64>,
        events: &EventList,
        context: &mut ProcessContext<'_>,
    ) -> ProcessStatus;
    fn editor(params: Arc<Self::Params>) -> Box<dyn Editor>;

    // Provided methods
    fn supports_in_place() -> bool
       where Self: Sized { ... }
    fn bus_layouts() -> Vec<BusLayout>
       where Self: Sized { ... }
    fn save_state(&self) -> Vec<u8>  { ... }
    fn snapshot_into(&self, buf: &mut Vec<u8>) -> bool { ... }
    fn load_state(&mut self, _data: &[u8]) -> Result<(), StateLoadError> { ... }
    fn state_changed(&mut self) { ... }
    fn migrate_state(_foreign: &ForeignState<'_>) -> Option<MigratedState>
       where Self: Sized { ... }
    fn latency(&self) -> u32 { ... }
    fn tail(&self) -> u32 { ... }
}
Expand description

The f64-buffer user-facing plugin trait. Same surface as PluginLogic but with the audio buffer pinned to f64.

Plugin authors don’t usually name this directly - truce::prelude64 re-exports it as PluginLogic, so the impl header reads the same regardless of which precision the prelude chose. Pick truce::prelude64 (and thus this leaf) when the DSP path runs in f64 end-to-end and the wrapper-boundary widen/narrow memcpy is worth the cleaner DSP code.

Required Associated Types§

Source

type Params: Params

The plugin’s parameter struct (#[derive(Params)]). Named here so the editor can be built from an Arc<Self::Params> without borrowing the plugin - see Self::editor.

Required Methods§

Source

fn reset(&mut self, sample_rate: f64, max_block_size: usize)

Reset for a new sample rate / block size. Called before the first process and any time the host reconfigures.

Source

fn process( &mut self, buffer: &mut AudioBuffer<'_, f64>, events: &EventList, context: &mut ProcessContext<'_>, ) -> ProcessStatus

Process one block of audio. Real-time - no allocations, locks, or I/O.

Source

fn editor(params: Arc<Self::Params>) -> Box<dyn Editor>

Construct the editor for this plugin. Required.

There is no auto-fallback - every plugin explicitly names which renderer it wants. For the built-in widget layout, call truce_gui::default_editor(params, layout); for custom renderers, construct an EguiEditor / IcedEditor / SlintEditor / hand-rolled Editor here. The choice of renderer crate the plugin’s Cargo.toml pulls IS the choice of editor.

An associated function, not a method: it receives the lock-free Arc<Self::Params> store the wrapper already holds, so the host can open the editor while audio is running without ever taking the plugin lock. Editors bind only to the param store (plus meters / transport, all lock-free); custom DSP state is read at runtime through the editor bridge, not at construction.

Provided Methods§

Source

fn supports_in_place() -> bool
where Self: Sized,

Opt into zero-copy in-place I/O. When this returns true, the format wrapper skips its safety memcpy on host-aliased buffers and hands the plugin the raw shared memory through AudioBuffer::in_out_mut(ch). The plugin must check AudioBuffer::is_in_place(ch) per channel before reading input(ch).

Default false: the wrapper copies aliased inputs into scratch so input(ch) and output(ch) are always disjoint. Costs one memcpy per aliased channel per block.

Source

fn bus_layouts() -> Vec<BusLayout>
where Self: Sized,

Supported audio bus configurations. The host picks one; the others are rejected at bus-config time before process is ever called. Default: stereo in, stereo out.

Source

fn save_state(&self) -> Vec<u8>

Serialize plugin-specific state (DSP state, not params - those are saved automatically). Default: delegates to Self::snapshot_into (empty when neither is overridden).

Runs on a host or GUI thread while the audio thread is paused at a block boundary (the wrapper’s plugin lock), so reading any field is safe - but an audio block that arrives mid-save waits for this to return. Keep it cheap: copy bytes out, don’t compute or compress here. To take this off the plugin lock entirely, override Self::snapshot_into instead.

Source

fn snapshot_into(&self, buf: &mut Vec<u8>) -> bool

Opt into lock-free state save. Serialize the same bytes Self::save_state would into buf (cleared first; capacity is retained across calls so a steady state is allocation-free).

The return value is a static capability, not a per-block flag: true means “this plugin publishes snapshots”, false means “it never does” (the default). Once you return true you must return true for the plugin’s whole lifetime - if the custom state empties out, clear buf and still return true (an empty blob), don’t return false. The shell latches the opt-in on the first published block; a later false is a contract violation that would otherwise leave the host reading a stale snapshot forever.

Called on the audio thread after each process block, under the same real-time rules as process - bounded, no unbounded allocation. The wrapper publishes the result into a lock-free slot the host reads without ever taking the plugin lock, so saving state while audio runs never stalls the audio thread. Overriding this is the preferred way to serialize custom state; the default Self::save_state delegates here.

Source

fn load_state(&mut self, _data: &[u8]) -> Result<(), StateLoadError>

Restore plugin-specific state.

Runs on the audio thread between blocks, with the same exclusive access process() has - writing any field is safe.

§Errors

Return Err(StateLoadError) when the blob is malformed or otherwise can’t be interpreted - the format wrapper logs the failure (and on hosts that support it, surfaces it to the DAW).

Source

fn state_changed(&mut self)

Called on the audio thread immediately after Self::load_state returns. Invalidate or recompute any caches the next process() reads. Default: no-op.

Source

fn migrate_state(_foreign: &ForeignState<'_>) -> Option<MigratedState>
where Self: Sized,

Translate foreign state - a previous framework’s blob, or a truce envelope saved under a different plugin id - into truce params + extra, so a plugin ported to truce keeps its users’ old sessions and presets. Runs on the host thread; receiverless so it can’t touch (or alias) the live instance. Return None for bytes you don’t recognize - the wrapper then reports load failure to the host, exactly as if this hook didn’t exist.

One-shot by construction: the next save writes a normal truce envelope, so this never becomes a permanent dual-format reader. Keyed formats (AU / LV2 / AAX) only see foreign bytes when truce.toml declares the legacy keys to probe ([plugin.legacy_state]).

Source

fn latency(&self) -> u32

Report latency in samples for plugin delay compensation.

Source

fn tail(&self) -> u32

Report tail time in samples (audio produced after input stops - reverbs, delays). u32::MAX for infinite tail.

Dyn Compatibility§

This trait is not dyn compatible.

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

Implementors§