Skip to main content
This quickstart guide will walk you through decoding your first audio file with Symphonia.

Prerequisites

  • Rust 1.53 or later
  • An audio file to decode (MP3, FLAC, OGG, WAV, etc.)
1

Add Symphonia to your project

Add Symphonia to your Cargo.toml. By default, only royalty-free formats are enabled (OGG, FLAC, Vorbis, PCM, Wave, WebM).For MP3 support, enable the mp3 feature:
Cargo.toml
[dependencies]
symphonia = { version = "0.5", features = ["mp3"] }
  • MP4/M4A container: Enable isomp4
  • AAC codec: Enable aac
  • ALAC codec: Enable alac
  • All formats: Enable all
Example with multiple formats:
symphonia = { version = "0.5", features = ["mp3", "isomp4", "aac"] }
2

Create a media source stream

Symphonia reads from a MediaSourceStream, which wraps any source implementing Read and Seek.
use symphonia::core::io::MediaSourceStream;
use std::fs::File;

fn main() {
    let args: Vec<String> = std::env::args().collect();
    let path = args.get(1).expect("file path not provided");
    
    // Open the audio file
    let file = Box::new(File::open(path).expect("failed to open file"));
    
    // Create the media source stream
    let mss = MediaSourceStream::new(file, Default::default());
}
For unseekable sources (like stdin), use ReadOnlySource:
use symphonia::core::io::ReadOnlySource;
let src = ReadOnlySource::new(std::io::stdin());
3

Probe the format and create a decoder

Use Symphonia’s probe to automatically detect the audio format and create a decoder for the track.
use symphonia::core::codecs::{DecoderOptions, CODEC_TYPE_NULL};
use symphonia::core::formats::FormatOptions;
use symphonia::core::meta::MetadataOptions;
use symphonia::core::probe::Hint;

// Create a hint (optional but improves detection)
let mut hint = Hint::new();
hint.with_extension("mp3");

// Use default options
let format_opts = FormatOptions::default();
let metadata_opts = MetadataOptions::default();
let decoder_opts = DecoderOptions::default();

// Probe the media source
let probed = symphonia::default::get_probe()
    .format(&hint, mss, &format_opts, &metadata_opts)
    .expect("unsupported format");

// Get the format reader
let mut format = probed.format;

// Find the first audio track
let track = format
    .tracks()
    .iter()
    .find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
    .expect("no supported audio tracks");

// Create a decoder for the track
let mut decoder = symphonia::default::get_codecs()
    .make(&track.codec_params, &decoder_opts)
    .expect("unsupported codec");

let track_id = track.id;
Enable gapless playback for a better listening experience:
let mut format_opts = FormatOptions::default();
format_opts.enable_gapless = true;
4

Decode packets in a loop

The decode loop retrieves packets from the format reader and decodes them into audio samples.
use symphonia::core::errors::Error;

loop {
    // Get the next packet
    let packet = match format.next_packet() {
        Ok(packet) => packet,
        Err(Error::ResetRequired) => {
            // Track list changed (e.g., chained OGG streams)
            // Recreate decoders and restart
            unimplemented!();
        }
        Err(Error::IoError(err)) if err.kind() == std::io::ErrorKind::UnexpectedEof => {
            // End of stream reached
            break;
        }
        Err(err) => {
            eprintln!("Error reading packet: {}", err);
            break;
        }
    };

    // Skip packets from other tracks
    if packet.track_id() != track_id {
        continue;
    }

    // Consume any new metadata
    while !format.metadata().is_latest() {
        format.metadata().pop();
        // Process metadata if needed
    }

    // Decode the packet
    match decoder.decode(&packet) {
        Ok(decoded) => {
            // Successfully decoded! Process audio samples here
            println!("Decoded {} samples", decoded.capacity());
        }
        Err(Error::IoError(_)) => {
            // Packet failed to decode, skip it
            continue;
        }
        Err(Error::DecodeError(_)) => {
            // Invalid data in packet, skip it
            continue;
        }
        Err(err) => {
            eprintln!("Unrecoverable decode error: {}", err);
            break;
        }
    }
}
Always check for UnexpectedEof to detect end-of-stream. Without this, your program will panic when the file ends.
5

