fix(seek/delay) seek < delay duration ate up the delay

This commit is contained in:
dvdsk 2024-04-05 15:13:34 +02:00
parent aa0880ddee
commit 7cf0451a1e
No known key found for this signature in database
GPG key ID: 6CF9D20C5709A836
4 changed files with 41 additions and 8 deletions

View file

@ -170,6 +170,15 @@ where
None None
} }
/// Only seeks within the current source.
// We can not go back to previous sources. We could implement seek such
// that it advances the queue if the position is beyond the current song.
//
// We would then however need to enable seeking backwards across sources too.
// That no longer seems in line with the queue behaviour.
//
// A final pain point is that we would need the total duration for the
// next few songs.
#[inline] #[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> { fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.current.try_seek(pos) self.current.try_seek(pos)

View file

@ -205,7 +205,7 @@ impl Sink {
/// ///
/// This blocks between 0 and ~5 milliseconds. /// This blocks between 0 and ~5 milliseconds.
/// ///
/// As long as the duration of the source is known seek is guaranteed to saturate /// As long as the duration of the source is known, seek is guaranteed to saturate
/// at the end of the source. For example given a source that reports a total duration /// at the end of the source. For example given a source that reports a total duration
/// of 42 seconds calling `try_seek()` with 60 seconds as argument will seek to /// of 42 seconds calling `try_seek()` with 60 seconds as argument will seek to
/// 42 seconds. /// 42 seconds.

View file

@ -242,6 +242,8 @@ where
self.total_duration self.total_duration
} }
/// Can not support seek, in the end state we lose the underlying source
/// which makes seeking back impossible.
#[inline] #[inline]
fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> { fn try_seek(&mut self, _: Duration) -> Result<(), SeekError> {
Err(SeekError::NotSupported { Err(SeekError::NotSupported {

View file

@ -4,19 +4,22 @@ use crate::{Sample, Source};
use super::SeekError; use super::SeekError;
fn remaining_samples(until_playback: Duration, sample_rate: u32, channels: u16) -> usize {
let ns = until_playback.as_secs() * 1_000_000_000 + until_playback.subsec_nanos() as u64;
let samples = ns * channels as u64 * sample_rate as u64 / 1_000_000_000;
samples as usize
}
/// Internal function that builds a `Delay` object. /// Internal function that builds a `Delay` object.
pub fn delay<I>(input: I, duration: Duration) -> Delay<I> pub fn delay<I>(input: I, duration: Duration) -> Delay<I>
where where
I: Source, I: Source,
I::Item: Sample, I::Item: Sample,
{ {
let duration_ns = duration.as_secs() * 1000000000 + duration.subsec_nanos() as u64;
let samples = duration_ns * input.sample_rate() as u64 / 1000000000 * input.channels() as u64;
Delay { Delay {
input, remaining_samples: remaining_samples(duration, input.sample_rate(), input.channels()),
remaining_samples: samples as usize,
requested_duration: duration, requested_duration: duration,
input,
} }
} }
@ -108,9 +111,28 @@ where
.map(|val| val + self.requested_duration) .map(|val| val + self.requested_duration)
} }
/// Pos is seen from the perspective of the api user.
///
/// # Example
///
/// ```ignore
/// use std::time::Duration;
///
/// let mut source = inner_source.delay(Duration::from_secs(10));
/// source.try_seek(Duration::from_secs(15));
///
/// // inner_source is now at pos: Duration::from_secs(5);
/// ```
///
#[inline] #[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> { fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
let pos_without_delay = pos.saturating_sub(self.requested_duration); if pos < self.requested_duration {
self.input.try_seek(pos_without_delay) self.input.try_seek(Duration::ZERO)?;
let until_playback = self.requested_duration - pos;
self.remaining_samples =
remaining_samples(until_playback, self.sample_rate(), self.channels());
}
let compensated_for_delay = pos.saturating_sub(self.requested_duration);
self.input.try_seek(compensated_for_delay)
} }
} }