AudioProcessor

Trait AudioProcessor 

Source
pub trait AudioProcessor: Send {
Show 16 methods // Required methods fn setup(&mut self, sample_rate: f64, max_buffer_size: usize); fn process( &mut self, buffer: &mut Buffer<'_>, aux: &mut AuxiliaryBuffers<'_>, context: &ProcessContext, ); // Provided methods fn set_active(&mut self, _active: bool) { ... } fn tail_samples(&self) -> u32 { ... } fn latency_samples(&self) -> u32 { ... } fn bypass_ramp_samples(&self) -> u32 { ... } fn supports_double_precision(&self) -> bool { ... } fn process_f64( &mut self, buffer: &mut Buffer<'_, f64>, _aux: &mut AuxiliaryBuffers<'_, f64>, context: &ProcessContext, ) { ... } fn save_state(&self) -> PluginResult<Vec<u8>> { ... } fn load_state(&mut self, _data: &[u8]) -> PluginResult<()> { ... } fn input_bus_count(&self) -> usize { ... } fn output_bus_count(&self) -> usize { ... } fn input_bus_info(&self, index: usize) -> Option<BusInfo> { ... } fn output_bus_info(&self, index: usize) -> Option<BusInfo> { ... } fn process_midi(&mut self, input: &[MidiEvent], output: &mut MidiBuffer) { ... } fn wants_midi(&self) -> bool { ... }
}
Expand description

Core trait for audio processing logic.

This trait defines the DSP (Digital Signal Processing) interface that plugin implementations must provide. It is designed to be format-agnostic, meaning the same implementation can be wrapped for VST3, CLAP, or other plugin formats.

§Thread Safety

Implementors must be Send because the plugin may be moved between threads. The process method is called on the audio thread and must be real-time safe:

  • No allocations
  • No locks (use lock-free structures)
  • No syscalls
  • No unbounded loops

Required Methods§

Source

fn setup(&mut self, sample_rate: f64, max_buffer_size: usize)

Called when audio processing setup changes.

This is called before audio processing begins, whenever the sample rate or maximum block size changes. Use this to initialize buffers, filters, or other sample-rate dependent state.

§Arguments
  • sample_rate - The sample rate in Hz (e.g., 44100.0, 48000.0)
  • max_buffer_size - Maximum number of samples per process call
Source

fn process( &mut self, buffer: &mut Buffer<'_>, aux: &mut AuxiliaryBuffers<'_>, context: &ProcessContext, )

Process an audio buffer with transport context.

This is the main DSP entry point, called on the audio thread for each block of audio. The buffer provides input samples and mutable output buffers for the main bus.

§Arguments
  • buffer - Main audio bus (stereo/surround input and output)
  • aux - Auxiliary buses (sidechain, aux sends) - ignore if not needed
  • context - Processing context with sample rate, buffer size, and transport info
§Real-Time Safety

This method must be real-time safe. Do not allocate, lock mutexes, or perform any operation with unbounded execution time.

§Example: Simple Gain
fn process(&mut self, buffer: &mut Buffer, _aux: &mut AuxiliaryBuffers, _context: &ProcessContext) {
    let gain = self.params.gain();
    for (input, output) in buffer.zip_channels() {
        for (i, o) in input.iter().zip(output.iter_mut()) {
            *o = *i * gain;
        }
    }
}
§Example: Tempo-Synced LFO
fn process(&mut self, buffer: &mut Buffer, _aux: &mut AuxiliaryBuffers, context: &ProcessContext) {
    // Calculate LFO rate synced to host tempo
    let lfo_hz = context.transport.tempo
        .map(|tempo| tempo / 60.0 / 4.0)  // 1 cycle per 4 beats
        .unwrap_or(2.0);                   // Fallback: 2 Hz

    let increment = (lfo_hz * 2.0 * std::f32::consts::PI) / context.sample_rate as f32;

    for (input, output) in buffer.zip_channels() {
        for (i, o) in input.iter().zip(output.iter_mut()) {
            let lfo = self.phase.sin();
            *o = *i * (1.0 + lfo * 0.5);
            self.phase += increment;
        }
    }
}
§Example: Sidechain Ducker
fn process(&mut self, buffer: &mut Buffer, aux: &mut AuxiliaryBuffers, _context: &ProcessContext) {
    let duck = aux.sidechain()
        .map(|sc| (sc.rms(0) * 4.0).min(1.0))
        .unwrap_or(0.0);

    buffer.copy_to_output();
    buffer.apply_output_gain(1.0 - duck * 0.8);
}

Provided Methods§

Source

fn set_active(&mut self, _active: bool)

Called when the plugin is activated or deactivated.

Activation typically happens when the user inserts the plugin into a track or opens a project. Deactivation happens when removed or project is closed.

Default implementation does nothing.

