rodio/source/
take.rs

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