mirror of
https://github.com/RustAudio/rodio
synced 2024-11-10 06:04:16 +00:00
adds try_seek for sink and all sources
This commit is contained in:
parent
745db82876
commit
202687b934
31 changed files with 316 additions and 77 deletions
|
@ -6,13 +6,13 @@ fn main() {
|
|||
let sink = rodio::Sink::try_new(&handle).unwrap();
|
||||
|
||||
let file = std::fs::File::open("examples/music.mp3").unwrap();
|
||||
sink.append_seekable(rodio::Decoder::new(BufReader::new(file)).unwrap());
|
||||
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||
sink.seek(Duration::from_secs(0));
|
||||
sink.try_seek(Duration::from_secs(0)).unwrap();
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||
sink.seek(Duration::from_secs(4));
|
||||
sink.try_seek(Duration::from_secs(4)).unwrap();
|
||||
|
||||
sink.sleep_until_end();
|
||||
}
|
||||
|
|
|
@ -44,6 +44,12 @@ where
|
|||
pub fn into_inner(self) -> I {
|
||||
self.input
|
||||
}
|
||||
|
||||
/// Get mutable access to the iterator
|
||||
#[inline]
|
||||
pub fn inner_mut(&mut self) -> &mut I {
|
||||
&mut self.input
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for ChannelCountConverter<I>
|
||||
|
|
|
@ -23,6 +23,12 @@ impl<I, O> DataConverter<I, O> {
|
|||
pub fn into_inner(self) -> I {
|
||||
self.input
|
||||
}
|
||||
|
||||
/// get mutable access to the iterator
|
||||
#[inline]
|
||||
pub fn inner_mut(&mut self) -> &mut I {
|
||||
&mut self.input
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> Iterator for DataConverter<I, O>
|
||||
|
|
|
@ -102,6 +102,12 @@ where
|
|||
self.input
|
||||
}
|
||||
|
||||
/// get mutable access to the iterator
|
||||
#[inline]
|
||||
pub fn inner_mut(&mut self) -> &mut I {
|
||||
&mut self.input
|
||||
}
|
||||
|
||||
fn next_input_frame(&mut self) {
|
||||
self.current_frame_pos_in_chunk += 1;
|
||||
|
||||
|
|
110
src/sink.rs
110
src/sink.rs
|
@ -3,10 +3,11 @@ use std::sync::{Arc, Mutex};
|
|||
use std::time::Duration;
|
||||
|
||||
#[cfg(feature = "crossbeam-channel")]
|
||||
use crossbeam_channel::Receiver;
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
#[cfg(not(feature = "crossbeam-channel"))]
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
use crate::source::SeekNotSupported;
|
||||
use crate::stream::{OutputStreamHandle, PlayError};
|
||||
use crate::{queue, source::Done, Sample, Source};
|
||||
use cpal::FromSample;
|
||||
|
@ -25,13 +26,44 @@ pub struct Sink {
|
|||
detached: bool,
|
||||
}
|
||||
|
||||
struct SeekOrder {
|
||||
pos: Duration,
|
||||
feedback: Sender<Result<(), SeekNotSupported>>,
|
||||
}
|
||||
|
||||
impl SeekOrder {
|
||||
fn new(pos: Duration) -> (Self, Receiver<Result<(), SeekNotSupported>>) {
|
||||
#[cfg(not(feature = "crossbeam-channel"))]
|
||||
let (tx, rx) = {
|
||||
use std::sync::mpsc;
|
||||
mpsc::channel()
|
||||
};
|
||||
|
||||
#[cfg(feature = "crossbeam-channel")]
|
||||
let (tx, rx) = {
|
||||
use crossbeam_channel::bounded;
|
||||
bounded(1)
|
||||
};
|
||||
(Self { pos, feedback: tx }, rx)
|
||||
}
|
||||
|
||||
fn attempt<S>(self, maybe_seekable: &mut S)
|
||||
where
|
||||
S: Source,
|
||||
S::Item: Sample + Send,
|
||||
{
|
||||
let res = maybe_seekable.try_seek(self.pos);
|
||||
let _ignore_reciever_dropped = self.feedback.send(res);
|
||||
}
|
||||
}
|
||||
|
||||
struct Controls {
|
||||
pause: AtomicBool,
|
||||
volume: Mutex<f32>,
|
||||
stopped: AtomicBool,
|
||||
speed: Mutex<f32>,
|
||||
to_clear: Mutex<u32>,
|
||||
seek: Mutex<Option<Duration>>,
|
||||
seek: Mutex<Option<SeekOrder>>,
|
||||
}
|
||||
|
||||
impl Sink {
|
||||
|
@ -109,61 +141,8 @@ impl Sink {
|
|||
amp.inner_mut()
|
||||
.inner_mut()
|
||||
.set_factor(*controls.speed.lock().unwrap());
|
||||
start_played.store(true, Ordering::SeqCst);
|
||||
})
|
||||
.convert_samples();
|
||||
self.sound_count.fetch_add(1, Ordering::Relaxed);
|
||||
let source = Done::new(source, self.sound_count.clone());
|
||||
*self.sleep_until_end.lock().unwrap() = Some(self.queue_tx.append_with_signal(source));
|
||||
}
|
||||
|
||||
/// Appends a sound to the queue of sounds to play.
|
||||
#[inline]
|
||||
pub fn append_seekable<S>(&self, source: S)
|
||||
where
|
||||
S: Source + Send + 'static,
|
||||
f32: FromSample<S::Item>,
|
||||
S::Item: Sample + Send,
|
||||
{
|
||||
// Wait for queue to flush then resume stopped playback
|
||||
if self.controls.stopped.load(Ordering::SeqCst) {
|
||||
if self.sound_count.load(Ordering::SeqCst) > 0 {
|
||||
self.sleep_until_end();
|
||||
}
|
||||
self.controls.stopped.store(false, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
let controls = self.controls.clone();
|
||||
|
||||
let start_played = AtomicBool::new(false);
|
||||
|
||||
let source = source
|
||||
.speed(1.0)
|
||||
.pausable(false)
|
||||
.amplify(1.0)
|
||||
.skippable()
|
||||
.stoppable()
|
||||
.periodic_access(Duration::from_millis(5), move |src| {
|
||||
if controls.stopped.load(Ordering::SeqCst) {
|
||||
src.stop();
|
||||
}
|
||||
{
|
||||
let mut to_clear = controls.to_clear.lock().unwrap();
|
||||
if *to_clear > 0 {
|
||||
let _ = src.inner_mut().skip();
|
||||
*to_clear -= 1;
|
||||
}
|
||||
}
|
||||
let amp = src.inner_mut().inner_mut();
|
||||
amp.set_factor(*controls.volume.lock().unwrap());
|
||||
amp.inner_mut()
|
||||
.set_paused(controls.pause.load(Ordering::SeqCst));
|
||||
amp.inner_mut()
|
||||
.inner_mut()
|
||||
.set_factor(*controls.speed.lock().unwrap());
|
||||
let seekable = amp.inner_mut().inner_mut().inner_mut();
|
||||
if let Some(pos) = controls.seek.lock().unwrap().take() {
|
||||
seekable.try_seek(pos).unwrap();
|
||||
if let Some(seek) = controls.seek.lock().unwrap().take() {
|
||||
seek.attempt(amp)
|
||||
}
|
||||
start_played.store(true, Ordering::SeqCst);
|
||||
})
|
||||
|
@ -219,9 +198,18 @@ impl Sink {
|
|||
|
||||
/// Set position
|
||||
///
|
||||
/// No effect if source does not implement `SourceExt`
|
||||
pub fn seek(&self, pos: Duration) {
|
||||
*self.controls.seek.lock().unwrap() = Some(pos);
|
||||
/// Try to seek to a pos, returns [`SeekNotSupported`] if seeking is not
|
||||
/// supported by the current source.
|
||||
pub fn try_seek(&self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
let (order, feedback) = SeekOrder::new(pos);
|
||||
*self.controls.seek.lock().unwrap() = Some(order);
|
||||
match feedback.recv() {
|
||||
Ok(seek_res) => seek_res,
|
||||
// The feedback channel closed. Probably another seekorder was set
|
||||
// invalidating this one and closing the feedback channel
|
||||
// ... or the audio thread panicked.
|
||||
Err(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Pauses playback of this sink.
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Internal function that builds a `Amplify` object.
|
||||
pub fn amplify<I>(input: I, factor: f32) -> Amplify<I>
|
||||
where
|
||||
|
@ -93,4 +95,9 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.input.total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::Source;
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
// Implemented following http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||
|
||||
/// Internal function that builds a `BltFilter` object.
|
||||
|
@ -147,6 +149,11 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.input.total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
@ -5,6 +5,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Internal function that builds a `Buffered` object.
|
||||
#[inline]
|
||||
pub fn buffered<I>(input: I) -> Buffered<I>
|
||||
|
@ -239,6 +241,11 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.total_duration
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, _: Duration) -> Result<(), SeekNotSupported> {
|
||||
Err(SeekNotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Clone for Buffered<I>
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Combines channels in input into a single mono source, then plays that mono sound
|
||||
/// to each channel at the volume given for that channel.
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -138,4 +140,9 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.input.total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Internal function that builds a `Delay` object.
|
||||
pub fn delay<I>(input: I, duration: Duration) -> Delay<I>
|
||||
where
|
||||
|
@ -105,4 +107,10 @@ where
|
|||
.total_duration()
|
||||
.map(|val| val + self.requested_duration)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
let pos_without_delay = pos.saturating_sub(self.requested_duration);
|
||||
self.input.try_seek(pos_without_delay)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// When the inner source is empty this decrements an `AtomicUsize`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Done<I> {
|
||||
|
@ -87,4 +89,9 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.input.total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// An empty source.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Empty<S>(PhantomData<S>);
|
||||
|
@ -53,4 +55,9 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
Some(Duration::new(0, 0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, _: Duration) -> Result<(), SeekNotSupported> {
|
||||
Err(SeekNotSupported)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// An empty source which executes a callback function
|
||||
pub struct EmptyCallback<S> {
|
||||
pub phantom_data: PhantomData<S>,
|
||||
|
@ -52,4 +54,9 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
Some(Duration::new(0, 0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, _: Duration) -> Result<(), SeekNotSupported> {
|
||||
Err(SeekNotSupported)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Internal function that builds a `FadeIn` object.
|
||||
pub fn fadein<I>(input: I, duration: Duration) -> FadeIn<I>
|
||||
where
|
||||
|
@ -105,4 +107,9 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.input.total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Builds a source that chains sources provided by an iterator.
|
||||
///
|
||||
/// The `iterator` parameter is an iterator that produces a source. The source is then played.
|
||||
|
@ -135,6 +137,15 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
if let Some(source) = self.current_source.as_mut() {
|
||||
source.try_seek(pos)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::cmp;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::source::SeekNotSupported;
|
||||
use crate::source::uniform::UniformSourceIterator;
|
||||
use crate::{Sample, Source};
|
||||
use cpal::{FromSample, Sample as CpalSample};
|
||||
|
@ -119,4 +120,10 @@ where
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input1.try_seek(pos)?;
|
||||
self.input1.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -355,6 +355,8 @@ where
|
|||
blt::high_pass(self, freq)
|
||||
}
|
||||
|
||||
/// Set position
|
||||
///
|
||||
/// Try to seek to a pos, returns [`SeekNotSupported`] if seeking is not
|
||||
/// supported by the current source.
|
||||
fn try_seek(&mut self, _: Duration) -> Result<(), SeekNotSupported> {
|
||||
|
@ -362,7 +364,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SeekNotSupported;
|
||||
|
||||
impl fmt::Display for SeekNotSupported {
|
||||
|
@ -396,6 +398,11 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
(**self).total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
(**self).try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Source for Box<dyn Source<Item = S> + Send>
|
||||
|
@ -421,6 +428,11 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
(**self).total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
(**self).try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Source for Box<dyn Source<Item = S> + Send + Sync>
|
||||
|
@ -446,5 +458,10 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
(**self).total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
(**self).try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Internal function that builds a `Pausable` object.
|
||||
pub fn pausable<I>(source: I, paused: bool) -> Pausable<I>
|
||||
where
|
||||
|
@ -115,4 +117,9 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.input.total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Internal function that builds a `PeriodicAccess` object.
|
||||
pub fn periodic<I, F>(source: I, period: Duration, modifier: F) -> PeriodicAccess<I, F>
|
||||
where
|
||||
|
@ -117,6 +119,11 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.input.total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -4,6 +4,8 @@ use crate::source::buffered::Buffered;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Internal function that builds a `Repeat` object.
|
||||
pub fn repeat<I>(input: I) -> Repeat<I>
|
||||
where
|
||||
|
@ -84,6 +86,11 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.inner.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Clone for Repeat<I>
|
||||
|
|
|
@ -4,6 +4,8 @@ use std::time::Duration;
|
|||
use crate::{Sample, Source};
|
||||
use cpal::{FromSample, Sample as CpalSample};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// An iterator that reads from a `Source` and converts the samples to a specific rate and
|
||||
/// channels count.
|
||||
///
|
||||
|
@ -95,4 +97,9 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.inner.total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.inner.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::Source;
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// An infinite source that produces a sine.
|
||||
///
|
||||
/// Always has a rate of 48kHz and one channel.
|
||||
|
@ -17,7 +19,7 @@ impl SineWave {
|
|||
#[inline]
|
||||
pub fn new(freq: f32) -> SineWave {
|
||||
SineWave {
|
||||
freq: freq,
|
||||
freq,
|
||||
num_sample: 0,
|
||||
}
|
||||
}
|
||||
|
@ -55,4 +57,12 @@ impl Source for SineWave {
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, _: Duration) -> Result<(), SeekNotSupported> {
|
||||
// It is possible to write an implementation that skips the right
|
||||
// amount of samples to get into the right phase. I do not think there
|
||||
// is a use case for that however.
|
||||
Err(SeekNotSupported)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
const NS_PER_SECOND: u128 = 1_000_000_000;
|
||||
|
||||
/// Internal function that builds a `SkipDuration` object.
|
||||
|
@ -157,6 +159,11 @@ where
|
|||
.unwrap_or_else(|| Duration::from_secs(0))
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::time::Duration;
|
|||
use crate::Sample;
|
||||
use crate::Source;
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Internal function that builds a `Skippable` object.
|
||||
pub fn skippable<I>(source: I) -> Skippable<I> {
|
||||
Skippable {
|
||||
|
@ -89,4 +91,9 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.input.total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::time::Duration;
|
|||
use crate::source::ChannelVolume;
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Combines channels in input into a single mono source, then plays that mono sound
|
||||
/// to each channel at the volume given for that channel.
|
||||
#[derive(Clone)]
|
||||
|
@ -117,4 +119,9 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.input.total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Internal function that builds a `Speed` object.
|
||||
pub fn speed<I>(input: I, factor: f32) -> Speed<I> {
|
||||
Speed { input, factor }
|
||||
|
@ -91,16 +93,17 @@ where
|
|||
|
||||
#[inline]
|
||||
fn total_duration(&self) -> Option<Duration> {
|
||||
// TODO: the crappy API of duration makes this code difficult to write
|
||||
if let Some(duration) = self.input.total_duration() {
|
||||
let as_ns = duration.as_secs() * 1000000000 + duration.subsec_nanos() as u64;
|
||||
let new_val = (as_ns as f32 / self.factor) as u64;
|
||||
Some(Duration::new(
|
||||
new_val / 1000000000,
|
||||
(new_val % 1000000000) as u32,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
self.input.total_duration().map(|d| d.mul_f32(self.factor))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
/* TODO: This might be wrong, I do not know how speed achieves its speedup
|
||||
* so I can not reason about the correctness.
|
||||
* <dvdsk noreply@davidsk.dev> */
|
||||
|
||||
// even after 24 hours of playback f32 has enough precision
|
||||
let pos_accounting_for_speedup = pos.mul_f32(self.factor);
|
||||
self.input.try_seek(pos_accounting_for_speedup)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Internal function that builds a `Stoppable` object.
|
||||
pub fn stoppable<I>(source: I) -> Stoppable<I> {
|
||||
Stoppable {
|
||||
|
@ -88,4 +90,9 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.input.total_duration()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// Internal function that builds a `TakeDuration` object.
|
||||
pub fn take_duration<I>(input: I, duration: Duration) -> TakeDuration<I>
|
||||
where
|
||||
|
@ -176,4 +178,9 @@ where
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
self.input.try_seek(pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ use cpal::FromSample;
|
|||
use crate::conversions::{ChannelCountConverter, DataConverter, SampleRateConverter};
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// An iterator that reads from a `Source` and converts the samples to a specific rate and
|
||||
/// channels count.
|
||||
///
|
||||
|
@ -137,6 +139,20 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
self.total_duration
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||
if let Some(input) = self.inner.as_mut() {
|
||||
input
|
||||
.inner_mut()
|
||||
.inner_mut()
|
||||
.inner_mut()
|
||||
.inner_mut()
|
||||
.try_seek(pos)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -145,6 +161,13 @@ struct Take<I> {
|
|||
n: Option<usize>,
|
||||
}
|
||||
|
||||
impl<I> Take<I> {
|
||||
#[inline]
|
||||
pub fn inner_mut(&mut self) -> &mut I {
|
||||
&mut self.iter
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for Take<I>
|
||||
where
|
||||
I: Iterator,
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::time::Duration;
|
|||
|
||||
use crate::{Sample, Source};
|
||||
|
||||
use super::SeekNotSupported;
|
||||
|
||||
/// An infinite source that produces zero.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Zero<S> {
|
||||
|
@ -77,4 +79,9 @@ where
|
|||
fn total_duration(&self) -> Option<Duration> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_seek(&mut self, _: Duration) -> Result<(), SeekNotSupported> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
28
tests/seek_error.rs
Normal file
28
tests/seek_error.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
use std::io::BufReader;
|
||||
use std::time::Duration;
|
||||
|
||||
// hound wav decoder does not support seeking
|
||||
#[cfg(feature = "hound")]
|
||||
#[test]
|
||||
fn seek_not_supported_returns_err() {
|
||||
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
|
||||
let sink = rodio::Sink::try_new(&handle).unwrap();
|
||||
|
||||
let file = std::fs::File::open("assets/music.wav").unwrap();
|
||||
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());
|
||||
let res = sink.try_seek(Duration::from_secs(5));
|
||||
assert_eq!(res, Err(rodio::source::SeekNotSupported));
|
||||
}
|
||||
|
||||
// mp3 decoder does support seeking
|
||||
#[cfg(feature = "mp3")]
|
||||
#[test]
|
||||
fn seek_supported_returns_ok() {
|
||||
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
|
||||
let sink = rodio::Sink::try_new(&handle).unwrap();
|
||||
|
||||
let file = std::fs::File::open("assets/music.mp3").unwrap();
|
||||
sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap());
|
||||
let res = sink.try_seek(Duration::from_secs(5));
|
||||
assert_eq!(res, Ok(()));
|
||||
}
|
Loading…
Reference in a new issue