Skip to main content

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    io::{BufReader, Read, Seek},
50    marker::PhantomData,
51    sync::Arc,
52    time::Duration,
53};
54
55#[allow(unused_imports)]
56use std::io::SeekFrom;
57
58use crate::{
59    common::{assert_error_traits, ChannelCount, SampleRate},
60    math::nz,
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.as_ref().map_or(nz!(1), |inner| inner.channels())
716    }
717
718    /// Returns the sample rate of the audio stream.
719    ///
720    /// Returns the default sample rate if there is no active decoder.
721    #[inline]
722    fn sample_rate(&self) -> SampleRate {
723        self.inner
724            .as_ref()
725            .map_or(nz!(44100), |inner| inner.sample_rate())
726    }
727
728    /// Returns the total duration of this audio source.
729    ///
730    /// Always returns `None` for looped decoders since they have no fixed end point -
731    /// they will continue playing indefinitely by seeking back to the start when reaching
732    /// the end of the audio data.
733    #[inline]
734    fn total_duration(&self) -> Option<Duration> {
735        None
736    }
737
738    /// Attempts to seek to a specific position in the audio stream.
739    ///
740    /// # Errors
741    ///
742    /// Returns `SeekError::NotSupported` if:
743    /// - There is no active decoder
744    /// - The underlying decoder does not support seeking
745    ///
746    /// May also return other `SeekError` variants if the underlying decoder's seek operation fails.
747    ///
748    /// # Note
749    ///
750    /// Even for looped playback, seeking past the end of the stream will not automatically
751    /// wrap around to the beginning - it will return an error just like a normal decoder.
752    /// Looping only occurs when reaching the end through normal playback.
753    fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
754        match &mut self.inner {
755            Some(inner) => inner.try_seek(pos),
756            None => Err(SeekError::Other(Arc::new(DecoderError::IoError(
757                "Looped source ended when it failed to loop back".to_string(),
758            )))),
759        }
760    }
761}
762
763/// Errors that can occur when creating a decoder.
764#[derive(Debug, thiserror::Error, Clone)]
765pub enum DecoderError {
766    /// The format of the data has not been recognized.
767    #[error("The format of the data has not been recognized.")]
768    UnrecognizedFormat,
769
770    /// An IO error occurred while reading, writing, or seeking the stream.
771    #[error("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    #[error("The stream contained malformed data and could not be decoded or demuxed: {0}")]
776    #[cfg(feature = "symphonia")]
777    DecodeError(&'static str),
778
779    /// A default or user-defined limit was reached while decoding or demuxing
780    /// the stream. Limits are used to prevent denial-of-service attacks from
781    /// malicious streams.
782    #[error(
783        "A default or user-defined limit was reached while decoding or demuxing the stream: {0}"
784    )]
785    #[cfg(feature = "symphonia")]
786    LimitError(&'static str),
787
788    /// The demuxer or decoder needs to be reset before continuing.
789    #[error("The demuxer or decoder needs to be reset before continuing.")]
790    #[cfg(feature = "symphonia")]
791    ResetRequired,
792
793    /// No streams were found by the decoder.
794    #[error("No streams were found by the decoder.")]
795    #[cfg(feature = "symphonia")]
796    NoStreams,
797}
798assert_error_traits!(DecoderError);