1use std::time::Duration;
2
3use super::SeekError;
4use crate::{
5 common::{ChannelCount, SampleRate},
6 Source,
7};
8
9pub fn periodic<I, F>(source: I, period: Duration, modifier: F) -> PeriodicAccess<I, F>
11where
12 I: Source,
13{
14 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 update_frequency: update_frequency.max(1),
24 samples_until_update: 1,
25 }
26}
27
28#[derive(Clone, Debug)]
30pub struct PeriodicAccess<I, F> {
31 input: I,
33
34 modifier: F,
36
37 update_frequency: u32,
39
40 samples_until_update: u32,
42}
43
44impl<I, F> PeriodicAccess<I, F>
45where
46 I: Source,
47
48 F: FnMut(&mut I),
49{
50 #[inline]
52 pub fn inner(&self) -> &I {
53 &self.input
54 }
55
56 #[inline]
58 pub fn inner_mut(&mut self) -> &mut I {
59 &mut self.input
60 }
61
62 #[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 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 assert_eq!(source.next(), Some(10.0));
149 assert_eq!(*cnt.borrow(), 1);
150 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 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(); }
172}