rodio/source/
periodic.rs

1use std::time::Duration;
2
3use super::SeekError;
4use crate::common::{ChannelCount, SampleRate};
5use crate::Source;
6
7/// Internal function that builds a `PeriodicAccess` object.
8pub fn periodic<I, F>(source: I, period: Duration, modifier: F) -> PeriodicAccess<I, F>
9where
10    I: Source,
11{
12    // TODO: handle the fact that the samples rate can change
13    // TODO: generally, just wrong
14    let update_ms = period.as_secs() as u32 * 1_000 + period.subsec_millis();
15    let update_frequency = (update_ms * source.sample_rate()) / 1000 * source.channels() as u32;
16
17    PeriodicAccess {
18        input: source,
19        modifier,
20        // Can overflow when subtracting if this is 0
21        update_frequency: if update_frequency == 0 {
22            1
23        } else {
24            update_frequency
25        },
26        samples_until_update: 1,
27    }
28}
29
30/// Calls a function on a source every time a period elapsed.
31#[derive(Clone, Debug)]
32pub struct PeriodicAccess<I, F> {
33    // The inner source.
34    input: I,
35
36    // Closure that gets access to `inner`.
37    modifier: F,
38
39    // The frequency with which local_volume should be updated by remote_volume
40    update_frequency: u32,
41
42    // How many samples remain until it is time to update local_volume with remote_volume.
43    samples_until_update: u32,
44}
45
46impl<I, F> PeriodicAccess<I, F>
47where
48    I: Source,
49
50    F: FnMut(&mut I),
51{
52    /// Returns a reference to the inner source.
53    #[inline]
54    pub fn inner(&self) -> &I {
55        &self.input
56    }
57
58    /// Returns a mutable reference to the inner source.
59    #[inline]
60    pub fn inner_mut(&mut self) -> &mut I {
61        &mut self.input
62    }
63
64    /// Returns the inner source.
65    #[inline]
66    pub fn into_inner(self) -> I {
67        self.input
68    }
69}
70
71impl<I, F> Iterator for PeriodicAccess<I, F>
72where
73    I: Source,
74
75    F: FnMut(&mut I),
76{
77    type Item = I::Item;
78
79    #[inline]
80    fn next(&mut self) -> Option<I::Item> {
81        self.samples_until_update -= 1;
82        if self.samples_until_update == 0 {
83            (self.modifier)(&mut self.input);
84            self.samples_until_update = self.update_frequency;
85        }
86
87        self.input.next()
88    }
89
90    #[inline]
91    fn size_hint(&self) -> (usize, Option<usize>) {
92        self.input.size_hint()
93    }
94}
95
96impl<I, F> Source for PeriodicAccess<I, F>
97where
98    I: Source,
99
100    F: FnMut(&mut I),
101{
102    #[inline]
103    fn current_span_len(&self) -> Option<usize> {
104        self.input.current_span_len()
105    }
106
107    #[inline]
108    fn channels(&self) -> ChannelCount {
109        self.input.channels()
110    }
111
112    #[inline]
113    fn sample_rate(&self) -> SampleRate {
114        self.input.sample_rate()
115    }
116
117    #[inline]
118    fn total_duration(&self) -> Option<Duration> {
119        self.input.total_duration()
120    }
121
122    #[inline]
123    fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
124        self.input.try_seek(pos)
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use std::cell::RefCell;
131    use std::time::Duration;
132
133    use crate::buffer::SamplesBuffer;
134    use crate::source::Source;
135
136    #[test]
137    fn stereo_access() {
138        // Stereo, 1Hz audio buffer
139        let inner = SamplesBuffer::new(2, 1, vec![10.0, -10.0, 10.0, -10.0, 20.0, -20.0]);
140
141        let cnt = RefCell::new(0);
142
143        let mut source = inner.periodic_access(Duration::from_millis(1000), |_src| {
144            *cnt.borrow_mut() += 1;
145        });
146
147        assert_eq!(*cnt.borrow(), 0);
148        // Always called on first access!
149        assert_eq!(source.next(), Some(10.0));
150        assert_eq!(*cnt.borrow(), 1);
151        // Called every 1 second afterwards
152        assert_eq!(source.next(), Some(-10.0));
153        assert_eq!(*cnt.borrow(), 1);
154        assert_eq!(source.next(), Some(10.0));
155        assert_eq!(*cnt.borrow(), 2);
156        assert_eq!(source.next(), Some(-10.0));
157        assert_eq!(*cnt.borrow(), 2);
158        assert_eq!(source.next(), Some(20.0));
159        assert_eq!(*cnt.borrow(), 3);
160        assert_eq!(source.next(), Some(-20.0));
161        assert_eq!(*cnt.borrow(), 3);
162    }
163
164    #[test]
165    fn fast_access_overflow() {
166        // 1hz is lower than 0.5 samples per 5ms
167        let inner = SamplesBuffer::new(1, 1, vec![10.0, -10.0, 10.0, -10.0, 20.0, -20.0]);
168        let mut source = inner.periodic_access(Duration::from_millis(5), |_src| {});
169
170        source.next();
171        source.next(); // Would overflow here.
172    }
173}