remove seek trait from source mods, added it to symphonia decoder, renamed the trait to SeekableSource

This commit is contained in:
dvdsk 2023-09-30 21:53:56 +02:00
parent 204a3f89c9
commit d47842fe57
No known key found for this signature in database
GPG key ID: 6CF9D20C5709A836
11 changed files with 65 additions and 98 deletions

View file

@ -15,7 +15,7 @@ claxon = { version = "0.4.2", optional = true }
hound = { version = "3.3.1", optional = true }
lewton = { version = "0.10", optional = true }
# minimp3_fixed = { version = "0.5.4", optional = true}
minimp3_fixed = { package = "minimp3", git = "https://github.com/dvdsk/minimp3-rs.git", optional = true }
minimp3 = { git = "https://github.com/dvdsk/minimp3-rs.git", optional = true }
symphonia = { version = "0.5.2", optional = true, default-features = false }
crossbeam-channel = { version = "0.5.8", optional = true }
@ -26,7 +26,7 @@ flac = ["claxon"]
vorbis = ["lewton"]
wav = ["hound"]
mp3 = ["symphonia-mp3"]
minimp3 = ["dep:minimp3_fixed"]
minimp3 = ["dep:minimp3"]
wasm-bindgen = ["cpal/wasm-bindgen"]
symphonia-aac = ["symphonia/aac"]
symphonia-all = ["symphonia-aac", "symphonia-flac", "symphonia-isomp4", "symphonia-mp3", "symphonia-vorbis", "symphonia-wav"]

View file

