use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use truce_core::buffer::AudioBuffer;
use truce_core::bus::BusLayout;
use truce_core::editor::Editor;
use truce_core::events::{EventBody, EventList};
use truce_core::info::PluginInfo;
use truce_core::plugin::PluginRuntime;
use truce_core::process::{ProcessContext, ProcessStatus};
use truce_params::Params;
use truce_params::sample::Sample;
use truce_plugin::PluginLogicCore;
pub struct StaticShell<P: Params, L: PluginLogicCore<S>, S: Sample = f32> {
pub params: Arc<P>,
logic: L,
meters: Arc<[AtomicU32; 256]>,
sample_rate: f64,
_sample: std::marker::PhantomData<fn() -> S>,
}
unsafe impl<P: Params, L: PluginLogicCore<S>, S: Sample> Send for StaticShell<P, L, S> {}
impl<P: Params + Default + 'static, L: PluginLogicCore<S> + 'static, S: Sample>
StaticShell<P, L, S>
{
pub fn from_parts(params: Arc<P>, logic: L) -> Self {
Self {
params,
logic,
meters: Arc::new(std::array::from_fn(|_| AtomicU32::new(0))),
sample_rate: 44100.0,
_sample: std::marker::PhantomData,
}
}
pub fn logic_ref(&self) -> &L {
&self.logic
}
pub fn logic_ref_mut(&mut self) -> &mut L {
&mut self.logic
}
}
impl<P: Params + Default + 'static, L: PluginLogicCore<S> + 'static, S: Sample> PluginRuntime
for StaticShell<P, L, S>
{
type Sample = S;
fn info() -> PluginInfo
where
Self: Sized,
{
unreachable!("StaticShell::info() should not be called statically")
}
fn bus_layouts() -> Vec<BusLayout>
where
Self: Sized,
{
unreachable!("StaticShell::bus_layouts() should not be called statically")
}
fn init(&mut self) {}
fn reset(&mut self, sample_rate: f64, max_block_size: usize) {
self.sample_rate = sample_rate;
self.params.set_sample_rate(sample_rate);
self.logic.reset(sample_rate, max_block_size);
}
fn process(
&mut self,
buffer: &mut AudioBuffer<S>,
events: &EventList,
context: &mut ProcessContext,
) -> ProcessStatus {
for e in events.iter() {
if let EventBody::ParamChange { id, value } = &e.body {
self.params.set_plain(*id, *value);
}
}
let params = &self.params;
let meters = &self.meters;
let param_fn = |id: u32| -> f64 { params.get_plain(id).unwrap_or(0.0) };
let meter_fn = |id: u32, v: f32| {
let idx = id.wrapping_sub(truce_params::METER_ID_BASE) as usize;
if let Some(slot) = meters.get(idx) {
slot.store(v.to_bits(), Ordering::Relaxed);
}
};
let mut ctx = ProcessContext::new(
context.transport,
context.sample_rate,
buffer.num_samples(),
&mut *context.output_events,
)
.with_params(¶m_fn)
.with_meters(&meter_fn);
self.logic.process(buffer, events, &mut ctx)
}
fn save_state(&self) -> Vec<u8> {
self.logic.save_state()
}
fn load_state(&mut self, data: &[u8]) -> Result<(), truce_core::state::StateLoadError> {
let result = self.logic.load_state(data);
PluginLogicCore::state_changed(&mut self.logic);
result
}
fn editor(&mut self) -> Option<Box<dyn Editor>> {
Some(PluginLogicCore::editor(&self.logic))
}
fn latency(&self) -> u32 {
self.logic.latency()
}
fn tail(&self) -> u32 {
self.logic.tail()
}
fn get_meter(&self, meter_id: u32) -> f32 {
let idx = meter_id.wrapping_sub(truce_params::METER_ID_BASE) as usize;
if let Some(slot) = self.meters.get(idx) {
f32::from_bits(slot.load(Ordering::Relaxed))
} else {
0.0
}
}
}
#[macro_export]
macro_rules! export_static {
(
params: $params:ty,
info: $info:expr,
logic: $logic:ty,
) => {
pub struct __HotShellWrapper {
inner: $crate::static_shell::StaticShell<$params, $logic, Sample>,
}
impl $crate::__macro_deps::truce_core::plugin::PluginRuntime for __HotShellWrapper {
type Sample = Sample;
fn supports_in_place() -> bool
where
Self: Sized,
{
<$logic as $crate::__macro_deps::truce_plugin::PluginLogicCore<Sample>>::supports_in_place()
}
fn info() -> $crate::__macro_deps::truce_core::info::PluginInfo
where
Self: Sized,
{
$info
}
fn bus_layouts() -> Vec<$crate::__macro_deps::truce_core::bus::BusLayout>
where
Self: Sized,
{
<$logic as $crate::__macro_deps::truce_plugin::PluginLogicCore<Sample>>::bus_layouts()
}
fn init(&mut self) {
self.inner.init();
}
fn reset(&mut self, sample_rate: f64, max_block_size: usize) {
self.inner.reset(sample_rate, max_block_size);
}
fn process(
&mut self,
buffer: &mut $crate::__macro_deps::truce_core::buffer::AudioBuffer<Sample>,
events: &$crate::__macro_deps::truce_core::events::EventList,
context: &mut $crate::__macro_deps::truce_core::process::ProcessContext,
) -> $crate::__macro_deps::truce_core::process::ProcessStatus {
self.inner.process(buffer, events, context)
}
fn save_state(&self) -> Vec<u8> {
self.inner.save_state()
}
fn load_state(
&mut self,
data: &[u8],
) -> Result<(), $crate::__macro_deps::truce_core::state::StateLoadError> {
self.inner.load_state(data)
}
fn editor(
&mut self,
) -> Option<Box<dyn $crate::__macro_deps::truce_core::editor::Editor>> {
self.inner.editor()
}
fn latency(&self) -> u32 {
self.inner.latency()
}
fn tail(&self) -> u32 {
self.inner.tail()
}
fn get_meter(&self, meter_id: u32) -> f32 {
self.inner.get_meter(meter_id)
}
}
impl $crate::__macro_deps::truce_core::export::PluginExport for __HotShellWrapper {
type Params = $params;
fn create() -> Self {
let params = std::sync::Arc::new(<$params>::new());
let logic = <$logic>::new(std::sync::Arc::clone(¶ms));
Self {
inner: $crate::static_shell::StaticShell::from_parts(params, logic),
}
}
fn params(&self) -> &$params {
&self.inner.params
}
fn params_arc(&self) -> std::sync::Arc<$params> {
std::sync::Arc::clone(&self.inner.params)
}
}
};
}