Skip to main content

Overview

Symphonia provides a comprehensive PCM decoder supporting a wide range of uncompressed audio formats including raw PCM (signed/unsigned integers and floating-point) and log-PCM codecs (A-law and μ-law).

Feature Flag

To enable PCM support, add the pcm feature to your Cargo.toml:
[dependencies]
symphonia = { version = "0.5", features = ["pcm"] }
Or use the standalone crate:
[dependencies]
symphonia-codec-pcm = "0.5"

Status

Status: Excellent
  • Decoding: Fully supported for all variants
  • Gapless Playback: Yes (inherent to PCM)
  • Performance: Minimal overhead

Decoder

The PCM decoder is implemented by the PcmDecoder struct:
use symphonia_codec_pcm::PcmDecoder;
use symphonia::core::codecs::{Decoder, DecoderOptions};

// Create decoder from codec parameters
let mut decoder = PcmDecoder::try_new(&codec_params, &DecoderOptions::default())?;

// Decode a packet
let decoded = decoder.decode(&packet)?;

Supported Formats

Signed Integer PCM

FormatCodec TypeBit DepthEndianness
S8CODEC_TYPE_PCM_S88-bitN/A
S16LECODEC_TYPE_PCM_S16LE16-bitLittle
S16BECODEC_TYPE_PCM_S16BE16-bitBig
S24LECODEC_TYPE_PCM_S24LE24-bitLittle
S24BECODEC_TYPE_PCM_S24BE24-bitBig
S32LECODEC_TYPE_PCM_S32LE32-bitLittle
S32BECODEC_TYPE_PCM_S32BE32-bitBig

Unsigned Integer PCM

FormatCodec TypeBit DepthEndianness
U8CODEC_TYPE_PCM_U88-bitN/A
U16LECODEC_TYPE_PCM_U16LE16-bitLittle
U16BECODEC_TYPE_PCM_U16BE16-bitBig
U24LECODEC_TYPE_PCM_U24LE24-bitLittle
U24BECODEC_TYPE_PCM_U24BE24-bitBig
U32LECODEC_TYPE_PCM_U32LE32-bitLittle
U32BECODEC_TYPE_PCM_U32BE32-bitBig

Floating-Point PCM

FormatCodec TypePrecisionEndianness
F32LECODEC_TYPE_PCM_F32LE32-bitLittle
F32BECODEC_TYPE_PCM_F32BE32-bitBig
F64LECODEC_TYPE_PCM_F64LE64-bitLittle
F64BECODEC_TYPE_PCM_F64BE64-bitBig

Log-PCM (Companded)

FormatCodec TypeDescription
A-lawCODEC_TYPE_PCM_ALAWITU-T G.711 A-law
μ-lawCODEC_TYPE_PCM_MULAWITU-T G.711 μ-law

Codec Types

use symphonia::core::codecs::*;

// Signed integer formats
CODEC_TYPE_PCM_S8
CODEC_TYPE_PCM_S16LE
CODEC_TYPE_PCM_S16BE
CODEC_TYPE_PCM_S24LE
CODEC_TYPE_PCM_S24BE
CODEC_TYPE_PCM_S32LE
CODEC_TYPE_PCM_S32BE

// Unsigned integer formats
CODEC_TYPE_PCM_U8
CODEC_TYPE_PCM_U16LE
CODEC_TYPE_PCM_U16BE
CODEC_TYPE_PCM_U24LE
CODEC_TYPE_PCM_U24BE
CODEC_TYPE_PCM_U32LE
CODEC_TYPE_PCM_U32BE

// Floating-point formats
CODEC_TYPE_PCM_F32LE
CODEC_TYPE_PCM_F32BE
CODEC_TYPE_PCM_F64LE
CODEC_TYPE_PCM_F64BE

// Log-PCM formats
CODEC_TYPE_PCM_ALAW
CODEC_TYPE_PCM_MULAW

Container Support

PCM is commonly used in:
  • WAV (RIFF WAVE)
  • AIFF (Audio Interchange File Format)
  • CAF (Core Audio Format)
  • Raw audio files
[dependencies]
# For WAV files
symphonia = { version = "0.5", features = ["pcm", "wav"] }

# For AIFF files
symphonia = { version = "0.5", features = ["pcm", "aiff"] }

Codec Parameters

PCM decoding requires specific parameters:
// Required parameters:
// - codec: The specific PCM codec type
// - sample_rate: Sample rate in Hz
// - channels or channel_layout: Channel configuration
// - max_frames_per_packet: Maximum frames per packet

// Optional but recommended:
// - bits_per_sample: Actual bit depth
// - bits_per_coded_sample: Coded bit depth (may differ)

Bit Depth Handling

PCM supports variable bit depths within a format:
// Example: 20-bit audio in 24-bit format
// bits_per_sample = 20
// bits_per_coded_sample = 24
// The decoder automatically shifts samples appropriately

