1use crate::prelude::*;
7
8#[cfg(not(target_has_atomic = "ptr"))]
10use alloc::rc::Rc as Shared;
11#[cfg(target_has_atomic = "ptr")]
12use alloc::sync::Arc as Shared;
13
14pub type SharedSamples = Shared<Samples>;
17
18#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
20#[non_exhaustive]
21pub enum SamplesError {
22 #[error("{0} channels: a sound has one or two")]
24 Channels(u32),
25 #[error("{0} samples a second: it must be from 1 to {max}", max = Samples::MAX_RATE)]
27 Rate(u32),
28 #[error("{samples} samples aren't whole frames of {channels} channels")]
30 PartialFrame {
31 samples: usize,
33 channels: u16,
35 },
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct Samples {
43 rate: u32,
44 channels: u16,
45 data: Vec<i16>,
46}
47
48impl Samples {
49 pub const MAX_RATE: u32 = 192_000;
51
52 pub fn new(rate: u32, channels: u32, samples: Vec<i16>) -> Result<Self, SamplesError> {
55 let channels = match channels {
56 1 => 1,
57 2 => 2,
58 other => return Err(SamplesError::Channels(other)),
59 };
60 if rate == 0 || rate > Self::MAX_RATE {
61 return Err(SamplesError::Rate(rate));
62 }
63 if !samples.len().is_multiple_of(usize::from(channels)) {
64 return Err(SamplesError::PartialFrame {
65 samples: samples.len(),
66 channels,
67 });
68 }
69 Ok(Self {
70 rate,
71 channels,
72 data: samples,
73 })
74 }
75
76 pub fn from_u8(rate: u32, channels: u32, bytes: &[u8]) -> Result<Self, SamplesError> {
80 let samples = bytes.iter().map(|&b| (i16::from(b) - 128) * 256).collect();
81 Self::new(rate, channels, samples)
82 }
83
84 pub const fn rate(&self) -> u32 {
86 self.rate
87 }
88
89 pub const fn channels(&self) -> u16 {
91 self.channels
92 }
93
94 pub fn samples(&self) -> &[i16] {
96 &self.data
97 }
98
99 pub fn into_samples(self) -> Vec<i16> {
101 self.data
102 }
103
104 pub fn frames(&self) -> u64 {
106 u64::try_from(self.data.len() / usize::from(self.channels)).unwrap_or(u64::MAX)
107 }
108
109 pub fn millis(&self) -> u64 {
111 let rate = u64::from(self.rate);
112 let frames = self.frames();
113 (frames / rate)
114 .saturating_mul(1000)
115 .saturating_add(frames % rate * 1000 / rate)
116 }
117
118 pub fn bytes(&self) -> u64 {
120 u64::try_from(self.data.len())
121 .unwrap_or(u64::MAX)
122 .saturating_mul(2)
123 }
124
125 pub fn to_wav(&self) -> Vec<u8> {
128 let block = self.channels * 2;
129 let data_len = wav_data_bytes(self.data.len(), block);
130 let written = usize::try_from(data_len / 2).unwrap_or(0);
131 let mut out = Vec::with_capacity(44 + written * 2);
132 out.extend_from_slice(b"RIFF");
133 out.extend_from_slice(&(36 + data_len).to_le_bytes());
134 out.extend_from_slice(b"WAVEfmt ");
135 out.extend_from_slice(&16u32.to_le_bytes());
136 out.extend_from_slice(&1u16.to_le_bytes());
137 out.extend_from_slice(&self.channels.to_le_bytes());
138 out.extend_from_slice(&self.rate.to_le_bytes());
139 let byte_rate = self.rate.saturating_mul(u32::from(block));
140 out.extend_from_slice(&byte_rate.to_le_bytes());
141 out.extend_from_slice(&block.to_le_bytes());
142 out.extend_from_slice(&16u16.to_le_bytes());
143 out.extend_from_slice(b"data");
144 out.extend_from_slice(&data_len.to_le_bytes());
145 for sample in &self.data[..written] {
146 out.extend_from_slice(&sample.to_le_bytes());
147 }
148 out
149 }
150}
151
152fn wav_data_bytes(samples: usize, block: u16) -> u32 {
155 let room = (u32::MAX - 36) / u32::from(block) * u32::from(block);
156 u32::try_from(samples.saturating_mul(2)).map_or(room, |bytes| bytes.min(room))
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn eight_bit_unsigned_samples_centre_on_128() {
165 let made = Samples::from_u8(11_025, 1, &[128, 255, 0, 129, 127]);
166 let made = made.unwrap_or_else(|e| panic!("{e}"));
167 assert_eq!(made.samples(), [0, 0x7F00, i16::MIN, 256, -256]);
168 assert_eq!((made.rate(), made.channels()), (11_025, 1));
169 let stereo = Samples::from_u8(8_000, 2, &[0, 255]);
170 let stereo = stereo.unwrap_or_else(|e| panic!("{e}"));
171 assert_eq!((stereo.channels(), stereo.frames()), (2, 1));
172 assert_eq!(
173 Samples::from_u8(8_000, 2, &[1, 2, 3]),
174 Err(SamplesError::PartialFrame {
175 samples: 3,
176 channels: 2
177 })
178 );
179 assert_eq!(Samples::from_u8(0, 1, &[1]), Err(SamplesError::Rate(0)));
180 assert_eq!(
181 Samples::from_u8(192_001, 1, &[1]),
182 Err(SamplesError::Rate(192_001))
183 );
184 assert_eq!(
185 Samples::from_u8(8_000, 3, &[1]),
186 Err(SamplesError::Channels(3))
187 );
188 assert_eq!(
189 Samples::new(8_000, 0, vec![]),
190 Err(SamplesError::Channels(0))
191 );
192 }
193
194 #[test]
195 fn length_and_size_are_exact() {
196 let second = Samples::new(44_100, 2, vec![0; 88_200]);
197 let second = second.unwrap_or_else(|e| panic!("{e}"));
198 assert_eq!(
199 (second.frames(), second.millis(), second.bytes()),
200 (44_100, 1000, 176_400)
201 );
202 let short = Samples::new(11_025, 1, vec![0; 1102]).unwrap_or_else(|e| panic!("{e}"));
203 assert_eq!(short.millis(), 99);
204 assert_eq!(short.clone().into_samples().len(), 1102);
205 let empty = Samples::new(1, 1, Vec::new()).unwrap_or_else(|e| panic!("{e}"));
206 assert_eq!((empty.frames(), empty.millis()), (0, 0));
207 }
208
209 #[test]
210 fn a_wav_holds_the_samples_after_a_44_byte_header() {
211 let made = Samples::new(44_100, 2, vec![1, -2, 0x7FFF, i16::MIN]);
212 let wav = made.unwrap_or_else(|e| panic!("{e}")).to_wav();
213 assert_eq!(wav.len(), 44 + 8);
214 assert_eq!(&wav[..4], b"RIFF");
215 assert_eq!(wav[4..8], 44u32.to_le_bytes());
216 assert_eq!(&wav[8..16], b"WAVEfmt ");
217 assert_eq!(wav[16..20], 16u32.to_le_bytes());
218 assert_eq!(wav[20..22], 1u16.to_le_bytes());
220 assert_eq!(wav[22..24], 2u16.to_le_bytes());
221 assert_eq!(wav[24..28], 44_100u32.to_le_bytes());
222 assert_eq!(wav[28..32], (44_100u32 * 4).to_le_bytes());
223 assert_eq!(wav[32..34], 4u16.to_le_bytes());
224 assert_eq!(wav[34..36], 16u16.to_le_bytes());
225 assert_eq!(&wav[36..40], b"data");
226 assert_eq!(wav[40..44], 8u32.to_le_bytes());
227 assert_eq!(wav[44..], [1, 0, 0xFE, 0xFF, 0xFF, 0x7F, 0, 0x80]);
228 }
229
230 #[test]
231 fn a_mono_wav_has_two_bytes_a_frame() {
232 let made = Samples::new(8_000, 1, vec![5, -5, 7]).unwrap_or_else(|e| panic!("{e}"));
233 let wav = made.to_wav();
234 assert_eq!(wav.len(), 44 + 6);
235 assert_eq!(wav[4..8], 42u32.to_le_bytes());
236 assert_eq!(wav[22..24], 1u16.to_le_bytes());
237 assert_eq!(wav[28..32], 16_000u32.to_le_bytes());
238 assert_eq!(wav[32..34], 2u16.to_le_bytes());
239 assert_eq!(wav[40..44], 6u32.to_le_bytes());
240 assert_eq!(wav[44..], [5, 0, 0xFB, 0xFF, 7, 0]);
241 }
242
243 #[test]
244 fn a_wav_says_all_its_samples_or_the_whole_frames_32_bits_can() {
245 assert_eq!(wav_data_bytes(0, 2), 0);
247 assert_eq!(wav_data_bytes(3, 2), 6);
248 assert_eq!(wav_data_bytes(2_147_483_629, 2), 4_294_967_258);
249 assert_eq!(wav_data_bytes(2_147_483_630, 2), 4_294_967_258);
250 assert_eq!(wav_data_bytes(2_147_483_628, 4), 4_294_967_256);
251 assert_eq!(wav_data_bytes(2_147_483_630, 4), 4_294_967_256);
252 assert_eq!(wav_data_bytes(usize::MAX, 2), 4_294_967_258);
253 assert_eq!(wav_data_bytes(usize::MAX, 4), 4_294_967_256);
254 }
255
256 proptest::proptest! {
257 #[test]
260 fn new_never_panics(count in 0usize..64, channels in 0u32..4, rate in 0u32..200_000) {
261 if let Ok(made) = Samples::new(rate, channels, vec![0; count]) {
262 proptest::prop_assert!((1..=Samples::MAX_RATE).contains(&made.rate()));
263 let channels = usize::from(made.channels());
264 proptest::prop_assert_eq!(made.samples().len() % channels, 0);
265 proptest::prop_assert_eq!(made.to_wav().len(), 44 + count * 2);
266 }
267 }
268 }
269}