Skip to main content

symphonium/
resource.rs

1use std::num::NonZeroU32;
2
3#[cfg(feature = "decode-native")]
4use symphonia::core::audio::{conv::FromSample, sample::u24};
5
6/// A resource of raw f32 audio samples stored in deinterleaved format.
7#[derive(Clone)]
8pub struct DecodedAudioF32 {
9    pub data: Vec<Vec<f32>>,
10    /// The sample rate of this audio resource.
11    pub sample_rate: NonZeroU32,
12    /// The sample rate of the audio resource before it was resampled
13    /// (if it was resampled).
14    pub original_sample_rate: NonZeroU32,
15}
16
17impl DecodedAudioF32 {
18    /// Construct a new `DecedAudioF32` resource.
19    ///
20    /// * `data` - The deinterleaved audio data.
21    /// * `sample_rate` - The sample rate of this resource.
22    /// * `original_sample_rate` - The sample rate of the audio resource before
23    ///   it was resampled (if it was resampled).
24    pub fn new(
25        data: Vec<Vec<f32>>,
26        sample_rate: NonZeroU32,
27        original_sample_rate: NonZeroU32,
28    ) -> Self {
29        let frames = data[0].len();
30
31        for ch in data.iter().skip(1) {
32            assert_eq!(ch.len(), frames);
33        }
34
35        Self {
36            data,
37            sample_rate,
38            original_sample_rate,
39        }
40    }
41
42    /// The number of channels in this resource.
43    pub fn channels(&self) -> usize {
44        self.data.len()
45    }
46
47    /// The length of this resource in frames (length of a single channel in
48    /// samples).
49    pub fn frames(&self) -> usize {
50        self.data[0].len()
51    }
52}
53
54impl std::fmt::Debug for DecodedAudioF32 {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        write!(
57            f,
58            "DecodedAudioF32 {{ channels: {}, frames: {}, sample_rate: {} }}",
59            self.data.len(),
60            self.data[0].len(),
61            self.sample_rate
62        )
63    }
64}
65
66#[cfg(feature = "decode-native")]
67impl From<DecodedAudioF32> for DecodedAudio {
68    fn from(value: DecodedAudioF32) -> Self {
69        let channels = value.channels();
70        let frames = value.frames();
71
72        DecodedAudio {
73            resource_type: DecodedAudioType::F32(value.data),
74            sample_rate: value.sample_rate,
75            original_sample_rate: value.original_sample_rate,
76            channels,
77            frames,
78        }
79    }
80}
81
82/// A resource of raw audio samples stored in deinterleaved format.
83///
84/// This struct stores samples
85/// in their native sample format when possible to save memory.
86#[cfg(feature = "decode-native")]
87#[derive(Debug, Clone)]
88pub struct DecodedAudio {
89    resource_type: DecodedAudioType,
90    sample_rate: NonZeroU32,
91    original_sample_rate: NonZeroU32,
92    channels: usize,
93    frames: usize,
94}
95
96/// The format of the raw audio samples stored in deinterleaved format.
97///
98/// Note there is no option for `I24` (signed 24 bit). This is because
99/// converting two's compliment numbers from 32 bit to 24 bit and vice-versa
100/// is tricky and less performant. Instead, just convert the data into `u24`
101/// format.
102#[cfg(feature = "decode-native")]
103#[derive(Clone)]
104pub enum DecodedAudioType {
105    U8(Vec<Vec<u8>>),
106    S8(Vec<Vec<i8>>),
107    U16(Vec<Vec<u16>>),
108    S16(Vec<Vec<i16>>),
109    /// The data is stored in native-endian byte order.
110    U24(Vec<Vec<[u8; 3]>>),
111    U32(Vec<Vec<u32>>),
112    S32(Vec<Vec<i32>>),
113    F32(Vec<Vec<f32>>),
114    F64(Vec<Vec<f64>>),
115}
116
117#[cfg(feature = "decode-native")]
118impl DecodedAudio {
119    /// Construct a new `DecodedAudio` resource.
120    ///
121    /// * `resource_type` - The deinterleaved audio data.
122    /// * `sample_rate` - The sample rate of this resource.
123    /// * `original_sample_rate` - The sample rate of the audio resource before
124    ///   it was resampled (if it was resampled).
125    pub fn new(
126        resource_type: DecodedAudioType,
127        sample_rate: NonZeroU32,
128        original_sample_rate: NonZeroU32,
129    ) -> Self {
130        let (channels, frames) = match &resource_type {
131            DecodedAudioType::U8(b) => {
132                let len = b[0].len();
133
134                for ch in b.iter().skip(1) {
135                    assert_eq!(ch.len(), len);
136                }
137
138                (b.len(), len)
139            }
140            DecodedAudioType::S8(b) => {
141                let len = b[0].len();
142
143                for ch in b.iter().skip(1) {
144                    assert_eq!(ch.len(), len);
145                }
146
147                (b.len(), len)
148            }
149            DecodedAudioType::U16(b) => {
150                let len = b[0].len();
151
152                for ch in b.iter().skip(1) {
153                    assert_eq!(ch.len(), len);
154                }
155
156                (b.len(), len)
157            }
158            DecodedAudioType::S16(b) => {
159                let len = b[0].len();
160
161                for ch in b.iter().skip(1) {
162                    assert_eq!(ch.len(), len);
163                }
164
165                (b.len(), len)
166            }
167            DecodedAudioType::U24(b) => {
168                let len = b[0].len();
169
170                for ch in b.iter().skip(1) {
171                    assert_eq!(ch.len(), len);
172                }
173
174                (b.len(), len)
175            }
176            DecodedAudioType::U32(b) => {
177                let len = b[0].len();
178
179                for ch in b.iter().skip(1) {
180                    assert_eq!(ch.len(), len);
181                }
182
183                (b.len(), len)
184            }
185            DecodedAudioType::S32(b) => {
186                let len = b[0].len();
187
188                for ch in b.iter().skip(1) {
189                    assert_eq!(ch.len(), len);
190                }
191
192                (b.len(), len)
193            }
194            DecodedAudioType::F32(b) => {
195                let len = b[0].len();
196
197                for ch in b.iter().skip(1) {
198                    assert_eq!(ch.len(), len);
199                }
200
201                (b.len(), len)
202            }
203            DecodedAudioType::F64(b) => {
204                let len = b[0].len();
205
206                for ch in b.iter().skip(1) {
207                    assert_eq!(ch.len(), len);
208                }
209
210                (b.len(), len)
211            }
212        };
213
214        Self {
215            resource_type,
216            sample_rate,
217            original_sample_rate,
218            channels,
219            frames,
220        }
221    }
222
223    /// The number of channels in this resource.
224    pub fn channels(&self) -> usize {
225        self.channels
226    }
227
228    /// The length of this resource in frames (length of a single channel in
229    /// samples).
230    pub fn frames(&self) -> usize {
231        self.frames
232    }
233
234    /// The sample rate of this resource in samples per second.
235    pub fn sample_rate(&self) -> NonZeroU32 {
236        self.sample_rate
237    }
238
239    /// The sample rate of the audio resource before it was resampled
240    /// (if it was resampled).
241    pub fn original_sample_rate(&self) -> NonZeroU32 {
242        self.original_sample_rate
243    }
244
245    /// Get an immutable reference to the raw sample data.
246    pub fn raw(&self) -> &DecodedAudioType {
247        &self.resource_type
248    }
249
250    /// Get a mutable reference to the raw sample data.
251    ///
252    /// Please do not change the length of any of the returned vecs.
253    pub fn raw_mut(&mut self) -> &mut DecodedAudioType {
254        &mut self.resource_type
255    }
256
257    /// Fill the buffer with samples from the given `channel`, starting from the
258    /// given `frame`.
259    ///
260    /// If the length of the buffer exceeds the length of the PCM resource, then
261    /// the remaining samples will be filled with zeros.
262    ///
263    /// This returns the number of frames that were copied into the buffer. (If
264    /// this number is less than the length of `buf`, then it means that the
265    /// remaining samples were filled with zeros.)
266    ///
267    /// The will return an error if the given channel does not exist.
268    pub fn fill_channel(
269        &self,
270        channel: usize,
271        frame: usize,
272        buf: &mut [f32],
273    ) -> Result<usize, FillChannelError> {
274        if channel >= self.channels {
275            return Err(FillChannelError {
276                got_index: channel,
277                num_channels: self.channels,
278            });
279        }
280
281        if frame >= self.frames {
282            // Out of range, fill with zeros instead.
283            buf.fill(0.0);
284            return Ok(0);
285        }
286
287        let fill_frames = if frame.saturating_add(buf.len()) > self.frames {
288            // Fill the out-of-range part with zeros.
289            let fill_frames = self.frames.saturating_sub(frame);
290            buf[fill_frames..].fill(0.0);
291            fill_frames
292        } else {
293            buf.len()
294        };
295        let end_frame = frame.saturating_add(fill_frames);
296
297        let buf_part = &mut buf[0..fill_frames];
298
299        match &self.resource_type {
300            DecodedAudioType::U8(pcm) => {
301                let pcm_part = &pcm[channel][frame..end_frame];
302
303                for i in 0..fill_frames {
304                    buf_part[i] = FromSample::from_sample(pcm_part[i]);
305                }
306            }
307            DecodedAudioType::S8(pcm) => {
308                let pcm_part = &pcm[channel][frame..end_frame];
309
310                for i in 0..fill_frames {
311                    buf_part[i] = FromSample::from_sample(pcm_part[i]);
312                }
313            }
314            DecodedAudioType::U16(pcm) => {
315                let pcm_part = &pcm[channel][frame..end_frame];
316
317                for i in 0..fill_frames {
318                    buf_part[i] = FromSample::from_sample(pcm_part[i]);
319                }
320            }
321            DecodedAudioType::S16(pcm) => {
322                let pcm_part = &pcm[channel][frame..end_frame];
323
324                for i in 0..fill_frames {
325                    buf_part[i] = FromSample::from_sample(pcm_part[i]);
326                }
327            }
328            DecodedAudioType::U24(pcm) => {
329                let pcm_part = &pcm[channel][frame..end_frame];
330
331                for i in 0..fill_frames {
332                    let b = &pcm_part[i];
333
334                    #[cfg(target_endian = "little")]
335                    let s = u24(u32::from_ne_bytes([b[0], b[1], b[2], 0]));
336
337                    #[cfg(target_endian = "big")]
338                    let s = u24(u32::from_ne_bytes([0, b[0], b[1], b[2]]));
339
340                    buf_part[i] = FromSample::from_sample(s);
341                }
342            }
343            DecodedAudioType::U32(pcm) => {
344                let pcm_part = &pcm[channel][frame..end_frame];
345
346                for i in 0..fill_frames {
347                    buf_part[i] = FromSample::from_sample(pcm_part[i]);
348                }
349            }
350            DecodedAudioType::S32(pcm) => {
351                let pcm_part = &pcm[channel][frame..end_frame];
352
353                for i in 0..fill_frames {
354                    buf_part[i] = FromSample::from_sample(pcm_part[i]);
355                }
356            }
357            DecodedAudioType::F32(pcm) => {
358                let pcm_part = &pcm[channel][frame..end_frame];
359
360                buf_part.copy_from_slice(pcm_part);
361            }
362            DecodedAudioType::F64(pcm) => {
363                let pcm_part = &pcm[channel][frame..end_frame];
364
365                for i in 0..fill_frames {
366                    buf_part[i] = pcm_part[i] as f32;
367                }
368            }
369        }
370
371        Ok(fill_frames)
372    }
373
374    /// Fill the stereo buffer with samples, starting from the given `frame`.
375    ///
376    /// If this resource has only one channel, then both channels will be
377    /// filled with the same data.
378    ///
379    /// If the length of the buffer exceeds the length of the PCM resource, then
380    /// the remaining samples will be filled with zeros.
381    ///
382    /// This returns the number of frames that were copied into the buffer. (If
383    /// this number is less than the length of `buf`, then it means that the
384    /// remaining samples were filled with zeros.)
385    pub fn fill_stereo(&self, frame: usize, buf_l: &mut [f32], buf_r: &mut [f32]) -> usize {
386        let buf_len = buf_l.len().min(buf_r.len());
387        let buf_l = &mut buf_l[..buf_len];
388        let buf_r = &mut buf_r[..buf_len];
389
390        let fill_frames = if self.channels > 0 {
391            self.fill_channel(0, frame, buf_l).unwrap()
392        } else {
393            return 0;
394        };
395
396        if self.channels > 1 {
397            self.fill_channel(1, frame, buf_r).unwrap();
398        } else {
399            buf_r.copy_from_slice(buf_l);
400        }
401
402        fill_frames
403    }
404
405    /// Consume this resource and return the raw samples.
406    pub fn into_raw(self) -> DecodedAudioType {
407        self.resource_type
408    }
409}
410
411#[cfg(feature = "decode-native")]
412impl std::fmt::Debug for DecodedAudioType {
413    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
414        match self {
415            Self::U8(c) => write!(
416                f,
417                "DecodedAudioType::U8 {{ channels: {}, frames: {} }}",
418                c.len(),
419                c[0].len()
420            ),
421            Self::S8(c) => write!(
422                f,
423                "DecodedAudioType::S8 {{ channels: {}, frames: {} }}",
424                c.len(),
425                c[0].len()
426            ),
427            Self::U16(c) => write!(
428                f,
429                "DecodedAudioType::U16 {{ channels: {}, frames: {} }}",
430                c.len(),
431                c[0].len()
432            ),
433            Self::S16(c) => write!(
434                f,
435                "DecodedAudioType::S16 {{ channels: {}, frames: {} }}",
436                c.len(),
437                c[0].len()
438            ),
439            Self::U24(c) => write!(
440                f,
441                "DecodedAudioType::U24 {{ channels: {}, frames: {} }}",
442                c.len(),
443                c[0].len()
444            ),
445            Self::U32(c) => write!(
446                f,
447                "DecodedAudioType::U32 {{ channels: {}, frames: {} }}",
448                c.len(),
449                c[0].len()
450            ),
451            Self::S32(c) => write!(
452                f,
453                "DecodedAudioType::S32 {{ channels: {}, frames: {} }}",
454                c.len(),
455                c[0].len()
456            ),
457            Self::F32(c) => write!(
458                f,
459                "DecodedAudioType::F32 {{ channels: {}, frames: {} }}",
460                c.len(),
461                c[0].len()
462            ),
463            Self::F64(c) => write!(
464                f,
465                "DecodedAudioType::F64 {{ channels: {}, frames: {} }}",
466                c.len(),
467                c[0].len()
468            ),
469        }
470    }
471}
472
473#[cfg(all(test, feature = "decode-native"))]
474mod tests {
475    use super::*;
476
477    #[test]
478    fn pcm_fill_range_test() {
479        let test_pcm = DecodedAudio::new(
480            DecodedAudioType::F32(vec![vec![1.0, 2.0, 3.0, 4.0]]),
481            NonZeroU32::new(44100).unwrap(),
482            NonZeroU32::new(44100).unwrap(),
483        );
484
485        let mut out_buf: [f32; 8] = [10.0; 8];
486
487        let fill_frames = test_pcm.fill_channel(0, 0, &mut out_buf[0..4]);
488        assert_eq!(fill_frames, Ok(4));
489        assert_eq!(&out_buf[0..4], &[1.0, 2.0, 3.0, 4.0]);
490
491        out_buf = [10.0; 8];
492        let fill_frames = test_pcm.fill_channel(0, 0, &mut out_buf[0..5]);
493        assert_eq!(fill_frames, Ok(4));
494        assert_eq!(&out_buf[0..5], &[1.0, 2.0, 3.0, 4.0, 0.0]);
495
496        out_buf = [10.0; 8];
497        let fill_frames = test_pcm.fill_channel(0, 2, &mut out_buf[0..4]);
498        assert_eq!(fill_frames, Ok(2));
499        assert_eq!(&out_buf[0..4], &[3.0, 4.0, 0.0, 0.0]);
500
501        out_buf = [10.0; 8];
502        let fill_frames = test_pcm.fill_channel(0, 3, &mut out_buf[0..4]);
503        assert_eq!(fill_frames, Ok(1));
504        assert_eq!(&out_buf[0..4], &[4.0, 0.0, 0.0, 0.0]);
505
506        out_buf = [10.0; 8];
507        let fill_frames = test_pcm.fill_channel(0, 4, &mut out_buf[0..4]);
508        assert_eq!(fill_frames, Ok(0));
509        assert_eq!(&out_buf[0..4], &[0.0, 0.0, 0.0, 0.0]);
510
511        out_buf = [10.0; 8];
512        let fill_frames = test_pcm.fill_channel(0, 1, &mut out_buf[0..2]);
513        assert_eq!(fill_frames, Ok(2));
514        assert_eq!(&out_buf[0..2], &[2.0, 3.0]);
515
516        out_buf = [10.0; 8];
517        let fill_frames = test_pcm.fill_channel(0, 1, &mut out_buf[0..4]);
518        assert_eq!(fill_frames, Ok(3));
519        assert_eq!(&out_buf[0..4], &[2.0, 3.0, 4.0, 0.0]);
520    }
521}
522
523/// An error occured while using [`DecodedAudio::fill_channel`].
524#[derive(Debug, Clone, Copy, PartialEq, Eq)]
525pub struct FillChannelError {
526    pub got_index: usize,
527    pub num_channels: usize,
528}
529
530impl std::error::Error for FillChannelError {}
531
532impl std::fmt::Display for FillChannelError {
533    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
534        write!(
535            f,
536            "channel index {} out of bounds for DecodedAudio with {} channels",
537            self.got_index, self.num_channels
538        )
539    }
540}