use alloc::rc::Rc;
use alloc::rc::Weak;
use core::ptr::NonNull;
use super::super::signals::synth_signal::SynthSignal;
use crate::capi_state::CApiState;
use crate::ctypes::*;
use crate::error::Error;
#[derive(Debug)]
enum Attachment {
None,
Channel(Weak<NonNull<CSoundChannel>>),
}
#[derive(Debug)]
pub struct SoundEffect {
ptr: NonNull<CSoundEffect>,
attachment: Attachment,
mix_modulator: Option<SynthSignal>,
}
impl SoundEffect {
pub(crate) fn from_ptr(ptr: *mut CSoundEffect) -> Self {
SoundEffect {
ptr: NonNull::new(ptr).unwrap(),
attachment: Attachment::None,
mix_modulator: None,
}
}
pub fn set_mix(&mut self, mix: f32) {
unsafe { Self::fns().setMix.unwrap()(self.cptr_mut(), mix) }
}
pub fn set_mix_modulator<T: AsRef<SynthSignal>>(&mut self, signal: Option<&T>) {
let modulator_ptr = signal.map_or_else(core::ptr::null_mut, |signal|
signal.as_ref().cptr() as *mut _);
unsafe { Self::fns().setMixModulator.unwrap()(self.cptr_mut(), modulator_ptr) }
self.mix_modulator = signal.map(|signal| signal.as_ref().clone());
}
pub fn mix_modulator(&mut self) -> Option<&SynthSignal> {
self.mix_modulator.as_ref()
}
pub(crate) fn attach_to_channel(
&mut self,
channel: &Rc<NonNull<CSoundChannel>>,
) -> Result<(), Error> {
match self.attachment {
Attachment::None => {
self.attachment = Attachment::Channel(Rc::downgrade(&channel));
let channel_api = CApiState::get().csound.channel;
unsafe { (*channel_api).addEffect.unwrap()(channel.as_ptr(), self.cptr_mut()) };
Ok(())
}
_ => Err(Error::AlreadyAttachedError),
}
}
pub(crate) fn detach_from_channel(
&mut self,
channel: &Rc<NonNull<CSoundChannel>>,
) -> Result<(), Error> {
match &mut self.attachment {
Attachment::Channel(weak_ptr) if weak_ptr.ptr_eq(&Rc::downgrade(channel)) => {
self.attachment = Attachment::None;
let channel_api = CApiState::get().csound.channel;
unsafe { (*channel_api).removeEffect.unwrap()(channel.as_ptr(), self.cptr_mut()) };
Ok(())
}
_ => Err(Error::NotFoundError),
}
}
pub(crate) fn cptr_mut(&mut self) -> *mut CSoundEffect {
self.ptr.as_ptr()
}
pub(crate) fn fns() -> &'static craydate_sys::playdate_sound_effect {
unsafe { &*CApiState::get().csound.effect }
}
}
impl Drop for SoundEffect {
fn drop(&mut self) {
match &self.attachment {
Attachment::None => (),
Attachment::Channel(weak_ptr) => {
if let Some(rc_ptr) = weak_ptr.upgrade() {
let r = self.detach_from_channel(&rc_ptr);
assert!(r.is_ok()); }
}
}
}
}
pub trait AsSoundEffect: AsRef<SoundEffect> + AsMut<SoundEffect> {
fn as_sound_effect(&self) -> &SoundEffect {
self.as_ref()
}
fn as_sound_effect_mut(&mut self) -> &mut SoundEffect {
self.as_mut()
}
}
impl<T> AsSoundEffect for T where T: AsRef<SoundEffect> + AsMut<SoundEffect> {}