Skip to main content

symphonia_core/audio/
sample.rs

1// Symphonia
2// Copyright (c) 2019-2026 The Project Symphonia Developers.
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
8//! The `sample` module defines the audio sample trait and implements any non-primitive sample data
9//! types.
10
11use std::fmt;
12
13use crate::util::clamp::{clamp_f32, clamp_f64, clamp_i24, clamp_u24};
14
15/// An enumeration of standard sample formats.
16#[derive(Copy, Clone, Debug)]
17pub enum SampleFormat {
18    /// Unsigned 8-bit integer.
19    U8,
20    /// Unsigned 16-bit integer.
21    U16,
22    /// Unsigned 24-bit integer.
23    U24,
24    /// Unsigned 32-bit integer.
25    U32,
26    /// Signed 8-bit integer.
27    S8,
28    /// Signed 16-bit integer.
29    S16,
30    /// Signed 24-bit integer.
31    S24,
32    /// Signed 32-bit integer.
33    S32,
34    /// Single precision (32-bit) floating point.
35    F32,
36    /// Double precision (64-bit) floating point.
37    F64,
38}
39
40/// The sample trait defines the mandatory operations and attributes an audio sample data type must
41/// implement and provide.
42pub trait Sample:
43    Copy
44    + Clone
45    + core::ops::Add<Output = Self>
46    + core::ops::Sub<Output = Self>
47    + Default
48    + PartialOrd
49    + PartialEq
50    + Sized
51{
52    /// The effective number of bits of the valid (clamped) sample range. Quantifies the dynamic
53    /// range of the sample format in bits.
54    const EFF_BITS: u32;
55
56    /// The mid-point value between the maximum and minimum sample value. If a sample is set to this
57    /// value it is silent.
58    const MID: Self;
59
60    /// If the sample format does not use the full range of the underlying data type, returns the
61    /// sample clamped to the valid range. Otherwise, returns the sample unchanged.
62    fn clamped(self) -> Self;
63}
64
65/// An unsigned 24-bit integer sample with an internal unsigned 32-bit integer representation.
66///
67/// There are **no** guarantees the sample is within the valid range 24-bit range. Use the
68/// [`Sample::clamped`] function to clamp the sample to the valid range.
69#[allow(non_camel_case_types)]
70#[repr(transparent)]
71#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
72pub struct u24(pub u32);
73
74/// A signed 24-bit integer sample with an internal signed 32-bit integer representation.
75///
76/// There are **no** guarantees the sample is within the valid range 24-bit range. Use the
77/// [`Sample::clamped`] function to clamp the sample to the valid range.
78#[allow(non_camel_case_types)]
79#[repr(transparent)]
80#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
81pub struct i24(pub i32);
82
83impl Sample for u8 {
84    const EFF_BITS: u32 = 8;
85    const MID: u8 = 128;
86
87    #[inline(always)]
88    fn clamped(self) -> Self {
89        self
90    }
91}
92
93impl Sample for i8 {
94    const EFF_BITS: u32 = 8;
95    const MID: i8 = 0;
96
97    #[inline(always)]
98    fn clamped(self) -> Self {
99        self
100    }
101}
102
103impl Sample for u16 {
104    const EFF_BITS: u32 = 16;
105    const MID: u16 = 32_768;
106
107    #[inline(always)]
108    fn clamped(self) -> Self {
109        self
110    }
111}
112
113impl Sample for i16 {
114    const EFF_BITS: u32 = 16;
115    const MID: i16 = 0;
116
117    #[inline(always)]
118    fn clamped(self) -> Self {
119        self
120    }
121}
122
123impl Sample for u24 {
124    const EFF_BITS: u32 = 24;
125    const MID: u24 = u24(8_388_608);
126
127    #[inline(always)]
128    fn clamped(self) -> Self {
129        u24(clamp_u24(self.0))
130    }
131}
132
133impl Sample for i24 {
134    const EFF_BITS: u32 = 24;
135    const MID: i24 = i24(0);
136
137    #[inline(always)]
138    fn clamped(self) -> Self {
139        i24(clamp_i24(self.0))
140    }
141}
142
143impl Sample for u32 {
144    const EFF_BITS: u32 = 32;
145    const MID: u32 = 2_147_483_648;
146
147    #[inline(always)]
148    fn clamped(self) -> Self {
149        self
150    }
151}
152
153impl Sample for i32 {
154    const EFF_BITS: u32 = 32;
155    const MID: i32 = 0;
156
157    #[inline(always)]
158    fn clamped(self) -> Self {
159        self
160    }
161}
162
163impl Sample for f32 {
164    const EFF_BITS: u32 = 24;
165    const MID: f32 = 0.0;
166
167    #[inline(always)]
168    fn clamped(self) -> Self {
169        clamp_f32(self)
170    }
171}
172
173impl Sample for f64 {
174    const EFF_BITS: u32 = 53;
175    const MID: f64 = 0.0;
176
177    #[inline(always)]
178    fn clamped(self) -> Self {
179        clamp_f64(self)
180    }
181}
182
183// Helper macros
184
185macro_rules! shl_impl {
186    ($t:ident, $f:ty) => {
187        impl core::ops::Shl<$f> for $t {
188            type Output = $t;
189
190            #[inline]
191            fn shl(self, other: $f) -> $t {
192                $t(self.0 << other)
193            }
194        }
195    };
196}
197
198macro_rules! shr_impl {
199    ($t:ident, $f:ty) => {
200        impl core::ops::Shr<$f> for $t {
201            type Output = $t;
202
203            #[inline]
204            fn shr(self, other: $f) -> $t {
205                $t(self.0 >> other)
206            }
207        }
208    };
209}
210
211macro_rules! impl_shifts {
212    ($t:ident, $f:ty) => {
213        shl_impl! { $t, $f }
214        shr_impl! { $t, $f }
215    };
216}
217
218// Implementation for i24
219
220impl i24 {
221    /// The largest value that can be represented by this integer type.
222    pub const MAX: i24 = i24(8_388_607);
223    /// The smallest value that can be represented by this integer type..
224    pub const MIN: i24 = i24(-8_388_608);
225
226    /// Get the underlying `i32` backing this `i24`.
227    #[inline(always)]
228    pub fn inner(self) -> i32 {
229        self.0
230    }
231
232    /// Return the memory representation of this `i24` as a byte array in little-endian byte order.
233    #[inline(always)]
234    pub fn to_le_bytes(self) -> [u8; 3] {
235        let b = self.0.to_le_bytes();
236
237        // In little-endian the MSB is the last byte. Drop it.
238        [b[0], b[1], b[2]]
239    }
240
241    /// Return the memory representation of this `i24` as a byte array in big-endian byte order.
242    #[inline(always)]
243    pub fn to_be_bytes(self) -> [u8; 3] {
244        let b = self.0.to_be_bytes();
245
246        // In big-endian the MSB is the first byte. Drop it.
247        [b[1], b[2], b[3]]
248    }
249
250    /// Return the memory representation of this `i24` as a byte array in native byte order.
251    #[inline(always)]
252    pub fn to_ne_bytes(self) -> [u8; 3] {
253        let b = self.0.to_ne_bytes();
254
255        if cfg!(target_endian = "little") {
256            // In little-endian the MSB is the last byte. Drop it.
257            [b[0], b[1], b[2]]
258        }
259        else {
260            // In big-endian the MSB is the first byte. Drop it.
261            [b[1], b[2], b[3]]
262        }
263    }
264}
265
266impl fmt::Display for i24 {
267    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268        write!(f, "{}", self.0)
269    }
270}
271
272impl From<i32> for i24 {
273    fn from(val: i32) -> Self {
274        i24(clamp_i24(val))
275    }
276}
277
278impl From<i16> for i24 {
279    fn from(val: i16) -> Self {
280        i24(i32::from(val))
281    }
282}
283
284impl From<i8> for i24 {
285    fn from(val: i8) -> Self {
286        i24(i32::from(val))
287    }
288}
289
290impl core::ops::Add<i24> for i24 {
291    type Output = i24;
292
293    #[inline]
294    fn add(self, other: Self) -> Self {
295        i24(self.0 + other.0)
296    }
297}
298
299impl core::ops::Sub<i24> for i24 {
300    type Output = i24;
301
302    #[inline]
303    fn sub(self, other: Self) -> Self {
304        i24(self.0 - other.0)
305    }
306}
307
308impl core::ops::Mul<i24> for i24 {
309    type Output = i24;
310
311    #[inline]
312    fn mul(self, other: Self) -> Self {
313        i24(self.0 * other.0)
314    }
315}
316
317impl core::ops::Div<i24> for i24 {
318    type Output = i24;
319
320    #[inline]
321    fn div(self, other: Self) -> Self {
322        i24(self.0 / other.0)
323    }
324}
325
326impl core::ops::Not for i24 {
327    type Output = i24;
328
329    #[inline]
330    fn not(self) -> Self {
331        i24(!self.0)
332    }
333}
334
335impl core::ops::Rem<i24> for i24 {
336    type Output = i24;
337
338    #[inline]
339    fn rem(self, other: Self) -> Self {
340        i24(self.0 % other.0)
341    }
342}
343
344impl core::ops::Shl<i24> for i24 {
345    type Output = i24;
346
347    #[inline]
348    fn shl(self, other: Self) -> Self {
349        i24(self.0 << other.0)
350    }
351}
352
353impl core::ops::Shr<i24> for i24 {
354    type Output = i24;
355
356    #[inline]
357    fn shr(self, other: Self) -> Self {
358        i24(self.0 >> other.0)
359    }
360}
361
362impl_shifts! { i24, u8 }
363impl_shifts! { i24, u16 }
364impl_shifts! { i24, u32 }
365impl_shifts! { i24, u64 }
366impl_shifts! { i24, u128 }
367impl_shifts! { i24, usize }
368
369impl_shifts! { i24, i8 }
370impl_shifts! { i24, i16 }
371impl_shifts! { i24, i32 }
372impl_shifts! { i24, i64 }
373impl_shifts! { i24, i128 }
374impl_shifts! { i24, isize }
375
376impl core::ops::BitAnd<i24> for i24 {
377    type Output = i24;
378
379    #[inline]
380    fn bitand(self, other: Self) -> Self {
381        i24(self.0 & other.0)
382    }
383}
384
385impl core::ops::BitOr<i24> for i24 {
386    type Output = i24;
387
388    #[inline]
389    fn bitor(self, other: Self) -> Self {
390        i24(self.0 | other.0)
391    }
392}
393
394impl core::ops::BitXor<i24> for i24 {
395    type Output = i24;
396
397    #[inline]
398    fn bitxor(self, other: Self) -> Self {
399        i24(self.0 ^ other.0)
400    }
401}
402
403// Implementation for u24
404
405impl u24 {
406    /// The largest value that can be represented by this integer type.
407    pub const MAX: u24 = u24(16_777_215);
408    /// The smallest value that can be represented by this integer type.
409    pub const MIN: u24 = u24(0);
410
411    /// Get the underlying `u32` backing this `u24`.
412    #[inline(always)]
413    pub fn inner(self) -> u32 {
414        self.0
415    }
416
417    /// Return the memory representation of this `u24` as a byte array in little-endian byte order.
418    #[inline(always)]
419    pub fn to_le_bytes(self) -> [u8; 3] {
420        let b = self.0.to_le_bytes();
421
422        // In little-endian the MSB is the last byte. Drop it.
423        [b[0], b[1], b[2]]
424    }
425
426    /// Return the memory representation of this `u24` as a byte array in big-endian byte order.
427    #[inline(always)]
428    pub fn to_be_bytes(self) -> [u8; 3] {
429        let b = self.0.to_be_bytes();
430
431        // In big-endian the MSB is the first byte. Drop it.
432        [b[1], b[2], b[3]]
433    }
434
435    /// Return the memory representation of this `u24` as a byte array in native byte order.
436    #[inline(always)]
437    pub fn to_ne_bytes(self) -> [u8; 3] {
438        let b = self.0.to_ne_bytes();
439
440        if cfg!(target_endian = "little") {
441            // In little-endian the MSB is the last byte. Drop it.
442            [b[0], b[1], b[2]]
443        }
444        else {
445            // In big-endian the MSB is the first byte. Drop it.
446            [b[1], b[2], b[3]]
447        }
448    }
449}
450
451impl fmt::Display for u24 {
452    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453        write!(f, "{}", self.0)
454    }
455}
456
457impl From<u32> for u24 {
458    fn from(val: u32) -> Self {
459        u24(clamp_u24(val))
460    }
461}
462
463impl From<u16> for u24 {
464    fn from(val: u16) -> Self {
465        u24(u32::from(val))
466    }
467}
468
469impl From<u8> for u24 {
470    fn from(val: u8) -> Self {
471        u24(u32::from(val))
472    }
473}
474
475impl core::ops::Add<u24> for u24 {
476    type Output = u24;
477
478    #[inline]
479    fn add(self, other: Self) -> Self {
480        u24(self.0 + other.0)
481    }
482}
483
484impl core::ops::Sub<u24> for u24 {
485    type Output = u24;
486
487    #[inline]
488    fn sub(self, other: Self) -> Self {
489        u24(self.0 - other.0)
490    }
491}
492
493impl core::ops::Mul<u24> for u24 {
494    type Output = u24;
495
496    #[inline]
497    fn mul(self, other: Self) -> Self {
498        u24(self.0 * other.0)
499    }
500}
501
502impl core::ops::Div<u24> for u24 {
503    type Output = u24;
504
505    #[inline]
506    fn div(self, other: Self) -> Self {
507        u24(self.0 / other.0)
508    }
509}
510
511impl core::ops::Not for u24 {
512    type Output = u24;
513
514    #[inline]
515    fn not(self) -> Self {
516        u24(!self.0)
517    }
518}
519
520impl core::ops::Rem<u24> for u24 {
521    type Output = u24;
522
523    #[inline]
524    fn rem(self, other: Self) -> Self {
525        u24(self.0 % other.0)
526    }
527}
528
529impl core::ops::Shl<u24> for u24 {
530    type Output = u24;
531
532    #[inline]
533    fn shl(self, other: Self) -> Self {
534        u24(self.0 << other.0)
535    }
536}
537
538impl core::ops::Shr<u24> for u24 {
539    type Output = u24;
540
541    #[inline]
542    fn shr(self, other: Self) -> Self {
543        u24(self.0 >> other.0)
544    }
545}
546
547impl_shifts! { u24, u8 }
548impl_shifts! { u24, u16 }
549impl_shifts! { u24, u32 }
550impl_shifts! { u24, u64 }
551impl_shifts! { u24, u128 }
552impl_shifts! { u24, usize }
553
554impl_shifts! { u24, i8 }
555impl_shifts! { u24, i16 }
556impl_shifts! { u24, i32 }
557impl_shifts! { u24, i64 }
558impl_shifts! { u24, i128 }
559impl_shifts! { u24, isize }
560
561impl core::ops::BitAnd<u24> for u24 {
562    type Output = u24;
563
564    #[inline]
565    fn bitand(self, other: Self) -> Self {
566        u24(self.0 & other.0)
567    }
568}
569
570impl core::ops::BitOr<u24> for u24 {
571    type Output = u24;
572
573    #[inline]
574    fn bitor(self, other: Self) -> Self {
575        u24(self.0 | other.0)
576    }
577}
578
579impl core::ops::BitXor<u24> for u24 {
580    type Output = u24;
581
582    #[inline]
583    fn bitxor(self, other: Self) -> Self {
584        u24(self.0 ^ other.0)
585    }
586}
587
588mod sealed {
589    /// A marker trait for indicating 1-byte alignment. This trait is sealed to prevent external
590    /// misuse.
591    pub trait ByteAligned: bytemuck::Pod {}
592}
593
594impl sealed::ByteAligned for [u8; 1] {}
595impl sealed::ByteAligned for [u8; 2] {}
596impl sealed::ByteAligned for [u8; 3] {}
597impl sealed::ByteAligned for [u8; 4] {}
598impl sealed::ByteAligned for [u8; 5] {}
599impl sealed::ByteAligned for [u8; 6] {}
600impl sealed::ByteAligned for [u8; 7] {}
601impl sealed::ByteAligned for [u8; 8] {}
602impl sealed::ByteAligned for [u8; 9] {}
603impl sealed::ByteAligned for [u8; 10] {}
604impl sealed::ByteAligned for [u8; 11] {}
605impl sealed::ByteAligned for [u8; 12] {}
606impl sealed::ByteAligned for [u8; 13] {}
607impl sealed::ByteAligned for [u8; 14] {}
608impl sealed::ByteAligned for [u8; 15] {}
609impl sealed::ByteAligned for [u8; 16] {}
610// Note: DO NOT implement for any types other than [u8; N].
611
612/// `SampleBytes` provides interfaces to get the packed byte representation of a `Sample`.
613///
614/// The packed byte representation is the byte representation of a sample in its native stream
615/// format. This may differ from its in-memory working representation.
616pub trait SampleBytes: Sample {
617    /// The data type that stores the packed byte representation of the sample.
618    ///
619    /// The `sealed::ByteAligned` trait bound constrains the allowable types this may be. Only byte
620    /// array types: `[u8; N]` for `N` between 1 and 16 inclusive are allowed.
621    type RawType: sealed::ByteAligned;
622
623    /// Return the sample in its packed byte representation in the native byte order.
624    fn to_ne_sample_bytes(self) -> Self::RawType;
625
626    /// Return the sample in its packed byte representation in the little-endian byte order.
627    fn to_le_sample_bytes(self) -> Self::RawType;
628
629    /// Return the sample in its packed byte representation in the big-endian byte order.
630    fn to_be_sample_bytes(self) -> Self::RawType;
631}
632
633impl SampleBytes for u8 {
634    type RawType = [u8; 1];
635
636    #[inline(always)]
637    fn to_ne_sample_bytes(self) -> Self::RawType {
638        self.to_ne_bytes()
639    }
640
641    #[inline(always)]
642    fn to_le_sample_bytes(self) -> Self::RawType {
643        self.to_le_bytes()
644    }
645
646    #[inline(always)]
647    fn to_be_sample_bytes(self) -> Self::RawType {
648        self.to_be_bytes()
649    }
650}
651
652impl SampleBytes for u16 {
653    type RawType = [u8; 2];
654
655    #[inline(always)]
656    fn to_ne_sample_bytes(self) -> Self::RawType {
657        self.to_ne_bytes()
658    }
659
660    #[inline(always)]
661    fn to_le_sample_bytes(self) -> Self::RawType {
662        self.to_le_bytes()
663    }
664
665    #[inline(always)]
666    fn to_be_sample_bytes(self) -> Self::RawType {
667        self.to_be_bytes()
668    }
669}
670
671impl SampleBytes for u24 {
672    type RawType = [u8; 3];
673
674    #[inline(always)]
675    fn to_ne_sample_bytes(self) -> Self::RawType {
676        self.to_ne_bytes()
677    }
678
679    #[inline(always)]
680    fn to_le_sample_bytes(self) -> Self::RawType {
681        self.to_le_bytes()
682    }
683
684    #[inline(always)]
685    fn to_be_sample_bytes(self) -> Self::RawType {
686        self.to_be_bytes()
687    }
688}
689
690impl SampleBytes for u32 {
691    type RawType = [u8; 4];
692
693    #[inline(always)]
694    fn to_ne_sample_bytes(self) -> Self::RawType {
695        self.to_ne_bytes()
696    }
697
698    #[inline(always)]
699    fn to_le_sample_bytes(self) -> Self::RawType {
700        self.to_le_bytes()
701    }
702
703    #[inline(always)]
704    fn to_be_sample_bytes(self) -> Self::RawType {
705        self.to_be_bytes()
706    }
707}
708
709impl SampleBytes for i8 {
710    type RawType = [u8; 1];
711
712    #[inline(always)]
713    fn to_ne_sample_bytes(self) -> Self::RawType {
714        self.to_ne_bytes()
715    }
716
717    #[inline(always)]
718    fn to_le_sample_bytes(self) -> Self::RawType {
719        self.to_le_bytes()
720    }
721
722    #[inline(always)]
723    fn to_be_sample_bytes(self) -> Self::RawType {
724        self.to_be_bytes()
725    }
726}
727
728impl SampleBytes for i16 {
729    type RawType = [u8; 2];
730
731    #[inline(always)]
732    fn to_ne_sample_bytes(self) -> Self::RawType {
733        self.to_ne_bytes()
734    }
735
736    #[inline(always)]
737    fn to_le_sample_bytes(self) -> Self::RawType {
738        self.to_le_bytes()
739    }
740
741    #[inline(always)]
742    fn to_be_sample_bytes(self) -> Self::RawType {
743        self.to_be_bytes()
744    }
745}
746
747impl SampleBytes for i24 {
748    type RawType = [u8; 3];
749
750    #[inline(always)]
751    fn to_ne_sample_bytes(self) -> Self::RawType {
752        self.to_ne_bytes()
753    }
754
755    #[inline(always)]
756    fn to_le_sample_bytes(self) -> Self::RawType {
757        self.to_le_bytes()
758    }
759
760    #[inline(always)]
761    fn to_be_sample_bytes(self) -> Self::RawType {
762        self.to_be_bytes()
763    }
764}
765
766impl SampleBytes for i32 {
767    type RawType = [u8; 4];
768
769    #[inline(always)]
770    fn to_ne_sample_bytes(self) -> Self::RawType {
771        self.to_ne_bytes()
772    }
773
774    #[inline(always)]
775    fn to_le_sample_bytes(self) -> Self::RawType {
776        self.to_le_bytes()
777    }
778
779    #[inline(always)]
780    fn to_be_sample_bytes(self) -> Self::RawType {
781        self.to_be_bytes()
782    }
783}
784
785impl SampleBytes for f32 {
786    type RawType = [u8; 4];
787
788    #[inline(always)]
789    fn to_ne_sample_bytes(self) -> Self::RawType {
790        self.to_ne_bytes()
791    }
792
793    #[inline(always)]
794    fn to_le_sample_bytes(self) -> Self::RawType {
795        self.to_le_bytes()
796    }
797
798    #[inline(always)]
799    fn to_be_sample_bytes(self) -> Self::RawType {
800        self.to_be_bytes()
801    }
802}
803
804impl SampleBytes for f64 {
805    type RawType = [u8; 8];
806
807    #[inline(always)]
808    fn to_ne_sample_bytes(self) -> Self::RawType {
809        self.to_ne_bytes()
810    }
811
812    #[inline(always)]
813    fn to_le_sample_bytes(self) -> Self::RawType {
814        self.to_le_bytes()
815    }
816
817    #[inline(always)]
818    fn to_be_sample_bytes(self) -> Self::RawType {
819        self.to_be_bytes()
820    }
821}