use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use core::marker::PhantomData;
use core::ptr::NonNull;
use super::super::sources::instrument::Instrument;
use super::super::SoundCompletionCallback;
use super::sequence_track::{SequenceTrack, SequenceTrackMut};
use crate::callback_builder::Constructed;
use crate::callbacks::RegisteredCallback;
use crate::capi_state::CApiState;
use crate::ctypes::*;
use crate::error::Error;
use crate::null_terminated::ToNullTerminatedString;
pub struct Sequence {
ptr: NonNull<CSoundSequence>,
finished_callback: Option<RegisteredCallback>,
user_created_tracks: Vec<NonNull<CSequenceTrack>>,
instruments: BTreeMap<u32, Instrument>,
}
impl Sequence {
fn from_ptr(ptr: *mut CSoundSequence) -> Self {
Sequence {
ptr: NonNull::new(ptr).unwrap(),
finished_callback: None,
user_created_tracks: Vec::new(),
instruments: BTreeMap::new(),
}
}
pub(crate) fn new() -> Self {
let ptr = unsafe { Self::fns().newSequence.unwrap()() };
Self::from_ptr(ptr)
}
pub fn from_midi_file(path: &str) -> Result<Self, Error> {
let mut seq = Self::new();
let r = unsafe {
Self::fns().loadMidiFile.unwrap()(seq.cptr_mut(), path.to_null_terminated_utf8().as_ptr())
};
match r {
0 => Err(Error::LoadMidiFileError),
_ => {
seq.create_instrument_for_each_track();
Ok(seq)
}
}
}
fn create_instrument_for_each_track(&mut self) {
let mut instruments = BTreeMap::new();
let mut count = self.tracks_count();
let mut index = 0;
while count > 0 {
let track_ptr = unsafe { Sequence::fns().getTrackAtIndex.unwrap()(self.cptr_mut(), index) };
if !track_ptr.is_null() {
count -= 1;
if !self.instruments.contains_key(&index) {
assert!(unsafe { SequenceTrack::fns().getInstrument.unwrap()(track_ptr) }.is_null());
let mut instrument = Instrument::new();
unsafe { SequenceTrack::fns().setInstrument.unwrap()(track_ptr, instrument.cptr_mut()) };
instruments.insert(index, instrument);
}
}
index += 1;
}
self.instruments = instruments;
}
pub(crate) fn set_track_instrument(&mut self, index: u32, instrument: Instrument) {
self.instruments.insert(index, instrument);
}
pub(crate) fn track_instrument(&self, index: u32) -> &Instrument {
self.instruments.get(&index).unwrap()
}
pub(crate) fn track_instrument_mut(&mut self, index: u32) -> &mut Instrument {
self.instruments.get_mut(&index).unwrap()
}
pub fn play<'a, T, F: Fn(T) + 'static>(
&mut self,
finished_callback: SoundCompletionCallback<'a, T, F, Constructed>,
) {
self.finished_callback = None;
let func = finished_callback.into_inner().and_then(|(callbacks, cb)| {
let key = self.cptr_mut() as usize;
let (func, reg) = callbacks.add_sequence_finished(key, cb);
self.finished_callback = Some(reg);
Some(func)
});
unsafe { Self::fns().play.unwrap()(self.cptr_mut(), func, core::ptr::null_mut()) }
}
pub fn stop(&mut self) {
unsafe { Self::fns().stop.unwrap()(self.cptr_mut()) }
}
pub fn all_notes_off(&mut self) {
unsafe { Self::fns().allNotesOff.unwrap()(self.cptr_mut()) }
}
pub fn is_playing(&self) -> bool {
unsafe { Self::fns().isPlaying.unwrap()(self.cptr() as *mut _) != 0 }
}
pub fn set_current_step(&mut self, time: u32) {
unsafe { Self::fns().setTime.unwrap()(self.cptr_mut(), time) }
}
pub fn current_step(&self) -> u32 {
unsafe { Self::fns().getTime.unwrap()(self.cptr() as *mut _) }
}
pub fn set_tempo(&mut self, steps_per_second: i32) {
unsafe { Self::fns().setTempo.unwrap()(self.cptr_mut(), steps_per_second) }
}
pub fn tempo(&mut self) -> i32 {
unsafe { Self::fns().getTempo.unwrap()(self.cptr() as *mut _) }
}
pub fn steps_count(&self) -> u32 {
unsafe { Self::fns().getLength.unwrap()(self.cptr() as *mut _) }
}
pub fn tracks_count(&self) -> u32 {
let c = unsafe { Self::fns().getTrackCount.unwrap()(self.cptr() as *mut _) };
c as u32
}
pub fn tracks<'a>(&'a self) -> impl Iterator<Item = SequenceTrack> + 'a {
SequenceTrackIter {
sequence: self,
next_index: 0,
count_left: self.tracks_count() as usize,
count_total: self.tracks_count() as usize,
}
}
pub fn tracks_mut<'a>(&'a mut self) -> impl Iterator<Item = SequenceTrackMut<'a>> + 'a {
SequenceTrackIterMut {
sequence: NonNull::new(self).unwrap(),
next_index: 0,
count_left: self.tracks_count() as usize,
count_total: self.tracks_count() as usize,
_marker: PhantomData,
}
}
pub fn create_track_at_index(&mut self, index: u32) -> SequenceTrackMut<'_> {
let track_ptr = unsafe { SequenceTrack::fns().newTrack.unwrap()() };
assert!(!track_ptr.is_null());
unsafe { Sequence::fns().setTrackAtIndex.unwrap()(self.cptr_mut(), track_ptr, index) };
let mut instrument = Instrument::new();
unsafe { SequenceTrack::fns().setInstrument.unwrap()(track_ptr, instrument.cptr_mut()) };
self.instruments.insert(index, instrument);
SequenceTrackMut::new(track_ptr, index, self, self.track_instrument_mut(index))
}
pub fn track_at_index(&self, index: u32) -> Option<SequenceTrack> {
if self.instruments.contains_key(&index) {
let track_ptr =
unsafe { Sequence::fns().getTrackAtIndex.unwrap()(self.cptr() as *mut _, index) };
assert!(!track_ptr.is_null());
Some(SequenceTrack::new(
track_ptr,
index,
self.track_instrument(index),
))
} else {
None
}
}
pub fn track_at_index_mut(&mut self, index: u32) -> Option<SequenceTrackMut<'_>> {
if self.instruments.contains_key(&index) {
let track_ptr = unsafe { Sequence::fns().getTrackAtIndex.unwrap()(self.cptr_mut(), index) };
assert!(!track_ptr.is_null());
Some(SequenceTrackMut::new(
track_ptr,
index,
self,
self.track_instrument_mut(index),
))
} else {
None
}
}
pub fn set_loops(&mut self, start_step: u32, end_step: u32, count: i32) {
unsafe {
Self::fns().setLoops.unwrap()(self.cptr_mut(), start_step as i32, end_step as i32, count)
}
}
pub(crate) fn cptr(&self) -> *const CSoundSequence {
self.ptr.as_ptr()
}
pub(crate) fn cptr_mut(&mut self) -> *mut CSoundSequence {
self.ptr.as_ptr()
}
pub(crate) fn fns() -> &'static craydate_sys::playdate_sound_sequence {
unsafe { &*CApiState::get().csound.sequence }
}
}
impl Drop for Sequence {
fn drop(&mut self) {
unsafe { Self::fns().freeSequence.unwrap()(self.cptr_mut()) }
for ptr in self.user_created_tracks.drain(..) {
unsafe { SequenceTrack::fns().freeTrack.unwrap()(ptr.as_ptr()) }
}
}
}
struct SequenceTrackIter<'a> {
sequence: &'a Sequence,
next_index: u32,
count_left: usize,
count_total: usize,
}
impl<'a> Iterator for SequenceTrackIter<'a> {
type Item = SequenceTrack<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.count_left == 0 {
None
} else {
loop {
let index = self.next_index;
self.next_index += 1;
let track_ptr = unsafe {
Sequence::fns().getTrackAtIndex.unwrap()(self.sequence.cptr() as *mut _, index)
};
if !track_ptr.is_null() {
self.count_left -= 1;
return Some(SequenceTrack::new(
track_ptr,
index,
self.sequence.track_instrument(index),
));
}
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.count_total, Some(self.count_total))
}
}
impl ExactSizeIterator for SequenceTrackIter<'_> {}
impl core::iter::FusedIterator for SequenceTrackIter<'_> {}
struct SequenceTrackIterMut<'a> {
sequence: NonNull<Sequence>,
next_index: u32,
count_left: usize,
count_total: usize,
_marker: PhantomData<&'a Sequence>,
}
impl<'a> Iterator for SequenceTrackIterMut<'a> {
type Item = SequenceTrackMut<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.count_left == 0 {
None
} else {
loop {
let index = self.next_index;
self.next_index += 1;
let track_ptr = unsafe {
Sequence::fns().getTrackAtIndex.unwrap()(self.sequence.as_mut().cptr_mut(), index)
};
if !track_ptr.is_null() {
self.count_left -= 1;
return Some(SequenceTrackMut::new(
track_ptr,
index,
self.sequence.as_ptr(),
unsafe { self.sequence.as_mut().track_instrument_mut(index) },
));
}
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.count_total, Some(self.count_total))
}
}
impl ExactSizeIterator for SequenceTrackIterMut<'_> {}
impl core::iter::FusedIterator for SequenceTrackIterMut<'_> {}