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}