craydate/sound/sources/
sample_player.rs

1use core::marker::PhantomData;
2use core::mem::ManuallyDrop;
3use core::ptr::NonNull;
4
5use super::super::audio_sample::AudioSample;
6use super::super::SoundCompletionCallback;
7use super::sound_source::{AsSoundSource, SoundSource};
8use crate::callback_builder::Constructed;
9use crate::callbacks::RegisteredCallback;
10use crate::capi_state::CApiState;
11use crate::ctypes::*;
12use crate::time::{RelativeTimeSpan, TimeDelta};
13
14/// A `SamplePlayer` will play an `AudioSample`.
15///
16/// The `SamplePlayer` acts as a `SoundSource` so it can be connected to a `SoundChannel` to play
17/// the sample to the device's audio output. The `SamplePlayer` holds a borrow on the `AudioSample`
18/// rather than taking ownership.
19#[derive(Debug)]
20pub struct SamplePlayer<'sample> {
21  source: ManuallyDrop<SoundSource>,
22  ptr: NonNull<CSamplePlayer>,
23  loop_callback: Option<RegisteredCallback>,
24  _marker: PhantomData<&'sample AudioSample>,
25}
26impl SamplePlayer<'_> {
27  /// Creates a new SamplePlayer.
28  pub fn new(sample: &AudioSample) -> Self {
29    let ptr = unsafe { Self::fns().newPlayer.unwrap()() };
30    // setSample() takes a mutable sample pointer but doesn't mutate any visible state.
31    unsafe { Self::fns().setSample.unwrap()(ptr, sample.cptr() as *mut _) }
32    SamplePlayer {
33      source: ManuallyDrop::new(SoundSource::from_ptr(ptr as *mut CSoundSource)),
34      ptr: NonNull::new(ptr).unwrap(),
35      loop_callback: None,
36      _marker: PhantomData,
37    }
38  }
39
40  /// Returns the length of AudioSample assigned to the player.
41  pub fn len(&self) -> TimeDelta {
42    // getLength() takes a mutable pointer it changes no visible state.
43    TimeDelta::from_seconds_lossy(unsafe { Self::fns().getLength.unwrap()(self.cptr() as *mut _) })
44  }
45
46  /// Starts playing the sample attached to the player.
47  ///
48  /// If repeat is greater than one, it loops the given number of times. If zero, it loops endlessly
49  /// until it is stopped with `stop()`. If negative one, it does ping-pong looping.
50  ///
51  /// Sets the playback rate for the player. 1.0 is normal speed, 0.5 is down an octave, 2.0 is up
52  /// an octave, etc.
53  pub fn play(&mut self, repeat: i32, rate: f32) {
54    // TODO: What does the return value of play() mean here?
55    let r = unsafe { Self::fns().play.unwrap()(self.cptr_mut(), repeat, rate) };
56    assert!(r != 0)
57  }
58  pub fn stop(&mut self) {
59    unsafe { Self::fns().stop.unwrap()(self.cptr_mut()) };
60  }
61  /// Pauses playback of the SamplePlayer.
62  pub fn pause(&mut self) {
63    unsafe { Self::fns().setPaused.unwrap()(self.cptr_mut(), 1) }
64  }
65  /// Resumes playback of the SamplePlayer.
66  pub fn unpause(&mut self) {
67    unsafe { Self::fns().setPaused.unwrap()(self.cptr_mut(), 1) }
68  }
69  /// Returns if the player is playing a sample.
70  pub fn is_playing(&self) -> bool {
71    // isPlaying() takes a mutable pointer it changes no visible state.
72    unsafe { Self::fns().isPlaying.unwrap()(self.cptr() as *mut _) != 0 }
73  }
74
75  /// Sets the current offset of the SamplePlayer.
76  pub fn set_offset(&mut self, offset: TimeDelta) {
77    unsafe { Self::fns().setOffset.unwrap()(self.cptr_mut(), offset.to_seconds()) };
78  }
79  /// Gets the current offset of the SamplePlayer.
80  pub fn offset(&mut self) -> TimeDelta {
81    // getOffset() takes a mutable pointer it changes no visible state.
82    TimeDelta::from_seconds_lossy(unsafe { Self::fns().getOffset.unwrap()(self.cptr() as *mut _) })
83  }
84
85  /// Sets the ping-pong range when `play()` is called with `repeat` of `-1`.
86  pub fn set_play_range(&mut self, play_range: RelativeTimeSpan) {
87    unsafe {
88      Self::fns().setPlayRange.unwrap()(
89        self.cptr_mut(),
90        play_range.start.to_sample_frames(),
91        play_range.end.to_sample_frames(),
92      )
93    };
94  }
95
96  /// Sets the playback rate for the SamplePlayer.
97  ///
98  /// 1.0 is normal speed, 0.5 is down an octave, 2.0 is up an octave, etc.
99  pub fn set_rate(&mut self, rate: f32) {
100    unsafe { Self::fns().setRate.unwrap()(self.cptr_mut(), rate) }
101  }
102  /// Gets the playback rate for the SamplePlayer.
103  pub fn rate(&self) -> f32 {
104    // getRate() takes a mutable pointer it changes no visible state.
105    unsafe { Self::fns().getRate.unwrap()(self.cptr() as *mut _) }
106  }
107
108  /// Sets a function to be called every time the sample loops.
109  ///
110  /// The callback will be registered as a system event, and the application will be notified to run
111  /// the callback via a `SystemEvent::Callback` event. When that occurs, the application's
112  /// `Callbacks` object which was used to construct the `completion_callback` can be `run()` to
113  /// execute the closure bound in the `completion_callback`.
114  ///
115  /// # Example
116  /// ```
117  /// let callbacks: Callbacks<i32> = Callbacks::new();
118  /// // Register a closure as a callback.
119  /// player.set_loop_callback(SoundCompletionCallback::with(&mut callbacks).call(|i: i32| {
120  ///   println("looped");
121  /// }));
122  /// match system_event_watcher.next() {
123  ///   SystemEvent::Callback => {
124  ///     // Run the closure registered above.
125  ///     callbacks.run(12);
126  ///   }
127  /// }
128  /// ```
129  pub fn set_loop_callback<'a, T, F: Fn(T) + 'static>(
130    &mut self,
131    loop_callback: SoundCompletionCallback<'a, T, F, Constructed>,
132  ) {
133    self.loop_callback = None;
134    let func = loop_callback.into_inner().and_then(|(callbacks, cb)| {
135      // This pointer is not aligned, but we will not deref it. It's only used as a map key.
136      let key = unsafe { self.as_source_mut().cptr().add(1) } as usize;
137      let (func, reg) = callbacks.add_sound_source_completion(key, cb);
138      self.loop_callback = Some(reg);
139      Some(func)
140    });
141    unsafe { Self::fns().setLoopCallback.unwrap()(self.cptr_mut(), func) }
142  }
143
144  pub(crate) fn cptr(&self) -> *const CSamplePlayer {
145    self.ptr.as_ptr()
146  }
147  pub(crate) fn cptr_mut(&mut self) -> *mut CSamplePlayer {
148    self.ptr.as_ptr()
149  }
150  pub(crate) fn fns() -> &'static craydate_sys::playdate_sound_sampleplayer {
151    unsafe { &*CApiState::get().csound.sampleplayer }
152  }
153}
154
155impl Drop for SamplePlayer<'_> {
156  fn drop(&mut self) {
157    self.set_loop_callback(SoundCompletionCallback::none());
158    // Ensure the SoundSource has a chance to clean up before it is freed.
159    unsafe { ManuallyDrop::drop(&mut self.source) };
160    unsafe { Self::fns().freePlayer.unwrap()(self.cptr_mut()) }
161  }
162}
163
164impl AsRef<SoundSource> for SamplePlayer<'_> {
165  fn as_ref(&self) -> &SoundSource {
166    &self.source
167  }
168}
169impl AsMut<SoundSource> for SamplePlayer<'_> {
170  fn as_mut(&mut self) -> &mut SoundSource {
171    &mut self.source
172  }
173}