@ -1,4 +1,5 @@
use std::io::BufReader;
use std::time::Duration;
fn main() {
let (_stream, handle) = rodio::OutputStream::try_default().unwrap();
@ -7,11 +8,11 @@ fn main() {
let file = std::fs::File::open("examples/music.mp3").unwrap();
sink.append_seekable(rodio::Decoder::new(BufReader::new(file)).unwrap());
loop {
std::thread::sleep(std::time::Duration::from_secs(2));
sink.set_pos(2.0);
dbg!("setting pos");
}
std::thread::sleep(std::time::Duration::from_secs(2));
sink.seek(Duration::from_secs(0));
std::thread::sleep(std::time::Duration::from_secs(2));
sink.seek(Duration::from_secs(4));
sink.sleep_until_end();
}

View file

@ -10,7 +10,7 @@ use std::str::FromStr;
use std::time::Duration;
use crate::Source;
use crate::SourceExt;
use crate::SeekableSource;
#[cfg(feature = "symphonia")]
use self::read_seek_source::ReadSeekSource;
@ -368,44 +368,23 @@ where
}
}
impl<R> Decoder<R>
where
R: Read + Seek,
{
fn request_pos(&mut self, pos: f32) -> bool {
match &mut self.0 {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(_) => false,
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(_) => false,
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(_) => false,
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.request_pos(pos),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(_) => false,
DecoderImpl::None(_) => false,
}
}
}
impl<R> SourceExt for Decoder<R>
impl<R> SeekableSource for Decoder<R>
where
R: Read + Seek,
{
fn request_pos(&mut self, pos: f32) -> bool {
fn seek(&mut self, pos: Duration) {
match &mut self.0 {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(_) => false,
DecoderImpl::Wav(_) => (),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(_) => false,
DecoderImpl::Vorbis(_) => (),
#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(_) => false,
DecoderImpl::Flac(_) => (),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.request_pos(pos),
DecoderImpl::Mp3(source) => source.seek(pos),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(_) => false,
DecoderImpl::None(_) => false,
DecoderImpl::Symphonia(source) => source.seek(pos),
DecoderImpl::None(_) => (),
}
}
}

View file

@ -1,16 +1,16 @@
use std::io::{Read, Seek, SeekFrom};
use std::time::Duration;
use crate::{Source, SourceExt};
use crate::{SeekableSource, Source};
use minimp3::Frame;
use minimp3::SeekDecoder as Decoder;
use minimp3::{Decoder, SeekDecoder};
pub struct Mp3Decoder<R>
where
R: Read + Seek,
{
decoder: Decoder<R>,
decoder: SeekDecoder<R>,
current_frame: Frame,
current_frame_offset: usize,
}
@ -23,10 +23,14 @@ where
if !is_mp3(data.by_ref()) {
return Err(data);
}
let mut decoder = Decoder::new(data).map_err(|_| ())?;
// TODO: figure out decode_frame vs next_frame <dvdsk noreply@davidsk.dev>
// let current_frame = decoder.next_frame().unwrap();
let current_frame = decoder.decode_frame().map_err(|_| ())?;
let mut decoder = SeekDecoder::new(data)
// paramaters are correct and minimp3 is used correctly
// thus if we crash here one of these invariants is broken:
.expect("should be able to allocate memory, perform IO");
let current_frame = decoder.decode_frame()
// the reader makes enough data availible therefore
// if we crash here the invariant broken is:
.expect("data should not corrupt");
Ok(Mp3Decoder {
decoder,
@ -64,15 +68,14 @@ where
}
}
impl<R> SourceExt for Mp3Decoder<R>
where
impl<R> SeekableSource for Mp3Decoder<R>
where
R: Read + Seek,
{
fn request_pos(&mut self, pos: f32) -> bool {
let pos = (pos * self.sample_rate() as f32) as u64;
fn seek(&mut self, pos: Duration) -> bool {
let pos = (pos.as_secs_f32() * self.sample_rate() as f32) as u64;
// do not trigger a sample_rate, channels and frame len update
// as the seek only takes effect after the current frame is done
dbg!("seek");
self.decoder.seek_samples(pos).is_ok()
}
}

View file

@ -8,12 +8,12 @@ use symphonia::{
io::MediaSourceStream,
meta::MetadataOptions,
probe::Hint,
units,
units::{self, Time},
},
default::get_probe,
};
use crate::Source;
use crate::{SeekableSource, Source};
use super::DecoderError;
@ -119,6 +119,23 @@ impl SymphoniaDecoder {
}
}
impl SeekableSource for SymphoniaDecoder {
fn seek(&mut self, pos: Duration) {
use symphonia::core::formats::{SeekMode, SeekTo};
let pos_fract = 1f64 / pos.subsec_nanos() as f64;
self.format
.seek(
SeekMode::Accurate,
SeekTo::Time {
time: Time::new(pos.as_secs(), pos_fract),
track_id: None,
},
)
.unwrap();
}
}
impl Source for SymphoniaDecoder {
#[inline]
fn current_frame_len(&self) -> Option<usize> {

View file

@ -129,6 +129,6 @@ pub mod static_buffer;
pub use crate::conversions::Sample;
pub use crate::decoder::Decoder;
pub use crate::sink::Sink;
pub use crate::source::{Source, SourceExt};
pub use crate::source::{Source, SeekableSource};
pub use crate::spatial_sink::SpatialSink;
pub use crate::stream::{OutputStream, OutputStreamHandle, PlayError, StreamError};

View file

@ -8,7 +8,7 @@ use crossbeam_channel::Receiver;
use std::sync::mpsc::Receiver;
use crate::stream::{OutputStreamHandle, PlayError};
use crate::{queue, source::Done, Sample, Source, SourceExt};
use crate::{queue, source::Done, Sample, Source, SeekableSource};
use cpal::FromSample;
/// Handle to an device that outputs sounds.
@ -31,7 +31,7 @@ struct Controls {
stopped: AtomicBool,
speed: Mutex<f32>,
to_clear: Mutex<u32>,
set_pos: Mutex<Option<f32>>,
seek: Mutex<Option<Duration>>,
}
impl Sink {
@ -57,7 +57,7 @@ impl Sink {
stopped: AtomicBool::new(false),
speed: Mutex::new(1.0),
to_clear: Mutex::new(0),
set_pos: Mutex::new(None),
seek: Mutex::new(None),
}),
sound_count: Arc::new(AtomicUsize::new(0)),
detached: false,
@ -121,7 +121,7 @@ impl Sink {
#[inline]
pub fn append_seekable<S>(&self, source: S)
where
S: Source + SourceExt + Send + 'static,
S: Source + SeekableSource + Send + 'static,
f32: FromSample<S::Item>,
S::Item: Sample + Send,
{
@ -162,8 +162,8 @@ impl Sink {
.inner_mut()
.set_factor(*controls.speed.lock().unwrap());
let seekable = amp.inner_mut().inner_mut().inner_mut();
if let Some(pos) = controls.set_pos.lock().unwrap().take() {
seekable.request_pos(pos);
if let Some(pos) = controls.seek.lock().unwrap().take() {
seekable.seek(pos);
}
start_played.store(true, Ordering::SeqCst);
})
@ -220,8 +220,8 @@ impl Sink {
/// Set position
///
/// No effect if source does not implement `SourceExt`
pub fn set_pos(&self, pos: f32) {
*self.controls.set_pos.lock().unwrap() = Some(pos);
pub fn seek(&self, pos: Duration) {
*self.controls.seek.lock().unwrap() = Some(pos);
}
/// Pauses playback of this sink.

View file

@ -1,6 +1,6 @@
use std::time::Duration;
use crate::{Sample, Source, SourceExt};
use crate::{Sample, Source};
/// Internal function that builds a `Amplify` object.
pub fn amplify<I>(input: I, factor: f32) -> Amplify<I>
@ -94,14 +94,3 @@ where
self.input.total_duration()
}
}
impl<I> SourceExt for Amplify<I>
where
I: Source,
I: SourceExt,
I::Item: Sample,
{
fn request_pos(&mut self, pos: f32) -> bool {
self.input.request_pos(pos)
}
}

View file

@ -428,7 +428,7 @@ where
}
}
pub trait SourceExt {
pub trait SeekableSource {
/// Seek to pos and whether the seek succeeded
fn request_pos(&mut self, pos: f32) -> bool;
fn seek(&mut self, pos: Duration);
}

View file

@ -1,6 +1,6 @@
use std::time::Duration;
use crate::{Sample, Source, SourceExt};
use crate::{Sample, Source};
/// Internal function that builds a `Pausable` object.
pub fn pausable<I>(source: I, paused: bool) -> Pausable<I>
@ -116,14 +116,3 @@ where
self.input.total_duration()
}
}
impl<I> SourceExt for Pausable<I>
where
I: Source,
I: SourceExt,
I::Item: Sample,
{
fn request_pos(&mut self, pos: f32) -> bool {
self.input.request_pos(pos)
}
}

View file

@ -1,6 +1,6 @@
use std::time::Duration;
use crate::{Sample, Source, SourceExt};
use crate::{Sample, Source};
/// Internal function that builds a `Stoppable` object.
pub fn stoppable<I>(source: I) -> Stoppable<I> {
@ -89,14 +89,3 @@ where
self.input.total_duration()
}
}
impl<I> SourceExt for Stoppable<I>
where
I: Source,
I: SourceExt,
I::Item: Sample,
{
fn request_pos(&mut self, pos: f32) -> bool {
self.input.request_pos(pos)
}
}