truce_loader/
static_shell.rs1use std::sync::Arc;
7use std::sync::atomic::{AtomicU32, Ordering};
8
9use truce_core::buffer::AudioBuffer;
10use truce_core::bus::BusLayout;
11use truce_core::editor::Editor;
12use truce_core::events::{EventBody, EventList};
13use truce_core::info::PluginInfo;
14use truce_core::plugin::Plugin;
15use truce_core::process::{ProcessContext, ProcessStatus};
16use truce_gui::PluginLogicCore;
17use truce_params::Params;
18use truce_params::sample::Sample;
19
20pub struct StaticShell<P: Params, L: PluginLogicCore<S>, S: Sample = f32> {
30 pub params: Arc<P>,
31 logic: L,
32 meters: Arc<[AtomicU32; 256]>,
33 sample_rate: f64,
34 _sample: std::marker::PhantomData<fn() -> S>,
35}
36
37unsafe impl<P: Params, L: PluginLogicCore<S>, S: Sample> Send for StaticShell<P, L, S> {}
47
48impl<P: Params + Default + 'static, L: PluginLogicCore<S> + 'static, S: Sample>
49 StaticShell<P, L, S>
50{
51 pub fn from_parts(params: Arc<P>, logic: L) -> Self {
54 Self {
55 params,
56 logic,
57 meters: Arc::new(std::array::from_fn(|_| AtomicU32::new(0))),
58 sample_rate: 44100.0,
59 _sample: std::marker::PhantomData,
60 }
61 }
62
63 pub fn logic_ref(&self) -> &L {
65 &self.logic
66 }
67
68 pub fn logic_ref_mut(&mut self) -> &mut L {
70 &mut self.logic
71 }
72
73 pub fn try_custom_editor(&self) -> Option<Box<dyn Editor>> {
75 self.logic.custom_editor()
76 }
77
78 pub fn try_builtin_editor(&self) -> Option<truce_gui::editor::BuiltinEditor<P>> {
81 let layout = self.logic.layout();
82 if layout.width == 0 || layout.height == 0 {
83 return None;
84 }
85 Some(truce_gui::editor::BuiltinEditor::new_grid(
86 Arc::clone(&self.params),
87 layout,
88 ))
89 }
90}
91
92impl<P: Params + Default + 'static, L: PluginLogicCore<S> + 'static, S: Sample> Plugin
93 for StaticShell<P, L, S>
94{
95 type Sample = S;
96
97 fn info() -> PluginInfo
98 where
99 Self: Sized,
100 {
101 unreachable!("StaticShell::info() should not be called statically")
102 }
103
104 fn bus_layouts() -> Vec<BusLayout>
105 where
106 Self: Sized,
107 {
108 unreachable!("StaticShell::bus_layouts() should not be called statically")
109 }
110
111 fn init(&mut self) {}
112
113 fn reset(&mut self, sample_rate: f64, max_block_size: usize) {
114 self.sample_rate = sample_rate;
115 self.params.set_sample_rate(sample_rate);
116 self.logic.reset(sample_rate, max_block_size);
117 }
118
119 fn process(
120 &mut self,
121 buffer: &mut AudioBuffer<S>,
122 events: &EventList,
123 context: &mut ProcessContext,
124 ) -> ProcessStatus {
125 for e in events.iter() {
129 if let EventBody::ParamChange { id, value } = &e.body {
130 self.params.set_plain(*id, *value);
131 }
132 }
133
134 let params = &self.params;
138 let meters = &self.meters;
139 let param_fn = |id: u32| -> f64 { params.get_plain(id).unwrap_or(0.0) };
140 let meter_fn = |id: u32, v: f32| {
141 let idx = id.wrapping_sub(truce_params::METER_ID_BASE) as usize;
144 if let Some(slot) = meters.get(idx) {
145 slot.store(v.to_bits(), Ordering::Relaxed);
146 }
147 };
148 let mut ctx = ProcessContext::new(
149 context.transport,
150 context.sample_rate,
151 buffer.num_samples(),
152 &mut *context.output_events,
153 )
154 .with_params(¶m_fn)
155 .with_meters(&meter_fn);
156
157 self.logic.process(buffer, events, &mut ctx)
158 }
159
160 fn save_state(&self) -> Vec<u8> {
161 self.logic.save_state()
162 }
163
164 fn load_state(&mut self, data: &[u8]) -> Result<(), truce_core::state::StateLoadError> {
165 let result = self.logic.load_state(data);
166 PluginLogicCore::state_changed(&mut self.logic);
171 result
172 }
173
174 fn editor(&mut self) -> Option<Box<dyn Editor>> {
175 if let Some(editor) = self.logic.custom_editor() {
176 return Some(editor);
177 }
178 #[cfg(target_os = "ios")]
182 {
183 self.try_builtin_editor()
184 .map(|e| Box::new(e) as Box<dyn Editor>)
185 }
186 #[cfg(not(target_os = "ios"))]
187 {
188 self.try_builtin_editor()
189 .map(|e| Box::new(truce_gpu::GpuEditor::new(e)) as Box<dyn Editor>)
190 }
191 }
192
193 fn latency(&self) -> u32 {
194 self.logic.latency()
195 }
196 fn tail(&self) -> u32 {
197 self.logic.tail()
198 }
199
200 fn get_meter(&self, meter_id: u32) -> f32 {
201 let idx = meter_id.wrapping_sub(truce_params::METER_ID_BASE) as usize;
206 if let Some(slot) = self.meters.get(idx) {
207 f32::from_bits(slot.load(Ordering::Relaxed))
208 } else {
209 0.0
210 }
211 }
212}
213
214#[macro_export]
237macro_rules! export_static {
238 (
239 params: $params:ty,
240 info: $info:expr,
241 logic: $logic:ty,
242 ) => {
243 pub struct __HotShellWrapper {
244 inner: $crate::static_shell::StaticShell<$params, $logic, Sample>,
251 }
252
253 impl $crate::__macro_deps::truce_core::plugin::Plugin for __HotShellWrapper {
254 type Sample = Sample;
255
256 fn supports_in_place() -> bool
257 where
258 Self: Sized,
259 {
260 <$logic as $crate::__macro_deps::truce_gui::PluginLogicCore<Sample>>::supports_in_place()
269 }
270
271 fn info() -> $crate::__macro_deps::truce_core::info::PluginInfo
272 where
273 Self: Sized,
274 {
275 $info
276 }
277
278 fn bus_layouts() -> Vec<$crate::__macro_deps::truce_core::bus::BusLayout>
279 where
280 Self: Sized,
281 {
282 <$logic as $crate::__macro_deps::truce_gui::PluginLogicCore<Sample>>::bus_layouts()
283 }
284
285 fn init(&mut self) {
286 self.inner.init();
287 }
288
289 fn reset(&mut self, sample_rate: f64, max_block_size: usize) {
290 self.inner.reset(sample_rate, max_block_size);
291 }
292
293 fn process(
294 &mut self,
295 buffer: &mut $crate::__macro_deps::truce_core::buffer::AudioBuffer<Sample>,
296 events: &$crate::__macro_deps::truce_core::events::EventList,
297 context: &mut $crate::__macro_deps::truce_core::process::ProcessContext,
298 ) -> $crate::__macro_deps::truce_core::process::ProcessStatus {
299 self.inner.process(buffer, events, context)
300 }
301
302 fn save_state(&self) -> Vec<u8> {
303 self.inner.save_state()
304 }
305
306 fn load_state(
307 &mut self,
308 data: &[u8],
309 ) -> Result<(), $crate::__macro_deps::truce_core::state::StateLoadError> {
310 self.inner.load_state(data)
311 }
312
313 fn editor(
314 &mut self,
315 ) -> Option<Box<dyn $crate::__macro_deps::truce_core::editor::Editor>> {
316 self.inner.editor()
317 }
318
319 fn latency(&self) -> u32 {
320 self.inner.latency()
321 }
322 fn tail(&self) -> u32 {
323 self.inner.tail()
324 }
325 fn get_meter(&self, meter_id: u32) -> f32 {
326 self.inner.get_meter(meter_id)
327 }
328 }
329
330 impl $crate::__macro_deps::truce_core::export::PluginExport for __HotShellWrapper {
331 type Params = $params;
332
333 fn create() -> Self {
334 let params = std::sync::Arc::new(<$params>::new());
335 let logic = <$logic>::new(std::sync::Arc::clone(¶ms));
336 Self {
337 inner: $crate::static_shell::StaticShell::from_parts(params, logic),
338 }
339 }
340
341 fn params(&self) -> &$params {
342 &self.inner.params
343 }
344
345 fn params_arc(&self) -> std::sync::Arc<$params> {
346 std::sync::Arc::clone(&self.inner.params)
347 }
348 }
349 };
350}