Skip to main content

aether_core/
state.rs

1//! State continuity framework.
2//!
3//! When a node is replaced or reconnected, its predecessor's state is transferred
4//! to preserve audio continuity (no clicks, no phase resets).
5
6/// Trait for nodes that carry persistent DSP state across mutations.
7pub trait Stateful {
8    type State: Copy;
9
10    fn capture_state(&self) -> Self::State;
11    fn restore_state(&mut self, state: Self::State);
12}
13
14/// Opaque state blob — large enough for all built-in node types.
15/// Stored as raw bytes to avoid generics in the graph.
16#[derive(Clone, Copy)]
17pub struct StateBlob {
18    pub bytes: [u8; 256],
19    pub len: usize,
20}
21
22impl StateBlob {
23    pub const EMPTY: Self = Self {
24        bytes: [0u8; 256],
25        len: 0,
26    };
27
28    pub fn from_value<T: Copy>(val: &T) -> Self {
29        let size = std::mem::size_of::<T>();
30        assert!(size <= 256, "StateBlob overflow: type too large");
31        let mut blob = Self::EMPTY;
32        blob.len = size;
33        // SAFETY: T is Copy, size is bounded, dst is valid.
34        unsafe {
35            std::ptr::copy_nonoverlapping(
36                val as *const T as *const u8,
37                blob.bytes.as_mut_ptr(),
38                size,
39            );
40        }
41        blob
42    }
43
44    pub fn to_value<T: Copy>(&self) -> T {
45        assert_eq!(self.len, std::mem::size_of::<T>());
46        // SAFETY: bytes were written from a valid T.
47        unsafe { std::ptr::read(self.bytes.as_ptr() as *const T) }
48    }
49}
50
51impl Default for StateBlob {
52    fn default() -> Self {
53        Self::EMPTY
54    }
55}