playdate_sound/
sample.rs

1//! Playdate sound sample API
2
3use alloc::boxed::Box;
4use core::ffi::c_char;
5use core::ffi::c_float;
6use core::ffi::c_int;
7use core::ops::Deref;
8use sys::ffi::CString;
9use sys::ffi::AudioSample;
10use fs::Path;
11use sys::ffi::SoundFormat;
12
13use crate::error::ApiError;
14use crate::error::Error;
15
16
17#[cfg_attr(feature = "bindings-derive-debug", derive(Debug))]
18pub struct Sample<Api: api::Api = api::Default>(pub(super) *mut AudioSample, Api);
19
20
21impl<Api: api::Api> Drop for Sample<Api> {
22	fn drop(&mut self) {
23		if !self.0.is_null() {
24			let f = self.1.free_sample();
25			unsafe { f(self.0) };
26			self.0 = core::ptr::null_mut();
27		}
28	}
29}
30
31
32impl<Api: Default + api::Api> Sample<Api> {
33	/// Allocates and returns a new [`Sample`] with a buffer large enough to load a file of length `bytes`.
34	///
35	/// Equivalent to [`sys::ffi::playdate_sound_sample::newSampleBuffer`]
36	#[doc(alias = "sys::ffi::playdate_sound_sample::newSampleBuffer")]
37	#[inline(always)]
38	pub fn new_with_size(bytes: c_int) -> Result<Self, Error> {
39		let api: Api = Default::default();
40		Self::new_with_size_with(api, bytes)
41	}
42
43	/// Retrieves `size` of file and allocate with that size.
44	///
45	/// __Does not load a file.__
46	///
47	/// Uses [`sys::ffi::playdate_sound_sample::newSampleBuffer`]
48	#[inline(always)]
49	pub fn new_for_file<P: AsRef<Path>>(path: P) -> Result<Self, ApiError> {
50		let api: Api = Default::default();
51		Self::new_for_file_with(api, path).map_err(Into::into)
52	}
53
54	/// Loads the file into the self.
55	///
56	/// Equivalent to [`sys::ffi::playdate_sound_sample::load`]
57	#[doc(alias = "sys::ffi::playdate_sound_sample::load")]
58	#[inline(always)]
59	pub fn new_from_file<P: AsRef<Path>>(path: P) -> Result<Self, ApiError> {
60		let api: Api = Default::default();
61		Self::new_from_file_with(api, path)
62	}
63
64
65	/// Returns a new [`Sample`] referencing the given audio data.
66	///
67	/// The sample keeps a reference to the `data` instead of copying it,
68	/// so the data must remain valid while the sample is active.
69	///
70	/// Equivalent to [`sys::ffi::playdate_sound_sample::newSampleFromData`]
71	#[doc(alias = "sys::ffi::playdate_sound_sample::newSampleFromData")]
72	pub fn new_from_data<'t>(data: &'t mut [u8],
73	                         format: SoundFormat,
74	                         sample_rate: u32)
75	                         -> Result<SampleWithData<'t, Api>, Error> {
76		let api: Api = Default::default();
77		Self::new_from_data_with(api, data, format, sample_rate)
78	}
79}
80
81
82impl<Api: api::Api> Sample<Api> {
83	/// Allocates and returns a new [`Sample`] with a buffer large enough to load a file of length `bytes`.
84	///
85	/// Equivalent to [`sys::ffi::playdate_sound_sample::newSampleBuffer`]
86	#[doc(alias = "sys::ffi::playdate_sound_sample::newSampleBuffer")]
87	pub fn new_with_size_with(api: Api, bytes: c_int) -> Result<Self, Error> {
88		let f = api.new_sample_buffer();
89		let ptr = unsafe { f(bytes) };
90		if ptr.is_null() {
91			Err(Error::Alloc)
92		} else {
93			Ok(Self(ptr, api))
94		}
95	}
96
97	/// Retrieves `size` of file and allocate with that size.
98	///
99	/// __Does not load a file.__
100	///
101	/// Uses [`sys::ffi::playdate_sound_sample::newSampleBuffer`]
102	pub fn new_for_file_with<P: AsRef<Path>>(api: Api, path: P) -> Result<Self, ApiError> {
103		let size = match fs::metadata(path) {
104			Ok(stats) => stats.size,
105			Err(err) => return Err(ApiError::from_err(err)),
106		};
107
108		Self::new_with_size_with(api, size as _).map_err(Into::into)
109	}
110
111	/// Loads the file into the self.
112	///
113	/// Equivalent to [`sys::ffi::playdate_sound_sample::load`]
114	#[doc(alias = "sys::ffi::playdate_sound_sample::load")]
115	pub fn new_from_file_with<P: AsRef<Path>>(api: Api, path: P) -> Result<Self, ApiError> {
116		let path_cs = CString::new(path.as_ref())?;
117		let path_ptr = path_cs.as_ptr() as *mut c_char;
118
119		let f = api.load();
120
121		let ptr = unsafe { f(path_ptr) };
122		if ptr.is_null() {
123			Err(crate::error::Error::Alloc.into())
124		} else {
125			Ok(Self(ptr, api))
126		}
127	}
128
129
130	/// Returns a new [`Sample`] referencing the given audio data.
131	///
132	/// The sample keeps a reference to the `data` instead of copying it,
133	/// so the `data` __must remain valid while the sample is active__.
134	///
135	/// Equivalent to [`sys::ffi::playdate_sound_sample::newSampleFromData`]
136	#[doc(alias = "sys::ffi::playdate_sound_sample::newSampleFromData")]
137	pub fn new_from_data_with<'t>(api: Api,
138	                              data: &'t mut [u8],
139	                              format: SoundFormat,
140	                              sample_rate: u32)
141	                              -> Result<SampleWithData<'t, Api>, Error> {
142		let f = api.new_sample_from_data();
143		let ptr = unsafe { f(data.as_mut_ptr(), format, sample_rate, data.len() as _, 0) };
144
145		if ptr.is_null() {
146			Err(Error::Alloc)
147		} else {
148			Ok(SampleWithData(Self(ptr, api), data))
149		}
150	}
151}
152
153
154impl<Api: api::Api> Sample<Api> {
155	/// Returns the length, in seconds.
156	///
157	/// Equivalent to [`sys::ffi::playdate_sound_sample::getLength`]
158	#[doc(alias = "sys::ffi::playdate_sound_sample::getLength")]
159	pub fn length(&self) -> c_float {
160		if self.0.is_null() {
161			0.0
162		} else {
163			let f = self.1.get_length();
164			unsafe { f(self.0) }
165		}
166	}
167
168	/// Equivalent to [`sys::ffi::playdate_sound_sample::getData`]
169	#[doc(alias = "sys::ffi::playdate_sound_sample::getData")]
170	pub fn get_data<'t>(&'t self) -> SampleData<'t> {
171		let mut format: SoundFormat = SoundFormat::kSound8bitMono;
172		let mut sample_rate: u32 = 0;
173		let mut byte_length: u32 = 0;
174
175		let mut boxed_data = Box::new(core::ptr::null_mut());
176		let data = Box::into_raw(boxed_data);
177
178		let f = self.1.get_data();
179		unsafe { f(self.0, data, &mut format, &mut sample_rate, &mut byte_length) };
180
181		boxed_data = unsafe { Box::from_raw(data) };
182		let data = unsafe { core::slice::from_raw_parts_mut::<u8>(*boxed_data, byte_length as usize) };
183
184		SampleData { data, sample_rate }
185	}
186}
187
188
189/// Sample over borrowed audio data.
190#[cfg_attr(feature = "bindings-derive-debug", derive(Debug))]
191pub struct SampleWithData<'t, Api: api::Api>(Sample<Api>, #[allow(dead_code)] &'t mut [u8]);
192
193impl<Api: api::Api> Deref for SampleWithData<'_, Api> {
194	type Target = Sample<Api>;
195	fn deref(&self) -> &Self::Target { &self.0 }
196}
197
198impl<Api: api::Api> AsRef<Sample<Api>> for SampleWithData<'_, Api> {
199	fn as_ref(&self) -> &Sample<Api> { &self.0 }
200}
201
202
203pub struct SampleData<'t> {
204	pub sample_rate: u32,
205	pub data: &'t mut [u8],
206}
207
208
209pub mod api {
210	use core::ffi::c_int;
211	use core::ffi::c_char;
212	use core::ffi::c_float;
213	use core::ptr::NonNull;
214	use sys::ffi::AudioSample;
215	use sys::ffi::SoundFormat;
216	use sys::ffi::playdate_sound_sample;
217
218
219	/// Default sound sample api end-point, ZST.
220	///
221	/// All calls approximately costs ~4 derefs.
222	#[derive(Debug, Clone, Copy, core::default::Default)]
223	pub struct Default;
224	impl Api for Default {}
225
226
227	/// Cached sound sample api end-point.
228	///
229	/// Stores one reference, so size on stack is eq `usize`.
230	///
231	/// All calls approximately costs ~1 deref.
232	#[derive(Clone, Copy)]
233	#[cfg_attr(feature = "bindings-derive-debug", derive(Debug))]
234	pub struct Cache(&'static playdate_sound_sample);
235
236	impl core::default::Default for Cache {
237		fn default() -> Self { Self(sys::api!(sound.sample)) }
238	}
239
240	impl From<*const playdate_sound_sample> for Cache {
241		#[inline(always)]
242		fn from(ptr: *const playdate_sound_sample) -> Self { Self(unsafe { ptr.as_ref() }.expect("sample")) }
243	}
244
245	impl From<&'static playdate_sound_sample> for Cache {
246		#[inline(always)]
247		fn from(r: &'static playdate_sound_sample) -> Self { Self(r) }
248	}
249
250	impl From<NonNull<playdate_sound_sample>> for Cache {
251		#[inline(always)]
252		fn from(ptr: NonNull<playdate_sound_sample>) -> Self { Self(unsafe { ptr.as_ref() }) }
253	}
254
255	impl From<&'_ NonNull<playdate_sound_sample>> for Cache {
256		#[inline(always)]
257		fn from(ptr: &NonNull<playdate_sound_sample>) -> Self { Self(unsafe { ptr.as_ref() }) }
258	}
259
260
261	impl Api for Cache {
262		fn new_sample_buffer(&self) -> unsafe extern "C" fn(byteCount: c_int) -> *mut AudioSample {
263			self.0.newSampleBuffer.expect("newSampleBuffer")
264		}
265
266		fn load_into_sample(&self) -> unsafe extern "C" fn(sample: *mut AudioSample, path: *const c_char) -> c_int {
267			self.0.loadIntoSample.expect("loadIntoSample")
268		}
269
270		fn load(&self) -> unsafe extern "C" fn(path: *const c_char) -> *mut AudioSample {
271			self.0.load.expect("load")
272		}
273
274		fn new_sample_from_data(
275			&self)
276			-> unsafe extern "C" fn(data: *mut u8,
277			                        format: SoundFormat,
278			                        sampleRate: u32,
279			                        byteCount: c_int,
280			                        shouldFreeData: c_int) -> *mut AudioSample {
281			self.0.newSampleFromData.expect("newSampleFromData")
282		}
283
284		fn get_data(
285			&self)
286			-> unsafe extern "C" fn(sample: *mut AudioSample,
287			                        data: *mut *mut u8,
288			                        format: *mut SoundFormat,
289			                        sampleRate: *mut u32,
290			                        bytelength: *mut u32) {
291			self.0.getData.expect("getData")
292		}
293
294		fn free_sample(&self) -> unsafe extern "C" fn(sample: *mut AudioSample) {
295			self.0.freeSample.expect("freeSample")
296		}
297
298		fn get_length(&self) -> unsafe extern "C" fn(sample: *mut AudioSample) -> c_float {
299			self.0.getLength.expect("getLength")
300		}
301	}
302
303
304	pub trait Api {
305		/// Returns [`sys::ffi::playdate_sound_sample::newSampleBuffer`]
306		#[doc(alias = "sys::ffi::playdate_sound_sample::newSampleBuffer")]
307		fn new_sample_buffer(&self) -> unsafe extern "C" fn(byteCount: c_int) -> *mut AudioSample {
308			*sys::api!(sound.sample.newSampleBuffer)
309		}
310
311
312		/// Returns [`sys::ffi::playdate_sound_sample::loadIntoSample`]
313		#[doc(alias = "sys::ffi::playdate_sound_sample::loadIntoSample")]
314		fn load_into_sample(&self) -> unsafe extern "C" fn(sample: *mut AudioSample, path: *const c_char) -> c_int {
315			*sys::api!(sound.sample.loadIntoSample)
316		}
317
318
319		/// Returns [`sys::ffi::playdate_sound_sample::load`]
320		#[doc(alias = "sys::ffi::playdate_sound_sample::load")]
321		fn load(&self) -> unsafe extern "C" fn(path: *const c_char) -> *mut AudioSample {
322			*sys::api!(sound.sample.load)
323		}
324
325
326		/// Returns [`sys::ffi::playdate_sound_sample::newSampleFromData`]
327		#[doc(alias = "sys::ffi::playdate_sound_sample::newSampleFromData")]
328		fn new_sample_from_data(
329			&self)
330			-> unsafe extern "C" fn(data: *mut u8,
331			                        format: SoundFormat,
332			                        sampleRate: u32,
333			                        byteCount: c_int,
334			                        shouldFreeData: c_int) -> *mut AudioSample {
335			*sys::api!(sound.sample.newSampleFromData)
336		}
337
338		/// Returns [`sys::ffi::playdate_sound_sample::getData`]
339		#[doc(alias = "sys::ffi::playdate_sound_sample::getData")]
340		fn get_data(
341			&self)
342			-> unsafe extern "C" fn(sample: *mut AudioSample,
343			                        data: *mut *mut u8,
344			                        format: *mut SoundFormat,
345			                        sampleRate: *mut u32,
346			                        bytelength: *mut u32) {
347			*sys::api!(sound.sample.getData)
348		}
349
350		/// Returns [`sys::ffi::playdate_sound_sample::freeSample`]
351		#[doc(alias = "sys::ffi::playdate_sound_sample::freeSample")]
352		fn free_sample(&self) -> unsafe extern "C" fn(sample: *mut AudioSample) {
353			*sys::api!(sound.sample.freeSample)
354		}
355
356		/// Returns [`sys::ffi::playdate_sound_sample::getLength`]
357		#[doc(alias = "sys::ffi::playdate_sound_sample::getLength")]
358		fn get_length(&self) -> unsafe extern "C" fn(sample: *mut AudioSample) -> c_float {
359			*sys::api!(sound.sample.getLength)
360		}
361	}
362}