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§
Sourceconst CLSID: Clsid
const CLSID: Clsid
CLSID under which the audio engine and regsvr32 identify
this APO. Must be unique per implementor.
Sourceconst NAME: &'static str
const NAME: &'static str
Human-readable APO name. Surfaced in Sound Settings and
elsewhere in the Windows audio UI.
Sourceconst CATEGORY: ApoCategory
const CATEGORY: ApoCategory
Category controlling where in the per-stream processing
graph the APO sits — see ApoCategory.
Required Methods§
Sourcefn new() -> Self
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.
Sourcefn process(
&mut self,
rt: &RealtimeContext,
input: ProcessInput<'_>,
output: &mut [f32],
) -> BufferFlags
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§
Sourcefn is_input_format_supported(&self, format: &Format) -> FormatNegotiation
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.
Sourcefn is_output_format_supported(&self, format: &Format) -> FormatNegotiation
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.
Sourcefn system_effects(&self) -> &[SystemEffect]
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).
Sourcefn set_system_effect_state(&mut self, id: &Clsid, state: SystemEffectState)
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.
Sourcefn lock_for_process(
&mut self,
input: &Format,
output: &Format,
) -> Result<(), HResult>
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.
Sourcefn unlock_for_process(&mut self)
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.