geng_rodio/source/
skip.rs

1use std::time::Duration;
2
3use crate::{Sample, Source};
4
5const NS_PER_SECOND: u128 = 1_000_000_000;
6
7/// Internal function that builds a `SkipDuration` object.
8pub fn skip_duration<I>(mut input: I, duration: Duration) -> SkipDuration<I>
9where
10    I: Source,
11    I::Item: Sample,
12{
13    do_skip_duration(&mut input, duration);
14    SkipDuration {
15        input,
16        skipped_duration: duration,
17    }
18}
19
20/// Skips specified `duration` of the given `input` source from it's current position.
21fn do_skip_duration<I>(input: &mut I, mut duration: Duration)
22where
23    I: Source,
24    I::Item: Sample,
25{
26    while duration > Duration::new(0, 0) {
27        if input.current_frame_len().is_none() {
28            // Sample rate and the amount of channels will be the same till the end.
29            do_skip_duration_unchecked(input, duration);
30            return;
31        }
32
33        // .unwrap() safety: if `current_frame_len()` is None, the body of the `if` statement
34        // above returns before we get here.
35        let frame_len: usize = input.current_frame_len().unwrap();
36        // If frame_len is zero, then there is no more data to skip. Instead
37        // just bail out.
38        if frame_len == 0 {
39            return;
40        }
41
42        let ns_per_sample: u128 =
43            NS_PER_SECOND / input.sample_rate() as u128 / input.channels() as u128;
44
45        // Check if we need to skip only part of the current frame.
46        if frame_len as u128 * ns_per_sample > duration.as_nanos() {
47            skip_samples(input, (duration.as_nanos() / ns_per_sample) as usize);
48            return;
49        }
50
51        skip_samples(input, frame_len as usize);
52
53        duration -= Duration::from_nanos((frame_len * ns_per_sample as usize) as u64);
54    }
55}
56
57/// Skips specified `duration` from the `input` source assuming that sample rate
58/// and amount of channels are not changing.
59fn do_skip_duration_unchecked<I>(input: &mut I, duration: Duration)
60where
61    I: Source,
62    I::Item: Sample,
63{
64    let samples_per_channel: u128 =
65        duration.as_nanos() * input.sample_rate() as u128 / NS_PER_SECOND;
66    let samples_to_skip: u128 = samples_per_channel * input.channels() as u128;
67
68    skip_samples(input, samples_to_skip as usize);
69}
70
71/// Skips `n` samples from the given `input` source.
72fn skip_samples<I>(input: &mut I, n: usize)
73where
74    I: Source,
75    I::Item: Sample,
76{
77    for _ in 0..n {
78        if input.next().is_none() {
79            break;
80        }
81    }
82}
83
84/// A source that skips specified duration of the given source from it's current position.
85#[derive(Clone, Debug)]
86pub struct SkipDuration<I> {
87    input: I,
88    skipped_duration: Duration,
89}
90
91impl<I> SkipDuration<I>
92where
93    I: Source,
94    I::Item: Sample,
95{
96    /// Returns a reference to the inner source.
97    #[inline]
98    pub fn inner(&self) -> &I {
99        &self.input
100    }
101
102    /// Returns a mutable reference to the inner source.
103    #[inline]
104    pub fn inner_mut(&mut self) -> &mut I {
105        &mut self.input
106    }
107
108    /// Returns the inner source.
109    #[inline]
110    pub fn into_inner(self) -> I {
111        self.input
112    }
113}
114
115impl<I> Iterator for SkipDuration<I>
116where
117    I: Source,
118    I::Item: Sample,
119{
120    type Item = <I as Iterator>::Item;
121
122    #[inline]
123    fn next(&mut self) -> Option<Self::Item> {
124        self.input.next()
125    }
126
127    #[inline]
128    fn size_hint(&self) -> (usize, Option<usize>) {
129        self.input.size_hint()
130    }
131}
132
133impl<I> Source for SkipDuration<I>
134where
135    I: Source,
136    I::Item: Sample,
137{
138    #[inline]
139    fn current_frame_len(&self) -> Option<usize> {
140        self.input.current_frame_len()
141    }
142
143    #[inline]
144    fn channels(&self) -> u16 {
145        self.input.channels()
146    }
147
148    #[inline]
149    fn sample_rate(&self) -> u32 {
150        self.input.sample_rate()
151    }
152
153    #[inline]
154    fn total_duration(&self) -> Option<Duration> {
155        self.input.total_duration().map(|val| {
156            val.checked_sub(self.skipped_duration)
157                .unwrap_or_else(|| Duration::from_secs(0))
158        })
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use std::time::Duration;
165
166    use crate::buffer::SamplesBuffer;
167    use crate::source::Source;
168
169    fn test_skip_duration_samples_left(
170        channels: u16,
171        sample_rate: u32,
172        seconds: u32,
173        seconds_to_skip: u32,
174    ) {
175        let data: Vec<f32> = (1..=(sample_rate * channels as u32 * seconds))
176            .map(|_| 0f32)
177            .collect();
178        let test_buffer = SamplesBuffer::new(channels, sample_rate, data);
179        let seconds_left = seconds.saturating_sub(seconds_to_skip);
180
181        let samples_left_expected = (sample_rate * channels as u32 * seconds_left) as usize;
182        let samples_left = test_buffer
183            .skip_duration(Duration::from_secs(seconds_to_skip as u64))
184            .count();
185
186        assert_eq!(samples_left, samples_left_expected);
187    }
188
189    macro_rules! skip_duration_test_block {
190        ($(channels: $ch:expr, sample rate: $sr:expr, seconds: $sec:expr, seconds to skip: $sec_to_skip:expr;)+) => {
191            $(
192                test_skip_duration_samples_left($ch, $sr, $sec, $sec_to_skip);
193            )+
194        }
195    }
196
197    #[test]
198    fn skip_duration_shorter_than_source() {
199        skip_duration_test_block! {
200            channels: 1, sample rate: 44100, seconds: 5, seconds to skip: 3;
201            channels: 1, sample rate: 96000, seconds: 5, seconds to skip: 3;
202
203            channels: 2, sample rate: 44100, seconds: 5, seconds to skip: 3;
204            channels: 2, sample rate: 96000, seconds: 5, seconds to skip: 3;
205
206            channels: 4, sample rate: 44100, seconds: 5, seconds to skip: 3;
207            channels: 4, sample rate: 96000, seconds: 5, seconds to skip: 3;
208        }
209    }
210
211    #[test]
212    fn skip_duration_zero_duration() {
213        skip_duration_test_block! {
214            channels: 1, sample rate: 44100, seconds: 5, seconds to skip: 0;
215            channels: 1, sample rate: 96000, seconds: 5, seconds to skip: 0;
216
217            channels: 2, sample rate: 44100, seconds: 5, seconds to skip: 0;
218            channels: 2, sample rate: 96000, seconds: 5, seconds to skip: 0;
219
220            channels: 4, sample rate: 44100, seconds: 5, seconds to skip: 0;
221            channels: 4, sample rate: 96000, seconds: 5, seconds to skip: 0;
222        }
223    }
224
225    #[test]
226    fn skip_duration_longer_than_source() {
227        skip_duration_test_block! {
228            channels: 1, sample rate: 44100, seconds: 1, seconds to skip: 5;
229            channels: 1, sample rate: 96000, seconds: 10, seconds to skip: 11;
230
231            channels: 2, sample rate: 44100, seconds: 1, seconds to skip: 5;
232            channels: 2, sample rate: 96000, seconds: 10, seconds to skip: 11;
233
234            channels: 4, sample rate: 44100, seconds: 1, seconds to skip: 5;
235            channels: 4, sample rate: 96000, seconds: 10, seconds to skip: 11;
236        }
237    }
238
239    #[test]
240    fn skip_duration_equal_to_source_length() {
241        skip_duration_test_block! {
242            channels: 1, sample rate: 44100, seconds: 1, seconds to skip: 1;
243            channels: 1, sample rate: 96000, seconds: 10, seconds to skip: 10;
244
245            channels: 2, sample rate: 44100, seconds: 1, seconds to skip: 1;
246            channels: 2, sample rate: 96000, seconds: 10, seconds to skip: 10;
247
248            channels: 4, sample rate: 44100, seconds: 1, seconds to skip: 1;
249            channels: 4, sample rate: 96000, seconds: 10, seconds to skip: 10;
250        }
251    }
252}