Skip to main content

rodio/source/
periodic.rs

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