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