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