Skip to main content

sim_lib_plugin_core/
adapter.rs

1use sim_lib_audio_graph_core::{PrepareConfig, ProcessBlock, Processor};
2
3use crate::{PluginDescriptor, PluginState};
4
5/// A live, format-agnostic plugin instance the host can prepare and run.
6///
7/// Implementors are the format-specific backends (vst3/clap/lv2 and the native
8/// `sim` format). The trait pairs a static [`PluginDescriptor`] with the
9/// mutable, real-time processing entry points shared by every backend and is
10/// `Send` so instances can move between threads.
11pub trait PluginInstance: Send {
12    /// Returns the descriptor that identifies this instance and its port and
13    /// parameter layout.
14    fn descriptor(&self) -> &PluginDescriptor;
15
16    /// Captures the instance's current persistable state.
17    ///
18    /// The default returns an empty [`PluginState`]; backends that carry
19    /// parameter or opaque data override this.
20    fn state(&self) -> PluginState {
21        PluginState::new()
22    }
23
24    /// Restores the instance from a captured [`PluginState`].
25    ///
26    /// The default ignores the state; stateful backends override this.
27    fn set_state(&mut self, _state: PluginState) {}
28
29    /// Prepares the instance for processing under the given configuration.
30    fn prepare(&mut self, cfg: PrepareConfig);
31
32    /// Clears any internal processing state without releasing resources.
33    fn reset(&mut self);
34
35    /// Processes one audio block in place.
36    fn process(&mut self, block: &mut ProcessBlock<'_>);
37
38    /// Returns and clears the last backend error hidden behind a trait method
39    /// that cannot return [`sim_kernel::Result`].
40    ///
41    /// The default reports no latent error. Fallible backends override this so
42    /// hosts using the trait path can audit failures after `process`,
43    /// `set_state`, or other non-`Result` entry points.
44    fn take_last_error(&mut self) -> Option<String> {
45        None
46    }
47
48    /// Returns the instance's reported latency in frames.
49    ///
50    /// The default reports the descriptor's [`PluginDescriptor::latency_frames`].
51    fn latency_frames(&self) -> u32 {
52        self.descriptor().latency_frames
53    }
54}
55
56/// Adapts a [`PluginInstance`] into an audio-graph [`Processor`].
57///
58/// The wrapper forwards prepare/reset/process to the held instance and maps the
59/// instance's reported latency onto the graph's tail-frame contract.
60#[derive(Clone, Debug)]
61pub struct HostedPluginProcessor<I> {
62    instance: I,
63}
64
65impl<I> HostedPluginProcessor<I> {
66    /// Wraps an instance so it can be inserted into an audio graph.
67    pub fn new(instance: I) -> Self {
68        Self { instance }
69    }
70
71    /// Returns a shared reference to the wrapped instance.
72    pub fn instance(&self) -> &I {
73        &self.instance
74    }
75
76    /// Returns a mutable reference to the wrapped instance.
77    pub fn instance_mut(&mut self) -> &mut I {
78        &mut self.instance
79    }
80
81    /// Consumes the wrapper and returns the wrapped instance.
82    pub fn into_inner(self) -> I {
83        self.instance
84    }
85}
86
87impl<I: PluginInstance> Processor for HostedPluginProcessor<I> {
88    fn prepare(&mut self, cfg: PrepareConfig) {
89        self.instance.prepare(cfg);
90    }
91
92    fn reset(&mut self) {
93        self.instance.reset();
94    }
95
96    fn process(&mut self, block: &mut ProcessBlock<'_>) {
97        self.instance.process(block);
98    }
99
100    fn tail_frames(&self) -> u64 {
101        u64::from(self.instance.latency_frames())
102    }
103}
104
105/// Presents an audio-graph [`Processor`] as a [`PluginInstance`].
106///
107/// This is the inverse adapter of [`HostedPluginProcessor`]: it pairs a bare
108/// processor with a descriptor and a held [`PluginState`], letting any
109/// processor be hosted as a native (`sim`-format) plugin. State is stored on the
110/// wrapper rather than pushed into the processor.
111#[derive(Clone, Debug)]
112pub struct ProcessorPlugin<P> {
113    descriptor: PluginDescriptor,
114    processor: P,
115    state: PluginState,
116}
117
118impl<P> ProcessorPlugin<P> {
119    /// Pairs a descriptor with a processor, starting from empty state.
120    pub fn new(descriptor: PluginDescriptor, processor: P) -> Self {
121        Self {
122            descriptor,
123            processor,
124            state: PluginState::new(),
125        }
126    }
127
128    /// Returns a shared reference to the wrapped processor.
129    pub fn processor(&self) -> &P {
130        &self.processor
131    }
132
133    /// Returns a mutable reference to the wrapped processor.
134    pub fn processor_mut(&mut self) -> &mut P {
135        &mut self.processor
136    }
137
138    /// Consumes the wrapper and returns the wrapped processor.
139    pub fn into_processor(self) -> P {
140        self.processor
141    }
142}
143
144impl<P: Processor> PluginInstance for ProcessorPlugin<P> {
145    fn descriptor(&self) -> &PluginDescriptor {
146        &self.descriptor
147    }
148
149    fn state(&self) -> PluginState {
150        self.state.clone()
151    }
152
153    fn set_state(&mut self, state: PluginState) {
154        self.state = state;
155    }
156
157    fn prepare(&mut self, cfg: PrepareConfig) {
158        self.processor.prepare(cfg);
159    }
160
161    fn reset(&mut self) {
162        self.processor.reset();
163    }
164
165    fn process(&mut self, block: &mut ProcessBlock<'_>) {
166        self.processor.process(block);
167    }
168}
169
170/// Implement [`PluginInstance`] for a `$ty<P>` newtype whose only relevant field
171/// is `inner: ProcessorPlugin<P>`, forwarding all six methods to it. The clap,
172/// lv2, and vst3 exported-processor adapters shared this forward block verbatim
173/// Call it at each adapter, where `Processor`, `ProcessBlock`, and
174/// `PrepareConfig` (from `sim_lib_audio_graph_core`) and the plugin-core trait
175/// types are already in scope.
176#[macro_export]
177macro_rules! forward_plugin_instance {
178    ($ty:ident) => {
179        impl<P: Processor> PluginInstance for $ty<P> {
180            fn descriptor(&self) -> &PluginDescriptor {
181                self.inner.descriptor()
182            }
183
184            fn state(&self) -> PluginState {
185                self.inner.state()
186            }
187
188            fn set_state(&mut self, state: PluginState) {
189                self.inner.set_state(state);
190            }
191
192            fn prepare(&mut self, cfg: PrepareConfig) {
193                self.inner.prepare(cfg);
194            }
195
196            fn reset(&mut self) {
197                self.inner.reset();
198            }
199
200            fn process(&mut self, block: &mut ProcessBlock<'_>) {
201                self.inner.process(block);
202            }
203
204            fn take_last_error(&mut self) -> Option<String> {
205                self.inner.take_last_error()
206            }
207        }
208    };
209}