mirror of
https://github.com/RustAudio/rodio
synced 2024-12-13 13:42:34 +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 sink = rodio::Sink::try_new(&handle).unwrap();
|
||||||
|
|
||||||
let file = std::fs::File::open("examples/music.mp3").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));
|
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));
|
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();
|
sink.sleep_until_end();
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,12 @@ where
|
||||||
pub fn into_inner(self) -> I {
|
pub fn into_inner(self) -> I {
|
||||||
self.input
|
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>
|
impl<I> Iterator for ChannelCountConverter<I>
|
||||||
|
|
|
@ -23,6 +23,12 @@ impl<I, O> DataConverter<I, O> {
|
||||||
pub fn into_inner(self) -> I {
|
pub fn into_inner(self) -> I {
|
||||||
self.input
|
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>
|
impl<I, O> Iterator for DataConverter<I, O>
|
||||||
|
|
|
@ -102,6 +102,12 @@ where
|
||||||
self.input
|
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) {
|
fn next_input_frame(&mut self) {
|
||||||
self.current_frame_pos_in_chunk += 1;
|
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;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[cfg(feature = "crossbeam-channel")]
|
#[cfg(feature = "crossbeam-channel")]
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::{Receiver, Sender};
|
||||||
#[cfg(not(feature = "crossbeam-channel"))]
|
#[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::stream::{OutputStreamHandle, PlayError};
|
||||||
use crate::{queue, source::Done, Sample, Source};
|
use crate::{queue, source::Done, Sample, Source};
|
||||||
use cpal::FromSample;
|
use cpal::FromSample;
|
||||||
|
@ -25,13 +26,44 @@ pub struct Sink {
|
||||||
detached: bool,
|
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 {
|
struct Controls {
|
||||||
pause: AtomicBool,
|
pause: AtomicBool,
|
||||||
volume: Mutex<f32>,
|
volume: Mutex<f32>,
|
||||||
stopped: AtomicBool,
|
stopped: AtomicBool,
|
||||||
speed: Mutex<f32>,
|
speed: Mutex<f32>,
|
||||||
to_clear: Mutex<u32>,
|
to_clear: Mutex<u32>,
|
||||||
seek: Mutex<Option<Duration>>,
|
seek: Mutex<Option<SeekOrder>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sink {
|
impl Sink {
|
||||||
|
@ -109,61 +141,8 @@ impl Sink {
|
||||||
amp.inner_mut()
|
amp.inner_mut()
|
||||||
.inner_mut()
|
.inner_mut()
|
||||||
.set_factor(*controls.speed.lock().unwrap());
|
.set_factor(*controls.speed.lock().unwrap());
|
||||||
start_played.store(true, Ordering::SeqCst);
|
if let Some(seek) = controls.seek.lock().unwrap().take() {
|
||||||
})
|
seek.attempt(amp)
|
||||||
.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();
|
|
||||||
}
|
}
|
||||||
start_played.store(true, Ordering::SeqCst);
|
start_played.store(true, Ordering::SeqCst);
|
||||||
})
|
})
|
||||||
|
@ -219,9 +198,18 @@ impl Sink {
|
||||||
|
|
||||||
/// Set position
|
/// Set position
|
||||||
///
|
///
|
||||||
/// No effect if source does not implement `SourceExt`
|
/// Try to seek to a pos, returns [`SeekNotSupported`] if seeking is not
|
||||||
pub fn seek(&self, pos: Duration) {
|
/// supported by the current source.
|
||||||
*self.controls.seek.lock().unwrap() = Some(pos);
|
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.
|
/// Pauses playback of this sink.
|
||||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
||||||
|
|
||||||
use crate::{Sample, Source};
|
use crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Internal function that builds a `Amplify` object.
|
/// Internal function that builds a `Amplify` object.
|
||||||
pub fn amplify<I>(input: I, factor: f32) -> Amplify<I>
|
pub fn amplify<I>(input: I, factor: f32) -> Amplify<I>
|
||||||
where
|
where
|
||||||
|
@ -93,4 +95,9 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.input.total_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 crate::Source;
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
// Implemented following http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
// Implemented following http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||||
|
|
||||||
/// Internal function that builds a `BltFilter` object.
|
/// Internal function that builds a `BltFilter` object.
|
||||||
|
@ -147,6 +149,11 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.input.total_duration()
|
self.input.total_duration()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||||
|
self.input.try_seek(pos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
@ -5,6 +5,8 @@ use std::time::Duration;
|
||||||
|
|
||||||
use crate::{Sample, Source};
|
use crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Internal function that builds a `Buffered` object.
|
/// Internal function that builds a `Buffered` object.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn buffered<I>(input: I) -> Buffered<I>
|
pub fn buffered<I>(input: I) -> Buffered<I>
|
||||||
|
@ -239,6 +241,11 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.total_duration
|
self.total_duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_seek(&mut self, _: Duration) -> Result<(), SeekNotSupported> {
|
||||||
|
Err(SeekNotSupported)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> Clone for Buffered<I>
|
impl<I> Clone for Buffered<I>
|
||||||
|
|
|
@ -2,6 +2,8 @@ use std::time::Duration;
|
||||||
|
|
||||||
use crate::{Sample, Source};
|
use crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Combines channels in input into a single mono source, then plays that mono sound
|
/// Combines channels in input into a single mono source, then plays that mono sound
|
||||||
/// to each channel at the volume given for that channel.
|
/// to each channel at the volume given for that channel.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -138,4 +140,9 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.input.total_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 crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// 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
|
||||||
|
@ -105,4 +107,10 @@ where
|
||||||
.total_duration()
|
.total_duration()
|
||||||
.map(|val| val + self.requested_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 crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// When the inner source is empty this decrements an `AtomicUsize`.
|
/// When the inner source is empty this decrements an `AtomicUsize`.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Done<I> {
|
pub struct Done<I> {
|
||||||
|
@ -87,4 +89,9 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.input.total_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 crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// An empty source.
|
/// An empty source.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct Empty<S>(PhantomData<S>);
|
pub struct Empty<S>(PhantomData<S>);
|
||||||
|
@ -53,4 +55,9 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
Some(Duration::new(0, 0))
|
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 crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// An empty source which executes a callback function
|
/// An empty source which executes a callback function
|
||||||
pub struct EmptyCallback<S> {
|
pub struct EmptyCallback<S> {
|
||||||
pub phantom_data: PhantomData<S>,
|
pub phantom_data: PhantomData<S>,
|
||||||
|
@ -52,4 +54,9 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
Some(Duration::new(0, 0))
|
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 crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Internal function that builds a `FadeIn` object.
|
/// Internal function that builds a `FadeIn` object.
|
||||||
pub fn fadein<I>(input: I, duration: Duration) -> FadeIn<I>
|
pub fn fadein<I>(input: I, duration: Duration) -> FadeIn<I>
|
||||||
where
|
where
|
||||||
|
@ -105,4 +107,9 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.input.total_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 crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Builds a source that chains sources provided by an iterator.
|
/// 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.
|
/// 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> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
None
|
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)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::source::SeekNotSupported;
|
||||||
use crate::source::uniform::UniformSourceIterator;
|
use crate::source::uniform::UniformSourceIterator;
|
||||||
use crate::{Sample, Source};
|
use crate::{Sample, Source};
|
||||||
use cpal::{FromSample, Sample as CpalSample};
|
use cpal::{FromSample, Sample as CpalSample};
|
||||||
|
@ -119,4 +120,10 @@ where
|
||||||
_ => None,
|
_ => 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)
|
blt::high_pass(self, freq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set position
|
||||||
|
///
|
||||||
/// Try to seek to a pos, returns [`SeekNotSupported`] if seeking is not
|
/// Try to seek to a pos, returns [`SeekNotSupported`] if seeking is not
|
||||||
/// supported by the current source.
|
/// supported by the current source.
|
||||||
fn try_seek(&mut self, _: Duration) -> Result<(), SeekNotSupported> {
|
fn try_seek(&mut self, _: Duration) -> Result<(), SeekNotSupported> {
|
||||||
|
@ -362,7 +364,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct SeekNotSupported;
|
pub struct SeekNotSupported;
|
||||||
|
|
||||||
impl fmt::Display for SeekNotSupported {
|
impl fmt::Display for SeekNotSupported {
|
||||||
|
@ -396,6 +398,11 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
(**self).total_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>
|
impl<S> Source for Box<dyn Source<Item = S> + Send>
|
||||||
|
@ -421,6 +428,11 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
(**self).total_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>
|
impl<S> Source for Box<dyn Source<Item = S> + Send + Sync>
|
||||||
|
@ -446,5 +458,10 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
(**self).total_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 crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Internal function that builds a `Pausable` object.
|
/// Internal function that builds a `Pausable` object.
|
||||||
pub fn pausable<I>(source: I, paused: bool) -> Pausable<I>
|
pub fn pausable<I>(source: I, paused: bool) -> Pausable<I>
|
||||||
where
|
where
|
||||||
|
@ -115,4 +117,9 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.input.total_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 crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Internal function that builds a `PeriodicAccess` object.
|
/// Internal function that builds a `PeriodicAccess` object.
|
||||||
pub fn periodic<I, F>(source: I, period: Duration, modifier: F) -> PeriodicAccess<I, F>
|
pub fn periodic<I, F>(source: I, period: Duration, modifier: F) -> PeriodicAccess<I, F>
|
||||||
where
|
where
|
||||||
|
@ -117,6 +119,11 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.input.total_duration()
|
self.input.total_duration()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||||
|
self.input.try_seek(pos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -4,6 +4,8 @@ use crate::source::buffered::Buffered;
|
||||||
|
|
||||||
use crate::{Sample, Source};
|
use crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Internal function that builds a `Repeat` object.
|
/// Internal function that builds a `Repeat` object.
|
||||||
pub fn repeat<I>(input: I) -> Repeat<I>
|
pub fn repeat<I>(input: I) -> Repeat<I>
|
||||||
where
|
where
|
||||||
|
@ -84,6 +86,11 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||||
|
self.inner.try_seek(pos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> Clone for Repeat<I>
|
impl<I> Clone for Repeat<I>
|
||||||
|
|
|
@ -4,6 +4,8 @@ use std::time::Duration;
|
||||||
use crate::{Sample, Source};
|
use crate::{Sample, Source};
|
||||||
use cpal::{FromSample, Sample as CpalSample};
|
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
|
/// An iterator that reads from a `Source` and converts the samples to a specific rate and
|
||||||
/// channels count.
|
/// channels count.
|
||||||
///
|
///
|
||||||
|
@ -95,4 +97,9 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.inner.total_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 crate::Source;
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// An infinite source that produces a sine.
|
/// An infinite source that produces a sine.
|
||||||
///
|
///
|
||||||
/// Always has a rate of 48kHz and one channel.
|
/// Always has a rate of 48kHz and one channel.
|
||||||
|
@ -17,7 +19,7 @@ impl SineWave {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(freq: f32) -> SineWave {
|
pub fn new(freq: f32) -> SineWave {
|
||||||
SineWave {
|
SineWave {
|
||||||
freq: freq,
|
freq,
|
||||||
num_sample: 0,
|
num_sample: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,4 +57,12 @@ impl Source for SineWave {
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
None
|
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 crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
const NS_PER_SECOND: u128 = 1_000_000_000;
|
const NS_PER_SECOND: u128 = 1_000_000_000;
|
||||||
|
|
||||||
/// Internal function that builds a `SkipDuration` object.
|
/// Internal function that builds a `SkipDuration` object.
|
||||||
|
@ -157,6 +159,11 @@ where
|
||||||
.unwrap_or_else(|| Duration::from_secs(0))
|
.unwrap_or_else(|| Duration::from_secs(0))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||||
|
self.input.try_seek(pos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -3,6 +3,8 @@ use std::time::Duration;
|
||||||
use crate::Sample;
|
use crate::Sample;
|
||||||
use crate::Source;
|
use crate::Source;
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Internal function that builds a `Skippable` object.
|
/// Internal function that builds a `Skippable` object.
|
||||||
pub fn skippable<I>(source: I) -> Skippable<I> {
|
pub fn skippable<I>(source: I) -> Skippable<I> {
|
||||||
Skippable {
|
Skippable {
|
||||||
|
@ -89,4 +91,9 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.input.total_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::source::ChannelVolume;
|
||||||
use crate::{Sample, Source};
|
use crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Combines channels in input into a single mono source, then plays that mono sound
|
/// Combines channels in input into a single mono source, then plays that mono sound
|
||||||
/// to each channel at the volume given for that channel.
|
/// to each channel at the volume given for that channel.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -117,4 +119,9 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.input.total_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 crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Internal function that builds a `Speed` object.
|
/// Internal function that builds a `Speed` object.
|
||||||
pub fn speed<I>(input: I, factor: f32) -> Speed<I> {
|
pub fn speed<I>(input: I, factor: f32) -> Speed<I> {
|
||||||
Speed { input, factor }
|
Speed { input, factor }
|
||||||
|
@ -91,16 +93,17 @@ where
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
// TODO: the crappy API of duration makes this code difficult to write
|
self.input.total_duration().map(|d| d.mul_f32(self.factor))
|
||||||
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;
|
#[inline]
|
||||||
Some(Duration::new(
|
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekNotSupported> {
|
||||||
new_val / 1000000000,
|
/* TODO: This might be wrong, I do not know how speed achieves its speedup
|
||||||
(new_val % 1000000000) as u32,
|
* so I can not reason about the correctness.
|
||||||
))
|
* <dvdsk noreply@davidsk.dev> */
|
||||||
} else {
|
|
||||||
None
|
// 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 crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Internal function that builds a `Stoppable` object.
|
/// Internal function that builds a `Stoppable` object.
|
||||||
pub fn stoppable<I>(source: I) -> Stoppable<I> {
|
pub fn stoppable<I>(source: I) -> Stoppable<I> {
|
||||||
Stoppable {
|
Stoppable {
|
||||||
|
@ -88,4 +90,9 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.input.total_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 crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// Internal function that builds a `TakeDuration` object.
|
/// Internal function that builds a `TakeDuration` object.
|
||||||
pub fn take_duration<I>(input: I, duration: Duration) -> TakeDuration<I>
|
pub fn take_duration<I>(input: I, duration: Duration) -> TakeDuration<I>
|
||||||
where
|
where
|
||||||
|
@ -176,4 +178,9 @@ where
|
||||||
None
|
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::conversions::{ChannelCountConverter, DataConverter, SampleRateConverter};
|
||||||
use crate::{Sample, Source};
|
use crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// An iterator that reads from a `Source` and converts the samples to a specific rate and
|
/// An iterator that reads from a `Source` and converts the samples to a specific rate and
|
||||||
/// channels count.
|
/// channels count.
|
||||||
///
|
///
|
||||||
|
@ -137,6 +139,20 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
self.total_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)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -145,6 +161,13 @@ struct Take<I> {
|
||||||
n: Option<usize>,
|
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>
|
impl<I> Iterator for Take<I>
|
||||||
where
|
where
|
||||||
I: Iterator,
|
I: Iterator,
|
||||||
|
|
|
@ -3,6 +3,8 @@ use std::time::Duration;
|
||||||
|
|
||||||
use crate::{Sample, Source};
|
use crate::{Sample, Source};
|
||||||
|
|
||||||
|
use super::SeekNotSupported;
|
||||||
|
|
||||||
/// An infinite source that produces zero.
|
/// An infinite source that produces zero.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Zero<S> {
|
pub struct Zero<S> {
|
||||||
|
@ -77,4 +79,9 @@ where
|
||||||
fn total_duration(&self) -> Option<Duration> {
|
fn total_duration(&self) -> Option<Duration> {
|
||||||
None
|
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