1use alloc::boxed::Box;
4use alloc::vec::Vec;
5
6use crate::error::Error;
7use crate::mix::SharedPcm;
8use crate::pcm::{Limits, Pcm, PcmFormat};
9
10const CHUNK_SAMPLES: usize = 8192;
12
13pub trait Source {
17 fn format(&self) -> PcmFormat;
19
20 fn total_frames(&self) -> Option<u64>;
23
24 fn read(&mut self, out: &mut [i16]) -> Result<usize, Error>;
31
32 fn rewind_to(&mut self, frame: u64) -> Result<(), Error> {
36 let _ = frame;
37 Err(Error::NoRewind)
38 }
39}
40
41impl<S: Source + ?Sized> Source for Box<S> {
42 fn format(&self) -> PcmFormat {
43 (**self).format()
44 }
45
46 fn total_frames(&self) -> Option<u64> {
47 (**self).total_frames()
48 }
49
50 fn read(&mut self, out: &mut [i16]) -> Result<usize, Error> {
51 (**self).read(out)
52 }
53
54 fn rewind_to(&mut self, frame: u64) -> Result<(), Error> {
55 (**self).rewind_to(frame)
56 }
57}
58
59pub(crate) fn room(format: PcmFormat, out: &[i16]) -> Result<usize, Error> {
61 let channels = format.channels.count();
62 let frames = out.len() / usize::from(channels);
63 if frames == 0 {
64 return Err(Error::NoRoom {
65 samples: out.len(),
66 channels,
67 });
68 }
69 Ok(frames)
70}
71
72pub fn read_all(source: &mut impl Source, limits: &Limits) -> Result<Pcm, Error> {
74 let format = source.format();
75 let channels = usize::from(format.channels.count());
76 if let Some(frames) = source.total_frames().filter(|f| *f > limits.max_frames) {
77 return Err(Error::TooLong {
78 frames,
79 limit: limits.max_frames,
80 });
81 }
82 let mut samples = Vec::new();
83 let mut chunk = [0; CHUNK_SAMPLES];
84 loop {
85 let frames = source.read(&mut chunk)?;
86 if frames == 0 {
87 return Pcm::new(format, samples, limits);
88 }
89 samples.extend_from_slice(&chunk[..frames * channels]);
90 let frames = samples.len() as u64 / channels as u64;
91 if frames > limits.max_frames {
92 return Err(Error::TooLong {
93 frames,
94 limit: limits.max_frames,
95 });
96 }
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct PcmSource<'a> {
103 pcm: &'a Pcm,
104 at: usize,
106}
107
108impl<'a> PcmSource<'a> {
109 pub const fn new(pcm: &'a Pcm) -> Self {
111 Self { pcm, at: 0 }
112 }
113}
114
115impl Source for PcmSource<'_> {
116 fn format(&self) -> PcmFormat {
117 self.pcm.format()
118 }
119
120 fn total_frames(&self) -> Option<u64> {
121 Some(self.pcm.frames())
122 }
123
124 fn read(&mut self, out: &mut [i16]) -> Result<usize, Error> {
125 let format = self.format();
126 let channels = usize::from(format.channels.count());
127 let room = room(format, out)?;
128 let left = &self.pcm.samples()[self.at..];
129 let frames = room.min(left.len() / channels);
130 let count = frames * channels;
131 out[..count].copy_from_slice(&left[..count]);
132 self.at += count;
133 Ok(frames)
134 }
135
136 fn rewind_to(&mut self, frame: u64) -> Result<(), Error> {
138 self.at = sample_index(self.pcm, frame);
139 Ok(())
140 }
141}
142
143fn sample_index(pcm: &Pcm, frame: u64) -> usize {
145 let channels = u64::from(pcm.format().channels.count());
146 let frame = frame.min(pcm.frames());
147 usize::try_from(frame * channels).unwrap_or(pcm.samples().len())
148}
149
150#[derive(Debug, Clone)]
153pub struct SharedSource {
154 pcm: SharedPcm,
155 at: usize,
157}
158
159impl SharedSource {
160 pub const fn new(pcm: SharedPcm) -> Self {
162 Self { pcm, at: 0 }
163 }
164}
165
166impl Source for SharedSource {
167 fn format(&self) -> PcmFormat {
168 self.pcm.format()
169 }
170
171 fn total_frames(&self) -> Option<u64> {
172 Some(self.pcm.frames())
173 }
174
175 fn read(&mut self, out: &mut [i16]) -> Result<usize, Error> {
176 let mut source = PcmSource {
177 pcm: &self.pcm,
178 at: self.at,
179 };
180 let frames = source.read(out)?;
181 self.at = source.at;
182 Ok(frames)
183 }
184
185 fn rewind_to(&mut self, frame: u64) -> Result<(), Error> {
187 self.at = sample_index(&self.pcm, frame);
188 Ok(())
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195 use crate::pcm::Channels;
196 use proptest::prelude::*;
197
198 fn format(channels: Channels) -> PcmFormat {
199 PcmFormat::new(8_000, u32::from(channels.count()), &Limits::default())
200 .unwrap_or_else(|e| panic!("{e}"))
201 }
202
203 fn pcm(channels: Channels, samples: Vec<i16>) -> Pcm {
204 Pcm::new(format(channels), samples, &Limits::default()).unwrap_or_else(|e| panic!("{e}"))
205 }
206
207 #[test]
208 fn a_read_gives_whole_frames_up_to_the_room() {
209 let sound = pcm(Channels::Stereo, vec![1, 2, 3, 4, 5, 6]);
210 let mut source = sound.source();
211 assert_eq!(source.format(), sound.format());
212 assert_eq!(source.total_frames(), Some(3));
213 let mut out = [0; 5];
214 assert_eq!(source.read(&mut out), Ok(2));
215 assert_eq!(out, [1, 2, 3, 4, 0]);
216 assert_eq!(source.read(&mut out), Ok(1));
217 assert_eq!(out[..2], [5, 6]);
218 assert_eq!(source.read(&mut out), Ok(0));
219 assert_eq!(source.read(&mut out), Ok(0));
220 }
221
222 #[test]
223 fn a_read_without_room_for_a_frame_says_so() {
224 let sound = pcm(Channels::Stereo, vec![1, 2]);
225 let mut one = [0; 1];
226 assert_eq!(
227 sound.source().read(&mut one),
228 Err(Error::NoRoom {
229 samples: 1,
230 channels: 2
231 })
232 );
233 assert_eq!(
234 sound.source().read(&mut []),
235 Err(Error::NoRoom {
236 samples: 0,
237 channels: 2
238 })
239 );
240 }
241
242 struct Endless(Channels, u32);
245
246 impl Source for Endless {
247 fn format(&self) -> PcmFormat {
248 format(self.0)
249 }
250
251 fn total_frames(&self) -> Option<u64> {
252 None
253 }
254
255 fn read(&mut self, out: &mut [i16]) -> Result<usize, Error> {
256 self.1 += 1;
257 assert!(self.1 < 16, "read on long past the limit");
258 out.fill(7);
259 Ok(out.len() / usize::from(self.0.count()))
260 }
261 }
262
263 #[test]
264 fn a_shared_source_reads_and_goes_back_as_a_borrowed_one_does() {
265 let sound = pcm(Channels::Stereo, vec![1, 2, 3, 4, 5, 6]);
266 let mut shared = SharedSource::new(SharedPcm::new(sound.clone()));
267 let mut borrowed = sound.source();
268 assert_eq!(shared.format(), sound.format());
269 assert_eq!(shared.total_frames(), Some(3));
270 let (mut a, mut b) = ([0; 4], [0; 4]);
271 assert_eq!(shared.read(&mut a), Ok(2));
272 assert_eq!(borrowed.read(&mut b), Ok(2));
273 assert_eq!(a, [1, 2, 3, 4]);
274 assert_eq!(a, b);
275 assert_eq!(shared.rewind_to(1), Ok(()));
276 assert_eq!(borrowed.rewind_to(1), Ok(()));
277 assert_eq!(shared.read(&mut a), Ok(2));
278 assert_eq!(borrowed.read(&mut b), Ok(2));
279 assert_eq!(a, [3, 4, 5, 6]);
280 assert_eq!(a, b);
281 assert_eq!(shared.rewind_to(9), Ok(()));
282 assert_eq!(borrowed.rewind_to(9), Ok(()));
283 assert_eq!(shared.read(&mut a), Ok(0));
284 assert_eq!(borrowed.read(&mut b), Ok(0));
285 }
286
287 #[test]
288 fn a_source_goes_back_only_if_it_says_it_can() {
289 let mut endless = Endless(Channels::Mono, 0);
290 assert_eq!(endless.rewind_to(0), Err(Error::NoRewind));
291 }
292
293 #[test]
294 fn read_all_stops_at_the_limit_of_an_endless_source() {
295 let limits = Limits::new(8_000, 10_000);
296 for (channels, chunk) in [(Channels::Mono, 8192), (Channels::Stereo, 4096)] {
297 let Err(Error::TooLong { frames, limit }) =
298 read_all(&mut Endless(channels, 0), &limits)
299 else {
300 panic!("it should have stopped");
301 };
302 assert_eq!(limit, 10_000);
303 assert!(frames > 10_000 && frames <= 10_000 + chunk, "{frames}");
305 }
306 }
307
308 #[test]
309 fn read_all_refuses_a_known_length_over_the_limit_before_reading() {
310 let sound = pcm(Channels::Mono, vec![0; 5]);
311 assert_eq!(
312 read_all(&mut sound.source(), &Limits::new(8_000, 4)),
313 Err(Error::TooLong {
314 frames: 5,
315 limit: 4
316 })
317 );
318 }
319
320 #[test]
321 fn read_all_takes_what_is_exactly_at_the_limit() {
322 let sound = pcm(Channels::Mono, vec![3; 5]);
323 assert_eq!(
324 read_all(&mut sound.source(), &Limits::new(8_000, 5)).as_ref(),
325 Ok(&sound)
326 );
327 }
328
329 proptest! {
330 #[test]
332 fn chunks_rebuild_the_whole(
333 samples in proptest::collection::vec(any::<i16>(), 0..512),
334 stereo: bool,
335 room in 2usize..64,
336 ) {
337 let channels = if stereo { Channels::Stereo } else { Channels::Mono };
338 let whole = samples.len() / usize::from(channels.count());
339 let samples = samples[..whole * usize::from(channels.count())].to_vec();
340 let sound = pcm(channels, samples.clone());
341 let mut source = sound.source();
342 let mut out = vec![0; room];
343 let mut rebuilt = Vec::new();
344 loop {
345 let frames = source.read(&mut out).unwrap_or_else(|e| panic!("{e}"));
346 if frames == 0 {
347 break;
348 }
349 prop_assert!(frames * usize::from(channels.count()) <= room);
350 rebuilt.extend_from_slice(&out[..frames * usize::from(channels.count())]);
351 }
352 prop_assert_eq!(rebuilt, samples);
353 let again = read_all(&mut sound.source(), &Limits::default());
354 prop_assert_eq!(again.as_ref(), Ok(&sound));
355 }
356 }
357}