use alloc::boxed::Box;
use alloc::rc::Rc;
use core::cell::RefCell;
use core::ffi::c_void;
use core::ptr::NonNull;
use super::synth_signal::{SynthSignal, SynthSignalSubclass};
use crate::capi_state::CApiState;
use crate::{ctypes::*, TimeTicks};
struct LfoFunctionData {
f: Box<dyn FnMut() -> f32>,
}
struct LfoSubclass {
ptr: NonNull<CSynthLfo>,
function_data: RefCell<Option<LfoFunctionData>>,
}
impl Drop for LfoSubclass {
fn drop(&mut self) {
unsafe { Lfo::fns().freeLFO.unwrap()(self.ptr.as_ptr()) }
}
}
impl SynthSignalSubclass for LfoSubclass {}
pub enum LfoFixedFunction {
Square,
Triangle,
Sine,
SampleAndHold,
SawtoothUp,
SawtoothDown,
}
impl LfoFixedFunction {
fn to_c(self) -> CSynthLfoType {
match self {
Self::Square => CSynthLfoType::kLFOTypeSquare,
Self::Triangle => CSynthLfoType::kLFOTypeTriangle,
Self::Sine => CSynthLfoType::kLFOTypeSine,
Self::SampleAndHold => CSynthLfoType::kLFOTypeSampleAndHold,
Self::SawtoothUp => CSynthLfoType::kLFOTypeSawtoothUp,
Self::SawtoothDown => CSynthLfoType::kLFOTypeSawtoothDown,
}
}
}
pub struct Lfo {
signal: SynthSignal,
subclass: Rc<LfoSubclass>,
}
impl Lfo {
fn from_ptr(ptr: *mut CSynthLfo) -> Self {
let subclass = Rc::new(LfoSubclass {
ptr: NonNull::new(ptr).unwrap(),
function_data: RefCell::new(None),
});
let signal = SynthSignal::new(ptr as *mut CSynthSignalValue, subclass.clone());
Lfo { signal, subclass }
}
pub fn new_with_fixed_function(
lfo_type: LfoFixedFunction,
rate: f32,
phase: f32,
center: f32,
depth: f32,
) -> Self {
let ptr = unsafe { Self::fns().newLFO.unwrap()(CSynthLfoType::kLFOTypeSine) };
let mut lfo = Self::from_ptr(ptr);
lfo.set_fixed_function(lfo_type, rate, phase, center, depth);
lfo
}
pub fn new_with_arpeggiation(steps: &[f32]) -> Self {
let ptr = unsafe { Self::fns().newLFO.unwrap()(CSynthLfoType::kLFOTypeArpeggiator) };
let mut lfo = Self::from_ptr(ptr);
lfo.set_arpeggiation(steps);
lfo
}
pub fn new_with_user_function(
&mut self,
interpolate: bool,
f: impl FnMut() -> f32 + Send + 'static,
) -> Self {
let ptr = unsafe { Self::fns().newLFO.unwrap()(CSynthLfoType::kLFOTypeFunction) };
let mut lfo = Self::from_ptr(ptr);
lfo.set_user_function(interpolate, f);
lfo
}
pub fn set_fixed_function(
&mut self,
lfo_type: LfoFixedFunction,
rate: f32,
phase: f32,
center: f32,
depth: f32,
) {
unsafe { Lfo::fns().setType.unwrap()(self.cptr_mut(), lfo_type.to_c()) };
unsafe { Lfo::fns().setRate.unwrap()(self.cptr_mut(), rate) };
unsafe { Lfo::fns().setPhase.unwrap()(self.cptr_mut(), phase) };
unsafe { Lfo::fns().setCenter.unwrap()(self.cptr_mut(), center) };
unsafe { Lfo::fns().setDepth.unwrap()(self.cptr_mut(), depth) };
}
pub fn set_arpeggiation(&mut self, steps: &[f32]) {
unsafe { Lfo::fns().setType.unwrap()(self.cptr_mut(), CSynthLfoType::kLFOTypeArpeggiator) };
unsafe {
Lfo::fns().setArpeggiation.unwrap()(
self.cptr_mut(),
steps.len() as i32,
steps.as_ptr() as *mut f32,
)
};
}
pub fn set_user_function(&mut self, interpolate: bool, f: impl FnMut() -> f32 + Send + 'static) {
unsafe { Lfo::fns().setType.unwrap()(self.cptr_mut(), CSynthLfoType::kLFOTypeFunction) };
unsafe extern "C" fn c_func(_clfo: *mut CSynthLfo, data: *mut c_void) -> f32 {
let data = data as *mut LfoFunctionData;
((*data).f)()
}
*self.subclass.function_data.borrow_mut() = Some(LfoFunctionData { f: Box::new(f) });
let data_ptr = unsafe {
self.subclass.function_data.borrow_mut().as_mut().unwrap_unchecked() as *mut LfoFunctionData
};
unsafe {
Lfo::fns().setFunction.unwrap()(
self.cptr_mut(),
Some(c_func),
data_ptr as *mut c_void,
interpolate as i32,
)
}
}
pub fn set_delay(&mut self, holdoff: TimeTicks, ramp_time: TimeTicks) {
unsafe {
Self::fns().setDelay.unwrap()(self.cptr_mut(), holdoff.to_seconds(), ramp_time.to_seconds())
}
}
pub fn set_retrigger(&mut self, retrigger: bool) {
unsafe { Self::fns().setRetrigger.unwrap()(self.cptr_mut(), retrigger as i32) }
}
pub fn set_global(&mut self, global: bool) {
unsafe { Self::fns().setGlobal.unwrap()(self.cptr_mut(), global as i32) }
}
pub fn get_value(&self) -> f32 {
unsafe { Self::fns().getValue.unwrap()(self.cptr() as *mut _) }
}
pub(crate) fn cptr(&self) -> *const CSynthLfo {
self.subclass.ptr.as_ptr()
}
pub(crate) fn cptr_mut(&mut self) -> *mut CSynthLfo {
self.subclass.ptr.as_ptr()
}
pub(crate) fn fns() -> &'static craydate_sys::playdate_sound_lfo {
unsafe { &*CApiState::get().csound.lfo }
}
}
impl AsRef<SynthSignal> for Lfo {
fn as_ref(&self) -> &SynthSignal {
&self.signal
}
}
impl AsMut<SynthSignal> for Lfo {
fn as_mut(&mut self) -> &mut SynthSignal {
&mut self.signal
}
}