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}