Source

fn tail_samples(&self) -> u32

Get the tail length in samples.

This indicates how many samples of audio “tail” the plugin produces after input stops (e.g., reverb decay). Return 0 for no tail, or u32::MAX for infinite tail.

Default returns 0 (no tail).

Source

fn latency_samples(&self) -> u32

Get the latency in samples.

If the plugin introduces processing latency (e.g., lookahead limiters), return the latency in samples here. The host can use this for delay compensation.

Default returns 0 (no latency).

Source

fn bypass_ramp_samples(&self) -> u32

Get the bypass ramp length in samples.

When bypass is engaged or disengaged, this defines the crossfade duration to avoid clicks. The host uses this value (combined with tail_samples()) to determine how long to continue calling process() after input stops.

Return 0 for instant bypass (no crossfade), or a sample count for smooth crossfading. Typical values:

  • 64 samples (~1.3ms at 48kHz) - fast, suitable for most effects
  • 256 samples (~5.3ms at 48kHz) - smoother, for sensitive material
  • 512+ samples - very smooth, for reverbs/delays with long tails

Default returns 64 samples.

§Example
fn bypass_ramp_samples(&self) -> u32 {
    // Use 10ms crossfade based on current sample rate
    (self.sample_rate * 0.01) as u32
}
Source

fn supports_double_precision(&self) -> bool

Returns true if the plugin supports native 64-bit (double precision) processing.

Override this to return true if your plugin implements process_f64() natively. When false (default), the framework will automatically convert 64-bit host buffers to 32-bit, call process(), and convert back.

§Performance Considerations
  • For most plugins, f32 is sufficient and the default conversion is fine
  • Implement native f64 only if your DSP algorithm benefits from double precision (e.g., IIR filters with long decay, precision-sensitive synthesis)
  • The conversion overhead is minimal (~few microseconds per buffer)

Default returns false.

Source

fn process_f64( &mut self, buffer: &mut Buffer<'_, f64>, _aux: &mut AuxiliaryBuffers<'_, f64>, context: &ProcessContext, )

Process an audio buffer at 64-bit (double) precision.

This is the f64 equivalent of process(). Override this method AND return true from supports_double_precision() to enable native 64-bit processing.

If supports_double_precision() returns false, this method is never called - the framework converts to f32 and calls process() instead.

§Default Implementation

The default implementation converts f64→f32, calls process(), then converts f32→f64. This allows any plugin to work in a 64-bit host without modification.

§Example: Native f64 Plugin
fn supports_double_precision(&self) -> bool {
    true
}

fn process_f64(
    &mut self,
    buffer: &mut Buffer<f64>,
    aux: &mut AuxiliaryBuffers<f64>,
    context: &ProcessContext,
) {
    let gain = self.params.gain_linear() as f64;
    for (input, output) in buffer.zip_channels() {
        for (i, o) in input.iter().zip(output.iter_mut()) {
            *o = *i * gain;
        }
    }
}
Source

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

Save the plugin state to bytes.

This is called when the DAW saves a project or preset. The returned bytes should contain all state needed to restore the plugin to its current configuration.

Default returns an empty vector.

Source

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

Load the plugin state from bytes.

This is called when the DAW loads a project or preset. The data is the same bytes returned from a previous save_state call.

Default does nothing.

Source

fn input_bus_count(&self) -> usize

Returns the number of audio input buses.

Default returns 1 (single stereo input).

Source

fn output_bus_count(&self) -> usize

Returns the number of audio output buses.

Default returns 1 (single stereo output).

Source

fn input_bus_info(&self, index: usize) -> Option<BusInfo>

Returns information about an input bus.

Default returns a stereo main bus for index 0.

Source

fn output_bus_info(&self, index: usize) -> Option<BusInfo>

Returns information about an output bus.

Default returns a stereo main bus for index 0.

Source

fn process_midi(&mut self, input: &[MidiEvent], output: &mut MidiBuffer)

Process MIDI events.

Called during processing with any incoming MIDI events. Plugins can transform events and add them to the output buffer, pass them through unchanged, or consume them entirely.

§Arguments
  • input - Slice of incoming MIDI events (sorted by sample_offset)
  • output - Buffer to write output MIDI events to
§Real-Time Safety

This method must be real-time safe. Do not allocate, lock mutexes, or perform any operation with unbounded execution time.

Note: Cloning a SysEx event allocates due to Box<SysEx>. SysEx events are rare in typical use cases. If strict real-time safety is required, override this method to handle SysEx specially.

§Default Implementation

The default implementation passes all events through unchanged.

Source

fn wants_midi(&self) -> bool

Returns whether this plugin processes MIDI events.

Override to return true if your plugin needs MIDI input/output. This is used by the host to determine event bus configuration.

Default returns false.

Implementors§