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 previously 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 the instance's reported latency in frames.
39    ///
40    /// The default reports the descriptor's [`PluginDescriptor::latency_frames`].
41    fn latency_frames(&self) -> u32 {
42        self.descriptor().latency_frames
43    }
44}
45
46/// Adapts a [`PluginInstance`] into an audio-graph [`Processor`].
47///
48/// The wrapper forwards prepare/reset/process to the held instance and maps the
49/// instance's reported latency onto the graph's tail-frame contract.
50#[derive(Clone, Debug)]
51pub struct HostedPluginProcessor<I> {
52    instance: I,
53}
54
55impl<I> HostedPluginProcessor<I> {
56    /// Wraps an instance so it can be inserted into an audio graph.
57    pub fn new(instance: I) -> Self {
58        Self { instance }
59    }
60
61    /// Returns a shared reference to the wrapped instance.
62    pub fn instance(&self) -> &I {
63        &self.instance
64    }
65
66    /// Returns a mutable reference to the wrapped instance.
67    pub fn instance_mut(&mut self) -> &mut I {
68        &mut self.instance
69    }
70
71    /// Consumes the wrapper and returns the wrapped instance.
72    pub fn into_inner(self) -> I {
73        self.instance
74    }
75}
76
77impl<I: PluginInstance> Processor for HostedPluginProcessor<I> {
78    fn prepare(&mut self, cfg: PrepareConfig) {
79        self.instance.prepare(cfg);
80    }
81
82    fn reset(&mut self) {
83        self.instance.reset();
84    }
85
86    fn process(&mut self, block: &mut ProcessBlock<'_>) {
87        self.instance.process(block);
88    }
89
90    fn tail_frames(&self) -> u64 {
91        u64::from(self.instance.latency_frames())
92    }
93}
94
95/// Presents an audio-graph [`Processor`] as a [`PluginInstance`].
96///
97/// This is the inverse adapter of [`HostedPluginProcessor`]: it pairs a bare
98/// processor with a descriptor and a held [`PluginState`], letting any
99/// processor be hosted as a native (`sim`-format) plugin. State is stored on the
100/// wrapper rather than pushed into the processor.
101#[derive(Clone, Debug)]
102pub struct ProcessorPlugin<P> {
103    descriptor: PluginDescriptor,
104    processor: P,
105    state: PluginState,
106}
107
108impl<P> ProcessorPlugin<P> {
109    /// Pairs a descriptor with a processor, starting from empty state.
110    pub fn new(descriptor: PluginDescriptor, processor: P) -> Self {
111        Self {
112            descriptor,
113            processor,
114            state: PluginState::new(),
115        }
116    }
117
118    /// Returns a shared reference to the wrapped processor.
119    pub fn processor(&self) -> &P {
120        &self.processor
121    }
122
123    /// Returns a mutable reference to the wrapped processor.
124    pub fn processor_mut(&mut self) -> &mut P {
125        &mut self.processor
126    }
127
128    /// Consumes the wrapper and returns the wrapped processor.
129    pub fn into_processor(self) -> P {
130        self.processor
131    }
132}
133
134impl<P: Processor> PluginInstance for ProcessorPlugin<P> {
135    fn descriptor(&self) -> &PluginDescriptor {
136        &self.descriptor
137    }
138
139    fn state(&self) -> PluginState {
140        self.state.clone()
141    }
142
143    fn set_state(&mut self, state: PluginState) {
144        self.state = state;
145    }
146
147    fn prepare(&mut self, cfg: PrepareConfig) {
148        self.processor.prepare(cfg);
149    }
150
151    fn reset(&mut self) {
152        self.processor.reset();
153    }
154
155    fn process(&mut self, block: &mut ProcessBlock<'_>) {
156        self.processor.process(block);
157    }
158}
159
160/// Implement [`PluginInstance`] for a `$ty<P>` newtype whose only relevant field
161/// is `inner: ProcessorPlugin<P>`, forwarding all six methods to it. The clap,
162/// lv2, and vst3 exported-processor adapters shared this forward block verbatim
163/// before OVERLAP6.15. Call it at each adapter, where `Processor`, `ProcessBlock`,
164/// and `PrepareConfig` (from `sim_lib_audio_graph_core`) and the plugin-core
165/// trait types are already in scope.
166#[macro_export]
167macro_rules! forward_plugin_instance {
168    ($ty:ident) => {
169        impl<P: Processor> PluginInstance for $ty<P> {
170            fn descriptor(&self) -> &PluginDescriptor {
171                self.inner.descriptor()
172            }
173
174            fn state(&self) -> PluginState {
175                self.inner.state()
176            }
177
178            fn set_state(&mut self, state: PluginState) {
179                self.inner.set_state(state);
180            }
181
182            fn prepare(&mut self, cfg: PrepareConfig) {
183                self.inner.prepare(cfg);
184            }
185
186            fn reset(&mut self) {
187                self.inner.reset();
188            }
189
190            fn process(&mut self, block: &mut ProcessBlock<'_>) {
191                self.inner.process(block);
192            }
193        }
194    };
195}