truce_loader/
static_shell.rs1use std::sync::atomic::{AtomicU32, Ordering};
7use std::sync::Arc;
8
9use truce_core::buffer::AudioBuffer;
10use truce_core::bus::BusLayout;
11use truce_core::events::{EventBody, EventList};
12use truce_core::info::PluginInfo;
13use truce_core::process::{ProcessContext, ProcessStatus};
14use truce_core::plugin::Plugin;
15use truce_params::Params;
16
17use crate::traits::PluginLogic;
18
19pub struct StaticShell<P: Params, L: PluginLogic> {
28 pub params: Arc<P>,
29 logic: L,
30 meters: Arc<[AtomicU32; 256]>,
31 sample_rate: f64,
32}
33
34unsafe impl<P: Params, L: PluginLogic> Send for StaticShell<P, L> {}
35
36impl<P: Params + Default + 'static, L: PluginLogic + 'static> StaticShell<P, L> {
37 pub fn new(params: P) -> Self {
38 Self {
39 params: Arc::new(params),
40 logic: L::new(),
41 meters: Arc::new(std::array::from_fn(|_| AtomicU32::new(0))),
42 sample_rate: 44100.0,
43 }
44 }
45
46 pub fn logic_ref(&self) -> &L {
48 &self.logic
49 }
50
51 pub fn logic_ref_mut(&mut self) -> &mut L {
53 &mut self.logic
54 }
55
56 pub fn try_custom_editor(&self) -> Option<Box<dyn truce_core::editor::Editor>> {
58 self.logic.custom_editor()
59 }
60
61 pub fn try_builtin_editor(&self) -> Option<truce_gui::editor::BuiltinEditor<P>> {
64 let layout = self.logic.layout();
65 if layout.width == 0 || layout.height == 0 {
66 return None;
67 }
68 Some(truce_gui::editor::BuiltinEditor::new_grid(Arc::clone(&self.params), layout))
69 }
70}
71
72impl<P: Params + Default + 'static, L: PluginLogic + 'static> Plugin for StaticShell<P, L> {
73 fn info() -> PluginInfo where Self: Sized {
74 unreachable!("StaticShell::info() should not be called statically")
75 }
76
77 fn bus_layouts() -> Vec<BusLayout> where Self: Sized {
78 unreachable!("StaticShell::bus_layouts() should not be called statically")
79 }
80
81 fn init(&mut self) {}
82
83 fn reset(&mut self, sample_rate: f64, max_block_size: usize) {
84 self.sample_rate = sample_rate;
85 self.params.set_sample_rate(sample_rate);
86 self.logic.reset(sample_rate, max_block_size);
87 }
88
89 fn process(
90 &mut self,
91 buffer: &mut AudioBuffer,
92 events: &EventList,
93 context: &mut ProcessContext,
94 ) -> ProcessStatus {
95 for e in events.iter() {
99 if let EventBody::ParamChange { id, value } = &e.body {
100 self.params.set_plain(*id, *value);
101 }
102 }
103
104 if let Some(plugin_params) = self.logic.params_mut() {
108 for info in self.params.param_infos() {
109 if let Some(value) = self.params.get_plain(info.id) {
110 plugin_params.set_plain(info.id, value);
111 }
112 }
113 }
114
115 let params = &self.params;
117 let meters = &self.meters;
118 let param_fn = |id: u32| -> f64 { params.get_plain(id).unwrap_or(0.0) };
119 let meter_fn = |id: u32, v: f32| {
120 if let Some(slot) = meters.get(id as usize) {
121 slot.store(v.to_bits(), Ordering::Relaxed);
122 }
123 };
124 let mut output_events = EventList::new();
125 let mut ctx = ProcessContext::new(
126 context.transport,
127 context.sample_rate,
128 buffer.num_samples(),
129 &mut output_events,
130 )
131 .with_params(¶m_fn)
132 .with_meters(&meter_fn);
133
134 let result = self.logic.process(buffer, events, &mut ctx);
135
136 for event in output_events.iter() {
138 context.output_events.push(event.clone());
139 }
140
141 result
142 }
143
144 fn save_state(&self) -> Option<Vec<u8>> {
145 let data = self.logic.save_state();
146 if data.is_empty() { None } else { Some(data) }
147 }
148
149 fn load_state(&mut self, data: &[u8]) {
150 self.logic.load_state(data);
151 }
152
153 fn editor(&mut self) -> Option<Box<dyn truce_core::editor::Editor>> {
154 if let Some(editor) = self.logic.custom_editor() {
155 return Some(editor);
156 }
157 self.try_builtin_editor()
158 .map(|e| Box::new(e) as Box<dyn truce_core::editor::Editor>)
159 }
160
161 fn latency(&self) -> u32 { self.logic.latency() }
162 fn tail(&self) -> u32 { self.logic.tail() }
163
164 fn get_meter(&self, meter_id: u32) -> f32 {
165 if let Some(slot) = self.meters.get(meter_id as usize) {
166 f32::from_bits(slot.load(Ordering::Relaxed))
167 } else {
168 0.0
169 }
170 }
171}
172
173#[macro_export]
195macro_rules! export_static {
196 (
197 params: $params:ty,
198 info: $info:expr,
199 bus_layouts: [$($layout:expr),* $(,)?],
200 logic: $logic:ty,
201 editor: { $($editor_body:tt)* },
202 ) => {
203 struct __HotShellWrapper {
204 inner: $crate::static_shell::StaticShell<$params, $logic>,
205 }
206
207 impl truce_core::plugin::Plugin for __HotShellWrapper {
208 fn info() -> truce_core::info::PluginInfo where Self: Sized {
209 $info
210 }
211
212 fn bus_layouts() -> Vec<truce_core::bus::BusLayout> where Self: Sized {
213 vec![$($layout),*]
214 }
215
216 fn init(&mut self) {
217 self.inner.init();
218 }
219
220 fn reset(&mut self, sample_rate: f64, max_block_size: usize) {
221 self.inner.reset(sample_rate, max_block_size);
222 }
223
224 fn process(
225 &mut self,
226 buffer: &mut truce_core::buffer::AudioBuffer,
227 events: &truce_core::events::EventList,
228 context: &mut truce_core::process::ProcessContext,
229 ) -> truce_core::process::ProcessStatus {
230 self.inner.process(buffer, events, context)
231 }
232
233 fn save_state(&self) -> Option<Vec<u8>> {
234 self.inner.save_state()
235 }
236
237 fn load_state(&mut self, data: &[u8]) {
238 self.inner.load_state(data);
239 }
240
241 fn editor(&mut self) -> Option<Box<dyn truce_core::editor::Editor>> {
242 if let Some(e) = self.inner.try_custom_editor() {
243 return Some(e);
244 }
245 if let Some(builtin) = self.inner.try_builtin_editor() {
246 return Some($($editor_body)*(builtin));
247 }
248 None
249 }
250
251 fn latency(&self) -> u32 { self.inner.latency() }
252 fn tail(&self) -> u32 { self.inner.tail() }
253 fn get_meter(&self, meter_id: u32) -> f32 { self.inner.get_meter(meter_id) }
254 }
255
256 impl truce_core::export::PluginExport for __HotShellWrapper {
257 type Params = $params;
258
259 fn create() -> Self {
260 Self {
261 inner: $crate::static_shell::StaticShell::new(<$params>::new()),
262 }
263 }
264
265 fn params(&self) -> &$params {
266 &self.inner.params
267 }
268
269 fn params_mut(&mut self) -> &mut $params {
270 std::sync::Arc::get_mut(&mut self.inner.params)
273 .expect("params_mut called while Arc has other refs")
274 }
275
276 fn params_arc(&self) -> std::sync::Arc<$params> {
277 std::sync::Arc::clone(&self.inner.params)
278 }
279 }
280 };
281}