rodio/decoder/mod.rs
1//! Decodes audio samples from various audio file formats.
2//!
3//! This module provides decoders for common audio formats like MP3, WAV, Vorbis and FLAC.
4//! It supports both one-shot playback and looped playback of audio files.
5//!
6//! # Usage
7//!
8//! The simplest way to decode files (automatically sets up seeking and duration):
9//! ```no_run
10//! use std::fs::File;
11//! use rodio::Decoder;
12//!
13//! let file = File::open("audio.mp3").unwrap();
14//! let decoder = Decoder::try_from(file).unwrap(); // Automatically sets byte_len from metadata
15//! ```
16//!
17//! For more control over decoder settings, use the builder pattern:
18//! ```no_run
19//! use std::fs::File;
20//! use rodio::Decoder;
21//!
22//! let file = File::open("audio.mp3").unwrap();
23//! let len = file.metadata().unwrap().len();
24//!
25//! let decoder = Decoder::builder()
26//! .with_data(file)
27//! .with_byte_len(len) // Enable seeking and duration calculation
28//! .with_seekable(true) // Enable seeking operations
29//! .with_hint("mp3") // Optional format hint
30//! .with_gapless(true) // Enable gapless playback
31//! .build()
32//! .unwrap();
33//! ```
34//!
35//! # Features
36//!
37//! The following audio formats are supported based on enabled features:
38//!
39//! - `wav` - WAV format support
40//! - `flac` - FLAC format support
41//! - `vorbis` - Vorbis format support
42//! - `mp3` - MP3 format support via minimp3
43//! - `symphonia` - Enhanced format support via the Symphonia backend
44//!
45//! When using `symphonia`, additional formats like AAC and MP4 containers become available
46//! if the corresponding features are enabled.
47
48use std::{
49 error::Error,
50 fmt,
51 io::{BufReader, Read, Seek},
52 marker::PhantomData,
53 time::Duration,
54};
55
56#[allow(unused_imports)]
57use std::io::SeekFrom;
58
59use crate::{
60 common::{ChannelCount, SampleRate},
61 source::{SeekError, Source},
62 Sample,
63};
64
65pub mod builder;
66pub use builder::{DecoderBuilder, Settings};
67
68#[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
69mod flac;
70#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
71mod mp3;
72#[cfg(feature = "symphonia")]
73mod read_seek_source;
74#[cfg(feature = "symphonia")]
75/// Symphonia decoders types
76pub mod symphonia;
77#[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
78mod vorbis;
79#[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
80mod wav;
81
82/// Source of audio samples decoded from an input stream.
83/// See the [module-level documentation](self) for examples and usage.
84pub struct Decoder<R: Read + Seek>(DecoderImpl<R>);
85
86/// Source of audio samples from decoding a file that never ends.
87/// When the end of the file is reached, the decoder starts again from the beginning.
88///
89/// A `LoopedDecoder` will attempt to seek back to the start of the stream when it reaches
90/// the end. If seeking fails for any reason (like IO errors), iteration will stop.
91///
92/// # Examples
93///
94/// ```no_run
95/// use std::fs::File;
96/// use rodio::Decoder;
97///
98/// let file = File::open("audio.mp3").unwrap();
99/// let looped_decoder = Decoder::new_looped(file).unwrap();
100/// ```
101pub struct LoopedDecoder<R: Read + Seek> {
102 /// The underlying decoder implementation.
103 inner: Option<DecoderImpl<R>>,
104 /// Configuration settings for the decoder.
105 settings: Settings,
106}
107
108// Cannot really reduce the size of the VorbisDecoder. There are not any
109// arrays just a lot of struct fields.
110#[allow(clippy::large_enum_variant)]
111enum DecoderImpl<R: Read + Seek> {
112 #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
113 Wav(wav::WavDecoder<R>),
114 #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
115 Vorbis(vorbis::VorbisDecoder<R>),
116 #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
117 Flac(flac::FlacDecoder<R>),
118 #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
119 Mp3(mp3::Mp3Decoder<R>),
120 #[cfg(feature = "symphonia")]
121 Symphonia(symphonia::SymphoniaDecoder, PhantomData<R>),
122 // This variant is here just to satisfy the compiler when there are no decoders enabled.
123 // It is unreachable and should never be constructed.
124 #[allow(dead_code)]
125 None(Unreachable, PhantomData<R>),
126}
127
128enum Unreachable {}
129
130impl<R: Read + Seek> DecoderImpl<R> {
131 #[inline]
132 fn next(&mut self) -> Option<Sample> {
133 match self {
134 #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
135 DecoderImpl::Wav(source) => source.next(),
136 #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
137 DecoderImpl::Vorbis(source) => source.next(),
138 #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
139 DecoderImpl::Flac(source) => source.next(),
140 #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
141 DecoderImpl::Mp3(source) => source.next(),
142 #[cfg(feature = "symphonia")]
143 DecoderImpl::Symphonia(source, PhantomData) => source.next(),
144 DecoderImpl::None(_, _) => unreachable!(),
145 }
146 }
147
148 #[inline]
149 fn size_hint(&self) -> (usize, Option<usize>) {
150 match self {
151 #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
152 DecoderImpl::Wav(source) => source.size_hint(),
153 #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
154 DecoderImpl::Vorbis(source) => source.size_hint(),
155 #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
156 DecoderImpl::Flac(source) => source.size_hint(),
157 #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
158 DecoderImpl::Mp3(source) => source.size_hint(),
159 #[cfg(feature = "symphonia")]
160 DecoderImpl::Symphonia(source, PhantomData) => source.size_hint(),
161 DecoderImpl::None(_, _) => unreachable!(),
162 }
163 }
164
165 #[inline]
166 fn current_span_len(&self) -> Option<usize> {
167 match self {
168 #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
169 DecoderImpl::Wav(source) => source.current_span_len(),
170 #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
171 DecoderImpl::Vorbis(source) => source.current_span_len(),
172 #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
173 DecoderImpl::Flac(source) => source.current_span_len(),
174 #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
175 DecoderImpl::Mp3(source) => source.current_span_len(),
176 #[cfg(feature = "symphonia")]
177 DecoderImpl::Symphonia(source, PhantomData) => source.current_span_len(),
178 DecoderImpl::None(_, _) => unreachable!(),
179 }
180 }
181
182 #[inline]
183 fn channels(&self) -> ChannelCount {
184 match self {
185 #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
186 DecoderImpl::Wav(source) => source.channels(),
187 #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
188 DecoderImpl::Vorbis(source) => source.channels(),
189 #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
190 DecoderImpl::Flac(source) => source.channels(),
191 #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
192 DecoderImpl::Mp3(source) => source.channels(),
193 #[cfg(feature = "symphonia")]
194 DecoderImpl::Symphonia(source, PhantomData) => source.channels(),
195 DecoderImpl::None(_, _) => unreachable!(),
196 }
197 }
198
199 #[inline]
200 fn sample_rate(&self) -> SampleRate {
201 match self {
202 #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
203 DecoderImpl::Wav(source) => source.sample_rate(),
204 #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
205 DecoderImpl::Vorbis(source) => source.sample_rate(),
206 #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
207 DecoderImpl::Flac(source) => source.sample_rate(),
208 #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
209 DecoderImpl::Mp3(source) => source.sample_rate(),
210 #[cfg(feature = "symphonia")]
211 DecoderImpl::Symphonia(source, PhantomData) => source.sample_rate(),
212 DecoderImpl::None(_, _) => unreachable!(),
213 }
214 }
215
216 /// Returns the total duration of this audio source.
217 ///
218 /// # Symphonia Notes
219 ///
220 /// For formats that lack timing information like MP3 and Vorbis, this requires the decoder to
221 /// be initialized with the correct byte length via `Decoder::builder().with_byte_len()`.
222 #[inline]
223 fn total_duration(&self) -> Option<Duration> {
224 match self {
225 #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
226 DecoderImpl::Wav(source) => source.total_duration(),
227 #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
228 DecoderImpl::Vorbis(source) => source.total_duration(),
229 #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
230 DecoderImpl::Flac(source) => source.total_duration(),
231 #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
232 DecoderImpl::Mp3(source) => source.total_duration(),
233 #[cfg(feature = "symphonia")]
234 DecoderImpl::Symphonia(source, PhantomData) => source.total_duration(),
235 DecoderImpl::None(_, _) => unreachable!(),
236 }
237 }
238
239 #[inline]
240 fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
241 match self {
242 #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
243 DecoderImpl::Wav(source) => source.try_seek(pos),
244 #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
245 DecoderImpl::Vorbis(source) => source.try_seek(pos),
246 #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
247 DecoderImpl::Flac(source) => source.try_seek(pos),
248 #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
249 DecoderImpl::Mp3(source) => source.try_seek(pos),
250 #[cfg(feature = "symphonia")]
251 DecoderImpl::Symphonia(source, PhantomData) => source.try_seek(pos),
252 DecoderImpl::None(_, _) => unreachable!(),
253 }
254 }
255}
256
257/// Converts a `File` into a `Decoder` with automatic optimizations.
258/// This is the preferred way to decode files as it enables seeking optimizations
259/// and accurate duration calculations.
260///
261/// This implementation:
262/// - Wraps the file in a `BufReader` for better performance
263/// - Gets the file length from metadata to improve seeking operations and duration accuracy
264/// - Enables seeking by default
265///
266/// # Errors
267///
268/// Returns an error if:
269/// - The file metadata cannot be read
270/// - The audio format cannot be recognized or is not supported
271///
272/// # Examples
273/// ```no_run
274/// use std::fs::File;
275/// use rodio::Decoder;
276///
277/// let file = File::open("audio.mp3").unwrap();
278/// let decoder = Decoder::try_from(file).unwrap();
279/// ```
280impl TryFrom<std::fs::File> for Decoder<BufReader<std::fs::File>> {
281 type Error = DecoderError;
282
283 fn try_from(file: std::fs::File) -> Result<Self, Self::Error> {
284 let len = file
285 .metadata()
286 .map_err(|e| Self::Error::IoError(e.to_string()))?
287 .len();
288
289 Self::builder()
290 .with_data(BufReader::new(file))
291 .with_byte_len(len)
292 .with_seekable(true)
293 .build()
294 }
295}
296
297/// Converts a `BufReader` into a `Decoder`.
298/// When working with files, prefer `TryFrom<File>` as it will automatically set byte_len
299/// for better seeking performance.
300///
301/// # Errors
302///
303/// Returns `DecoderError::UnrecognizedFormat` if the audio format could not be determined
304/// or is not supported.
305///
306/// # Examples
307/// ```no_run
308/// use std::fs::File;
309/// use std::io::BufReader;
310/// use rodio::Decoder;
311///
312/// let file = File::open("audio.mp3").unwrap();
313/// let reader = BufReader::new(file);
314/// let decoder = Decoder::try_from(reader).unwrap();
315/// ```
316impl<R> TryFrom<BufReader<R>> for Decoder<BufReader<R>>
317where
318 R: Read + Seek + Send + Sync + 'static,
319{
320 type Error = DecoderError;
321
322 fn try_from(data: BufReader<R>) -> Result<Self, Self::Error> {
323 Self::new(data)
324 }
325}
326
327/// Converts a `Cursor` into a `Decoder`.
328/// When working with files, prefer `TryFrom<File>` as it will automatically set byte_len
329/// for better seeking performance.
330///
331/// This is useful for decoding audio data that's already in memory.
332///
333/// # Errors
334///
335/// Returns `DecoderError::UnrecognizedFormat` if the audio format could not be determined
336/// or is not supported.
337///
338/// # Examples
339/// ```no_run
340/// use std::io::Cursor;
341/// use rodio::Decoder;
342///
343/// let data = std::fs::read("audio.mp3").unwrap();
344/// let cursor = Cursor::new(data);
345/// let decoder = Decoder::try_from(cursor).unwrap();
346/// ```
347impl<T> TryFrom<std::io::Cursor<T>> for Decoder<std::io::Cursor<T>>
348where
349 T: AsRef<[u8]> + Send + Sync + 'static,
350{
351 type Error = DecoderError;
352
353 fn try_from(data: std::io::Cursor<T>) -> Result<Self, Self::Error> {
354 Self::new(data)
355 }
356}
357
358impl<R: Read + Seek + Send + Sync + 'static> Decoder<R> {
359 /// Returns a builder for creating a new decoder with customizable settings.
360 ///
361 /// # Examples
362 /// ```no_run
363 /// use std::fs::File;
364 /// use rodio::Decoder;
365 ///
366 /// let file = File::open("audio.mp3").unwrap();
367 /// let decoder = Decoder::builder()
368 /// .with_data(file)
369 /// .with_hint("mp3")
370 /// .with_gapless(true)
371 /// .build()
372 /// .unwrap();
373 /// ```
374 pub fn builder() -> DecoderBuilder<R> {
375 DecoderBuilder::new()
376 }
377
378 /// Builds a new decoder with default settings.
379 ///
380 /// Attempts to automatically detect the format of the source of data.
381 ///
382 /// # Errors
383 ///
384 /// Returns `DecoderError::UnrecognizedFormat` if the audio format could not be determined
385 /// or is not supported.
386 pub fn new(data: R) -> Result<Self, DecoderError> {
387 DecoderBuilder::new().with_data(data).build()
388 }
389
390 /// Builds a new looped decoder with default settings.
391 ///
392 /// Attempts to automatically detect the format of the source of data.
393 /// The decoder will restart from the beginning when it reaches the end.
394 ///
395 /// # Errors
396 ///
397 /// Returns `DecoderError::UnrecognizedFormat` if the audio format could not be determined
398 /// or is not supported.
399 pub fn new_looped(data: R) -> Result<LoopedDecoder<R>, DecoderError> {
400 DecoderBuilder::new().with_data(data).build_looped()
401 }
402
403 /// Builds a new decoder with WAV format hint.
404 ///
405 /// This method provides a hint that the data is WAV format, which may help the decoder
406 /// identify the format more quickly. However, if WAV decoding fails, other formats
407 /// will still be attempted.
408 ///
409 /// # Errors
410 ///
411 /// Returns `DecoderError::UnrecognizedFormat` if no suitable decoder was found.
412 ///
413 /// # Examples
414 /// ```no_run
415 /// use rodio::Decoder;
416 /// use std::fs::File;
417 ///
418 /// let file = File::open("audio.wav").unwrap();
419 /// let decoder = Decoder::new_wav(file).unwrap();
420 /// ```
421 #[cfg(any(feature = "hound", feature = "symphonia-wav"))]
422 pub fn new_wav(data: R) -> Result<Self, DecoderError> {
423 DecoderBuilder::new()
424 .with_data(data)
425 .with_hint("wav")
426 .build()
427 }
428
429 /// Builds a new decoder with FLAC format hint.
430 ///
431 /// This method provides a hint that the data is FLAC format, which may help the decoder
432 /// identify the format more quickly. However, if FLAC decoding fails, other formats
433 /// will still be attempted.
434 ///
435 /// # Errors
436 ///
437 /// Returns `DecoderError::UnrecognizedFormat` if no suitable decoder was found.
438 ///
439 /// # Examples
440 /// ```no_run
441 /// use rodio::Decoder;
442 /// use std::fs::File;
443 ///
444 /// let file = File::open("audio.flac").unwrap();
445 /// let decoder = Decoder::new_flac(file).unwrap();
446 /// ```
447 #[cfg(any(feature = "claxon", feature = "symphonia-flac"))]
448 pub fn new_flac(data: R) -> Result<Self, DecoderError> {
449 DecoderBuilder::new()
450 .with_data(data)
451 .with_hint("flac")
452 .build()
453 }
454
455 /// Builds a new decoder with Vorbis format hint.
456 ///
457 /// This method provides a hint that the data is Vorbis format, which may help the decoder
458 /// identify the format more quickly. However, if Vorbis decoding fails, other formats
459 /// will still be attempted.
460 ///
461 /// # Errors
462 ///
463 /// Returns `DecoderError::UnrecognizedFormat` if no suitable decoder was found.
464 ///
465 /// # Examples
466 /// ```no_run
467 /// use rodio::Decoder;
468 /// use std::fs::File;
469 ///
470 /// let file = File::open("audio.ogg").unwrap();
471 /// let decoder = Decoder::new_vorbis(file).unwrap();
472 /// ```
473 #[cfg(any(feature = "lewton", feature = "symphonia-vorbis"))]
474 pub fn new_vorbis(data: R) -> Result<Self, DecoderError> {
475 DecoderBuilder::new()
476 .with_data(data)
477 .with_hint("ogg")
478 .build()
479 }
480
481 /// Builds a new decoder with MP3 format hint.
482 ///
483 /// This method provides a hint that the data is MP3 format, which may help the decoder
484 /// identify the format more quickly. However, if MP3 decoding fails, other formats
485 /// will still be attempted.
486 ///
487 /// # Errors
488 ///
489 /// Returns `DecoderError::UnrecognizedFormat` if no suitable decoder was found.
490 ///
491 /// # Examples
492 /// ```no_run
493 /// use rodio::Decoder;
494 /// use std::fs::File;
495 ///
496 /// let file = File::open("audio.mp3").unwrap();
497 /// let decoder = Decoder::new_mp3(file).unwrap();
498 /// ```
499 #[cfg(any(feature = "minimp3", feature = "symphonia-mp3"))]
500 pub fn new_mp3(data: R) -> Result<Self, DecoderError> {
501 DecoderBuilder::new()
502 .with_data(data)
503 .with_hint("mp3")
504 .build()
505 }
506
507 /// Builds a new decoder with AAC format hint.
508 ///
509 /// This method provides a hint that the data is AAC format, which may help the decoder
510 /// identify the format more quickly. However, if AAC decoding fails, other formats
511 /// will still be attempted.
512 ///
513 /// # Errors
514 ///
515 /// Returns `DecoderError::UnrecognizedFormat` if no suitable decoder was found.
516 ///
517 /// # Examples
518 /// ```no_run
519 /// use rodio::Decoder;
520 /// use std::fs::File;
521 ///
522 /// let file = File::open("audio.aac").unwrap();
523 /// let decoder = Decoder::new_aac(file).unwrap();
524 /// ```
525 #[cfg(feature = "symphonia-aac")]
526 pub fn new_aac(data: R) -> Result<Self, DecoderError> {
527 DecoderBuilder::new()
528 .with_data(data)
529 .with_hint("aac")
530 .build()
531 }
532
533 /// Builds a new decoder with MP4 container format hint.
534 ///
535 /// This method provides a hint that the data is in MP4 container format by setting
536 /// the MIME type to "audio/mp4". This may help the decoder identify the format
537 /// more quickly. However, if MP4 decoding fails, other formats will still be attempted.
538 ///
539 /// # Errors
540 ///
541 /// Returns `DecoderError::UnrecognizedFormat` if no suitable decoder was found.
542 ///
543 /// # Examples
544 /// ```no_run
545 /// use rodio::Decoder;
546 /// use std::fs::File;
547 ///
548 /// let file = File::open("audio.m4a").unwrap();
549 /// let decoder = Decoder::new_mp4(file).unwrap();
550 /// ```
551 #[cfg(feature = "symphonia-isomp4")]
552 pub fn new_mp4(data: R) -> Result<Self, DecoderError> {
553 DecoderBuilder::new()
554 .with_data(data)
555 .with_mime_type("audio/mp4")
556 .build()
557 }
558}
559
560impl<R> Iterator for Decoder<R>
561where
562 R: Read + Seek,
563{
564 type Item = Sample;
565
566 #[inline]
567 fn next(&mut self) -> Option<Self::Item> {
568 self.0.next()
569 }
570
571 #[inline]
572 fn size_hint(&self) -> (usize, Option<usize>) {
573 self.0.size_hint()
574 }
575}
576
577impl<R> Source for Decoder<R>
578where
579 R: Read + Seek,
580{
581 #[inline]
582 fn current_span_len(&self) -> Option<usize> {
583 self.0.current_span_len()
584 }
585
586 #[inline]
587 fn channels(&self) -> ChannelCount {
588 self.0.channels()
589 }
590
591 fn sample_rate(&self) -> SampleRate {
592 self.0.sample_rate()
593 }
594
595 #[inline]
596 fn total_duration(&self) -> Option<Duration> {
597 self.0.total_duration()
598 }
599
600 #[inline]
601 fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
602 self.0.try_seek(pos)
603 }
604}
605
606impl<R> Iterator for LoopedDecoder<R>
607where
608 R: Read + Seek,
609{
610 type Item = Sample;
611
612 /// Returns the next sample in the audio stream.
613 ///
614 /// When the end of the stream is reached, attempts to seek back to the start
615 /// and continue playing. If seeking fails, or if no decoder is available,
616 /// returns `None`.
617 fn next(&mut self) -> Option<Self::Item> {
618 if let Some(inner) = &mut self.inner {
619 if let Some(sample) = inner.next() {
620 return Some(sample);
621 }
622
623 // Take ownership of the decoder to reset it
624 let decoder = self.inner.take()?;
625 let (new_decoder, sample) = match decoder {
626 #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
627 DecoderImpl::Wav(source) => {
628 let mut reader = source.into_inner();
629 reader.seek(SeekFrom::Start(0)).ok()?;
630 let mut source = wav::WavDecoder::new(reader).ok()?;
631 let sample = source.next();
632 (DecoderImpl::Wav(source), sample)
633 }
634 #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
635 DecoderImpl::Vorbis(source) => {
636 use lewton::inside_ogg::OggStreamReader;
637 let mut reader = source.into_inner().into_inner();
638 reader.seek_bytes(SeekFrom::Start(0)).ok()?;
639 let mut source = vorbis::VorbisDecoder::from_stream_reader(
640 OggStreamReader::from_ogg_reader(reader).ok()?,
641 );
642 let sample = source.next();
643 (DecoderImpl::Vorbis(source), sample)
644 }
645 #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
646 DecoderImpl::Flac(source) => {
647 let mut reader = source.into_inner();
648 reader.seek(SeekFrom::Start(0)).ok()?;
649 let mut source = flac::FlacDecoder::new(reader).ok()?;
650 let sample = source.next();
651 (DecoderImpl::Flac(source), sample)
652 }
653 #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
654 DecoderImpl::Mp3(source) => {
655 let mut reader = source.into_inner();
656 reader.seek(SeekFrom::Start(0)).ok()?;
657 let mut source = mp3::Mp3Decoder::new(reader).ok()?;
658 let sample = source.next();
659 (DecoderImpl::Mp3(source), sample)
660 }
661 #[cfg(feature = "symphonia")]
662 DecoderImpl::Symphonia(source, PhantomData) => {
663 let mut reader = source.into_inner();
664 reader.seek(SeekFrom::Start(0)).ok()?;
665 let mut source =
666 symphonia::SymphoniaDecoder::new(reader, &self.settings).ok()?;
667 let sample = source.next();
668 (DecoderImpl::Symphonia(source, PhantomData), sample)
669 }
670 };
671 self.inner = Some(new_decoder);
672 sample
673 } else {
674 None
675 }
676 }
677
678 /// Returns the size hint for this iterator.
679 ///
680 /// The lower bound is:
681 /// - The minimum number of samples remaining in the current iteration if there is an active decoder
682 /// - 0 if there is no active decoder (inner is None)
683 ///
684 /// The upper bound is always `None` since the decoder loops indefinitely.
685 /// This differs from non-looped decoders which may provide a finite upper bound.
686 ///
687 /// Note that even with an active decoder, reaching the end of the stream may result
688 /// in the decoder becoming inactive if seeking back to the start fails.
689 #[inline]
690 fn size_hint(&self) -> (usize, Option<usize>) {
691 (
692 self.inner.as_ref().map_or(0, |inner| inner.size_hint().0),
693 None,
694 )
695 }
696}
697
698impl<R> Source for LoopedDecoder<R>
699where
700 R: Read + Seek,
701{
702 /// Returns the current span length of the underlying decoder.
703 ///
704 /// Returns `None` if there is no active decoder.
705 #[inline]
706 fn current_span_len(&self) -> Option<usize> {
707 self.inner.as_ref()?.current_span_len()
708 }
709
710 /// Returns the number of channels in the audio stream.
711 ///
712 /// Returns the default channel count if there is no active decoder.
713 #[inline]
714 fn channels(&self) -> ChannelCount {
715 self.inner
716 .as_ref()
717 .map_or(ChannelCount::default(), |inner| inner.channels())
718 }
719
720 /// Returns the sample rate of the audio stream.
721 ///
722 /// Returns the default sample rate if there is no active decoder.
723 #[inline]
724 fn sample_rate(&self) -> SampleRate {
725 self.inner
726 .as_ref()
727 .map_or(SampleRate::default(), |inner| inner.sample_rate())
728 }
729
730 /// Returns the total duration of this audio source.
731 ///
732 /// Always returns `None` for looped decoders since they have no fixed end point -
733 /// they will continue playing indefinitely by seeking back to the start when reaching
734 /// the end of the audio data.
735 #[inline]
736 fn total_duration(&self) -> Option<Duration> {
737 None
738 }
739
740 /// Attempts to seek to a specific position in the audio stream.
741 ///
742 /// # Errors
743 ///
744 /// Returns `SeekError::NotSupported` if:
745 /// - There is no active decoder
746 /// - The underlying decoder does not support seeking
747 ///
748 /// May also return other `SeekError` variants if the underlying decoder's seek operation fails.
749 ///
750 /// # Note
751 ///
752 /// Even for looped playback, seeking past the end of the stream will not automatically
753 /// wrap around to the beginning - it will return an error just like a normal decoder.
754 /// Looping only occurs when reaching the end through normal playback.
755 fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
756 match &mut self.inner {
757 Some(inner) => inner.try_seek(pos),
758 None => Err(SeekError::Other(Box::new(DecoderError::IoError(
759 "Looped source ended when it failed to loop back".to_string(),
760 )))),
761 }
762 }
763}
764
765/// Errors that can occur when creating a decoder.
766#[derive(Debug, Clone)]
767pub enum DecoderError {
768 /// The format of the data has not been recognized.
769 UnrecognizedFormat,
770
771 /// An IO error occurred while reading, writing, or seeking the stream.
772 IoError(String),
773
774 /// The stream contained malformed data and could not be decoded or demuxed.
775 #[cfg(feature = "symphonia")]
776 DecodeError(&'static str),
777
778 /// A default or user-defined limit was reached while decoding or demuxing the stream. Limits
779 /// are used to prevent denial-of-service attacks from malicious streams.
780 #[cfg(feature = "symphonia")]
781 LimitError(&'static str),
782
783 /// The demuxer or decoder needs to be reset before continuing.
784 #[cfg(feature = "symphonia")]
785 ResetRequired,
786
787 /// No streams were found by the decoder.
788 #[cfg(feature = "symphonia")]
789 NoStreams,
790}
791
792impl fmt::Display for DecoderError {
793 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
794 let text = match self {
795 DecoderError::UnrecognizedFormat => "Unrecognized format",
796 DecoderError::IoError(msg) => &msg[..],
797 #[cfg(feature = "symphonia")]
798 DecoderError::DecodeError(msg) => msg,
799 #[cfg(feature = "symphonia")]
800 DecoderError::LimitError(msg) => msg,
801 #[cfg(feature = "symphonia")]
802 DecoderError::ResetRequired => "Reset required",
803 #[cfg(feature = "symphonia")]
804 DecoderError::NoStreams => "No streams",
805 };
806 write!(f, "{text}")
807 }
808}
809
810impl Error for DecoderError {}