bbx_plugin/
handle.rs

1//! Opaque handle wrapper for the DSP graph.
2//!
3//! This module provides the opaque `BbxGraph` type that C code uses
4//! to reference the Rust effects chain, along with the generic
5//! `GraphInner` wrapper that holds any `PluginDsp` implementation.
6
7use bbx_dsp::{ChannelLayout, PluginDsp, context::DspContext};
8
9/// Opaque handle representing a DSP effects chain.
10///
11/// This type has zero size and prevents C code from directly
12/// accessing the internal Rust structures. All operations must
13/// go through the FFI functions.
14#[repr(C)]
15pub struct BbxGraph {
16    _private: [u8; 0],
17}
18
19/// Internal wrapper holding the plugin DSP and context.
20///
21/// Generic over any type implementing `PluginDsp`.
22pub struct GraphInner<D: PluginDsp> {
23    /// DSP context with sample rate, buffer size, etc.
24    pub context: DspContext,
25
26    /// The plugin's DSP graph implementation.
27    pub dsp: D,
28
29    /// Whether the graph has been prepared for playback.
30    pub prepared: bool,
31}
32
33impl<D: PluginDsp> GraphInner<D> {
34    /// Create a new GraphInner with default configuration.
35    pub fn new() -> Self {
36        Self {
37            context: DspContext {
38                sample_rate: 44100.0,
39                buffer_size: 512,
40                num_channels: 2,
41                current_sample: 0,
42                channel_layout: ChannelLayout::default(),
43            },
44            dsp: D::new(),
45            prepared: false,
46        }
47    }
48
49    /// Prepare the effects chain for playback with the given audio specifications.
50    ///
51    /// When the `ftz-daz` feature is enabled, this also configures CPU-level
52    /// FTZ/DAZ modes to prevent denormal performance penalties.
53    pub fn prepare(&mut self, sample_rate: f64, buffer_size: usize, num_channels: usize) {
54        #[cfg(feature = "ftz-daz")]
55        bbx_core::denormal::enable_ftz_daz();
56
57        self.context = DspContext {
58            sample_rate,
59            buffer_size,
60            num_channels,
61            current_sample: 0,
62            channel_layout: ChannelLayout::default(),
63        };
64
65        self.dsp.prepare(&self.context);
66        self.prepared = true;
67    }
68
69    /// Reset the effects chain state.
70    pub fn reset(&mut self) {
71        self.dsp.reset();
72    }
73}
74
75impl<D: PluginDsp> Default for GraphInner<D> {
76    fn default() -> Self {
77        Self::new()
78    }
79}
80
81/// Convert a raw pointer to a GraphInner reference.
82///
83/// # Safety
84///
85/// The caller must ensure:
86/// - The pointer is valid and was created by `handle_from_graph` with the same DSP type.
87/// - The returned reference is not used beyond the lifetime of the handle.
88/// - No other mutable references to the same handle exist concurrently.
89#[inline]
90pub unsafe fn graph_from_handle<'a, D: PluginDsp>(handle: *mut BbxGraph) -> &'a mut GraphInner<D> {
91    unsafe { &mut *(handle as *mut GraphInner<D>) }
92}
93
94/// Convert a GraphInner to an opaque handle.
95#[inline]
96pub fn handle_from_graph<D: PluginDsp>(inner: Box<GraphInner<D>>) -> *mut BbxGraph {
97    Box::into_raw(inner) as *mut BbxGraph
98}