1use alloc::vec::Vec;
4
5use crate::error::Error;
6use crate::source::PcmSource;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum Channels {
11 Mono,
13 Stereo,
15}
16
17impl Channels {
18 pub const fn from_count(count: u32) -> Result<Self, Error> {
20 match count {
21 1 => Ok(Self::Mono),
22 2 => Ok(Self::Stereo),
23 other => Err(Error::Channels(other)),
24 }
25 }
26
27 pub const fn count(self) -> u16 {
29 match self {
30 Self::Mono => 1,
31 Self::Stereo => 2,
32 }
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39#[non_exhaustive]
40pub struct Limits {
41 pub max_rate: u32,
43 pub max_frames: u64,
45}
46
47impl Limits {
48 pub const DEFAULT_MAX_RATE: u32 = 192_000;
50 pub const DEFAULT_MAX_FRAMES: u64 = 1 << 26;
53
54 pub const fn new(max_rate: u32, max_frames: u64) -> Self {
56 Self {
57 max_rate,
58 max_frames,
59 }
60 }
61}
62
63impl Default for Limits {
64 fn default() -> Self {
65 Self::new(Self::DEFAULT_MAX_RATE, Self::DEFAULT_MAX_FRAMES)
66 }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71#[non_exhaustive]
72pub struct PcmFormat {
73 pub rate: u32,
75 pub channels: Channels,
77}
78
79impl PcmFormat {
80 pub fn new(rate: u32, channels: u32, limits: &Limits) -> Result<Self, Error> {
82 let channels = Channels::from_count(channels)?;
83 if rate == 0 || rate > limits.max_rate {
84 return Err(Error::Rate {
85 rate,
86 max: limits.max_rate,
87 });
88 }
89 Ok(Self { rate, channels })
90 }
91
92 pub fn samples_of(&self, frames: u64) -> Option<usize> {
94 let samples = frames.checked_mul(u64::from(self.channels.count()))?;
95 usize::try_from(samples).ok()
96 }
97
98 pub fn millis_of(&self, frames: u64) -> Option<u64> {
101 let rate = u64::from(self.rate);
102 let whole = frames / rate;
103 let part = frames % rate;
104 whole.checked_mul(1000)?.checked_add(part * 1000 / rate)
105 }
106
107 pub fn frames_of_millis(&self, millis: u64) -> Option<u64> {
109 let rate = u64::from(self.rate);
110 let whole = millis / 1000;
111 let part = millis % 1000;
112 whole.checked_mul(rate)?.checked_add(part * rate / 1000)
113 }
114}
115
116pub const fn from_i8(sample: i8) -> i16 {
118 (sample as i16) << 8
119}
120
121pub fn from_i24(sample: i32) -> i16 {
124 clamp_i16(sample >> 8)
125}
126
127pub fn from_i32(sample: i32) -> i16 {
129 clamp_i16(sample >> 16)
130}
131
132fn clamp_i16(value: i32) -> i16 {
133 i16::try_from(value.clamp(i32::from(i16::MIN), i32::from(i16::MAX))).unwrap_or(0)
134}
135
136#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct Pcm {
139 format: PcmFormat,
140 samples: Vec<i16>,
141}
142
143impl Pcm {
144 pub fn new(format: PcmFormat, samples: Vec<i16>, limits: &Limits) -> Result<Self, Error> {
146 let channels = format.channels.count();
147 let count = samples.len() as u64;
148 if !count.is_multiple_of(u64::from(channels)) {
149 return Err(Error::PartialFrame {
150 samples: count,
151 channels,
152 });
153 }
154 let frames = count / u64::from(channels);
155 if frames > limits.max_frames {
156 return Err(Error::TooLong {
157 frames,
158 limit: limits.max_frames,
159 });
160 }
161 Ok(Self { format, samples })
162 }
163
164 pub const fn format(&self) -> PcmFormat {
166 self.format
167 }
168
169 pub fn samples(&self) -> &[i16] {
171 &self.samples
172 }
173
174 pub fn into_samples(self) -> Vec<i16> {
176 self.samples
177 }
178
179 pub fn frames(&self) -> u64 {
181 self.samples.len() as u64 / u64::from(self.format.channels.count())
182 }
183
184 pub fn millis(&self) -> u64 {
186 self.format.millis_of(self.frames()).unwrap_or(u64::MAX)
188 }
189
190 pub const fn source(&self) -> PcmSource<'_> {
192 PcmSource::new(self)
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199 use proptest::prelude::*;
200
201 const STEREO: PcmFormat = PcmFormat {
202 rate: 44_100,
203 channels: Channels::Stereo,
204 };
205
206 #[test]
207 fn a_sound_has_one_or_two_channels() {
208 assert_eq!(Channels::from_count(1).map(Channels::count), Ok(1));
209 assert_eq!(Channels::from_count(2).map(Channels::count), Ok(2));
210 assert_eq!(Channels::from_count(0), Err(Error::Channels(0)));
211 assert_eq!(Channels::from_count(3), Err(Error::Channels(3)));
212 }
213
214 #[test]
215 fn a_rate_is_from_one_to_the_limit() {
216 let limits = Limits::new(48_000, 10);
217 let bad = |rate| Err(Error::Rate { rate, max: 48_000 });
218 assert_eq!(PcmFormat::new(0, 1, &limits), bad(0));
219 assert_eq!(PcmFormat::new(48_001, 1, &limits), bad(48_001));
220 assert_eq!(
221 PcmFormat::new(48_000, 1, &limits).map(|f| f.rate),
222 Ok(48_000)
223 );
224 assert_eq!(PcmFormat::new(1, 2, &limits).map(|f| f.rate), Ok(1));
225 assert_eq!(PcmFormat::new(1, 5, &limits), Err(Error::Channels(5)));
226 }
227
228 #[test]
229 fn the_default_limits_are_the_ones_written() {
230 let limits = Limits::default();
231 assert_eq!(limits.max_rate, 192_000);
232 assert_eq!(limits.max_frames, 67_108_864);
233 }
234
235 #[test]
236 fn arithmetic_is_exact_and_checked() {
237 assert_eq!(STEREO.samples_of(3), Some(6));
238 assert_eq!(STEREO.samples_of(u64::MAX), None);
239 assert_eq!(STEREO.millis_of(44_100), Some(1000));
240 assert_eq!(STEREO.millis_of(66_150), Some(1500));
241 assert_eq!(STEREO.millis_of(44), Some(0));
242 assert_eq!(STEREO.millis_of(45), Some(1));
243 assert_eq!(STEREO.frames_of_millis(1500), Some(66_150));
244 assert_eq!(STEREO.frames_of_millis(1), Some(44));
245 let fast = PcmFormat {
246 rate: 1,
247 channels: Channels::Mono,
248 };
249 assert_eq!(fast.millis_of(u64::MAX), None);
250 assert_eq!(
251 fast.millis_of(u64::MAX / 1000),
252 Some(u64::MAX / 1000 * 1000)
253 );
254 let high = PcmFormat {
255 rate: u32::MAX,
256 channels: Channels::Mono,
257 };
258 assert_eq!(high.frames_of_millis(u64::MAX), None);
259 }
260
261 #[test]
262 fn widths_keep_the_fraction_of_full_scale() {
263 assert_eq!(from_i8(i8::MAX), 0x7F00);
264 assert_eq!(from_i8(i8::MIN), i16::MIN);
265 assert_eq!(from_i8(1), 256);
266 assert_eq!(from_i24(0x7F_FFFF), i16::MAX);
267 assert_eq!(from_i24(-0x80_0000), i16::MIN);
268 assert_eq!(from_i24(0x100), 1);
269 assert_eq!(from_i24(0x0FF), 0);
270 assert_eq!(from_i24(i32::MAX), i16::MAX);
271 assert_eq!(from_i24(i32::MIN), i16::MIN);
272 assert_eq!(from_i32(i32::MAX), i16::MAX);
273 assert_eq!(from_i32(i32::MIN), i16::MIN);
274 assert_eq!(from_i32(0x1_0000), 1);
275 }
276
277 #[test]
278 fn pcm_holds_whole_frames_within_the_limit() {
279 let limits = Limits::new(48_000, 2);
280 let pcm = Pcm::new(STEREO, vec![1, 2, 3, 4], &limits);
281 let pcm = pcm.unwrap_or_else(|e| panic!("{e}"));
282 assert_eq!(pcm.frames(), 2);
283 assert_eq!(pcm.samples(), [1, 2, 3, 4]);
284 assert_eq!(pcm.format(), STEREO);
285 assert_eq!(pcm.millis(), 0);
286 assert_eq!(pcm.clone().into_samples(), [1, 2, 3, 4]);
287 assert_eq!(
288 Pcm::new(STEREO, vec![1, 2, 3], &limits),
289 Err(Error::PartialFrame {
290 samples: 3,
291 channels: 2
292 })
293 );
294 assert_eq!(
295 Pcm::new(STEREO, vec![0; 6], &limits),
296 Err(Error::TooLong {
297 frames: 3,
298 limit: 2
299 })
300 );
301 }
302
303 #[test]
304 fn a_seconds_worth_of_pcm_plays_a_second() {
305 let samples = vec![0; 88_200];
306 let pcm = Pcm::new(STEREO, samples, &Limits::default());
307 assert_eq!(pcm.map(|p| p.millis()), Ok(1000));
308 }
309
310 proptest! {
311 #[test]
313 fn frames_and_millis_round_trip(rate in 1u32..=192_000, frames in 0u64..1 << 40) {
314 let format = PcmFormat { rate, channels: Channels::Mono };
315 let millis = format.millis_of(frames).unwrap_or(0);
316 let back = format.frames_of_millis(millis).unwrap_or(0);
317 prop_assert!(back <= frames);
318 prop_assert!(frames - back <= u64::from(rate) / 1000 + 1);
319 }
320
321 #[test]
324 fn pcm_new_never_panics(count in 0usize..64, stereo: bool, limit in 0u64..64) {
325 let channels = if stereo { Channels::Stereo } else { Channels::Mono };
326 let format = PcmFormat { rate: 8_000, channels };
327 let limits = Limits::new(8_000, limit);
328 if let Ok(pcm) = Pcm::new(format, vec![0; count], &limits) {
329 prop_assert!(pcm.frames() <= limit);
330 prop_assert_eq!(pcm.samples().len() % usize::from(channels.count()), 0);
331 }
332 }
333 }
334}