mirror of
https://github.com/RustAudio/rodio
synced 2024-11-10 06:04:16 +00:00
Add TrackPosition to Source
This adds a new TrackPosition trait which counts the amount of times `next()` is called (where it returns `Some`) and provide a function to get the position, which is a simple calculation. This is added to the Sink struct by default and the accuracy of this depends on the interval of `periodic_access`. I wasn't able to add testing to the sink counter part, because I wanted to also test `try_seek`, but it seems like I would need to have some threading code to call `next` on sink in another thread, which didn't look that good and resulted in a flaky test so only a 'unit test' in ``position.rs`. Resolves #457 Closes #510
This commit is contained in:
parent
ae9eaedc22
commit
e1108abe71
4 changed files with 162 additions and 0 deletions
14
src/sink.rs
14
src/sink.rs
|
@ -64,6 +64,7 @@ struct Controls {
|
|||
speed: Mutex<f32>,
|
||||
to_clear: Mutex<u32>,
|
||||
seek: Mutex<Option<SeekOrder>>,
|
||||
position: Mutex<f64>,
|
||||
}
|
||||
|
||||
impl Sink {
|
||||
|
@ -90,6 +91,7 @@ impl Sink {
|
|||
speed: Mutex::new(1.0),
|
||||
to_clear: Mutex::new(0),
|
||||
seek: Mutex::new(None),
|
||||
position: Mutex::new(0.0),
|
||||
}),
|
||||
sound_count: Arc::new(AtomicUsize::new(0)),
|
||||
detached: false,
|
||||
|
@ -119,6 +121,7 @@ impl Sink {
|
|||
|
||||
let source = source
|
||||
.speed(1.0)
|
||||
.trackable()
|
||||
.pausable(false)
|
||||
.amplify(1.0)
|
||||
.skippable()
|
||||
|
@ -127,12 +130,16 @@ impl Sink {
|
|||
.periodic_access(Duration::from_millis(5), move |src| {
|
||||
if controls.stopped.load(Ordering::SeqCst) {
|
||||
src.stop();
|
||||
*controls.position.lock().unwrap() = 0.0;
|
||||
}
|
||||
{
|
||||
let mut to_clear = controls.to_clear.lock().unwrap();
|
||||
if *to_clear > 0 {
|
||||
src.inner_mut().skip();
|
||||
*to_clear -= 1;
|
||||
*controls.position.lock().unwrap() = 0.0;
|
||||
} else {
|
||||
*controls.position.lock().unwrap() = src.inner().inner().inner().inner().get_pos();
|
||||
}
|
||||
}
|
||||
let amp = src.inner_mut().inner_mut();
|
||||
|
@ -140,6 +147,7 @@ impl Sink {
|
|||
amp.inner_mut()
|
||||
.set_paused(controls.pause.load(Ordering::SeqCst));
|
||||
amp.inner_mut()
|
||||
.inner_mut()
|
||||
.inner_mut()
|
||||
.set_factor(*controls.speed.lock().unwrap());
|
||||
if let Some(seek) = controls.seek.lock().unwrap().take() {
|
||||
|
@ -309,6 +317,12 @@ impl Sink {
|
|||
pub fn len(&self) -> usize {
|
||||
self.sound_count.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Returns the position of the sound that's being played.
|
||||
#[inline]
|
||||
pub fn get_pos(&self) -> f64 {
|
||||
*self.controls.position.lock().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Sink {
|
||||
|
|
|
@ -21,6 +21,7 @@ pub use self::from_iter::{from_iter, FromIter};
|
|||
pub use self::mix::Mix;
|
||||
pub use self::pausable::Pausable;
|
||||
pub use self::periodic::PeriodicAccess;
|
||||
pub use self::position::TrackPosition;
|
||||
pub use self::repeat::Repeat;
|
||||
pub use self::samples_converter::SamplesConverter;
|
||||
pub use self::sine::SineWave;
|
||||
|
@ -48,6 +49,7 @@ mod from_iter;
|
|||
mod mix;
|
||||
mod pausable;
|
||||
mod periodic;
|
||||
mod position;
|
||||
mod repeat;
|
||||
mod samples_converter;
|
||||
mod sine;
|
||||
|
@ -333,6 +335,13 @@ where
|
|||
skippable::skippable(self)
|
||||
}
|
||||
|
||||
fn trackable(self) -> TrackPosition<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
position::trackable(self)
|
||||
}
|
||||
|
||||
/// Applies a low-pass filter to the source.
|
||||
/// **Warning**: Probably buggy.
|
||||
#[inline]
|
||||
|
|
133
src/source/position.rs
Normal file
133
src/source/position.rs
Normal file
|
@ -0,0 +1,133 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekError;
|
||||
|
||||
/// Internal function that builds a `TrackPosition` object.
|
||||
pub fn trackable<I>(source: I) -> TrackPosition<I> {
|
||||
TrackPosition {
|
||||
input: source,
|
||||
samples_elapsed: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TrackPosition<I> {
|
||||
input: I,
|
||||
samples_elapsed: usize,
|
||||
}
|
||||
|
||||
impl<I> TrackPosition<I> {
|
||||
/// Returns a reference to the inner source.
|
||||
#[inline]
|
||||
pub fn inner(&self) -> &I {
|
||||
&self.input
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the inner source.
|
||||
#[inline]
|
||||
pub fn inner_mut(&mut self) -> &mut I {
|
||||
&mut self.input
|
||||
}
|
||||
|
||||
/// Returns the inner source.
|
||||
#[inline]
|
||||
pub fn into_inner(self) -> I {
|
||||
self.input
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> TrackPosition<I>
|
||||
where
|
||||
I: Source,
|
||||
I::Item: Sample,
|
||||
{
|
||||
/// Returns the inner source.
|
||||
#[inline]
|
||||
pub fn get_pos(&self) -> f64 {
|
||||
self.samples_elapsed as f64 / self.input.sample_rate() as f64 / self.input.channels() as f64
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for TrackPosition<I>
|
||||
where
|
||||
I: Source,
|
||||
I::Item: Sample,
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<I::Item> {
|
||||
let item = self.input.next();
|
||||
if item.is_some() {
|
||||
self.samples_elapsed += 1;
|
||||
};
|
||||
item
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.input.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Source for TrackPosition<I>
|
||||
where
|
||||
I: Source,
|
||||
I::Item: Sample,
|
||||
{
|
||||
#[inline]
|
||||
fn current_frame_len(&self) -> Option<usize> {
|
||||
self.input.current_frame_len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn channels(&self) -> u16 {
|
||||
self.input.channels()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sample_rate(&self) -> u32 {
|
||||
self.input.sample_rate()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.input.total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
|
||||
let result = self.input.try_seek(pos);
|
||||
if result.is_ok() {
|
||||
self.samples_elapsed = (pos.as_secs_f64()
|
||||
* self.input.sample_rate() as f64
|
||||
* self.input.channels() as f64) as usize;
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::buffer::SamplesBuffer;
|
||||
use crate::source::Source;
|
||||
|
||||
#[test]
|
||||
fn test_position() {
|
||||
let inner = SamplesBuffer::new(1, 1, vec![10i16, -10, 10, -10, 20, -20]);
|
||||
let mut source = inner.trackable();
|
||||
|
||||
assert_eq!(source.get_pos(), 0.0);
|
||||
source.next();
|
||||
assert_eq!(source.get_pos(), 1.0);
|
||||
source.next();
|
||||
assert_eq!(source.get_pos(), 2.0);
|
||||
|
||||
assert_eq!(source.try_seek(Duration::new(1, 0)).is_ok(), true);
|
||||
assert_eq!(source.get_pos(), 1.0);
|
||||
}
|
||||
}
|
|
@ -195,4 +195,10 @@ impl SpatialSink {
|
|||
pub fn try_seek(&self, pos: Duration) -> Result<(), SeekError> {
|
||||
self.sink.try_seek(pos)
|
||||
}
|
||||
|
||||
/// Returns the position of the sound that's being played.
|
||||
#[inline]
|
||||
pub fn get_pos(&self) -> f64 {
|
||||
self.sink.get_pos()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue