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