rodio/source/
from_iter.rs

1use std::time::Duration;
2
3use crate::{Sample, Source};
4
5use super::SeekError;
6
7/// Builds a source that chains sources provided by an iterator.
8///
9/// The `iterator` parameter is an iterator that produces a source. The source is then played.
10/// Whenever the source ends, the `iterator` is used again in order to produce the source that is
11/// played next.
12///
13/// If the `iterator` produces `None`, then the sound ends.
14pub fn from_iter<I>(iterator: I) -> FromIter<I::IntoIter>
15where
16    I: IntoIterator,
17{
18    let mut iterator = iterator.into_iter();
19    let first_source = iterator.next();
20
21    FromIter {
22        iterator,
23        current_source: first_source,
24    }
25}
26
27/// A source that chains sources provided by an iterator.
28#[derive(Clone)]
29pub struct FromIter<I>
30where
31    I: Iterator,
32{
33    // The iterator that provides sources.
34    iterator: I,
35    // Is only ever `None` if the first element of the iterator is `None`.
36    current_source: Option<I::Item>,
37}
38
39impl<I> Iterator for FromIter<I>
40where
41    I: Iterator,
42    I::Item: Iterator + Source,
43    <I::Item as Iterator>::Item: Sample,
44{
45    type Item = <I::Item as Iterator>::Item;
46
47    #[inline]
48    fn next(&mut self) -> Option<<I::Item as Iterator>::Item> {
49        loop {
50            if let Some(src) = &mut self.current_source {
51                if let Some(value) = src.next() {
52                    return Some(value);
53                }
54            }
55
56            if let Some(src) = self.iterator.next() {
57                self.current_source = Some(src);
58            } else {
59                return None;
60            }
61        }
62    }
63
64    #[inline]
65    fn size_hint(&self) -> (usize, Option<usize>) {
66        if let Some(cur) = &self.current_source {
67            (cur.size_hint().0, None)
68        } else {
69            (0, None)
70        }
71    }
72}
73
74impl<I> Source for FromIter<I>
75where
76    I: Iterator,
77    I::Item: Iterator + Source,
78    <I::Item as Iterator>::Item: Sample,
79{
80    #[inline]
81    fn current_frame_len(&self) -> Option<usize> {
82        // This function is non-trivial because the boundary between the current source and the
83        // next must be a frame boundary as well.
84        //
85        // The current sound is free to return `None` for `current_frame_len()`, in which case
86        // we *should* return the number of samples remaining the current sound.
87        // This can be estimated with `size_hint()`.
88        //
89        // If the `size_hint` is `None` as well, we are in the worst case scenario. To handle this
90        // situation we force a frame to have a maximum number of samples indicate by this
91        // constant.
92        const THRESHOLD: usize = 10240;
93
94        // Try the current `current_frame_len`.
95        if let Some(src) = &self.current_source {
96            if let Some(val) = src.current_frame_len() {
97                if val != 0 {
98                    return Some(val);
99                }
100            }
101        }
102
103        // Try the size hint.
104        if let Some(src) = &self.current_source {
105            if let Some(val) = src.size_hint().1 {
106                if val < THRESHOLD && val != 0 {
107                    return Some(val);
108                }
109            }
110        }
111
112        // Otherwise we use the constant value.
113        Some(THRESHOLD)
114    }
115
116    #[inline]
117    fn channels(&self) -> u16 {
118        if let Some(src) = &self.current_source {
119            src.channels()
120        } else {
121            // Dummy value that only happens if the iterator was empty.
122            2
123        }
124    }
125
126    #[inline]
127    fn sample_rate(&self) -> u32 {
128        if let Some(src) = &self.current_source {
129            src.sample_rate()
130        } else {
131            // Dummy value that only happens if the iterator was empty.
132            44100
133        }
134    }
135
136    #[inline]
137    fn total_duration(&self) -> Option<Duration> {
138        None
139    }
140
141    #[inline]
142    fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
143        if let Some(source) = self.current_source.as_mut() {
144            source.try_seek(pos)
145        } else {
146            Ok(())
147        }
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use crate::buffer::SamplesBuffer;
154    use crate::source::{from_iter, Source};
155
156    #[test]
157    fn basic() {
158        let mut rx = from_iter((0..2).map(|n| {
159            if n == 0 {
160                SamplesBuffer::new(1, 48000, vec![10i16, -10, 10, -10])
161            } else if n == 1 {
162                SamplesBuffer::new(2, 96000, vec![5i16, 5, 5, 5])
163            } else {
164                unreachable!()
165            }
166        }));
167
168        assert_eq!(rx.channels(), 1);
169        assert_eq!(rx.sample_rate(), 48000);
170        assert_eq!(rx.next(), Some(10));
171        assert_eq!(rx.next(), Some(-10));
172        assert_eq!(rx.next(), Some(10));
173        assert_eq!(rx.next(), Some(-10));
174        /*assert_eq!(rx.channels(), 2);
175        assert_eq!(rx.sample_rate(), 96000);*/
176        // FIXME: not working
177        assert_eq!(rx.next(), Some(5));
178        assert_eq!(rx.next(), Some(5));
179        assert_eq!(rx.next(), Some(5));
180        assert_eq!(rx.next(), Some(5));
181        assert_eq!(rx.next(), None);
182    }
183}