Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Elapsed Duration functionality for the Decoder/LoopedDecoder #510

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions examples/sample_count.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use rodio::Decoder;
use std::fs::File;
use std::io::BufReader;

fn main() {
let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap();

let file = BufReader::new(File::open("assets/music.flac").unwrap());
let decoder = Decoder::new(file).unwrap();
let info = decoder.get_info();
let sink = rodio::Sink::try_new(&stream_handle).unwrap();
sink.append(decoder);
while sink.len() > 0 {
println!("Elapsed Duration: {:?}", info.elapsed_duration().unwrap());
}
}
170 changes: 140 additions & 30 deletions src/decoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ use std::io::{Read, Seek, SeekFrom};
use std::marker::Sync;
use std::mem;
use std::str::FromStr;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;

use crate::Source;
use crate::{Sample, Source};

#[cfg(feature = "symphonia")]
use self::read_seek_source::ReadSeekSource;
Expand All @@ -32,13 +34,21 @@ mod wav;
/// Source of audio samples from decoding a file.
///
/// Supports MP3, WAV, Vorbis and Flac.
pub struct Decoder<R>(DecoderImpl<R>)
pub struct Decoder<R>
where
R: Read + Seek;
R: Read + Seek,
{
decoder: DecoderImpl<R>,
info: Arc<DecoderInfo>,
}

pub struct LoopedDecoder<R>(DecoderImpl<R>)
pub struct LoopedDecoder<R>
where
R: Read + Seek;
R: Read + Seek,
{
decoder: DecoderImpl<R>,
info: Arc<DecoderInfo>,
}

enum DecoderImpl<R>
where
Expand All @@ -54,7 +64,50 @@ where
Mp3(mp3::Mp3Decoder<R>),
#[cfg(feature = "symphonia")]
Symphonia(symphonia::SymphoniaDecoder),
None(::std::marker::PhantomData<R>),
None(std::marker::PhantomData<R>),
}

pub struct DecoderInfo {
channels: u16,
sample_rate: u32,
samples_elapsed: AtomicUsize,
}

impl DecoderInfo {
fn from_source<S>(source: &S) -> Self
where
S: Source,
S::Item: Sample,
{
Self {
channels: source.channels(),
sample_rate: source.sample_rate(),
samples_elapsed: AtomicUsize::default(),
}
}

#[inline]
pub fn channels(&self) -> u16 {
self.channels
}

#[inline]
pub fn sample_rate(&self) -> u32 {
self.sample_rate
}

#[inline]
pub fn elapsed_samples(&self) -> usize {
self.samples_elapsed.load(Ordering::Relaxed)
}

#[inline]
pub fn elapsed_duration(&self) -> Option<Duration> {
Copy link
Collaborator

@dvdsk dvdsk Oct 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might not be the best name, elapsed sounds like its the time since the song started playing, however it is the position of the song right? So maybe name it .... position, playback_pos or something else

Cause this could get confusing with seeking.

Duration::try_from_secs_f64(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forgive me but why do we return an Option here? is there a combination of elapsed_samples, sample rate and channels that makes try_from_secs_f64 fail? I would love a comment explaining that.

Or is it there because some combination of sources could not support elapsed_duration?

self.elapsed_samples() as f64 / self.sample_rate() as f64 / self.channels() as f64,
)
.ok()
}
}

impl<R> Decoder<R>
Expand All @@ -70,31 +123,46 @@ where
let data = match wav::WavDecoder::new(data) {
Err(data) => data,
Ok(decoder) => {
return Ok(Decoder(DecoderImpl::Wav(decoder)));
let info = Arc::new(DecoderInfo::from_source(&decoder));
return Ok(Decoder {
decoder: DecoderImpl::Wav(decoder),
info,
});
}
};

#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))]
let data = match flac::FlacDecoder::new(data) {
Err(data) => data,
Ok(decoder) => {
return Ok(Decoder(DecoderImpl::Flac(decoder)));
let info = Arc::new(DecoderInfo::from_source(&decoder));
return Ok(Decoder {
decoder: DecoderImpl::Flac(decoder),
info,
});
}
};

#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
let data = match vorbis::VorbisDecoder::new(data) {
Err(data) => data,
Ok(decoder) => {
return Ok(Decoder(DecoderImpl::Vorbis(decoder)));
let info = Arc::new(DecoderInfo::from_source(&decoder));
return Ok(Decoder {
decoder: DecoderImpl::Vorbis(decoder),
info,
});
}
};

#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
let data = match mp3::Mp3Decoder::new(data) {
Err(data) => data,
Ok(decoder) => {
return Ok(Decoder(DecoderImpl::Mp3(decoder)));
return Ok(Decoder {
decoder: DecoderImpl::Mp3(decoder),
info: Arc::new(DecoderInfo::from_source(&decoder)),
});
}
};

Expand All @@ -108,7 +176,11 @@ where
match symphonia::SymphoniaDecoder::new(mss, None) {
Err(e) => Err(e),
Ok(decoder) => {
return Ok(Decoder(DecoderImpl::Symphonia(decoder)));
let info = Arc::new(DecoderInfo::from_source(&decoder));
Ok(Decoder {
decoder: DecoderImpl::Symphonia(decoder),
info,
})
}
}
}
Expand All @@ -124,7 +196,13 @@ where
pub fn new_wav(data: R) -> Result<Decoder<R>, DecoderError> {
match wav::WavDecoder::new(data) {
Err(_) => Err(DecoderError::UnrecognizedFormat),
Ok(decoder) => Ok(Decoder(DecoderImpl::Wav(decoder))),
Ok(decoder) => {
let info = Arc::new(DecoderInfo::from_source(&decoder));
Ok(Decoder {
decoder: DecoderImpl::Wav(decoder),
info,
})
}
}
}

