rodio/source/
mix.rs

1use std::cmp;
2use std::time::Duration;
3
4use crate::common::{ChannelCount, SampleRate};
5use crate::source::uniform::UniformSourceIterator;
6use crate::source::SeekError;
7use crate::Source;
8
9/// Internal function that builds a `Mix` object.
10pub fn mix<I1, I2>(input1: I1, input2: I2) -> Mix<I1, I2>
11where
12    I1: Source,
13    I2: Source,
14{
15    let channels = input1.channels();
16    let rate = input1.sample_rate();
17
18    Mix {
19        input1: UniformSourceIterator::new(input1, channels, rate),
20        input2: UniformSourceIterator::new(input2, channels, rate),
21    }
22}
23
24/// Filter that modifies each sample by a given value.
25#[derive(Clone)]
26pub struct Mix<I1, I2>
27where
28    I1: Source,
29    I2: Source,
30{
31    input1: UniformSourceIterator<I1>,
32    input2: UniformSourceIterator<I2>,
33}
34
35impl<I1, I2> Iterator for Mix<I1, I2>
36where
37    I1: Source,
38    I2: Source,
39{
40    type Item = I1::Item;
41
42    #[inline]
43    fn next(&mut self) -> Option<I1::Item> {
44        let s1 = self.input1.next();
45        let s2 = self.input2.next();
46
47        match (s1, s2) {
48            (Some(s1), Some(s2)) => Some(s1 + s2),
49            (Some(s1), None) => Some(s1),
50            (None, Some(s2)) => Some(s2),
51            (None, None) => None,
52        }
53    }
54
55    #[inline]
56    fn size_hint(&self) -> (usize, Option<usize>) {
57        let s1 = self.input1.size_hint();
58        let s2 = self.input2.size_hint();
59
60        let min = cmp::max(s1.0, s2.0);
61        let max = match (s1.1, s2.1) {
62            (Some(s1), Some(s2)) => Some(cmp::max(s1, s2)),
63            _ => None,
64        };
65
66        (min, max)
67    }
68}
69
70impl<I1, I2> ExactSizeIterator for Mix<I1, I2>
71where
72    I1: Source + ExactSizeIterator,
73    I2: Source + ExactSizeIterator,
74{
75}
76
77impl<I1, I2> Source for Mix<I1, I2>
78where
79    I1: Source,
80    I2: Source,
81{
82    #[inline]
83    fn current_span_len(&self) -> Option<usize> {
84        let f1 = self.input1.current_span_len();
85        let f2 = self.input2.current_span_len();
86
87        match (f1, f2) {
88            (Some(f1), Some(f2)) => Some(cmp::min(f1, f2)),
89            _ => None,
90        }
91    }
92
93    #[inline]
94    fn channels(&self) -> ChannelCount {
95        self.input1.channels()
96    }
97
98    #[inline]
99    fn sample_rate(&self) -> SampleRate {
100        self.input1.sample_rate()
101    }
102
103    #[inline]
104    fn total_duration(&self) -> Option<Duration> {
105        let f1 = self.input1.total_duration();
106        let f2 = self.input2.total_duration();
107
108        match (f1, f2) {
109            (Some(f1), Some(f2)) => Some(cmp::max(f1, f2)),
110            _ => None,
111        }
112    }
113
114    /// Will only attempt a seek if both underlying sources support seek.
115    #[inline]
116    fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
117        Err(SeekError::NotSupported {
118            underlying_source: std::any::type_name::<Self>(),
119        })
120
121        // uncomment when #510 is implemented (query position of playback)
122        // TODO use source_intact to check if rollback makes sense
123
124        // let org_pos = self.input1.playback_pos();
125        // self.input1.try_seek(pos)?;
126        //
127        // let res = self.input2.try_seek(pos);
128        // if res.is_err() { // rollback seek in input1
129        //     self.input1.try_seek(org_pos)?;
130        // }
131        //
132        // res
133    }
134}