geng_rodio/source/
take.rs

1use std::time::Duration;
2
3use crate::{Sample, Source};
4
5/// Internal function that builds a `TakeDuration` object.
6pub fn take_duration<I>(input: I, duration: Duration) -> TakeDuration<I>
7where
8    I: Source,
9    I::Item: Sample,
10{
11    TakeDuration {
12        current_frame_len: input.current_frame_len(),
13        duration_per_sample: TakeDuration::get_duration_per_sample(&input),
14        input,
15        remaining_duration: duration,
16        requested_duration: duration,
17        filter: None,
18    }
19}
20
21/// A filter that can be applied to a `TakeDuration`.
22#[derive(Clone, Debug)]
23enum DurationFilter {
24    FadeOut,
25}
26impl DurationFilter {
27    fn apply<I: Iterator>(
28        &self,
29        sample: <I as Iterator>::Item,
30        parent: &TakeDuration<I>,
31    ) -> <I as Iterator>::Item
32    where
33        I::Item: Sample,
34    {
35        use self::DurationFilter::*;
36        match self {
37            FadeOut => {
38                let remaining = parent.remaining_duration.as_millis() as f32;
39                let total = parent.requested_duration.as_millis() as f32;
40                sample.amplify(remaining / total)
41            }
42        }
43    }
44}
45
46const NANOS_PER_SEC: u64 = 1_000_000_000;
47
48/// A source that truncates the given source to a certain duration.
49#[derive(Clone, Debug)]
50pub struct TakeDuration<I> {
51    input: I,
52    remaining_duration: Duration,
53    requested_duration: Duration,
54    filter: Option<DurationFilter>,
55    // Remaining samples in current frame.
56    current_frame_len: Option<usize>,
57    // Only updated when the current frame len is exhausted.
58    duration_per_sample: Duration,
59}
60
61impl<I> TakeDuration<I>
62where
63    I: Source,
64    I::Item: Sample,
65{
66    /// Returns the duration elapsed for each sample extracted.
67    #[inline]
68    fn get_duration_per_sample(input: &I) -> Duration {
69        let ns = NANOS_PER_SEC / (input.sample_rate() as u64 * input.channels() as u64);
70        // \|/ the maximum value of `ns` is one billion, so this can't fail
71        Duration::new(0, ns as u32)
72    }
73
74    /// Returns a reference to the inner source.
75    #[inline]
76    pub fn inner(&self) -> &I {
77        &self.input
78    }
79
80    /// Returns a mutable reference to the inner source.
81    #[inline]
82    pub fn inner_mut(&mut self) -> &mut I {
83        &mut self.input
84    }
85
86    /// Returns the inner source.
87    #[inline]
88    pub fn into_inner(self) -> I {
89        self.input
90    }
91
92    pub fn set_filter_fadeout(&mut self) {
93        self.filter = Some(DurationFilter::FadeOut);
94    }
95
96    pub fn clear_filter(&mut self) {
97        self.filter = None;
98    }
99}
100
101impl<I> Iterator for TakeDuration<I>
102where
103    I: Source,
104    I::Item: Sample,
105{
106    type Item = <I as Iterator>::Item;
107
108    fn next(&mut self) -> Option<<I as Iterator>::Item> {
109        if let Some(frame_len) = self.current_frame_len.take() {
110            if frame_len > 0 {
111                self.current_frame_len = Some(frame_len - 1);
112            } else {
113                self.current_frame_len = self.input.current_frame_len();
114                // Sample rate might have changed
115                self.duration_per_sample = Self::get_duration_per_sample(&self.input);
116            }
117        }
118
119        if self.remaining_duration <= self.duration_per_sample {
120            None
121        } else if let Some(sample) = self.input.next() {
122            let sample = match &self.filter {
123                Some(filter) => filter.apply(sample, &self),
124                None => sample,
125            };
126
127            self.remaining_duration -= self.duration_per_sample;
128
129            Some(sample)
130        } else {
131            None
132        }
133    }
134
135    // TODO: size_hint
136}
137
138impl<I> Source for TakeDuration<I>
139where
140    I: Iterator + Source,
141    I::Item: Sample,
142{
143    #[inline]
144    fn current_frame_len(&self) -> Option<usize> {
145        let remaining_nanos = self.remaining_duration.as_secs() * NANOS_PER_SEC
146            + self.remaining_duration.subsec_nanos() as u64;
147        let nanos_per_sample = self.duration_per_sample.as_secs() * NANOS_PER_SEC
148            + self.duration_per_sample.subsec_nanos() as u64;
149        let remaining_samples = (remaining_nanos / nanos_per_sample) as usize;
150
151        self.input
152            .current_frame_len()
153            .filter(|value| *value < remaining_samples)
154            .or(Some(remaining_samples))
155    }
156
157    #[inline]
158    fn channels(&self) -> u16 {
159        self.input.channels()
160    }
161
162    #[inline]
163    fn sample_rate(&self) -> u32 {
164        self.input.sample_rate()
165    }
166
167    #[inline]
168    fn total_duration(&self) -> Option<Duration> {
169        if let Some(duration) = self.input.total_duration() {
170            if duration < self.requested_duration {
171                Some(duration)
172            } else {
173                Some(self.requested_duration)
174            }
175        } else {
176            None
177        }
178    }
179}