craydate/sound/midi/
sequence.rs

1use alloc::collections::BTreeMap;
2use alloc::vec::Vec;
3use core::marker::PhantomData;
4use core::ptr::NonNull;
5
6use super::super::sources::instrument::Instrument;
7use super::super::SoundCompletionCallback;
8use super::sequence_track::{SequenceTrack, SequenceTrackMut};
9use crate::callback_builder::Constructed;
10use crate::callbacks::RegisteredCallback;
11use crate::capi_state::CApiState;
12use crate::ctypes::*;
13use crate::error::Error;
14use crate::null_terminated::ToNullTerminatedString;
15
16/// Represents a MIDI music file, as a collection of `SequenceTrack`s that can be played together.
17pub struct Sequence {
18  ptr: NonNull<CSoundSequence>,
19  finished_callback: Option<RegisteredCallback>,
20
21  // Holds ownership of user-created tracks. Loading a MIDI file generates Playdate-owned tracks
22  // which are not represented here.
23  user_created_tracks: Vec<NonNull<CSequenceTrack>>,
24  // The set of instruments attached to tracks. Some of the tracks are owned by Playdate, and some
25  // are owned by the this Sequence type. But all instruments are owned by this Sequence.
26  instruments: BTreeMap<u32, Instrument>,
27}
28impl Sequence {
29  fn from_ptr(ptr: *mut CSoundSequence) -> Self {
30    Sequence {
31      ptr: NonNull::new(ptr).unwrap(),
32      finished_callback: None,
33      user_created_tracks: Vec::new(),
34      instruments: BTreeMap::new(),
35    }
36  }
37
38  /// Constructs a new `Sequence`, which is a set of `SequenceTrack`s that can be played together.
39  pub(crate) fn new() -> Self {
40    let ptr = unsafe { Self::fns().newSequence.unwrap()() };
41    Self::from_ptr(ptr)
42  }
43
44  /// Loads the midi file at `path` and constructs a `Sequence` from it.
45  ///
46  /// Returns an `Error::LoadMidiFileError` if loading the file did not succeed. No further
47  /// information about why the load failed is available.
48  pub fn from_midi_file(path: &str) -> Result<Self, Error> {
49    let mut seq = Self::new();
50    let r = unsafe {
51      Self::fns().loadMidiFile.unwrap()(seq.cptr_mut(), path.to_null_terminated_utf8().as_ptr())
52    };
53    match r {
54      0 => Err(Error::LoadMidiFileError),
55      _ => {
56        seq.create_instrument_for_each_track();
57        Ok(seq)
58      }
59    }
60  }
61
62  /// Create an instrument for each track that doesn't have one set yet, so that all tracks in the
63  /// `Sequence` always have an `Instrument`.
64  fn create_instrument_for_each_track(&mut self) {
65    let mut instruments = BTreeMap::new();
66    let mut count = self.tracks_count();
67    let mut index = 0;
68    while count > 0 {
69      // The track indices may not be contiguous, so we have to look for which indices don't have a
70      // null track.
71      let track_ptr = unsafe { Sequence::fns().getTrackAtIndex.unwrap()(self.cptr_mut(), index) };
72      if !track_ptr.is_null() {
73        count -= 1;
74        if !self.instruments.contains_key(&index) {
75          assert!(unsafe { SequenceTrack::fns().getInstrument.unwrap()(track_ptr) }.is_null());
76          let mut instrument = Instrument::new();
77          unsafe { SequenceTrack::fns().setInstrument.unwrap()(track_ptr, instrument.cptr_mut()) };
78          instruments.insert(index, instrument);
79        }
80      }
81      index += 1;
82    }
83    self.instruments = instruments;
84  }
85
86  /// Called from `SequenceTrack`, where an `Instrument` can be set on it. This holds ownership of
87  /// that `Instrument`.
88  pub(crate) fn set_track_instrument(&mut self, index: u32, instrument: Instrument) {
89    self.instruments.insert(index, instrument);
90  }
91  /// Gives access to the `Instrument` of a `SequenceTrack` from the `SequenceTrack`.
92  pub(crate) fn track_instrument(&self, index: u32) -> &Instrument {
93    self.instruments.get(&index).unwrap()
94  }
95  /// Gives access to the `Instrument` of a `SequenceTrack` from the `SequenceTrack`.
96  pub(crate) fn track_instrument_mut(&mut self, index: u32) -> &mut Instrument {
97    self.instruments.get_mut(&index).unwrap()
98  }
99
100  /// Starts playing the sequence.
101  ///
102  /// The `finished_callback` is an optional closure to be called when the sequence finishes playing
103  /// or is stopped. It not `SoundCompletionCallback::none()`, the callback will be registered as a
104  /// system event, and the application will be notified to run the callback via a
105  /// `SystemEvent::Callback` event. When that occurs, the application's `Callbacks` object which
106  /// was used to construct the `completion_callback` can be `run()` to execute the closure bound in
107  /// the `completion_callback`.
108  ///
109  /// # Example
110  /// ```
111  /// let callbacks: Callbacks<i32> = Callbacks::new();
112  /// // Register a closure as a callback.
113  /// sequence.play(SoundCompletionCallback::with(&mut callbacks).call(|i: i32| {
114  ///   println("playing done");
115  /// }));
116  /// match system_event_watcher.next() {
117  ///   SystemEvent::Callback => {
118  ///     // Run the closure registered above.
119  ///     callbacks.run(12);
120  ///   }
121  /// }
122  /// ```
123  pub fn play<'a, T, F: Fn(T) + 'static>(
124    &mut self,
125    finished_callback: SoundCompletionCallback<'a, T, F, Constructed>,
126  ) {
127    self.finished_callback = None;
128    let func = finished_callback.into_inner().and_then(|(callbacks, cb)| {
129      let key = self.cptr_mut() as usize;
130      let (func, reg) = callbacks.add_sequence_finished(key, cb);
131      self.finished_callback = Some(reg);
132      Some(func)
133    });
134    unsafe { Self::fns().play.unwrap()(self.cptr_mut(), func, core::ptr::null_mut()) }
135  }
136
137  /// Stops playing the sequence.
138  pub fn stop(&mut self) {
139    unsafe { Self::fns().stop.unwrap()(self.cptr_mut()) }
140  }
141
142  /// Sends a stop signal to all playing notes on all tracks.
143  pub fn all_notes_off(&mut self) {
144    unsafe { Self::fns().allNotesOff.unwrap()(self.cptr_mut()) }
145  }
146
147  /// Returns if the sequence is currently playing.
148  pub fn is_playing(&self) -> bool {
149    // isPlaying() takes a mutable pointer but doesn't mutate any visible state.
150    unsafe { Self::fns().isPlaying.unwrap()(self.cptr() as *mut _) != 0 }
151  }
152
153  /// Sets the current time in the sequence, in steps since the start of the MIDI file.
154  ///
155  /// Note that which step this moves the sequence to depends on the current tempo.
156  pub fn set_current_step(&mut self, time: u32) {
157    unsafe { Self::fns().setTime.unwrap()(self.cptr_mut(), time) }
158  }
159  /// Gets the current time in the sequence, in steps since the start of the file.
160  ///
161  /// Note that which step this refers to depends on the current tempo.
162  pub fn current_step(&self) -> u32 {
163    // getTime() takes a mutable pointer but doesn't mutate any visible state.
164    unsafe { Self::fns().getTime.unwrap()(self.cptr() as *mut _) }
165  }
166
167  /// Sets the tempo of the sequence, in steps per second.
168  pub fn set_tempo(&mut self, steps_per_second: i32) {
169    unsafe { Self::fns().setTempo.unwrap()(self.cptr_mut(), steps_per_second) }
170  }
171  /// Gets the tempo of the sequence, in steps per second.
172  pub fn tempo(&mut self) -> i32 {
173    // getTempo() takes a mutable pointer but doesn't mutate any visible state.
174    unsafe { Self::fns().getTempo.unwrap()(self.cptr() as *mut _) }
175  }
176
177  /// Returns the length of the longest track in the sequence.
178  ///
179  /// See also `SequenceTrack::steps_count()`.
180  pub fn steps_count(&self) -> u32 {
181    // getLength() takes a mutable pointer but doesn't mutate any visible state.
182    unsafe { Self::fns().getLength.unwrap()(self.cptr() as *mut _) }
183  }
184
185  /// Returns the number of tracks in the sequence.
186  pub fn tracks_count(&self) -> u32 {
187    // getTrackCount() takes a mutable pointer but doesn't mutate any visible state.
188    let c = unsafe { Self::fns().getTrackCount.unwrap()(self.cptr() as *mut _) };
189    // getTrackCount() returns i32, but getTrackAtIndex takes u32. If anything, we could expect
190    // getTrackCount() to change to u32 one day, so we'll cast to that instead of the other way.
191    c as u32
192  }
193
194  /// Returns an iterator over all the tracks in the `Sequence`.
195  pub fn tracks<'a>(&'a self) -> impl Iterator<Item = SequenceTrack> + 'a {
196    SequenceTrackIter {
197      sequence: self,
198      next_index: 0,
199      count_left: self.tracks_count() as usize,
200      count_total: self.tracks_count() as usize,
201    }
202  }
203  /// Returns a mutable iterator over all the tracks in the `Sequence`.
204  pub fn tracks_mut<'a>(&'a mut self) -> impl Iterator<Item = SequenceTrackMut<'a>> + 'a {
205    SequenceTrackIterMut {
206      sequence: NonNull::new(self).unwrap(),
207      next_index: 0,
208      count_left: self.tracks_count() as usize,
209      count_total: self.tracks_count() as usize,
210      _marker: PhantomData,
211    }
212  }
213
214  /// Creates a new `SequenceTrack` at the given `index`, replacing an existing track if there was
215  /// one.
216  pub fn create_track_at_index(&mut self, index: u32) -> SequenceTrackMut<'_> {
217    let track_ptr = unsafe { SequenceTrack::fns().newTrack.unwrap()() };
218    assert!(!track_ptr.is_null());
219    unsafe { Sequence::fns().setTrackAtIndex.unwrap()(self.cptr_mut(), track_ptr, index) };
220    let mut instrument = Instrument::new();
221    unsafe { SequenceTrack::fns().setInstrument.unwrap()(track_ptr, instrument.cptr_mut()) };
222    self.instruments.insert(index, instrument);
223    SequenceTrackMut::new(track_ptr, index, self, self.track_instrument_mut(index))
224  }
225  /// Gets the `SequenceTrack` at the given `index` if there is one. Otherwise, returns `None`.
226  pub fn track_at_index(&self, index: u32) -> Option<SequenceTrack> {
227    if self.instruments.contains_key(&index) {
228      // getTrackAtIndex() takes a mutable pointer but doesn't mutate any visible state.
229      let track_ptr =
230        unsafe { Sequence::fns().getTrackAtIndex.unwrap()(self.cptr() as *mut _, index) };
231      assert!(!track_ptr.is_null());
232      Some(SequenceTrack::new(
233        track_ptr,
234        index,
235        self.track_instrument(index),
236      ))
237    } else {
238      None
239    }
240  }
241  /// Gets the `SequenceTrack` at the given `index` if there is one. Otherwise, returns `None`.
242  pub fn track_at_index_mut(&mut self, index: u32) -> Option<SequenceTrackMut<'_>> {
243    if self.instruments.contains_key(&index) {
244      let track_ptr = unsafe { Sequence::fns().getTrackAtIndex.unwrap()(self.cptr_mut(), index) };
245      assert!(!track_ptr.is_null());
246      Some(SequenceTrackMut::new(
247        track_ptr,
248        index,
249        self,
250        self.track_instrument_mut(index),
251      ))
252    } else {
253      None
254    }
255  }
256
257  /// Sets the looping range of the sequence.
258  ///
259  /// If loops is 0, the loop repeats endlessly.
260  pub fn set_loops(&mut self, start_step: u32, end_step: u32, count: i32) {
261    // BUG: The step numbers should be u32 but the Playdate C Api has them as `int`:
262    // <https://devforum.play.date/t/playdate-sound-sequence-setloops-takes-int-but-should-take-uint32-t/4980>
263    unsafe {
264      Self::fns().setLoops.unwrap()(self.cptr_mut(), start_step as i32, end_step as i32, count)
265    }
266  }
267
268  pub(crate) fn cptr(&self) -> *const CSoundSequence {
269    self.ptr.as_ptr()
270  }
271  pub(crate) fn cptr_mut(&mut self) -> *mut CSoundSequence {
272    self.ptr.as_ptr()
273  }
274  pub(crate) fn fns() -> &'static craydate_sys::playdate_sound_sequence {
275    unsafe { &*CApiState::get().csound.sequence }
276  }
277}
278
279impl Drop for Sequence {
280  fn drop(&mut self) {
281    // The instruments will be dropped after the sequence-owned tracks that refer to them.
282    unsafe { Self::fns().freeSequence.unwrap()(self.cptr_mut()) }
283    // The instruments will be dropped after the sequence-owned tracks that refer to them.
284    for ptr in self.user_created_tracks.drain(..) {
285      unsafe { SequenceTrack::fns().freeTrack.unwrap()(ptr.as_ptr()) }
286    }
287  }
288}
289
290struct SequenceTrackIter<'a> {
291  sequence: &'a Sequence,
292  next_index: u32,
293  count_left: usize,
294  count_total: usize,
295}
296impl<'a> Iterator for SequenceTrackIter<'a> {
297  type Item = SequenceTrack<'a>;
298
299  fn next(&mut self) -> Option<Self::Item> {
300    if self.count_left == 0 {
301      None
302    } else {
303      loop {
304        let index = self.next_index;
305        self.next_index += 1;
306        let track_ptr = unsafe {
307          // getTrackAtIndex() takes a mutable pointer but doesn't mutate any visible state.
308          Sequence::fns().getTrackAtIndex.unwrap()(self.sequence.cptr() as *mut _, index)
309        };
310        if !track_ptr.is_null() {
311          self.count_left -= 1;
312          return Some(SequenceTrack::new(
313            track_ptr,
314            index,
315            self.sequence.track_instrument(index),
316          ));
317        }
318      }
319    }
320  }
321
322  fn size_hint(&self) -> (usize, Option<usize>) {
323    (self.count_total, Some(self.count_total))
324  }
325}
326impl ExactSizeIterator for SequenceTrackIter<'_> {}
327impl core::iter::FusedIterator for SequenceTrackIter<'_> {}
328
329struct SequenceTrackIterMut<'a> {
330  sequence: NonNull<Sequence>,
331  next_index: u32,
332  count_left: usize,
333  count_total: usize,
334  _marker: PhantomData<&'a Sequence>,
335}
336impl<'a> Iterator for SequenceTrackIterMut<'a> {
337  type Item = SequenceTrackMut<'a>;
338
339  fn next(&mut self) -> Option<Self::Item> {
340    if self.count_left == 0 {
341      None
342    } else {
343      loop {
344        let index = self.next_index;
345        self.next_index += 1;
346        let track_ptr = unsafe {
347          Sequence::fns().getTrackAtIndex.unwrap()(self.sequence.as_mut().cptr_mut(), index)
348        };
349        if !track_ptr.is_null() {
350          self.count_left -= 1;
351          return Some(SequenceTrackMut::new(
352            track_ptr,
353            index,
354            self.sequence.as_ptr(),
355            unsafe { self.sequence.as_mut().track_instrument_mut(index) },
356          ));
357        }
358      }
359    }
360  }
361
362  fn size_hint(&self) -> (usize, Option<usize>) {
363    (self.count_total, Some(self.count_total))
364  }
365}
366impl ExactSizeIterator for SequenceTrackIterMut<'_> {}
367impl core::iter::FusedIterator for SequenceTrackIterMut<'_> {}