Skip to main content
Symphonia provides a comprehensive set of primitives for working with decoded audio data. These primitives handle different sample formats, provide efficient planar audio storage, and enable safe conversion between formats.

Core Audio Types

Symphonia’s audio system is built around several key types:

AudioBuffer and AudioBufferRef

AudioBuffer<S>

AudioBuffer<S> (symphonia-core/src/audio.rs:283) is the fundamental audio container. It stores multi-channel audio in a planar format.
pub struct AudioBuffer<S: Sample> {
    buf: Vec<S>,           // All channel data
    spec: SignalSpec,      // Signal characteristics
    n_frames: usize,       // Number of frames written
    n_capacity: usize,     // Maximum frames
}

Planar vs Interleaved Layout

Samples are stored channel-by-channel:
[L L L L L L L L] [R R R R R R R R]
 ^^^^^^^^^^^^^^     ^^^^^^^^^^^^^^
 Left channel       Right channel
Benefits:
  • Cache-friendly for DSP operations
  • Easier to apply per-channel processing
  • Native format for most decoders

AudioBufferRef

AudioBufferRef (symphonia-core/src/audio.rs:419) is an any-type wrapper returned by decoders. It hides the specific sample format using an enum:
pub enum AudioBufferRef<'a> {
    U8(Cow<'a, AudioBuffer<u8>>),
    U16(Cow<'a, AudioBuffer<u16>>),
    U24(Cow<'a, AudioBuffer<u24>>),
    U32(Cow<'a, AudioBuffer<u32>>),
    S8(Cow<'a, AudioBuffer<i8>>),
    S16(Cow<'a, AudioBuffer<i16>>),
    S24(Cow<'a, AudioBuffer<i24>>),
    S32(Cow<'a, AudioBuffer<i32>>),
    F32(Cow<'a, AudioBuffer<f32>>),
    F64(Cow<'a, AudioBuffer<f64>>),
}
Decoders return AudioBufferRef because different codecs produce different sample formats. Your application must handle this polymorphism.

Working with AudioBufferRef

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

match decoded {
    AudioBufferRef::F32(buf) => {
        // Process f32 samples
        for &sample in buf.chan(0) {
            println!("Sample: {}", sample);
        }
    }
    AudioBufferRef::S16(buf) => {
        // Process i16 samples
    }
    _ => {
        // Handle other formats
    }
}

Sample Formats

Symphonia supports all common audio sample formats via the Sample trait.

Supported Sample Types

TypeBitsRangeUse Case
u880 to 255Legacy formats, WAV
i88-128 to 127Telephony
u16160 to 65535-
i1616-32768 to 32767CD audio, most hardware
u24240 to 16777215High-res audio
i2424-8388608 to 8388607Studio recording
u32320 to 4294967295-
i3232-2147483648 to 2147483647Studio processing

Sample Trait

All sample types implement the Sample trait, which provides:
pub trait Sample: Copy + Clone + Send + Sync + ... {
    /// The equilibrium value (silence)
    const MID: Self;
    
    // Conversion traits are implemented separately
}
The MID constant represents silence:
  • Unsigned: mid-point (128 for u8, 32768 for u16)
  • Signed: zero (0)
  • Float: zero (0.0)

Sample Conversions

Symphonia provides automatic sample format conversion:
use symphonia::core::conv::IntoSample;

let i16_sample: i16 = 16384;
let f32_sample: f32 = i16_sample.into_sample();  // ≈ 0.5

let f32_sample: f32 = 0.5;
let i16_sample: i16 = f32_sample.into_sample();  // 16384

SampleBuffer

SampleBuffer<S> (symphonia-core/src/audio.rs:717) converts AudioBufferRef to a typed, interleaved sample buffer.

Creating and Using SampleBuffer

use symphonia::core::audio::SampleBuffer;

// Create buffer for decoded audio
let mut sample_buf = SampleBuffer::<f32>::new(
    decoded.capacity() as u64,
    *decoded.spec(),
);

// Copy interleaved (LRLRLR...)
sample_buf.copy_interleaved_ref(decoded);

// Access samples
let samples: &[f32] = sample_buf.samples();
let len = sample_buf.len();  // Total sample count
Always recreate SampleBuffer if you receive Error::ResetRequired from the decoder, as the signal spec may have changed.

RawSampleBuffer

RawSampleBuffer<S> (symphonia-core/src/audio.rs:997) is like SampleBuffer, but provides samples as raw bytes.

When to Use RawSampleBuffer

Use RawSampleBuffer when:
  • Interfacing with C APIs
  • Writing to file formats
  • Sending over network sockets
  • Working with FFI boundaries
use symphonia::core::audio::RawSampleBuffer;

let mut raw_buf = RawSampleBuffer::<f32>::new(
    decoded.capacity() as u64,
    *decoded.spec(),
);

// Copy as interleaved bytes
raw_buf.copy_interleaved_ref(decoded);

// Get as byte slice
let bytes: &[u8] = raw_buf.as_bytes();

SignalSpec

SignalSpec (symphonia-core/src/audio.rs:164) describes the characteristics of an audio signal.
pub struct SignalSpec {
    pub rate: u32,         // Sample rate in Hz
    pub channels: Channels, // Channel configuration
}

Creating SignalSpec

use symphonia::core::audio::{SignalSpec, Channels};

// Stereo at 44.1kHz
let spec = SignalSpec::new(
    44100,
    Channels::FRONT_LEFT | Channels::FRONT_RIGHT
);

Channels and Layouts

Channels Bitmask

Channels (symphonia-core/src/audio.rs:36) is a bitmask representing channel configuration:
use symphonia::core::audio::Channels;

// Mono
let mono = Channels::FRONT_LEFT;

// Stereo
let stereo = Channels::FRONT_LEFT | Channels::FRONT_RIGHT;

// 2.1 (Stereo + LFE)
let stereo_lfe = Channels::FRONT_LEFT 
               | Channels::FRONT_RIGHT 
               | Channels::LFE1;

// 5.1 Surround
let surround = Channels::FRONT_LEFT
             | Channels::FRONT_RIGHT
             | Channels::FRONT_CENTRE
             | Channels::REAR_LEFT
             | Channels::REAR_RIGHT
             | Channels::LFE1;

Channel Layouts

Pre-defined layouts for common configurations:
pub enum Layout {
    Mono,           // Single center channel
    Stereo,         // Left + Right
    TwoPointOne,    // Left + Right + LFE
    FivePointOne,   // Front L/R/C + Rear L/R + LFE
}

// Convert to Channels
let channels = Layout::Stereo.into_channels();

Signal Trait

The Signal trait (symphonia-core/src/audio.rs:501) provides methods for manipulating audio buffers:
use symphonia::core::audio::Signal;

// Render silence
buffer.render_silence(Some(1024));

// Render with custom function
buffer.render(Some(1024), |planes, frame_idx| {
    for plane in planes.planes() {
        plane[frame_idx] = 0.0;  // Generate samples
    }
    Ok(())
})?;

Best Practices

Create SampleBuffer and RawSampleBuffer once and reuse them:
// Good
let mut sample_buf = SampleBuffer::<f32>::new(capacity, spec);
loop {
    let decoded = decoder.decode(&packet)?;
    sample_buf.copy_interleaved_ref(decoded);  // Reuse
}

// Bad - allocates every iteration
loop {
    let decoded = decoder.decode(&packet)?;
    let mut sample_buf = SampleBuffer::<f32>::new(capacity, spec);
    sample_buf.copy_interleaved_ref(decoded);
}
Use f32 samples for DSP operations:
  • Normalized range [-1.0, 1.0]
  • No overflow/underflow concerns
  • Native format for most audio libraries
  • Good precision for audio work
Be prepared for Error::ResetRequired:
match decoder.decode(&packet) {
    Ok(decoded) => { /* process */ }
    Err(Error::ResetRequired) => {
        // Recreate buffers with new spec
        decoder.reset();
        // Re-examine codec parameters
    }
    Err(e) => return Err(e),
}
If you only need one channel or specific processing, access channels directly:
// More efficient than copying to SampleBuffer
match decoded {
    AudioBufferRef::F32(buf) => {
        let left = buf.chan(0);
        let right = buf.chan(1);
        // Process directly
    }
    _ => { /* handle other formats */ }
}

Next Steps

Media Sources

Learn how Symphonia reads audio data from different sources

Decoding Audio

Complete guide to decoding audio files

Processing Audio

Apply effects and transformations to decoded audio

Exporting Audio

Convert and export audio to different formats

Build docs developers (and LLMs) love