rodio/source/
skip.rs

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