rodio/source/
skip.rs

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