use std::time::Duration;
use crate::{Sample, Source};
use super::SeekError;
pub fn track_position<I>(source: I) -> TrackPosition<I> {
TrackPosition {
input: source,
samples_counted: 0,
offset_duration: 0.0,
current_frame_sample_rate: 0,
current_frame_channels: 0,
current_frame_len: None,
}
}
#[derive(Debug)]
pub struct TrackPosition<I> {
input: I,
samples_counted: usize,
offset_duration: f64,
current_frame_sample_rate: u32,
current_frame_channels: u16,
current_frame_len: Option<usize>,
}
impl<I> TrackPosition<I> {
#[inline]
pub fn inner(&self) -> &I {
&self.input
}
#[inline]
pub fn inner_mut(&mut self) -> &mut I {
&mut self.input
}
#[inline]
pub fn into_inner(self) -> I {
self.input
}
}
impl<I> TrackPosition<I>
where
I: Source,
I::Item: Sample,
{
#[inline]
pub fn get_pos(&self) -> Duration {
let seconds = self.samples_counted as f64
/ self.input.sample_rate() as f64
/ self.input.channels() as f64
+ self.offset_duration;
Duration::from_secs_f64(seconds)
}
#[inline]
fn set_current_frame(&mut self) {
self.current_frame_len = self.current_frame_len();
self.current_frame_sample_rate = self.sample_rate();
self.current_frame_channels = self.channels();
}
}
impl<I> Iterator for TrackPosition<I>
where
I: Source,
I::Item: Sample,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<I::Item> {
if self.current_frame_len.is_none() {
self.set_current_frame();
}
let item = self.input.next();
if item.is_some() {
self.samples_counted += 1;
if Some(self.samples_counted) == self.current_frame_len() {
self.offset_duration += self.samples_counted as f64
/ self.current_frame_sample_rate as f64
/ self.current_frame_channels as f64;
self.samples_counted = 0;
self.set_current_frame();
};
};
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.offset_duration = pos.as_secs_f64();
self.samples_counted = 0;
}
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.track_position();
assert_eq!(source.get_pos().as_secs_f32(), 0.0);
source.next();
assert_eq!(source.get_pos().as_secs_f32(), 1.0);
source.next();
assert_eq!(source.get_pos().as_secs_f32(), 2.0);
assert_eq!(source.try_seek(Duration::new(1, 0)).is_ok(), true);
assert_eq!(source.get_pos().as_secs_f32(), 1.0);
}
#[test]
fn test_position_in_presence_of_speedup() {
let inner = SamplesBuffer::new(1, 1, vec![10i16, -10, 10, -10, 20, -20]);
let mut source = inner.speed(2.0).track_position();
assert_eq!(source.get_pos().as_secs_f32(), 0.0);
source.next();
assert_eq!(source.get_pos().as_secs_f32(), 0.5);
source.next();
assert_eq!(source.get_pos().as_secs_f32(), 1.0);
assert_eq!(source.try_seek(Duration::new(1, 0)).is_ok(), true);
assert_eq!(source.get_pos().as_secs_f32(), 1.0);
}
}