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