rodio/source/
position.rs
1use std::time::Duration;
2
3use crate::{Sample, Source};
4
5use super::SeekError;
6
7pub fn track_position<I>(source: I) -> TrackPosition<I> {
10 TrackPosition {
11 input: source,
12 samples_counted: 0,
13 offset_duration: 0.0,
14 current_frame_sample_rate: 0,
15 current_frame_channels: 0,
16 current_frame_len: None,
17 }
18}
19
20#[derive(Debug)]
22pub struct TrackPosition<I> {
23 input: I,
24 samples_counted: usize,
25 offset_duration: f64,
26 current_frame_sample_rate: u32,
27 current_frame_channels: u16,
28 current_frame_len: Option<usize>,
29}
30
31impl<I> TrackPosition<I> {
32 #[inline]
34 pub fn inner(&self) -> &I {
35 &self.input
36 }
37
38 #[inline]
40 pub fn inner_mut(&mut self) -> &mut I {
41 &mut self.input
42 }
43
44 #[inline]
46 pub fn into_inner(self) -> I {
47 self.input
48 }
49}
50
51impl<I> TrackPosition<I>
52where
53 I: Source,
54 I::Item: Sample,
55{
56 #[inline]
67 pub fn get_pos(&self) -> Duration {
68 let seconds = self.samples_counted as f64
69 / self.input.sample_rate() as f64
70 / self.input.channels() as f64
71 + self.offset_duration;
72 Duration::from_secs_f64(seconds)
73 }
74
75 #[inline]
76 fn set_current_frame(&mut self) {
77 self.current_frame_len = self.current_frame_len();
78 self.current_frame_sample_rate = self.sample_rate();
79 self.current_frame_channels = self.channels();
80 }
81}
82
83impl<I> Iterator for TrackPosition<I>
84where
85 I: Source,
86 I::Item: Sample,
87{
88 type Item = I::Item;
89
90 #[inline]
91 fn next(&mut self) -> Option<I::Item> {
92 if self.current_frame_len.is_none() {
94 self.set_current_frame();
95 }
96
97 let item = self.input.next();
98 if item.is_some() {
99 self.samples_counted += 1;
100
101 if Some(self.samples_counted) == self.current_frame_len() {
104 self.offset_duration += self.samples_counted as f64
105 / self.current_frame_sample_rate as f64
106 / self.current_frame_channels as f64;
107
108 self.samples_counted = 0;
110 self.set_current_frame();
111 };
112 };
113 item
114 }
115
116 #[inline]
117 fn size_hint(&self) -> (usize, Option<usize>) {
118 self.input.size_hint()
119 }
120}
121
122impl<I> Source for TrackPosition<I>
123where
124 I: Source,
125 I::Item: Sample,
126{
127 #[inline]
128 fn current_frame_len(&self) -> Option<usize> {
129 self.input.current_frame_len()
130 }
131
132 #[inline]
133 fn channels(&self) -> u16 {
134 self.input.channels()
135 }
136
137 #[inline]
138 fn sample_rate(&self) -> u32 {
139 self.input.sample_rate()
140 }
141
142 #[inline]
143 fn total_duration(&self) -> Option<Duration> {
144 self.input.total_duration()
145 }
146
147 #[inline]
148 fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
149 let result = self.input.try_seek(pos);
150 if result.is_ok() {
151 self.offset_duration = pos.as_secs_f64();
152 self.samples_counted = 0;
156 }
157 result
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use std::time::Duration;
164
165 use crate::buffer::SamplesBuffer;
166 use crate::source::Source;
167
168 #[test]
169 fn test_position() {
170 let inner = SamplesBuffer::new(1, 1, vec![10i16, -10, 10, -10, 20, -20]);
171 let mut source = inner.track_position();
172
173 assert_eq!(source.get_pos().as_secs_f32(), 0.0);
174 source.next();
175 assert_eq!(source.get_pos().as_secs_f32(), 1.0);
176
177 source.next();
178 assert_eq!(source.get_pos().as_secs_f32(), 2.0);
179
180 assert_eq!(source.try_seek(Duration::new(1, 0)).is_ok(), true);
181 assert_eq!(source.get_pos().as_secs_f32(), 1.0);
182 }
183
184 #[test]
185 fn test_position_in_presence_of_speedup() {
186 let inner = SamplesBuffer::new(1, 1, vec![10i16, -10, 10, -10, 20, -20]);
187 let mut source = inner.speed(2.0).track_position();
188
189 assert_eq!(source.get_pos().as_secs_f32(), 0.0);
190 source.next();
191 assert_eq!(source.get_pos().as_secs_f32(), 0.5);
192
193 source.next();
194 assert_eq!(source.get_pos().as_secs_f32(), 1.0);
195
196 assert_eq!(source.try_seek(Duration::new(1, 0)).is_ok(), true);
197 assert_eq!(source.get_pos().as_secs_f32(), 1.0);
198 }
199}