Skip to content

Commit fd9747d

Browse files
authored
Merge pull request #585 from Gusted/add-position
Add TrackPosition to Source
2 parents 297bd99 + 16a1440 commit fd9747d

File tree

4 files changed

+198
-0
lines changed

4 files changed

+198
-0
lines changed

src/sink.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct Controls {
6464
speed: Mutex<f32>,
6565
to_clear: Mutex<u32>,
6666
seek: Mutex<Option<SeekOrder>>,
67+
position: Mutex<f64>,
6768
}
6869

6970
impl Sink {
@@ -90,6 +91,7 @@ impl Sink {
9091
speed: Mutex::new(1.0),
9192
to_clear: Mutex::new(0),
9293
seek: Mutex::new(None),
94+
position: Mutex::new(0.0),
9395
}),
9496
sound_count: Arc::new(AtomicUsize::new(0)),
9597
detached: false,
@@ -119,6 +121,7 @@ impl Sink {
119121

120122
let source = source
121123
.speed(1.0)
124+
.track_position()
122125
.pausable(false)
123126
.amplify(1.0)
124127
.skippable()
@@ -127,19 +130,24 @@ impl Sink {
127130
.periodic_access(Duration::from_millis(5), move |src| {
128131
if controls.stopped.load(Ordering::SeqCst) {
129132
src.stop();
133+
*controls.position.lock().unwrap() = 0.0;
130134
}
131135
{
132136
let mut to_clear = controls.to_clear.lock().unwrap();
133137
if *to_clear > 0 {
134138
src.inner_mut().skip();
135139
*to_clear -= 1;
140+
*controls.position.lock().unwrap() = 0.0;
141+
} else {
142+
*controls.position.lock().unwrap() = src.inner().inner().inner().inner().get_pos();
136143
}
137144
}
138145
let amp = src.inner_mut().inner_mut();
139146
amp.set_factor(*controls.volume.lock().unwrap());
140147
amp.inner_mut()
141148
.set_paused(controls.pause.load(Ordering::SeqCst));
142149
amp.inner_mut()
150+
.inner_mut()
143151
.inner_mut()
144152
.set_factor(*controls.speed.lock().unwrap());
145153
if let Some(seek) = controls.seek.lock().unwrap().take() {
@@ -309,6 +317,12 @@ impl Sink {
309317
pub fn len(&self) -> usize {
310318
self.sound_count.load(Ordering::Relaxed)
311319
}
320+
321+
/// Returns the position of the sound that's being played.
322+
#[inline]
323+
pub fn get_pos(&self) -> f64 {
324+
*self.controls.position.lock().unwrap()
325+
}
312326
}
313327

314328
impl Drop for Sink {

src/source/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub use self::from_iter::{from_iter, FromIter};
2121
pub use self::mix::Mix;
2222
pub use self::pausable::Pausable;
2323
pub use self::periodic::PeriodicAccess;
24+
pub use self::position::TrackPosition;
2425
pub use self::repeat::Repeat;
2526
pub use self::samples_converter::SamplesConverter;
2627
pub use self::sine::SineWave;
@@ -48,6 +49,7 @@ mod from_iter;
4849
mod mix;
4950
mod pausable;
5051
mod periodic;
52+
mod position;
5153
mod repeat;
5254
mod samples_converter;
5355
mod sine;
@@ -333,6 +335,13 @@ where
333335
skippable::skippable(self)
334336
}
335337

338+
fn track_position(self) -> TrackPosition<Self>
339+
where
340+
Self: Sized,
341+
{
342+
position::track_position(self)
343+
}
344+
336345
/// Applies a low-pass filter to the source.
337346
/// **Warning**: Probably buggy.
338347
#[inline]

src/source/position.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
use std::time::Duration;
2+
3+
use crate::{Sample, Source};
4+
5+
use super::SeekError;
6+
7+
/// Internal function that builds a `TrackPosition` object.
8+
pub fn track_position<I>(source: I) -> TrackPosition<I> {
9+
TrackPosition {
10+
input: source,
11+
samples_counted: 0,
12+
offset_duration: 0.0,
13+
current_frame_sample_rate: 0,
14+
current_frame_channels: 0,
15+
current_frame_len: None,
16+
}
17+
}
18+
19+
#[derive(Debug)]
20+
pub struct TrackPosition<I> {
21+
input: I,
22+
samples_counted: usize,
23+
offset_duration: f64,
24+
current_frame_sample_rate: u32,
25+
current_frame_channels: u16,
26+
current_frame_len: Option<usize>,
27+
}
28+
29+
impl<I> TrackPosition<I> {
30+
/// Returns a reference to the inner source.
31+
#[inline]
32+
pub fn inner(&self) -> &I {
33+
&self.input
34+
}
35+
36+
/// Returns a mutable reference to the inner source.
37+
#[inline]
38+
pub fn inner_mut(&mut self) -> &mut I {
39+
&mut self.input
40+
}
41+
42+
/// Returns the inner source.
43+
#[inline]
44+
pub fn into_inner(self) -> I {
45+
self.input
46+
}
47+
}
48+
49+
impl<I> TrackPosition<I>
50+
where
51+
I: Source,
52+
I::Item: Sample,
53+
{
54+
/// Returns the position of the source.
55+
#[inline]
56+
pub fn get_pos(&self) -> f64 {
57+
self.samples_counted as f64 / self.input.sample_rate() as f64 / self.input.channels() as f64
58+
+ self.offset_duration
59+
}
60+
61+
#[inline]
62+
fn set_current_frame(&mut self) {
63+
self.current_frame_len = self.current_frame_len();
64+
self.current_frame_sample_rate = self.sample_rate();
65+
self.current_frame_channels = self.channels();
66+
}
67+
}
68+
69+
impl<I> Iterator for TrackPosition<I>
70+
where
71+
I: Source,
72+
I::Item: Sample,
73+
{
74+
type Item = I::Item;
75+
76+
#[inline]
77+
fn next(&mut self) -> Option<I::Item> {
78+
// This should only be executed once at the first call to next.
79+
if self.current_frame_len.is_none() {
80+
self.set_current_frame();
81+
}
82+
83+
let item = self.input.next();
84+
if item.is_some() {
85+
self.samples_counted += 1;
86+
87+
// At the end of a frame add the duration of this frame to
88+
// offset_duration and start collecting samples again.
89+
if Some(self.samples_counted) == self.current_frame_len() {
90+
self.offset_duration += self.samples_counted as f64
91+
/ self.current_frame_sample_rate as f64
92+
/ self.current_frame_channels as f64;
93+
94+
// Reset.
95+
self.samples_counted = 0;
96+
self.set_current_frame();
97+
};
98+
};
99+
item
100+
}
101+
102+
#[inline]
103+
fn size_hint(&self) -> (usize, Option<usize>) {
104+
self.input.size_hint()
105+
}
106+
}
107+
108+
impl<I> Source for TrackPosition<I>
109+
where
110+
I: Source,
111+
I::Item: Sample,
112+
{
113+
#[inline]
114+
fn current_frame_len(&self) -> Option<usize> {
115+
self.input.current_frame_len()
116+
}
117+
118+
#[inline]
119+
fn channels(&self) -> u16 {
120+
self.input.channels()
121+
}
122+
123+
#[inline]
124+
fn sample_rate(&self) -> u32 {
125+
self.input.sample_rate()
126+
}
127+
128+
#[inline]
129+
fn total_duration(&self) -> Option<Duration> {
130+
self.input.total_duration()
131+
}
132+
133+
#[inline]
134+
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
135+
let result = self.input.try_seek(pos);
136+
if result.is_ok() {
137+
self.offset_duration = pos.as_secs_f64();
138+
// This assumes that the seek implementation of the codec always
139+
// starts again at the beginning of a frame. Which is the case with
140+
// symphonia.
141+
self.samples_counted = 0;
142+
}
143+
result
144+
}
145+
}
146+
147+
#[cfg(test)]
148+
mod tests {
149+
use std::time::Duration;
150+
151+
use crate::buffer::SamplesBuffer;
152+
use crate::source::Source;
153+
154+
#[test]
155+
fn test_position() {
156+
let inner = SamplesBuffer::new(1, 1, vec![10i16, -10, 10, -10, 20, -20]);
157+
let mut source = inner.track_position();
158+
159+
assert_eq!(source.get_pos(), 0.0);
160+
source.next();
161+
assert_eq!(source.get_pos(), 1.0);
162+
163+
source.next();
164+
assert_eq!(source.get_pos(), 2.0);
165+
166+
assert_eq!(source.try_seek(Duration::new(1, 0)).is_ok(), true);
167+
assert_eq!(source.get_pos(), 1.0);
168+
}
169+
}

src/spatial_sink.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,10 @@ impl SpatialSink {
195195
pub fn try_seek(&self, pos: Duration) -> Result<(), SeekError> {
196196
self.sink.try_seek(pos)
197197
}
198+
199+
/// Returns the position of the sound that's being played.
200+
#[inline]
201+
pub fn get_pos(&self) -> f64 {
202+
self.sink.get_pos()
203+
}
198204
}

0 commit comments

Comments
 (0)