use alloc::rc::{Rc, Weak};
use core::ptr::NonNull;
use super::super::{SoundCompletionCallback, StereoVolume};
use crate::callback_builder::Constructed;
use crate::callbacks::RegisteredCallback;
use crate::capi_state::CApiState;
use crate::ctypes::*;
use crate::error::Error;
#[derive(Debug)]
enum Attachment {
None,
Channel(Weak<NonNull<CSoundChannel>>),
}
impl Attachment {
fn is_none(&self) -> bool {
match self {
Self::None => true,
_ => false,
}
}
}
#[derive(Debug)]
pub struct SoundSource {
ptr: NonNull<CSoundSource>,
attachment: Attachment,
completion_callback: Option<RegisteredCallback>,
}
impl SoundSource {
pub(crate) fn from_ptr(ptr: *mut CSoundSource) -> Self {
SoundSource {
ptr: NonNull::new(ptr).unwrap(),
attachment: Attachment::None,
completion_callback: None,
}
}
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 r = unsafe {
(*CApiState::get().csound.channel).addSource.unwrap()(channel.as_ptr(), self.cptr_mut())
};
assert!(r != 0);
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)) => {
let r = unsafe {
(*CApiState::get().csound.channel).removeSource.unwrap()(
channel.as_ptr(),
self.cptr_mut(),
)
};
self.attachment = Attachment::None;
assert!(r != 0);
return Ok(());
}
_ => Err(Error::NotFoundError),
}
}
pub(crate) fn is_attached(&self) -> bool {
!self.attachment.is_none()
}
pub fn volume(&self) -> StereoVolume {
let mut v = StereoVolume::zero();
unsafe {
Self::fns().getVolume.unwrap()(
self.cptr() as *mut _,
v.left.as_mut_ptr(),
v.right.as_mut_ptr(),
)
};
v
}
pub fn set_volume(&mut self, v: StereoVolume) {
unsafe { Self::fns().setVolume.unwrap()(self.cptr_mut(), v.left.into(), v.right.into()) }
}
pub fn is_playing(&self) -> bool {
unsafe { Self::fns().isPlaying.unwrap()(self.cptr() as *mut _) != 0 }
}
pub fn set_completion_callback<'a, T, F: Fn(T) + 'static>(
&mut self,
completion_callback: SoundCompletionCallback<'a, T, F, Constructed>,
) {
self.completion_callback = None;
let func = completion_callback.into_inner().and_then(|(callbacks, cb)| {
let key = self.cptr_mut() as usize;
let (func, reg) = callbacks.add_sound_source_completion(key, cb);
self.completion_callback = Some(reg);
Some(func)
});
unsafe { Self::fns().setFinishCallback.unwrap()(self.cptr_mut(), func) }
}
pub(crate) fn cptr(&self) -> *const CSoundSource {
self.ptr.as_ptr()
}
pub(crate) fn cptr_mut(&mut self) -> *mut CSoundSource {
self.ptr.as_ptr()
}
pub(crate) fn fns() -> &'static craydate_sys::playdate_sound_source {
unsafe { &*CApiState::get().csound.source }
}
}
impl Drop for SoundSource {
fn drop(&mut self) {
self.set_completion_callback(SoundCompletionCallback::none());
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 AsSoundSource: AsRef<SoundSource> + AsMut<SoundSource> {
fn as_source(&self) -> &SoundSource {
self.as_ref()
}
fn as_source_mut(&mut self) -> &mut SoundSource {
self.as_mut()
}
}
impl<T> AsSoundSource for T where T: AsRef<SoundSource> + AsMut<SoundSource> {}