pub fn graph_add_processor<T: AudioProcessor>(
graph: &mut ove_audio_graph,
processor: &'static mut T,
name: &[u8],
) -> Result<i32, Error>Expand description
Register a custom processor node on the graph.
All FFI trampolines are generated internally — no unsafe or
extern "C" needed in application code. The processor’s process
method is called from the audio thread for every period.
§Lifetime contract — &'static mut T
The bound on processor is &'static mut T, not &mut T with an
inferred shorter lifetime. This is load-bearing:
- The audio thread holds a raw pointer to
T(the registered context) and dereferences it as&mut Ton every period. That pointer must remain valid until theGraphis dropped. Graph::dropdoes not call intoT::dropor release the processor’s storage. The destroy slot in the C ops vtable is leftNonefor processor nodes;ove_audio_graph_deinitsimply walks past it. Lifetime management ofTis entirely the caller’s responsibility.
§Recommended patterns
// Preferred — works in both heap and zero-heap modes:
static MY_PROC: InitMut<MyProc> = InitMut::new();
MY_PROC.init(MyProc::new());
// SAFETY: single owner — `MY_PROC` is only ever handed to the audio
// graph; no other code touches it.
let p: &'static mut MyProc = unsafe { MY_PROC.get_mut() };
graph.add_processor(p, b"my\0")?;
// Heap-only:
let p: &'static mut MyProc = Box::leak(Box::new(MyProc::new()));
graph.add_processor(p, b"my\0")?;
// Older style, no allocator dependency:
static mut PROC: MyProc = MyProc::new();
graph.add_processor(unsafe { &mut PROC }, b"my\0")?;§Anti-patterns (compile but break the contract)
-
Lifetime laundering. Do not
transmutea shorter lifetime into'staticto satisfy the bound:ⓘfn install(g: &mut Graph, p: &mut MyProc) -> Result<u32, Error> { // WRONG — `p` likely dies before the audio thread next runs: g.add_processor(unsafe { core::mem::transmute(p) }, b"p\0") } let mut local = MyProc::new(); install(&mut g, &mut local)?; // `local` drops at scope end g.build()?; g.start()?; // audio thread → dangling pointer -
Re-initialising an
InitMutwhile registered. The cell’s storage outlives the program, butInitMut::init()constructs a freshTon top of the old bytes. The audio thread’s&mut Tstill points at the same address, now aliasing a freshly-initialised value — instant UB under Rust’s&mutexclusivity rule. Unregister (drop theGraph) before re-initialising the cell.
§Aliasing rule
While the processor is registered, the audio thread effectively
holds an exclusive &mut T borrow for every period. The caller
MUST NOT construct a second &mut T to the same processor (via
unsafe { &mut *STATIC.as_ptr() }, transmute, or any other route)
until the Graph has been dropped. Multiple & shared borrows
across threads are also UB — process takes &mut self.
§Errors
Returns an error if the graph is full or not in IDLE state
(i.e. Graph::build has already been called).