Expand All @@ -139,7 +217,13 @@ where
pub fn new_flac(data: R) -> Result<Decoder<R>, DecoderError> {
match flac::FlacDecoder::new(data) {
Err(_) => Err(DecoderError::UnrecognizedFormat),
Ok(decoder) => Ok(Decoder(DecoderImpl::Flac(decoder))),
Ok(decoder) => {
let info = Arc::new(DecoderInfo::from_source(&decoder));
Ok(Decoder {
decoder: DecoderImpl::Flac(decoder),
info,
})
}
}
}

Expand All @@ -154,7 +238,13 @@ where
pub fn new_vorbis(data: R) -> Result<Decoder<R>, DecoderError> {
match vorbis::VorbisDecoder::new(data) {
Err(_) => Err(DecoderError::UnrecognizedFormat),
Ok(decoder) => Ok(Decoder(DecoderImpl::Vorbis(decoder))),
Ok(decoder) => {
let info = Arc::new(DecoderInfo::from_source(&decoder));
Ok(Decoder {
decoder: DecoderImpl::Vorbis(decoder),
info,
})
}
}
}

Expand All @@ -163,7 +253,10 @@ where
pub fn new_mp3(data: R) -> Result<Decoder<R>, DecoderError> {
match mp3::Mp3Decoder::new(data) {
Err(_) => Err(DecoderError::UnrecognizedFormat),
Ok(decoder) => Ok(Decoder(DecoderImpl::Mp3(decoder))),
Ok(decoder) => Ok(Decoder {
decoder: DecoderImpl::Mp3(decoder),
info: Arc::new(DecoderInfo::from_source(&decoder)),
}),
}
}

Expand Down Expand Up @@ -195,10 +288,18 @@ where
match symphonia::SymphoniaDecoder::new(mss, Some(hint)) {
Err(e) => Err(e),
Ok(decoder) => {
return Ok(Decoder(DecoderImpl::Symphonia(decoder)));
let info = Arc::new(DecoderInfo::from_source(&decoder));
Ok(Decoder {
decoder: DecoderImpl::Symphonia(decoder),
info,
})
}
}
}

pub fn get_info(&self) -> Arc<DecoderInfo> {
self.info.clone()
}
}

#[derive(Debug)]
Expand Down Expand Up @@ -249,7 +350,10 @@ where
R: Read + Seek,
{
fn new(decoder: Decoder<R>) -> LoopedDecoder<R> {
Self(decoder.0)
Self {
decoder: decoder.decoder,
info: decoder.info,
}
}
}

Expand All @@ -261,7 +365,7 @@ where

#[inline]
fn next(&mut self) -> Option<i16> {
match &mut self.0 {
if let Some(sample) = match &mut self.decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.next(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
Expand All @@ -273,12 +377,17 @@ where
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source) => source.next(),
DecoderImpl::None(_) => None,
} {
self.info.samples_elapsed.fetch_add(1, Ordering::Relaxed);
Some(sample)
} else {
None
}
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.0 {
match &self.decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.size_hint(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
Expand All @@ -300,7 +409,7 @@ where
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
match &self.0 {
match &self.decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.current_frame_len(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
Expand All @@ -317,7 +426,7 @@ where

#[inline]
fn channels(&self) -> u16 {
match &self.0 {
match &self.decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.channels(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
Expand All @@ -334,7 +443,7 @@ where

#[inline]
fn sample_rate(&self) -> u32 {
match &self.0 {
match &self.decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.sample_rate(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
Expand All @@ -351,7 +460,7 @@ where

#[inline]
fn total_duration(&self) -> Option<Duration> {
match &self.0 {
match &self.decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.total_duration(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
Expand All @@ -375,7 +484,7 @@ where

#[inline]
fn next(&mut self) -> Option<i16> {
if let Some(sample) = match &mut self.0 {
if let Some(sample) = match &mut self.decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.next(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
Expand All @@ -388,9 +497,10 @@ where
DecoderImpl::Symphonia(source) => source.next(),
DecoderImpl::None(_) => None,
} {
self.info.samples_elapsed.fetch_add(1, Ordering::Relaxed);
Some(sample)
} else {
let decoder = mem::replace(&mut self.0, DecoderImpl::None(Default::default()));
let decoder = mem::replace(&mut self.decoder, DecoderImpl::None(Default::default()));
let (decoder, sample) = match decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => {
Expand Down Expand Up @@ -437,14 +547,14 @@ where
}
none @ DecoderImpl::None(_) => (none, None),
};
self.0 = decoder;
self.decoder = decoder;
sample
}
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
match &self.0 {
match &self.decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => (source.size_hint().0, None),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
Expand All @@ -466,7 +576,7 @@ where
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
match &self.0 {
match &self.decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.current_frame_len(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
Expand All @@ -483,7 +593,7 @@ where

#[inline]
fn channels(&self) -> u16 {
match &self.0 {
match &self.decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.channels(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
Expand All @@ -500,7 +610,7 @@ where

#[inline]
fn sample_rate(&self) -> u32 {
match &self.0 {
match &self.decoder {
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.sample_rate(),
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
Expand Down