1use core::{num::NonZeroUsize, ops::Range};
2
3#[cfg(not(feature = "std"))]
4use bevy_platform::prelude::Vec;
5
6pub trait SampleResource: Send + Sync + 'static {
8 fn num_channels(&self) -> NonZeroUsize;
10
11 fn len_frames(&self) -> u64;
15
16 fn fill_buffers(
28 &self,
29 buffers: &mut [&mut [f32]],
30 buffer_range: Range<usize>,
31 start_frame: u64,
32 );
33}
34
35pub struct InterleavedResourceI16 {
36 pub data: Vec<i16>,
37 pub channels: NonZeroUsize,
38}
39
40impl SampleResource for InterleavedResourceI16 {
41 fn num_channels(&self) -> NonZeroUsize {
42 self.channels
43 }
44
45 fn len_frames(&self) -> u64 {
46 (self.data.len() / self.channels.get()) as u64
47 }
48
49 fn fill_buffers(
50 &self,
51 buffers: &mut [&mut [f32]],
52 buffer_range: Range<usize>,
53 start_frame: u64,
54 ) {
55 fill_buffers_interleaved(
56 buffers,
57 buffer_range,
58 start_frame as usize,
59 self.channels,
60 &self.data,
61 pcm_i16_to_f32,
62 );
63 }
64}
65
66pub struct InterleavedResourceU16 {
67 pub data: Vec<u16>,
68 pub channels: NonZeroUsize,
69}
70
71impl SampleResource for InterleavedResourceU16 {
72 fn num_channels(&self) -> NonZeroUsize {
73 self.channels
74 }
75
76 fn len_frames(&self) -> u64 {
77 (self.data.len() / self.channels.get()) as u64
78 }
79
80 fn fill_buffers(
81 &self,
82 buffers: &mut [&mut [f32]],
83 buffer_range: Range<usize>,
84 start_frame: u64,
85 ) {
86 fill_buffers_interleaved(
87 buffers,
88 buffer_range,
89 start_frame as usize,
90 self.channels,
91 &self.data,
92 pcm_u16_to_f32,
93 );
94 }
95}
96
97pub struct InterleavedResourceF32 {
98 pub data: Vec<f32>,
99 pub channels: NonZeroUsize,
100}
101
102impl SampleResource for InterleavedResourceF32 {
103 fn num_channels(&self) -> NonZeroUsize {
104 self.channels
105 }
106
107 fn len_frames(&self) -> u64 {
108 (self.data.len() / self.channels.get()) as u64
109 }
110
111 fn fill_buffers(
112 &self,
113 buffers: &mut [&mut [f32]],
114 buffer_range: Range<usize>,
115 start_frame: u64,
116 ) {
117 fill_buffers_interleaved(
118 buffers,
119 buffer_range,
120 start_frame as usize,
121 self.channels,
122 &self.data,
123 |s| s,
124 );
125 }
126}
127
128impl SampleResource for Vec<Vec<i16>> {
129 fn num_channels(&self) -> NonZeroUsize {
130 NonZeroUsize::new(self.len()).unwrap()
131 }
132
133 fn len_frames(&self) -> u64 {
134 self[0].len() as u64
135 }
136
137 fn fill_buffers(
138 &self,
139 buffers: &mut [&mut [f32]],
140 buffer_range: Range<usize>,
141 start_frame: u64,
142 ) {
143 fill_buffers_deinterleaved(
144 buffers,
145 buffer_range,
146 start_frame as usize,
147 self.as_slice(),
148 pcm_i16_to_f32,
149 );
150 }
151}
152
153impl SampleResource for Vec<Vec<u16>> {
154 fn num_channels(&self) -> NonZeroUsize {
155 NonZeroUsize::new(self.len()).unwrap()
156 }
157
158 fn len_frames(&self) -> u64 {
159 self[0].len() as u64
160 }
161
162 fn fill_buffers(
163 &self,
164 buffers: &mut [&mut [f32]],
165 buffer_range: Range<usize>,
166 start_frame: u64,
167 ) {
168 fill_buffers_deinterleaved(
169 buffers,
170 buffer_range,
171 start_frame as usize,
172 self.as_slice(),
173 pcm_u16_to_f32,
174 );
175 }
176}
177
178impl SampleResource for Vec<Vec<f32>> {
179 fn num_channels(&self) -> NonZeroUsize {
180 NonZeroUsize::new(self.len()).unwrap()
181 }
182
183 fn len_frames(&self) -> u64 {
184 self[0].len() as u64
185 }
186
187 fn fill_buffers(
188 &self,
189 buffers: &mut [&mut [f32]],
190 buffer_range: Range<usize>,
191 start_frame: u64,
192 ) {
193 fill_buffers_deinterleaved_f32(buffers, buffer_range, start_frame as usize, self);
194 }
195}
196
197#[inline]
198pub fn pcm_i16_to_f32(s: i16) -> f32 {
199 f32::from(s) * (1.0 / core::i16::MAX as f32)
200}
201
202#[inline]
203pub fn pcm_u16_to_f32(s: u16) -> f32 {
204 ((f32::from(s)) * (2.0 / core::u16::MAX as f32)) - 1.0
205}
206
207pub fn fill_buffers_interleaved<T: Clone + Copy>(
209 buffers: &mut [&mut [f32]],
210 buffer_range: Range<usize>,
211 start_frame: usize,
212 channels: NonZeroUsize,
213 data: &[T],
214 convert: impl Fn(T) -> f32,
215) {
216 let start_frame = start_frame as usize;
217 let channels = channels.get();
218
219 let frames = buffer_range.end - buffer_range.start;
220
221 if channels == 1 {
222 for (buf_s, &src_s) in buffers[0][buffer_range.clone()]
224 .iter_mut()
225 .zip(&data[start_frame..start_frame + frames])
226 {
227 *buf_s = convert(src_s);
228 }
229 return;
230 }
231
232 if channels == 2 && buffers.len() >= 2 {
233 let (buf0, buf1) = buffers.split_first_mut().unwrap();
235 let buf0 = &mut buf0[buffer_range.clone()];
236 let buf1 = &mut buf1[0][buffer_range.clone()];
237
238 let src_slice = &data[start_frame * 2..(start_frame + frames) * 2];
239
240 for (src_chunk, (buf0_s, buf1_s)) in src_slice
241 .chunks_exact(2)
242 .zip(buf0.iter_mut().zip(buf1.iter_mut()))
243 {
244 *buf0_s = convert(src_chunk[0]);
245 *buf1_s = convert(src_chunk[1]);
246 }
247
248 return;
249 }
250
251 let src_slice = &data[start_frame * channels..(start_frame + frames) * channels];
252 for (ch_i, buf_ch) in (0..channels).zip(buffers.iter_mut()) {
253 for (src_chunk, buf_s) in src_slice
254 .chunks_exact(channels)
255 .zip(buf_ch[buffer_range.clone()].iter_mut())
256 {
257 *buf_s = convert(src_chunk[ch_i]);
258 }
259 }
260}
261
262pub fn fill_buffers_deinterleaved<T: Clone + Copy, V: AsRef<[T]>>(
264 buffers: &mut [&mut [f32]],
265 buffer_range: Range<usize>,
266 start_frame: usize,
267 data: &[V],
268 convert: impl Fn(T) -> f32,
269) {
270 let start_frame = start_frame as usize;
271 let frames = buffer_range.end - buffer_range.start;
272
273 if data.len() == 2 && buffers.len() >= 2 {
274 let (buf0, buf1) = buffers.split_first_mut().unwrap();
276 let buf0 = &mut buf0[buffer_range.clone()];
277 let buf1 = &mut buf1[0][buffer_range.clone()];
278 let s0 = &data[0].as_ref()[start_frame..start_frame + frames];
279 let s1 = &data[1].as_ref()[start_frame..start_frame + frames];
280
281 for i in 0..frames {
282 buf0[i] = convert(s0[i]);
283 buf1[i] = convert(s1[i]);
284 }
285
286 return;
287 }
288
289 for (buf, ch) in buffers.iter_mut().zip(data.iter()) {
290 for (buf_s, &ch_s) in buf[buffer_range.clone()]
291 .iter_mut()
292 .zip(ch.as_ref()[start_frame..start_frame + frames].iter())
293 {
294 *buf_s = convert(ch_s);
295 }
296 }
297}
298
299pub fn fill_buffers_deinterleaved_f32<V: AsRef<[f32]>>(
301 buffers: &mut [&mut [f32]],
302 buffer_range: Range<usize>,
303 start_frame: usize,
304 data: &[V],
305) {
306 let start_frame = start_frame as usize;
307
308 for (buf, ch) in buffers.iter_mut().zip(data.iter()) {
309 buf[buffer_range.clone()].copy_from_slice(
310 &ch.as_ref()[start_frame..start_frame + buffer_range.end - buffer_range.start],
311 );
312 }
313}
314
315#[cfg(feature = "symphonium")]
316pub struct DecodedAudio(pub symphonium::DecodedAudio);
319
320#[cfg(feature = "symphonium")]
321impl DecodedAudio {
322 pub fn duration_seconds(&self) -> f64 {
323 self.0.frames() as f64 / self.0.sample_rate() as f64
324 }
325
326 pub fn into_dyn_resource(self) -> crate::collector::ArcGc<dyn SampleResource> {
327 crate::collector::ArcGc::new_unsized(|| {
328 bevy_platform::sync::Arc::new(self) as bevy_platform::sync::Arc<dyn SampleResource>
329 })
330 }
331}
332
333#[cfg(feature = "symphonium")]
334impl From<DecodedAudio> for crate::collector::ArcGc<dyn SampleResource> {
335 fn from(value: DecodedAudio) -> Self {
336 value.into_dyn_resource()
337 }
338}
339
340#[cfg(feature = "symphonium")]
341impl SampleResource for DecodedAudio {
342 fn num_channels(&self) -> NonZeroUsize {
343 NonZeroUsize::new(self.0.channels()).unwrap()
344 }
345
346 fn len_frames(&self) -> u64 {
347 self.0.frames() as u64
348 }
349
350 fn fill_buffers(
351 &self,
352 buffers: &mut [&mut [f32]],
353 buffer_range: Range<usize>,
354 start_frame: u64,
355 ) {
356 let channels = self.0.channels().min(buffers.len());
357
358 if channels == 2 {
359 let (b1, b2) = buffers.split_first_mut().unwrap();
360
361 self.0.fill_stereo(
362 start_frame as usize,
363 &mut b1[buffer_range.clone()],
364 &mut b2[0][buffer_range.clone()],
365 );
366 } else {
367 for (ch_i, b) in buffers[0..channels].iter_mut().enumerate() {
368 self.0
369 .fill_channel(ch_i, start_frame as usize, &mut b[buffer_range.clone()])
370 .unwrap();
371 }
372 }
373 }
374}
375
376#[cfg(feature = "symphonium")]
377impl From<symphonium::DecodedAudio> for DecodedAudio {
378 fn from(data: symphonium::DecodedAudio) -> Self {
379 Self(data)
380 }
381}
382
383#[cfg(feature = "symphonium")]
384pub struct DecodedAudioF32(pub symphonium::DecodedAudioF32);
387
388#[cfg(feature = "symphonium")]
389impl DecodedAudioF32 {
390 pub fn duration_seconds(&self, sample_rate: u32) -> f64 {
391 self.0.frames() as f64 / sample_rate as f64
392 }
393}
394
395#[cfg(feature = "symphonium")]
396impl SampleResource for DecodedAudioF32 {
397 fn num_channels(&self) -> NonZeroUsize {
398 NonZeroUsize::new(self.0.channels()).unwrap()
399 }
400
401 fn len_frames(&self) -> u64 {
402 self.0.frames() as u64
403 }
404
405 fn fill_buffers(
406 &self,
407 buffers: &mut [&mut [f32]],
408 buffer_range: Range<usize>,
409 start_frame: u64,
410 ) {
411 fill_buffers_deinterleaved_f32(buffers, buffer_range, start_frame as usize, &self.0.data);
412 }
413}
414
415#[cfg(feature = "symphonium")]
416impl From<symphonium::DecodedAudioF32> for DecodedAudioF32 {
417 fn from(data: symphonium::DecodedAudioF32) -> Self {
418 Self(data)
419 }
420}
421
422#[cfg(feature = "symphonium")]
429pub fn load_audio_file<P: AsRef<std::path::Path>>(
430 loader: &mut symphonium::SymphoniumLoader,
431 path: P,
432 #[cfg(feature = "symphonium_resample")] sample_rate: core::num::NonZeroU32,
433 #[cfg(feature = "symphonium_resample")] resample_quality: symphonium::ResampleQuality,
434) -> Result<DecodedAudio, symphonium::error::LoadError> {
435 loader
436 .load(
437 path,
438 #[cfg(feature = "symphonium_resample")]
439 Some(sample_rate.get()),
440 #[cfg(feature = "symphonium_resample")]
441 resample_quality,
442 None,
443 )
444 .map(|d| DecodedAudio(d))
445}
446
447#[cfg(feature = "symphonium")]
457pub fn load_audio_file_from_source(
458 loader: &mut symphonium::SymphoniumLoader,
459 source: Box<dyn symphonium::symphonia::core::io::MediaSource>,
460 hint: Option<symphonium::symphonia::core::probe::Hint>,
461 #[cfg(feature = "symphonium_resample")] sample_rate: core::num::NonZeroU32,
462 #[cfg(feature = "symphonium_resample")] resample_quality: symphonium::ResampleQuality,
463) -> Result<DecodedAudio, symphonium::error::LoadError> {
464 loader
465 .load_from_source(
466 source,
467 hint,
468 #[cfg(feature = "symphonium_resample")]
469 Some(sample_rate.get()),
470 #[cfg(feature = "symphonium_resample")]
471 resample_quality,
472 None,
473 )
474 .map(|d| DecodedAudio(d))
475}
476
477#[cfg(feature = "symphonium_stretch")]
488pub fn load_audio_file_stretched<P: AsRef<std::path::Path>>(
489 loader: &mut symphonium::SymphoniumLoader,
490 path: P,
491 sample_rate: core::num::NonZeroU32,
492 stretch: f64,
493) -> Result<DecodedAudio, symphonium::error::LoadError> {
494 loader
495 .load_f32_stretched(path, stretch, Some(sample_rate.get()), None)
496 .map(|d| DecodedAudio(d.into()))
497}
498
499#[cfg(feature = "symphonium_stretch")]
512pub fn load_audio_file_from_source_stretched(
513 loader: &mut symphonium::SymphoniumLoader,
514 source: Box<dyn symphonium::symphonia::core::io::MediaSource>,
515 hint: Option<symphonium::symphonia::core::probe::Hint>,
516 sample_rate: core::num::NonZeroU32,
517 stretch: f64,
518) -> Result<DecodedAudio, symphonium::error::LoadError> {
519 loader
520 .load_f32_from_source_stretched(source, hint, stretch, Some(sample_rate.get()), None)
521 .map(|d| DecodedAudio(d.into()))
522}
523
524#[cfg(feature = "symphonium")]
525pub fn decoded_to_resource(
528 data: symphonium::DecodedAudio,
529) -> bevy_platform::sync::Arc<dyn SampleResource> {
530 bevy_platform::sync::Arc::new(DecodedAudio(data))
531}
532
533#[cfg(feature = "symphonium")]
534pub fn decoded_f32_to_resource(
537 data: symphonium::DecodedAudioF32,
538) -> bevy_platform::sync::Arc<dyn SampleResource> {
539 bevy_platform::sync::Arc::new(DecodedAudioF32(data))
540}