Skip to main content

firewheel_core/
sample_resource.rs

1use core::{
2    num::{NonZeroU32, NonZeroUsize},
3    ops::Range,
4};
5
6#[cfg(not(feature = "std"))]
7use bevy_platform::prelude::Vec;
8
9/// Trait returning information about a resource of audio samples
10pub trait SampleResourceInfo: Send + Sync + 'static {
11    /// The number of channels in this resource.
12    fn num_channels(&self) -> NonZeroUsize;
13
14    /// The length of this resource in samples (of a single channel of audio).
15    ///
16    /// Not to be confused with video frames.
17    fn len_frames(&self) -> u64;
18
19    /// The sample rate of this resource.
20    ///
21    /// Returns `None` if the sample rate is unknown.
22    fn sample_rate(&self) -> Option<NonZeroU32> {
23        None
24    }
25}
26
27/// A resource of audio samples.
28pub trait SampleResource: SampleResourceInfo {
29    /// Fill the given buffers with audio data starting from the given
30    /// starting frame in the resource.
31    ///
32    /// * `buffers` - The buffers to fill with data. If the length of `buffers`
33    /// is greater than the number of channels in this resource, then ignore
34    /// the extra buffers.
35    /// * `buffer_range` - The range inside each buffer slice in which to
36    /// fill with data. Do not fill any data outside of this range.
37    /// * `start_frame` - The sample (of a single channel of audio) in the
38    /// resource at which to start copying from. Not to be confused with video
39    /// frames.
40    fn fill_buffers(
41        &self,
42        buffers: &mut [&mut [f32]],
43        buffer_range: Range<usize>,
44        start_frame: u64,
45    );
46}
47
48/// A resource of audio samples stored as de-interleaved f32 values.
49pub trait SampleResourceF32: SampleResourceInfo {
50    /// Get the the buffer for a given channel.
51    fn channel(&self, i: usize) -> Option<&[f32]>;
52}
53
54#[derive(Clone)]
55pub struct InterleavedResourceI16 {
56    pub data: Vec<i16>,
57    pub channels: NonZeroUsize,
58    pub sample_rate: Option<NonZeroU32>,
59}
60
61impl SampleResourceInfo for InterleavedResourceI16 {
62    fn num_channels(&self) -> NonZeroUsize {
63        self.channels
64    }
65
66    fn len_frames(&self) -> u64 {
67        (self.data.len() / self.channels.get()) as u64
68    }
69
70    fn sample_rate(&self) -> Option<NonZeroU32> {
71        self.sample_rate
72    }
73}
74
75impl SampleResource for InterleavedResourceI16 {
76    fn fill_buffers(
77        &self,
78        buffers: &mut [&mut [f32]],
79        buffer_range: Range<usize>,
80        start_frame: u64,
81    ) {
82        fill_buffers_interleaved(
83            buffers,
84            buffer_range,
85            start_frame as usize,
86            self.channels,
87            &self.data,
88            pcm_i16_to_f32,
89        );
90    }
91}
92
93impl core::fmt::Debug for InterleavedResourceI16 {
94    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
95        write!(
96            f,
97            "InterleavedResourceI16 {{ channels: {}, frames: {} }}",
98            self.channels.get(),
99            self.data.len() / self.channels.get(),
100        )
101    }
102}
103
104#[derive(Clone)]
105pub struct InterleavedResourceU16 {
106    pub data: Vec<u16>,
107    pub channels: NonZeroUsize,
108    pub sample_rate: Option<NonZeroU32>,
109}
110
111impl SampleResourceInfo for InterleavedResourceU16 {
112    fn num_channels(&self) -> NonZeroUsize {
113        self.channels
114    }
115
116    fn len_frames(&self) -> u64 {
117        (self.data.len() / self.channels.get()) as u64
118    }
119
120    fn sample_rate(&self) -> Option<NonZeroU32> {
121        self.sample_rate
122    }
123}
124
125impl SampleResource for InterleavedResourceU16 {
126    fn fill_buffers(
127        &self,
128        buffers: &mut [&mut [f32]],
129        buffer_range: Range<usize>,
130        start_frame: u64,
131    ) {
132        fill_buffers_interleaved(
133            buffers,
134            buffer_range,
135            start_frame as usize,
136            self.channels,
137            &self.data,
138            pcm_u16_to_f32,
139        );
140    }
141}
142
143impl core::fmt::Debug for InterleavedResourceU16 {
144    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
145        write!(
146            f,
147            "InterleavedResourceU16 {{ channels: {}, frames: {} }}",
148            self.channels.get(),
149            self.data.len() / self.channels.get(),
150        )
151    }
152}
153
154#[derive(Clone)]
155pub struct InterleavedResourceF32 {
156    pub data: Vec<f32>,
157    pub channels: NonZeroUsize,
158    pub sample_rate: Option<NonZeroU32>,
159}
160
161impl SampleResourceInfo for InterleavedResourceF32 {
162    fn num_channels(&self) -> NonZeroUsize {
163        self.channels
164    }
165
166    fn len_frames(&self) -> u64 {
167        (self.data.len() / self.channels.get()) as u64
168    }
169
170    fn sample_rate(&self) -> Option<NonZeroU32> {
171        self.sample_rate
172    }
173}
174
175impl SampleResource for InterleavedResourceF32 {
176    fn fill_buffers(
177        &self,
178        buffers: &mut [&mut [f32]],
179        buffer_range: Range<usize>,
180        start_frame: u64,
181    ) {
182        fill_buffers_interleaved(
183            buffers,
184            buffer_range,
185            start_frame as usize,
186            self.channels,
187            &self.data,
188            |s| s,
189        );
190    }
191}
192
193impl core::fmt::Debug for InterleavedResourceF32 {
194    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
195        write!(
196            f,
197            "InterleavedResourceF32 {{ channels: {}, frames: {} }}",
198            self.channels.get(),
199            self.data.len() / self.channels.get(),
200        )
201    }
202}
203
204impl SampleResourceInfo for Vec<Vec<i16>> {
205    fn num_channels(&self) -> NonZeroUsize {
206        NonZeroUsize::new(self.len()).unwrap()
207    }
208
209    fn len_frames(&self) -> u64 {
210        self[0].len() as u64
211    }
212}
213
214impl SampleResource for Vec<Vec<i16>> {
215    fn fill_buffers(
216        &self,
217        buffers: &mut [&mut [f32]],
218        buffer_range: Range<usize>,
219        start_frame: u64,
220    ) {
221        fill_buffers_deinterleaved(
222            buffers,
223            buffer_range,
224            start_frame as usize,
225            self.as_slice(),
226            pcm_i16_to_f32,
227        );
228    }
229}
230
231impl SampleResourceInfo for Vec<Vec<u16>> {
232    fn num_channels(&self) -> NonZeroUsize {
233        NonZeroUsize::new(self.len()).unwrap()
234    }
235
236    fn len_frames(&self) -> u64 {
237        self[0].len() as u64
238    }
239}
240
241impl SampleResource for Vec<Vec<u16>> {
242    fn fill_buffers(
243        &self,
244        buffers: &mut [&mut [f32]],
245        buffer_range: Range<usize>,
246        start_frame: u64,
247    ) {
248        fill_buffers_deinterleaved(
249            buffers,
250            buffer_range,
251            start_frame as usize,
252            self.as_slice(),
253            pcm_u16_to_f32,
254        );
255    }
256}
257
258impl SampleResourceInfo for Vec<Vec<f32>> {
259    fn num_channels(&self) -> NonZeroUsize {
260        NonZeroUsize::new(self.len()).unwrap()
261    }
262
263    fn len_frames(&self) -> u64 {
264        self[0].len() as u64
265    }
266}
267
268impl SampleResource for Vec<Vec<f32>> {
269    fn fill_buffers(
270        &self,
271        buffers: &mut [&mut [f32]],
272        buffer_range: Range<usize>,
273        start_frame: u64,
274    ) {
275        fill_buffers_deinterleaved_f32(buffers, buffer_range, start_frame as usize, self);
276    }
277}
278
279impl SampleResourceF32 for Vec<Vec<f32>> {
280    fn channel(&self, i: usize) -> Option<&[f32]> {
281        self.get(i).map(|data| data.as_slice())
282    }
283}
284
285#[inline]
286pub fn pcm_i16_to_f32(s: i16) -> f32 {
287    f32::from(s) * (1.0 / core::i16::MAX as f32)
288}
289
290#[inline]
291pub fn pcm_u16_to_f32(s: u16) -> f32 {
292    ((f32::from(s)) * (2.0 / core::u16::MAX as f32)) - 1.0
293}
294
295/// A helper method to fill buffers from a resource of interleaved samples.
296pub fn fill_buffers_interleaved<T: Clone + Copy>(
297    buffers: &mut [&mut [f32]],
298    buffer_range: Range<usize>,
299    start_frame: usize,
300    channels: NonZeroUsize,
301    data: &[T],
302    convert: impl Fn(T) -> f32,
303) {
304    let start_frame = start_frame as usize;
305    let channels = channels.get();
306
307    let frames = buffer_range.end - buffer_range.start;
308
309    if channels == 1 {
310        // Mono, no need to deinterleave.
311        for (buf_s, &src_s) in buffers[0][buffer_range.clone()]
312            .iter_mut()
313            .zip(&data[start_frame..start_frame + frames])
314        {
315            *buf_s = convert(src_s);
316        }
317        return;
318    }
319
320    if channels == 2 && buffers.len() >= 2 {
321        // Provide an optimized loop for stereo.
322        let (buf0, buf1) = buffers.split_first_mut().unwrap();
323        let buf0 = &mut buf0[buffer_range.clone()];
324        let buf1 = &mut buf1[0][buffer_range.clone()];
325
326        let src_slice = &data[start_frame * 2..(start_frame + frames) * 2];
327
328        for (src_chunk, (buf0_s, buf1_s)) in src_slice
329            .chunks_exact(2)
330            .zip(buf0.iter_mut().zip(buf1.iter_mut()))
331        {
332            *buf0_s = convert(src_chunk[0]);
333            *buf1_s = convert(src_chunk[1]);
334        }
335
336        return;
337    }
338
339    let src_slice = &data[start_frame * channels..(start_frame + frames) * channels];
340    for (ch_i, buf_ch) in (0..channels).zip(buffers.iter_mut()) {
341        for (src_chunk, buf_s) in src_slice
342            .chunks_exact(channels)
343            .zip(buf_ch[buffer_range.clone()].iter_mut())
344        {
345            *buf_s = convert(src_chunk[ch_i]);
346        }
347    }
348}
349
350/// A helper method to fill buffers from a resource of deinterleaved samples.
351pub fn fill_buffers_deinterleaved<T: Clone + Copy, V: AsRef<[T]>>(
352    buffers: &mut [&mut [f32]],
353    buffer_range: Range<usize>,
354    start_frame: usize,
355    data: &[V],
356    convert: impl Fn(T) -> f32,
357) {
358    let start_frame = start_frame as usize;
359    let frames = buffer_range.end - buffer_range.start;
360
361    if data.len() == 2 && buffers.len() >= 2 {
362        // Provide an optimized loop for stereo.
363        let (buf0, buf1) = buffers.split_first_mut().unwrap();
364        let buf0 = &mut buf0[buffer_range.clone()];
365        let buf1 = &mut buf1[0][buffer_range.clone()];
366        let s0 = &data[0].as_ref()[start_frame..start_frame + frames];
367        let s1 = &data[1].as_ref()[start_frame..start_frame + frames];
368
369        for i in 0..frames {
370            buf0[i] = convert(s0[i]);
371            buf1[i] = convert(s1[i]);
372        }
373
374        return;
375    }
376
377    for (buf, ch) in buffers.iter_mut().zip(data.iter()) {
378        for (buf_s, &ch_s) in buf[buffer_range.clone()]
379            .iter_mut()
380            .zip(ch.as_ref()[start_frame..start_frame + frames].iter())
381        {
382            *buf_s = convert(ch_s);
383        }
384    }
385}
386
387/// A helper method to fill buffers from a resource of deinterleaved `f32` samples.
388pub fn fill_buffers_deinterleaved_f32<V: AsRef<[f32]>>(
389    buffers: &mut [&mut [f32]],
390    buffer_range: Range<usize>,
391    start_frame: usize,
392    data: &[V],
393) {
394    let start_frame = start_frame as usize;
395
396    for (buf, ch) in buffers.iter_mut().zip(data.iter()) {
397        buf[buffer_range.clone()].copy_from_slice(
398            &ch.as_ref()[start_frame..start_frame + buffer_range.end - buffer_range.start],
399        );
400    }
401}