Skip to main content

rtc_media/audio/
sample.rs

1use std::io::{Cursor, Read};
2
3use byteorder::{ByteOrder, ReadBytesExt};
4#[cfg(test)]
5use nearly_eq::NearlyEq;
6
7#[derive(Eq, PartialEq, Copy, Clone, Default, Debug)]
8#[repr(transparent)]
9/// One audio sample of raw type `Raw` (`i16`, `f32`, …).
10///
11/// A transparent newtype, so a `[Sample<T>]` can be reinterpreted as `[T]` without copying.
12pub struct Sample<Raw>(Raw);
13
14impl From<i16> for Sample<i16> {
15    #[inline]
16    fn from(raw: i16) -> Self {
17        Self(raw)
18    }
19}
20
21impl From<f32> for Sample<f32> {
22    #[inline]
23    fn from(raw: f32) -> Self {
24        Self(raw.clamp(-1.0, 1.0))
25    }
26}
27
28macro_rules! impl_from_sample_for_raw {
29    ($raw:ty) => {
30        impl From<Sample<$raw>> for $raw {
31            #[inline]
32            fn from(sample: Sample<$raw>) -> $raw {
33                sample.0
34            }
35        }
36    };
37}
38
39impl_from_sample_for_raw!(i16);
40impl_from_sample_for_raw!(f32);
41
42// impl From<Sample<i16>> for Sample<i64> {
43//     #[inline]
44//     fn from(sample: Sample<i16>) -> Self {
45//         // Fast but imprecise approach:
46//         // Perform crude but fast upsample by bit-shifting the raw value:
47//         Self::from((sample.0 as i64) << 16)
48
49//         // Slow but precise approach:
50//         // Perform a proper but expensive lerp from
51//         // i16::MIN..i16::MAX to i32::MIN..i32::MAX:
52
53//         // let value = sample.0 as i64;
54
55//         // let from = if value <= 0 { i16::MIN } else { i16::MAX } as i64;
56//         // let to = if value <= 0 { i32::MIN } else { i32::MAX } as i64;
57
58//         // Self::from((value * to + from / 2) / from)
59//     }
60// }
61
62impl From<Sample<i16>> for Sample<f32> {
63    #[inline]
64    fn from(sample: Sample<i16>) -> Self {
65        let divisor = if sample.0 < 0 {
66            i16::MIN as f32
67        } else {
68            i16::MAX as f32
69        }
70        .abs();
71        Self::from((sample.0 as f32) / divisor)
72    }
73}
74
75impl From<Sample<f32>> for Sample<i16> {
76    #[inline]
77    fn from(sample: Sample<f32>) -> Self {
78        let multiplier = if sample.0 < 0.0 {
79            i16::MIN as f32
80        } else {
81            i16::MAX as f32
82        }
83        .abs();
84        Self::from((sample.0 * multiplier) as i16)
85    }
86}
87
88trait FromBytes: Sized {
89    fn from_reader<B: ByteOrder, R: Read>(reader: &mut R) -> Result<Self, std::io::Error>;
90
91    fn from_bytes<B: ByteOrder>(bytes: &[u8]) -> Result<Self, std::io::Error> {
92        let mut cursor = Cursor::new(bytes);
93        Self::from_reader::<B, _>(&mut cursor)
94    }
95}
96
97impl FromBytes for Sample<i16> {
98    fn from_reader<B: ByteOrder, R: Read>(reader: &mut R) -> Result<Self, std::io::Error> {
99        reader.read_i16::<B>().map(Self::from)
100    }
101}
102
103impl FromBytes for Sample<f32> {
104    fn from_reader<B: ByteOrder, R: Read>(reader: &mut R) -> Result<Self, std::io::Error> {
105        reader.read_f32::<B>().map(Self::from)
106    }
107}
108
109#[cfg(test)]
110impl<Raw> NearlyEq<Self, Raw> for Sample<Raw>
111where
112    Raw: NearlyEq<Raw, Raw>,
113{
114    fn eps() -> Raw {
115        Raw::eps()
116    }
117
118    fn eq(&self, other: &Self, eps: &Raw) -> bool {
119        NearlyEq::eq(&self.0, &other.0, eps)
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use nearly_eq::assert_nearly_eq;
126
127    use super::*;
128
129    #[test]
130    fn sample_i16_from_i16() {
131        // i16:
132        assert_eq!(Sample::<i16>::from(i16::MIN).0, i16::MIN);
133        assert_eq!(Sample::<i16>::from(i16::MIN / 2).0, i16::MIN / 2);
134        assert_eq!(Sample::<i16>::from(0).0, 0);
135        assert_eq!(Sample::<i16>::from(i16::MAX / 2).0, i16::MAX / 2);
136        assert_eq!(Sample::<i16>::from(i16::MAX).0, i16::MAX);
137    }
138
139    #[test]
140    fn sample_f32_from_f32() {
141        assert_eq!(Sample::<f32>::from(-1.0).0, -1.0);
142        assert_eq!(Sample::<f32>::from(-0.5).0, -0.5);
143        assert_eq!(Sample::<f32>::from(0.0).0, 0.0);
144        assert_eq!(Sample::<f32>::from(0.5).0, 0.5);
145        assert_eq!(Sample::<f32>::from(1.0).0, 1.0);
146
147        // For any values outside of -1.0..=1.0 we expect clamping:
148        assert_eq!(Sample::<f32>::from(f32::MIN).0, -1.0);
149        assert_eq!(Sample::<f32>::from(f32::MAX).0, 1.0);
150    }
151
152    #[test]
153    fn sample_i16_from_sample_f32() {
154        assert_nearly_eq!(
155            Sample::<i16>::from(Sample::<f32>::from(-1.0)),
156            Sample::from(i16::MIN)
157        );
158        assert_nearly_eq!(
159            Sample::<i16>::from(Sample::<f32>::from(-0.5)),
160            Sample::from(i16::MIN / 2)
161        );
162        assert_nearly_eq!(
163            Sample::<i16>::from(Sample::<f32>::from(0.0)),
164            Sample::from(0)
165        );
166        assert_nearly_eq!(
167            Sample::<i16>::from(Sample::<f32>::from(0.5)),
168            Sample::from(i16::MAX / 2)
169        );
170        assert_nearly_eq!(
171            Sample::<i16>::from(Sample::<f32>::from(1.0)),
172            Sample::from(i16::MAX)
173        );
174    }
175
176    #[test]
177    fn sample_f32_from_sample_i16() {
178        assert_nearly_eq!(
179            Sample::<f32>::from(Sample::<i16>::from(i16::MIN)),
180            Sample::from(-1.0)
181        );
182        assert_nearly_eq!(
183            Sample::<f32>::from(Sample::<i16>::from(i16::MIN / 2)),
184            Sample::from(-0.5)
185        );
186        assert_nearly_eq!(
187            Sample::<f32>::from(Sample::<i16>::from(0)),
188            Sample::from(0.0)
189        );
190        assert_nearly_eq!(
191            Sample::<f32>::from(Sample::<i16>::from(i16::MAX / 2)),
192            Sample::from(0.5),
193            0.0001 // rounding error due to i16::MAX being odd
194        );
195        assert_nearly_eq!(
196            Sample::<f32>::from(Sample::<i16>::from(i16::MAX)),
197            Sample::from(1.0)
198        );
199    }
200}