Convert to interleaved samples

Use SampleBuffer to convert decoded samples to a specific format (e.g., f32) in interleaved order.
use symphonia::core::audio::SampleBuffer;

let mut sample_buf: Option<SampleBuffer<f32>> = None;
let mut sample_count = 0;

// Inside the decode loop, after successful decode:
match decoder.decode(&packet) {
    Ok(decoded) => {
        // Create sample buffer on first packet
        if sample_buf.is_none() {
            let spec = *decoded.spec();
            let duration = decoded.capacity() as u64;
            sample_buf = Some(SampleBuffer::<f32>::new(duration, spec));
        }

        // Copy decoded samples to interleaved buffer
        if let Some(buf) = &mut sample_buf {
            buf.copy_interleaved_ref(decoded);
            
            // Access the samples
            let samples = buf.samples();
            sample_count += samples.len();
            println!("Total samples decoded: {}", sample_count);
            
            // Now you can play, process, or save these samples
        }
    }
    // ... error handling
}

Complete Example

Here’s a complete, working program that decodes an audio file and counts the samples:
use std::env;
use std::fs::File;
use symphonia::core::audio::SampleBuffer;
use symphonia::core::codecs::{DecoderOptions, CODEC_TYPE_NULL};
use symphonia::core::errors::Error;
use symphonia::core::formats::FormatOptions;
use symphonia::core::io::MediaSourceStream;
use symphonia::core::meta::MetadataOptions;
use symphonia::core::probe::Hint;

fn main() {
    let args: Vec<String> = env::args().collect();
    let path = args.get(1).expect("usage: program <file>");

    // Open file and create media source stream
    let file = Box::new(File::open(path).expect("failed to open file"));
    let mss = MediaSourceStream::new(file, Default::default());

    // Probe the format
    let hint = Hint::new();
    let format_opts = FormatOptions::default();
    let metadata_opts = MetadataOptions::default();
    
    let probed = symphonia::default::get_probe()
        .format(&hint, mss, &format_opts, &metadata_opts)
        .expect("unsupported format");

    let mut format = probed.format;

    // Find first audio track
    let track = format
        .tracks()
        .iter()
        .find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
        .expect("no audio tracks");

    // Create decoder
    let decoder_opts = DecoderOptions::default();
    let mut decoder = symphonia::default::get_codecs()
        .make(&track.codec_params, &decoder_opts)
        .expect("unsupported codec");

    let track_id = track.id;
    let mut sample_buf: Option<SampleBuffer<f32>> = None;
    let mut sample_count = 0;

    // Decode loop
    loop {
        let packet = match format.next_packet() {
            Ok(packet) => packet,
            Err(Error::IoError(err)) if err.kind() == std::io::ErrorKind::UnexpectedEof => {
                break;
            }
            Err(err) => {
                eprintln!("Error: {}", err);
                break;
            }
        };

        if packet.track_id() != track_id {
            continue;
        }

        match decoder.decode(&packet) {
            Ok(decoded) => {
                if sample_buf.is_none() {
                    let spec = *decoded.spec();
                    let duration = decoded.capacity() as u64;
                    sample_buf = Some(SampleBuffer::<f32>::new(duration, spec));
                }

                if let Some(buf) = &mut sample_buf {
                    buf.copy_interleaved_ref(decoded);
                    sample_count += buf.samples().len();
                    print!("\rDecoded {} samples", sample_count);
                }
            }
            Err(Error::DecodeError(_)) => continue,
            Err(_) => break,
        }
    }

    println!("\nDone! Total samples: {}", sample_count);
}
Run it with:
cargo run --release -- path/to/audio.mp3

Common Errors

The audio format isn’t enabled. Check your Cargo.toml features and add the required format support.
The file may be corrupted, or it might be a video file. If decoding video files, search specifically for audio tracks instead of using the default track.
The codec isn’t enabled. Enable the appropriate codec feature in Cargo.toml (e.g., mp3, aac, alac).

Next Steps

Core Concepts

Learn about demuxing, decoding, and Symphonia’s architecture

API Reference

Explore the complete API documentation

Working with Audio Data

Learn how to access and process decoded audio samples

Metadata

Extract and handle audio metadata and tags

Build docs developers (and LLMs) love