remu_audio/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    time::Duration,
52};
53
54#[allow(unused_imports)]
55use std::io::SeekFrom;
56
57use rodio::{
58    decoder::DecoderError,
59    source::{SeekError, Source},
60    ChannelCount, Sample, SampleRate,
61};
62
63pub mod builder;
64pub use builder::{DecoderBuilder, Settings};
65
66mod read_seek_source;
67/// Symphonia decoders types
68pub mod symphonia;
69
70/// Source of audio samples decoded from an input stream.
71/// See the [module-level documentation](self) for examples and usage.
72pub struct Decoder<R: Read + Seek>(DecoderImpl<R>);
73
74/// Source of audio samples from decoding a file that never ends.
75/// When the end of the file is reached, the decoder starts again from the beginning.
76///
77/// A `LoopedDecoder` will attempt to seek back to the start of the stream when it reaches
78/// the end. If seeking fails for any reason (like IO errors), iteration will stop.
79///
80/// # Examples
81///
82/// ```no_run
83/// use std::fs::File;
84/// use rodio::Decoder;
85///
86/// let file = File::open("audio.mp3").unwrap();
87/// let looped_decoder = Decoder::new_looped(file).unwrap();
88/// ```
89pub struct LoopedDecoder<R: Read + Seek> {
90    /// The underlying decoder implementation.
91    inner: Option<DecoderImpl<R>>,
92    /// Configuration settings for the decoder.
93    settings: Settings,
94}
95
96// Cannot really reduce the size of the VorbisDecoder. There are not any
97// arrays just a lot of struct fields.
98#[allow(clippy::large_enum_variant)]
99enum DecoderImpl<R: Read + Seek> {
100    Symphonia(symphonia::SymphoniaDecoder, PhantomData<R>),
101    // This variant is here just to satisfy the compiler when there are no decoders enabled.
102    // It is unreachable and should never be constructed.
103    #[allow(dead_code)]
104    None(Unreachable, PhantomData<R>),
105}
106
107enum Unreachable {}
108
109impl<R: Read + Seek> DecoderImpl<R> {
110    #[inline]
111    fn next(&mut self) -> Option<Sample> {
112        match self {
113            DecoderImpl::Symphonia(source, PhantomData) => source.next(),
114            DecoderImpl::None(_, _) => unreachable!(),
115        }
116    }
117
118    #[inline]
119    fn size_hint(&self) -> (usize, Option<usize>) {
120        match self {
121            DecoderImpl::Symphonia(source, PhantomData) => source.size_hint(),
122            DecoderImpl::None(_, _) => unreachable!(),
123        }
124    }
125
126    #[inline]
127    fn current_span_len(&self) -> Option<usize> {
128        match self {
129            DecoderImpl::Symphonia(source, PhantomData) => source.current_span_len(),
130            DecoderImpl::None(_, _) => unreachable!(),
131        }
132    }
133
134    #[inline]
135    fn channels(&self) -> ChannelCount {
136        match self {
137            DecoderImpl::Symphonia(source, PhantomData) => source.channels(),
138            DecoderImpl::None(_, _) => unreachable!(),
139        }
140    }
141
142    #[inline]
143    fn sample_rate(&self) -> SampleRate {
144        match self {
145            DecoderImpl::Symphonia(source, PhantomData) => source.sample_rate(),
146            DecoderImpl::None(_, _) => unreachable!(),
147        }
148    }
149
150    /// Returns the total duration of this audio source.
151    ///
152    /// # Symphonia Notes
153    ///
154    /// For formats that lack timing information like MP3 and Vorbis, this requires the decoder to
155    /// be initialized with the correct byte length via `Decoder::builder().with_byte_len()`.
156    #[inline]
157    fn total_duration(&self) -> Option<Duration> {
158        match self {
159            DecoderImpl::Symphonia(source, PhantomData) => source.total_duration(),
160            DecoderImpl::None(_, _) => unreachable!(),
161        }
162    }
163
164    #[inline]
165    fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
166        match self {
167            DecoderImpl::Symphonia(source, PhantomData) => source.try_seek(pos),
168            DecoderImpl::None(_, _) => unreachable!(),
169        }
170    }
171}
172
173/// Converts a `File` into a `Decoder` with automatic optimizations.
174/// This is the preferred way to decode files as it enables seeking optimizations
175/// and accurate duration calculations.
176///
177/// This implementation:
178/// - Wraps the file in a `BufReader` for better performance
179/// - Gets the file length from metadata to improve seeking operations and duration accuracy
180/// - Enables seeking by default
181///
182/// # Errors
183///
184/// Returns an error if:
185/// - The file metadata cannot be read
186/// - The audio format cannot be recognized or is not supported
187///
188/// # Examples
189/// ```no_run
190/// use std::fs::File;
191/// use rodio::Decoder;
192///
193/// let file = File::open("audio.mp3").unwrap();
194/// let decoder = Decoder::try_from(file).unwrap();
195/// ```
196impl TryFrom<std::fs::File> for Decoder<BufReader<std::fs::File>> {
197    type Error = DecoderError;
198
199    fn try_from(file: std::fs::File) -> Result<Self, Self::Error> {
200        let len = file
201            .metadata()
202            .map_err(|e| Self::Error::IoError(e.to_string()))?
203            .len();
204
205        Self::builder()
206            .with_data(BufReader::new(file))
207            .with_byte_len(len)
208            .with_seekable(true)
209            .build()
210    }
211}
212
213/// Converts a `BufReader` into a `Decoder`.
214/// When working with files, prefer `TryFrom<File>` as it will automatically set byte_len
215/// for better seeking performance.
216///
217/// # Errors
218///
219/// Returns `DecoderError::UnrecognizedFormat` if the audio format could not be determined
220/// or is not supported.
221///
222/// # Examples
223/// ```no_run
224/// use std::fs::File;
225/// use std::io::BufReader;
226/// use rodio::Decoder;
227///
228/// let file = File::open("audio.mp3").unwrap();
229/// let reader = BufReader::new(file);
230/// let decoder = Decoder::try_from(reader).unwrap();
231/// ```
232impl<R> TryFrom<BufReader<R>> for Decoder<BufReader<R>>
233where
234    R: Read + Seek + Send + Sync + 'static,
235{
236    type Error = DecoderError;
237
238    fn try_from(data: BufReader<R>) -> Result<Self, Self::Error> {
239        Self::new(data)
240    }
241}
242
243/// Converts a `Cursor` into a `Decoder`.
244/// When working with files, prefer `TryFrom<File>` as it will automatically set byte_len
245/// for better seeking performance.
246///
247/// This is useful for decoding audio data that's already in memory.
248///
249/// # Errors
250///
251/// Returns `DecoderError::UnrecognizedFormat` if the audio format could not be determined
252/// or is not supported.
253///
254/// # Examples
255/// ```no_run
256/// use std::io::Cursor;
257/// use rodio::Decoder;
258///
259/// let data = std::fs::read("audio.mp3").unwrap();
260/// let cursor = Cursor::new(data);
261/// let decoder = Decoder::try_from(cursor).unwrap();
262/// ```
263impl<T> TryFrom<std::io::Cursor<T>> for Decoder<std::io::Cursor<T>>
264where
265    T: AsRef<[u8]> + Send + Sync + 'static,
266{
267    type Error = DecoderError;
268
269    fn try_from(data: std::io::Cursor<T>) -> Result<Self, Self::Error> {
270        Self::new(data)
271    }
272}
273
274impl<R: Read + Seek + Send + Sync + 'static> Decoder<R> {
275    /// Returns a builder for creating a new decoder with customizable settings.
276    ///
277    /// # Examples
278    /// ```no_run
279    /// use std::fs::File;
280    /// use rodio::Decoder;
281    ///
282    /// let file = File::open("audio.mp3").unwrap();
283    /// let decoder = Decoder::builder()
284    ///     .with_data(file)
285    ///     .with_hint("mp3")
286    ///     .with_gapless(true)
287    ///     .build()
288    ///     .unwrap();
289    /// ```
290    pub fn builder() -> DecoderBuilder<R> {
291        DecoderBuilder::new()
292    }
293
294    /// Builds a new decoder with default settings.
295    ///
296    /// Attempts to automatically detect the format of the source of data.
297    ///
298    /// # Errors
299    ///
300    /// Returns `DecoderError::UnrecognizedFormat` if the audio format could not be determined
301    /// or is not supported.
302    pub fn new(data: R) -> Result<Self, DecoderError> {
303        DecoderBuilder::new().with_data(data).build()
304    }
305
306    /// Builds a new looped decoder with default settings.
307    ///
308    /// Attempts to automatically detect the format of the source of data.
309    /// The decoder will restart from the beginning when it reaches the end.
310    ///
311    /// # Errors
312    ///
313    /// Returns `DecoderError::UnrecognizedFormat` if the audio format could not be determined
314    /// or is not supported.
315    pub fn new_looped(data: R) -> Result<LoopedDecoder<R>, DecoderError> {
316        DecoderBuilder::new().with_data(data).build_looped()
317    }
318
319    /// Builds a new decoder with WAV format hint.
320    ///
321    /// This method provides a hint that the data is WAV format, which may help the decoder
322    /// identify the format more quickly. However, if WAV decoding fails, other formats
323    /// will still be attempted.
324    ///
325    /// # Errors
326    ///
327    /// Returns `DecoderError::UnrecognizedFormat` if no suitable decoder was found.
328    ///
329    /// # Examples
330    /// ```no_run
331    /// use rodio::Decoder;
332    /// use std::fs::File;
333    ///
334    /// let file = File::open("audio.wav").unwrap();
335    /// let decoder = Decoder::new_wav(file).unwrap();
336    /// ```
337    pub fn new_wav(data: R) -> Result<Self, DecoderError> {
338        DecoderBuilder::new()
339            .with_data(data)
340            .with_hint("wav")
341            .build()
342    }
343
344    /// Builds a new decoder with FLAC format hint.
345    ///
346    /// This method provides a hint that the data is FLAC format, which may help the decoder
347    /// identify the format more quickly. However, if FLAC decoding fails, other formats
348    /// will still be attempted.
349    ///
350    /// # Errors
351    ///
352    /// Returns `DecoderError::UnrecognizedFormat` if no suitable decoder was found.
353    ///
354    /// # Examples
355    /// ```no_run
356    /// use rodio::Decoder;
357    /// use std::fs::File;
358    ///
359    /// let file = File::open("audio.flac").unwrap();
360    /// let decoder = Decoder::new_flac(file).unwrap();
361    /// ```
362    pub fn new_flac(data: R) -> Result<Self, DecoderError> {
363        DecoderBuilder::new()
364            .with_data(data)
365            .with_hint("flac")
366            .build()
367    }
368
369    /// Builds a new decoder with Vorbis format hint.
370    ///
371    /// This method provides a hint that the data is Vorbis format, which may help the decoder
372    /// identify the format more quickly. However, if Vorbis decoding fails, other formats
373    /// will still be attempted.
374    ///
375    /// # Errors
376    ///
377    /// Returns `DecoderError::UnrecognizedFormat` if no suitable decoder was found.
378    ///
379    /// # Examples
380    /// ```no_run
381    /// use rodio::Decoder;
382    /// use std::fs::File;
383    ///
384    /// let file = File::open("audio.ogg").unwrap();
385    /// let decoder = Decoder::new_vorbis(file).unwrap();
386    /// ```
387    pub fn new_vorbis(data: R) -> Result<Self, DecoderError> {
388        DecoderBuilder::new()
389            .with_data(data)
390            .with_hint("ogg")
391            .build()
392    }
393
394    /// Builds a new decoder with MP3 format hint.
395    ///
396    /// This method provides a hint that the data is MP3 format, which may help the decoder
397    /// identify the format more quickly. However, if MP3 decoding fails, other formats
398    /// will still be attempted.
399    ///
400    /// # Errors
401    ///
402    /// Returns `DecoderError::UnrecognizedFormat` if no suitable decoder was found.
403    ///
404    /// # Examples
405    /// ```no_run
406    /// use rodio::Decoder;
407    /// use std::fs::File;
408    ///
409    /// let file = File::open("audio.mp3").unwrap();
410    /// let decoder = Decoder::new_mp3(file).unwrap();
411    /// ```
412    pub fn new_mp3(data: R) -> Result<Self, DecoderError> {
413        DecoderBuilder::new()
414            .with_data(data)
415            .with_hint("mp3")
416            .build()
417    }
418
419    /// Builds a new decoder with AAC format hint.
420    ///
421    /// This method provides a hint that the data is AAC format, which may help the decoder
422    /// identify the format more quickly. However, if AAC decoding fails, other formats
423    /// will still be attempted.
424    ///
425    /// # Errors
426    ///
427    /// Returns `DecoderError::UnrecognizedFormat` if no suitable decoder was found.
428    ///
429    /// # Examples
430    /// ```no_run
431    /// use rodio::Decoder;
432    /// use std::fs::File;
433    ///
434    /// let file = File::open("audio.aac").unwrap();
435    /// let decoder = Decoder::new_aac(file).unwrap();
436    /// ```
437    pub fn new_aac(data: R) -> Result<Self, DecoderError> {
438        DecoderBuilder::new()
439            .with_data(data)
440            .with_hint("aac")
441            .build()
442    }
443
444    /// Builds a new decoder with MP4 container format hint.
445    ///
446    /// This method provides a hint that the data is in MP4 container format by setting
447    /// the MIME type to "audio/mp4". This may help the decoder identify the format
448    /// more quickly. However, if MP4 decoding fails, other formats will still be attempted.
449    ///
450    /// # Errors
451    ///
452    /// Returns `DecoderError::UnrecognizedFormat` if no suitable decoder was found.
453    ///
454    /// # Examples
455    /// ```no_run
456    /// use rodio::Decoder;
457    /// use std::fs::File;
458    ///
459    /// let file = File::open("audio.m4a").unwrap();
460    /// let decoder = Decoder::new_mp4(file).unwrap();
461    /// ```
462    pub fn new_mp4(data: R) -> Result<Self, DecoderError> {
463        DecoderBuilder::new()
464            .with_data(data)
465            .with_mime_type("audio/mp4")
466            .build()
467    }
468}
469
470impl<R> Iterator for Decoder<R>
471where
472    R: Read + Seek,
473{
474    type Item = Sample;
475
476    #[inline]
477    fn next(&mut self) -> Option<Self::Item> {
478        self.0.next()
479    }
480
481    #[inline]
482    fn size_hint(&self) -> (usize, Option<usize>) {
483        self.0.size_hint()
484    }
485}
486
487impl<R> Source for Decoder<R>
488where
489    R: Read + Seek,
490{
491    #[inline]
492    fn current_span_len(&self) -> Option<usize> {
493        self.0.current_span_len()
494    }
495
496    #[inline]
497    fn channels(&self) -> ChannelCount {
498        self.0.channels()
499    }
500
501    fn sample_rate(&self) -> SampleRate {
502        self.0.sample_rate()
503    }
504
505    #[inline]
506    fn total_duration(&self) -> Option<Duration> {
507        self.0.total_duration()
508    }
509
510    #[inline]
511    fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
512        self.0.try_seek(pos)
513    }
514}
515
516impl<R> Iterator for LoopedDecoder<R>
517where
518    R: Read + Seek,
519{
520    type Item = Sample;
521
522    /// Returns the next sample in the audio stream.
523    ///
524    /// When the end of the stream is reached, attempts to seek back to the start
525    /// and continue playing. If seeking fails, or if no decoder is available,
526    /// returns `None`.
527    fn next(&mut self) -> Option<Self::Item> {
528        if let Some(inner) = &mut self.inner {
529            if let Some(sample) = inner.next() {
530                return Some(sample);
531            }
532
533            // Take ownership of the decoder to reset it
534            let decoder = self.inner.take()?;
535            let (new_decoder, sample) = match decoder {
536                DecoderImpl::Symphonia(source, PhantomData) => {
537                    let mut reader = source.into_inner();
538                    reader.seek(SeekFrom::Start(0)).ok()?;
539                    let mut source =
540                        symphonia::SymphoniaDecoder::new(reader, &self.settings).ok()?;
541                    let sample = source.next();
542                    (DecoderImpl::Symphonia(source, PhantomData), sample)
543                }
544            };
545            self.inner = Some(new_decoder);
546            sample
547        } else {
548            None
549        }
550    }
551
552    /// Returns the size hint for this iterator.
553    ///
554    /// The lower bound is:
555    /// - The minimum number of samples remaining in the current iteration if there is an active decoder
556    /// - 0 if there is no active decoder (inner is None)
557    ///
558    /// The upper bound is always `None` since the decoder loops indefinitely.
559    /// This differs from non-looped decoders which may provide a finite upper bound.
560    ///
561    /// Note that even with an active decoder, reaching the end of the stream may result
562    /// in the decoder becoming inactive if seeking back to the start fails.
563    #[inline]
564    fn size_hint(&self) -> (usize, Option<usize>) {
565        (
566            self.inner.as_ref().map_or(0, |inner| inner.size_hint().0),
567            None,
568        )
569    }
570}
571
572impl<R> Source for LoopedDecoder<R>
573where
574    R: Read + Seek,
575{
576    /// Returns the current span length of the underlying decoder.
577    ///
578    /// Returns `None` if there is no active decoder.
579    #[inline]
580    fn current_span_len(&self) -> Option<usize> {
581        self.inner.as_ref()?.current_span_len()
582    }
583
584    /// Returns the number of channels in the audio stream.
585    ///
586    /// Returns the default channel count if there is no active decoder.
587    #[inline]
588    fn channels(&self) -> ChannelCount {
589        self.inner
590            .as_ref()
591            .map_or(ChannelCount::default(), |inner| inner.channels())
592    }
593
594    /// Returns the sample rate of the audio stream.
595    ///
596    /// Returns the default sample rate if there is no active decoder.
597    #[inline]
598    fn sample_rate(&self) -> SampleRate {
599        self.inner
600            .as_ref()
601            .map_or(SampleRate::default(), |inner| inner.sample_rate())
602    }
603
604    /// Returns the total duration of this audio source.
605    ///
606    /// Always returns `None` for looped decoders since they have no fixed end point -
607    /// they will continue playing indefinitely by seeking back to the start when reaching
608    /// the end of the audio data.
609    #[inline]
610    fn total_duration(&self) -> Option<Duration> {
611        None
612    }
613
614    /// Attempts to seek to a specific position in the audio stream.
615    ///
616    /// # Errors
617    ///
618    /// Returns `SeekError::NotSupported` if:
619    /// - There is no active decoder
620    /// - The underlying decoder does not support seeking
621    ///
622    /// May also return other `SeekError` variants if the underlying decoder's seek operation fails.
623    ///
624    /// # Note
625    ///
626    /// Even for looped playback, seeking past the end of the stream will not automatically
627    /// wrap around to the beginning - it will return an error just like a normal decoder.
628    /// Looping only occurs when reaching the end through normal playback.
629    fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
630        match &mut self.inner {
631            Some(inner) => inner.try_seek(pos),
632            None => Err(SeekError::Other(Box::new(DecoderError::IoError(
633                "Looped source ended when it failed to loop back".to_string(),
634            )))),
635        }
636    }
637}