1use std::time::Duration;
2
3use crate::{Sample, Source};
4
5use super::SeekError;
6
7pub fn periodic<I, F>(source: I, period: Duration, modifier: F) -> PeriodicAccess<I, F>
9where
10 I: Source,
11 I::Item: Sample,
12{
13 let update_ms = period.as_secs() as u32 * 1_000 + period.subsec_millis();
16 let update_frequency = (update_ms * source.sample_rate()) / 1000 * source.channels() as u32;
17
18 PeriodicAccess {
19 input: source,
20 modifier,
21 update_frequency: if update_frequency == 0 {
23 1
24 } else {
25 update_frequency
26 },
27 samples_until_update: 1,
28 }
29}
30
31#[derive(Clone, Debug)]
33pub struct PeriodicAccess<I, F> {
34 input: I,
36
37 modifier: F,
39
40 update_frequency: u32,
42
43 samples_until_update: u32,
45}
46
47impl<I, F> PeriodicAccess<I, F>
48where
49 I: Source,
50 I::Item: Sample,
51 F: FnMut(&mut I),
52{
53 #[inline]
55 pub fn inner(&self) -> &I {
56 &self.input
57 }
58
59 #[inline]
61 pub fn inner_mut(&mut self) -> &mut I {
62 &mut self.input
63 }
64
65 #[inline]
67 pub fn into_inner(self) -> I {
68 self.input
69 }
70}
71
72impl<I, F> Iterator for PeriodicAccess<I, F>
73where
74 I: Source,
75 I::Item: Sample,
76 F: FnMut(&mut I),
77{
78 type Item = I::Item;
79
80 #[inline]
81 fn next(&mut self) -> Option<I::Item> {
82 self.samples_until_update -= 1;
83 if self.samples_until_update == 0 {
84 (self.modifier)(&mut self.input);
85 self.samples_until_update = self.update_frequency;
86 }
87
88 self.input.next()
89 }
90
91 #[inline]
92 fn size_hint(&self) -> (usize, Option<usize>) {
93 self.input.size_hint()
94 }
95}
96
97impl<I, F> Source for PeriodicAccess<I, F>
98where
99 I: Source,
100 I::Item: Sample,
101 F: FnMut(&mut I),
102{
103 #[inline]
104 fn current_frame_len(&self) -> Option<usize> {
105 self.input.current_frame_len()
106 }
107
108 #[inline]
109 fn channels(&self) -> u16 {
110 self.input.channels()
111 }
112
113 #[inline]
114 fn sample_rate(&self) -> u32 {
115 self.input.sample_rate()
116 }
117
118 #[inline]
119 fn total_duration(&self) -> Option<Duration> {
120 self.input.total_duration()
121 }
122
123 #[inline]
124 fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
125 self.input.try_seek(pos)
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use std::cell::RefCell;
132 use std::time::Duration;
133
134 use crate::buffer::SamplesBuffer;
135 use crate::source::Source;
136
137 #[test]
138 fn stereo_access() {
139 let inner = SamplesBuffer::new(2, 1, vec![10i16, -10, 10, -10, 20, -20]);
141
142 let cnt = RefCell::new(0);
143
144 let mut source = inner.periodic_access(Duration::from_millis(1000), |_src| {
145 *cnt.borrow_mut() += 1;
146 });
147
148 assert_eq!(*cnt.borrow(), 0);
149 assert_eq!(source.next(), Some(10));
151 assert_eq!(*cnt.borrow(), 1);
152 assert_eq!(source.next(), Some(-10));
154 assert_eq!(*cnt.borrow(), 1);
155 assert_eq!(source.next(), Some(10));
156 assert_eq!(*cnt.borrow(), 2);
157 assert_eq!(source.next(), Some(-10));
158 assert_eq!(*cnt.borrow(), 2);
159 assert_eq!(source.next(), Some(20));
160 assert_eq!(*cnt.borrow(), 3);
161 assert_eq!(source.next(), Some(-20));
162 assert_eq!(*cnt.borrow(), 3);
163 }
164
165 #[test]
166 fn fast_access_overflow() {
167 let inner = SamplesBuffer::new(1, 1, vec![10i16, -10, 10, -10, 20, -20]);
169 let mut source = inner.periodic_access(Duration::from_millis(5), |_src| {});
170
171 source.next();
172 source.next(); }
174}