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
}
/// 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]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.current.try_seek(pos)

View file

@ -205,7 +205,7 @@ impl Sink {
///
/// 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
/// of 42 seconds calling `try_seek()` with 60 seconds as argument will seek to
/// 42 seconds.

View file

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

View file

@ -4,19 +4,22 @@ use crate::{Sample, Source};
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.
pub fn delay<I>(input: I, duration: Duration) -> Delay<I>
where
I: Source,
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 {
input,
remaining_samples: samples as usize,
remaining_samples: remaining_samples(duration, input.sample_rate(), input.channels()),
requested_duration: duration,
input,
}
}
@ -108,9 +111,28 @@ where
.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]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
let pos_without_delay = pos.saturating_sub(self.requested_duration);
self.input.try_seek(pos_without_delay)
if pos < self.requested_duration {
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)
}
}