Skip to main content

ProcessingObject

Trait ProcessingObject 

Source
pub trait ProcessingObject: Sized + Send {
    const CLSID: Clsid;
    const NAME: &'static str;
    const COPYRIGHT: &'static str;
    const CATEGORY: ApoCategory;

    // Required methods
    fn new() -> Self;
    fn process(
        &mut self,
        rt: &RealtimeContext,
        input: ProcessInput<'_>,
        output: &mut [f32],
    ) -> BufferFlags;

    // Provided methods
    fn is_input_format_supported(&self, format: &Format) -> FormatNegotiation { ... }
    fn is_output_format_supported(&self, format: &Format) -> FormatNegotiation { ... }
    fn system_effects(&self) -> &[SystemEffect] { ... }
    fn set_system_effect_state(&mut self, id: &Clsid, state: SystemEffectState) { ... }
    fn lock_for_process(
        &mut self,
        input: &Format,
        output: &Format,
    ) -> Result<(), HResult> { ... }
    fn unlock_for_process(&mut self) { ... }
}
Expand description

User-implemented Audio Processing Object.

Each implementor represents one CLSID-identified APO with a distinct name, category, and processing behaviour. The framework’s COM harness instantiates the type via Self::new, drives the format-negotiation / LockForProcess / APOProcess / UnlockForProcess sequence, and routes the audio engine’s calls into the corresponding trait methods.

§Default format negotiation

The default Self::is_input_format_supported / Self::is_output_format_supported implementations accept any IEEE-float32 stream and suggest a float32 alternative for anything else. This matches the canonical Windows audio engine negotiation and is the format the Self::process callback’s &[f32] / &mut [f32] parameters assume.

Implementors that want to handle integer PCM or other formats directly should override these methods and use Format’s accessors to do their own typed slicing inside process.

§Realtime safety

Self::process takes a RealtimeContext reference. Any helper function callable from process should also accept &RealtimeContext, which makes its presence in the call stack visible at compile time. The realtime path must be allocation-free and lock-free per CLAUDE.md prohibitions 1 and 2.

Required Associated Constants§

Source

const CLSID: Clsid

CLSID under which the audio engine and regsvr32 identify this APO. Must be unique per implementor.

Source

const NAME: &'static str

Human-readable APO name. Surfaced in Sound Settings and elsewhere in the Windows audio UI.

Source

const COPYRIGHT: &'static str

Copyright notice carried in the registered class metadata.

Source

const CATEGORY: ApoCategory

Category controlling where in the per-stream processing graph the APO sits — see ApoCategory.

Required Methods§

Source

fn new() -> Self

Construct a fresh APO instance.

Called by the framework’s class factory once per CoCreateInstance invocation from the audio engine. Heap allocation is allowed here; it is not allowed inside Self::process.

Source

fn process( &mut self, rt: &RealtimeContext, input: ProcessInput<'_>, output: &mut [f32], ) -> BufferFlags

Process one audio buffer.

Realtime-critical: must be allocation-free, lock-free, and must not call into the kernel. Reachable callees should take &RealtimeContext to make the constraint visible throughout the call graph.

input carries the host’s input samples and the BufferFlags the host stamped on the buffer (the APO is free to short-circuit when ProcessInput::is_silent is true). output is the interleaved float32 buffer to write into; the same length as input.samples() (the framework enforces this before dispatching).

The return value becomes the u32BufferFlags field of the host’s output APO_CONNECTION_PROPERTY — typically BufferFlags::VALID for normal audio, or BufferFlags::SILENT when the APO knows it wrote pure silence and the engine may skip downstream work.

Provided Methods§

Source

fn is_input_format_supported(&self, format: &Format) -> FormatNegotiation

Decide whether format is acceptable as an input format.

The default implementation accepts any IEEE-float32 stream and suggests pcm_float32(format.sample_rate(), format.channels()) otherwise.

Source

fn is_output_format_supported(&self, format: &Format) -> FormatNegotiation

Decide whether format is acceptable as an output format.

The default implementation mirrors Self::is_input_format_supported.

Source

fn system_effects(&self) -> &[SystemEffect]

List of system effects this APO advertises to the audio engine via IAudioSystemEffects2::GetEffectsList.

The default returns an empty slice — the APO is registered but exposes no granular effects in the Sound Settings UI. Implementors that want per-effect toggles should override this with a slice borrowed from &self.

Called from non-realtime threads; allocation is permitted in implementations that need it (though the default’s constant slice is allocation-free).

Source

fn set_system_effect_state(&mut self, id: &Clsid, state: SystemEffectState)

Toggle the state of one of this APO’s advertised effects.

Called by the audio engine through IAudioSystemEffects3::SetAudioSystemEffectState whenever the user flips an effect toggle in the Sound Settings UI. The framework dispatches into this method only for effects the user advertised with controllable: true; if id does not match any advertised effect the framework returns E_INVALIDARG and does not invoke the method.

The default implementation is a no-op. Implementors that want to react to state changes (skip processing when off, reload internal state, etc.) override this method.

Called from a non-realtime thread and may race with the realtime process callback. Implementors that read effect state from process should mediate via atomics or a realtime-safe primitive.

Source

fn lock_for_process( &mut self, input: &Format, output: &Format, ) -> Result<(), HResult>

Prepare for processing under the supplied input/output formats.

Called once between Initialize and the first Self::process invocation. This is where implementors should pre-allocate internal buffers; allocation in Self::process is prohibited.

Returning an HResult failure aborts lock and surfaces to the audio engine as an IsInitialized=FALSE state.

Source

fn unlock_for_process(&mut self)

Release any resources acquired during Self::lock_for_process.

Always paired with a prior successful lock_for_process. Allocator use is allowed.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§