Skip to content

Commit 5ecd76b

Browse files
committed
refactor: use SampleRate type throughout
style: cargo fmt
1 parent 46ebd9e commit 5ecd76b

File tree

6 files changed

+740
-59
lines changed

6 files changed

+740
-59
lines changed

src/decoder/flac.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -277,14 +277,14 @@ where
277277
let reader = FlacReader::new_ext(data, READER_OPTIONS).expect("should still be flac");
278278

279279
let spec = reader.streaminfo();
280-
let sample_rate = spec.sample_rate;
280+
let sample_rate = SampleRate::new(spec.sample_rate)
281+
.expect("flac data should never have a zero sample rate");
281282
let max_block_size = spec.max_block_size as usize * spec.channels as usize;
282283

283284
// `samples` in FLAC means "inter-channel samples" aka frames
284285
// so we do not divide by `self.channels` here.
285286
let total_samples = spec.samples;
286-
let total_duration =
287-
total_samples.map(|s| utils::samples_to_duration(s, sample_rate as u64));
287+
let total_duration = total_samples.map(|s| utils::samples_to_duration(s, sample_rate));
288288

289289
Ok(Self {
290290
reader: Some(reader),
@@ -293,8 +293,7 @@ where
293293
current_block_off: 0,
294294
bits_per_sample: BitDepth::new(spec.bits_per_sample)
295295
.expect("flac should never have zero bits per sample"),
296-
sample_rate: SampleRate::new(sample_rate)
297-
.expect("flac data should never have a zero sample rate"),
296+
sample_rate,
298297
channels: ChannelCount::new(
299298
spec.channels
300299
.try_into()

src/decoder/looped_improved.rs

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
use std::{
2+
io::{Read, Seek},
3+
marker::PhantomData,
4+
sync::Arc,
5+
time::Duration,
6+
};
7+
8+
use crate::{
9+
common::{ChannelCount, SampleRate},
10+
math::nz,
11+
source::{SeekError, Source},
12+
BitDepth, Sample,
13+
};
14+
15+
use super::{builder::Settings, DecoderError, DecoderImpl};
16+
17+
#[cfg(feature = "claxon")]
18+
use super::flac;
19+
#[cfg(feature = "minimp3")]
20+
use super::mp3;
21+
#[cfg(feature = "symphonia")]
22+
use super::symphonia;
23+
#[cfg(feature = "lewton")]
24+
use super::vorbis;
25+
#[cfg(feature = "hound")]
26+
use super::wav;
27+
28+
/// Decoder that loops indefinitely by seeking back to the start when reaching the end.
29+
///
30+
/// Uses fast seeking for seekable sources with gapless playback, otherwise recreates the
31+
/// decoder while caching metadata to avoid expensive file scanning.
32+
pub struct LoopedDecoder<R: Read + Seek> {
33+
pub(super) inner: Option<DecoderImpl<R>>,
34+
pub(super) settings: Settings,
35+
cached_duration: Option<Duration>,
36+
}
37+
38+
impl<R> LoopedDecoder<R>
39+
where
40+
R: Read + Seek,
41+
{
42+
pub(super) fn new(decoder: DecoderImpl<R>, settings: Settings) -> Self {
43+
Self {
44+
inner: Some(decoder),
45+
settings,
46+
cached_duration: None,
47+
}
48+
}
49+
50+
/// Recreates decoder with cached metadata to avoid expensive file scanning.
51+
fn recreate_decoder_with_cache(
52+
&mut self,
53+
decoder: DecoderImpl<R>,
54+
) -> Option<(DecoderImpl<R>, Option<Sample>)> {
55+
let mut fast_settings = self.settings.clone();
56+
fast_settings.total_duration = self.cached_duration;
57+
58+
let (new_decoder, sample) = match decoder {
59+
#[cfg(feature = "hound")]
60+
DecoderImpl::Wav(source) => {
61+
let mut reader = source.into_inner();
62+
reader.rewind().ok()?;
63+
let mut source = wav::WavDecoder::new_with_settings(reader, &fast_settings).ok()?;
64+
let sample = source.next();
65+
(DecoderImpl::Wav(source), sample)
66+
}
67+
#[cfg(feature = "lewton")]
68+
DecoderImpl::Vorbis(source) => {
69+
let mut reader = source.into_inner().into_inner().into_inner();
70+
reader.rewind().ok()?;
71+
let mut source =
72+
vorbis::VorbisDecoder::new_with_settings(reader, &fast_settings).ok()?;
73+
let sample = source.next();
74+
(DecoderImpl::Vorbis(source), sample)
75+
}
76+
#[cfg(feature = "claxon")]
77+
DecoderImpl::Flac(source) => {
78+
let mut reader = source.into_inner();
79+
reader.rewind().ok()?;
80+
let mut source =
81+
flac::FlacDecoder::new_with_settings(reader, &fast_settings).ok()?;
82+
let sample = source.next();
83+
(DecoderImpl::Flac(source), sample)
84+
}
85+
#[cfg(feature = "minimp3")]
86+
DecoderImpl::Mp3(source) => {
87+
let mut reader = source.into_inner();
88+
reader.rewind().ok()?;
89+
let mut source = mp3::Mp3Decoder::new_with_settings(reader, &fast_settings).ok()?;
90+
let sample = source.next();
91+
(DecoderImpl::Mp3(source), sample)
92+
}
93+
#[cfg(feature = "symphonia")]
94+
DecoderImpl::Symphonia(source, PhantomData) => {
95+
let mut reader = source.into_inner();
96+
reader.rewind().ok()?;
97+
let mut source =
98+
symphonia::SymphoniaDecoder::new_with_settings(reader, &fast_settings).ok()?;
99+
let sample = source.next();
100+
(DecoderImpl::Symphonia(source, PhantomData), sample)
101+
}
102+
DecoderImpl::None(_, _) => return None,
103+
};
104+
Some((new_decoder, sample))
105+
}
106+
}
107+
108+
impl<R> Iterator for LoopedDecoder<R>
109+
where
110+
R: Read + Seek,
111+
{
112+
type Item = Sample;
113+
114+
fn next(&mut self) -> Option<Self::Item> {
115+
if let Some(inner) = &mut self.inner {
116+
if let Some(sample) = inner.next() {
117+
return Some(sample);
118+
}
119+
120+
// Cache duration on first loop to avoid recalculation
121+
if self.cached_duration.is_none() {
122+
self.cached_duration = inner.total_duration();
123+
}
124+
125+
// Fast gapless seeking when available
126+
if self.settings.gapless
127+
&& self.settings.is_seekable
128+
&& inner.try_seek(Duration::ZERO).is_ok()
129+
{
130+
return inner.next();
131+
}
132+
133+
// Recreation fallback with cached metadata
134+
let decoder = self.inner.take()?;
135+
let (new_decoder, sample) = self.recreate_decoder_with_cache(decoder)?;
136+
self.inner = Some(new_decoder);
137+
sample
138+
} else {
139+
None
140+
}
141+
}
142+
143+
fn size_hint(&self) -> (usize, Option<usize>) {
144+
(
145+
self.inner.as_ref().map_or(0, |inner| inner.size_hint().0),
146+
None, // Infinite
147+
)
148+
}
149+
}
150+
151+
impl<R> Source for LoopedDecoder<R>
152+
where
153+
R: Read + Seek,
154+
{
155+
fn current_span_len(&self) -> Option<usize> {
156+
self.inner.as_ref()?.current_span_len()
157+
}
158+
159+
fn channels(&self) -> ChannelCount {
160+
self.inner.as_ref().map_or(nz!(1), |inner| inner.channels())
161+
}
162+
163+
fn sample_rate(&self) -> SampleRate {
164+
self.inner
165+
.as_ref()
166+
.map_or(nz!(44100), |inner| inner.sample_rate())
167+
}
168+
169+
/// Always returns `None` since looped decoders have no fixed end.
170+
fn total_duration(&self) -> Option<Duration> {
171+
None
172+
}
173+
174+
fn bits_per_sample(&self) -> Option<BitDepth> {
175+
self.inner.as_ref()?.bits_per_sample()
176+
}
177+
178+
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
179+
match &mut self.inner {
180+
Some(inner) => inner.try_seek(pos),
181+
None => Err(SeekError::Other(Arc::new(DecoderError::IoError(
182+
"Looped source ended when it failed to loop back".to_string(),
183+
)))),
184+
}
185+
}
186+
}

0 commit comments

Comments
 (0)