Sample Rates

Supports all sample rates, common ones include:
  • 8 kHz (telephony)
  • 11.025 kHz, 22.05 kHz, 44.1 kHz (CD quality)
  • 16 kHz, 32 kHz, 48 kHz, 96 kHz, 192 kHz
  • 88.2 kHz, 176.4 kHz (high-resolution)

Channel Support

Supports any channel configuration from mono to many channels.

Usage Example

use symphonia::core::codecs::{CODEC_TYPE_PCM_S16LE, DecoderOptions};
use symphonia::core::formats::FormatOptions;
use symphonia::core::io::MediaSourceStream;
use symphonia::core::meta::MetadataOptions;
use symphonia::core::probe::Hint;
use std::fs::File;

fn decode_wav_file(path: &str) -> Result<(), Box<dyn std::error::Error>> {
    // Open the media source
    let file = Box::new(File::open(path)?);
    let mss = MediaSourceStream::new(file, Default::default());

    // Create a hint
    let mut hint = Hint::new();
    hint.with_extension("wav");

    // Probe the media source
    let probed = symphonia::default::get_probe()
        .format(&hint, mss, &FormatOptions::default(), &MetadataOptions::default())?;

    let mut format = probed.format;
    let track = &format.tracks()[0];

    println!("Codec: {:?}", track.codec_params.codec);
    println!("Sample Rate: {:?}", track.codec_params.sample_rate);
    println!("Channels: {:?}", track.codec_params.channels);
    println!("Bit Depth: {:?}", track.codec_params.bits_per_sample);

    // Create the decoder
    let mut decoder = symphonia::default::get_codecs()
        .make(&track.codec_params, &DecoderOptions::default())?;

    // Decode packets
    loop {
        let packet = match format.next_packet() {
            Ok(packet) => packet,
            Err(_) => break,
        };

        let decoded = decoder.decode(&packet)?;
        // Process decoded audio...
    }

    Ok(())
}

Working with Different Sample Formats

use symphonia::core::audio::AudioBufferRef;
use symphonia::core::conv::{FromSample, IntoSample};

let decoded = decoder.decode(&packet)?;

match decoded {
    AudioBufferRef::U8(buf) => {
        // 8-bit unsigned samples
    }
    AudioBufferRef::S16(buf) => {
        // 16-bit signed samples (most common)
        for channel in 0..buf.spec().channels.count() {
            let samples = buf.chan(channel);
            // Process i16 samples...
        }
    }
    AudioBufferRef::S24(buf) => {
        // 24-bit signed samples
    }
    AudioBufferRef::S32(buf) => {
        // 32-bit signed samples
    }
    AudioBufferRef::F32(buf) => {
        // 32-bit float samples
    }
    AudioBufferRef::F64(buf) => {
        // 64-bit float samples
    }
    _ => {}
}

A-law and μ-law Decoding

Log-PCM formats are automatically converted to linear PCM:
use symphonia::core::codecs::{CODEC_TYPE_PCM_ALAW, CODEC_TYPE_PCM_MULAW};

// A-law and μ-law are decoded to S16
// 8-bit companded → 16-bit linear PCM

let decoded = decoder.decode(&packet)?;
match decoded {
    AudioBufferRef::S16(buf) => {
        // Linear 16-bit samples from A-law/μ-law
    }
    _ => unreachable!(),
}

A-law

Used primarily in European telephony:
  • Better signal-to-noise ratio for low amplitude signals
  • Dynamic range: ~13-bit equivalent

μ-law

Used primarily in North American and Japanese telephony:
  • Slightly different companding curve
  • Dynamic range: ~13-bit equivalent

Performance

  • Pure Rust implementation with no unsafe code
  • Minimal overhead: Direct memory copies for most formats
  • Zero-copy where possible
  • Efficient bit-packing handling for 24-bit formats
  • Optimized transfer functions for A-law and μ-law

Sample Format Conversion

Use Symphonia’s conversion utilities to convert between sample formats:
use symphonia::core::conv::{FromSample, IntoSample};

// Convert any sample type to f32
let float_sample: f32 = sample.into_sample();

// Convert from f32 to any sample type  
let int_sample: i16 = f32::from_sample(0.5);

Limitations

  • Planar PCM formats are not yet supported
  • No encoding support (decode-only)

Common Use Cases

CD Audio (CDDA)

// 16-bit signed, 44.1 kHz, stereo, little-endian
CODEC_TYPE_PCM_S16LE

Professional Audio

// 24-bit signed, 96 kHz or 192 kHz
CODEC_TYPE_PCM_S24LE

Telephony

// 8-bit A-law or μ-law, 8 kHz, mono
CODEC_TYPE_PCM_ALAW
CODEC_TYPE_PCM_MULAW

High-Resolution Audio

// 32-bit float, 192 kHz
CODEC_TYPE_PCM_F32LE

Documentation

See Also

Build docs developers (and LLMs) love