MoosicBox Audio Decoder
High-performance audio decoding library supporting multiple audio formats for the MoosicBox ecosystem.
Overview
The MoosicBox Audio Decoder package provides:
- Multi-Format Support: Decode MP3, FLAC, AAC, Opus, Vorbis, and more
- High Performance: Optimized decoding with minimal memory footprint
- Streaming Support: Decode audio streams without loading entire files
- Metadata Extraction: Extract audio metadata during decoding
- Error Recovery: Robust error handling and stream recovery
- Cross-Platform: Works on Linux, macOS, Windows, and embedded systems
- Low Latency: Minimal buffering for real-time applications
- Thread Safety: Safe for concurrent use across multiple threads
Features
Supported Formats
- MP3: MPEG-1/2/2.5 Layer III with all bitrates and sample rates
- FLAC: Free Lossless Audio Codec with full specification support
- AAC: Advanced Audio Coding (LC, HE-AAC, HE-AACv2)
- Opus: Modern codec optimized for internet streaming
- Vorbis: Ogg Vorbis with full quality range support
- WAV: Uncompressed PCM and compressed formats
- AIFF: Audio Interchange File Format
- M4A: MPEG-4 Audio container format
- WMA: Windows Media Audio (basic support)
Decoding Capabilities
- Streaming Decoding: Process audio without loading entire files
- Seeking Support: Fast seeking to arbitrary positions
- Gapless Playback: Seamless transitions between tracks
- Sample Rate Conversion: On-the-fly resampling
- Channel Mixing: Convert between mono, stereo, and multi-channel
- Bit Depth Conversion: Support for 16-bit, 24-bit, and 32-bit output
- Metadata Preservation: Extract and preserve audio metadata
Performance Features
- Zero-Copy Decoding: Minimize memory allocations
- Vectorized Operations: SIMD optimizations where available
- Adaptive Buffering: Dynamic buffer sizing based on content
- Multi-Threading: Parallel decoding for supported formats
- Memory Efficient: Configurable memory usage limits
- Cache Friendly: Optimized memory access patterns
Installation
From Source
sudo apt update
sudo apt install build-essential pkg-config
sudo apt install libflac-dev libvorbis-dev libopus-dev
sudo apt install libmp3lame-dev libfaad-dev
git clone https://github.com/MoosicBox/MoosicBox.git
cd MoosicBox
cargo build --release --package moosicbox_audio_decoder
Cargo Dependencies
[dependencies]
moosicbox_audio_decoder = { path = "../audio_decoder" }
moosicbox_audio_decoder = {
path = "../audio_decoder",
features = ["mp3", "flac", "aac", "opus", "vorbis"]
}
Usage
Basic Decoding
use moosicbox_audio_decoder::{AudioDecoder, DecoderConfig, AudioFormat};
use std::fs::File;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("music.flac")?;
let mut decoder = AudioDecoder::new(file)?;
let info = decoder.info();
println!("Format: {:?}", info.format);
println!("Sample rate: {} Hz", info.sample_rate);
println!("Channels: {}", info.channels);
println!("Duration: {:.2} seconds", info.duration_seconds());
let mut buffer = vec![0f32; 4096];
while let Ok(samples_read) = decoder.read_samples(&mut buffer) {
if samples_read == 0 {
break; }
process_audio_samples(&buffer[..samples_read]);
}
Ok(())
}
fn process_audio_samples(samples: &[f32]) {
println!("Processed {} samples", samples.len());
}
Advanced Configuration
use moosicbox_audio_decoder::{
AudioDecoder, DecoderConfig, OutputFormat, ResamplingQuality
};
async fn decode_with_config() -> Result<(), Box<dyn std::error::Error>> {
let config = DecoderConfig {
output_format: OutputFormat {
sample_rate: Some(44100), channels: Some(2), bit_depth: Some(16), sample_format: SampleFormat::S16LE,
},
buffer_size: 8192,
max_memory_usage: 64 * 1024 * 1024, enable_simd: true,
threading: ThreadingMode::Auto,
resampling_quality: ResamplingQuality::High,
dithering: true,
error_recovery: true,
strict_mode: false,
};
let file = File::open("music.mp3")?;
let mut decoder = AudioDecoder::with_config(file, config)?;
let mut output_buffer = vec![0i16; 4096];
while let Ok(samples) = decoder.read_samples_i16(&mut output_buffer) {
if samples == 0 { break; }
process_i16_samples(&output_buffer[..samples]);
}
Ok(())
}
Streaming Decoding
use moosicbox_audio_decoder::{StreamingDecoder, StreamSource};
use tokio::io::AsyncRead;
async fn decode_stream<R: AsyncRead + Unpin>(
stream: R
) -> Result<(), Box<dyn std::error::Error>> {
let source = StreamSource::new(stream);
let mut decoder = StreamingDecoder::new(source).await?;
let mut chunk_buffer = vec![0f32; 2048];
loop {
match decoder.read_chunk(&mut chunk_buffer).await {
Ok(0) => break, Ok(samples) => {
process_streaming_chunk(&chunk_buffer[..samples]);
},
Err(e) if e.is_recoverable() => {
eprintln!("Recoverable error: {}", e);
continue;
},
Err(e) => return Err(e.into()),
}
}
Ok(())
}
fn process_streaming_chunk(samples: &[f32]) {
println!("Processing {} samples", samples.len());
}
Seeking and Random Access
use moosicbox_audio_decoder::{AudioDecoder, SeekMode};
use std::time::Duration;
fn demonstrate_seeking() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("long_track.flac")?;
let mut decoder = AudioDecoder::new(file)?;
let target_time = Duration::from_secs(120);
decoder.seek(target_time, SeekMode::Accurate)?;
let mut buffer = vec![0f32; 4096];
let samples_read = decoder.read_samples(&mut buffer)?;
println!("Reading from {}s position: {} samples",
target_time.as_secs(), samples_read);
let sample_offset = 44100 * 60; decoder.seek_samples(sample_offset, SeekMode::Fast)?;
let current_pos = decoder.current_position();
println!("Current position: {:.2}s", current_pos.as_secs_f64());
Ok(())
}
Metadata Extraction
use moosicbox_audio_decoder::{AudioDecoder, MetadataExtractor};
fn extract_metadata() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("song.mp3")?;
let decoder = AudioDecoder::new(file)?;
let metadata = decoder.metadata();
println!("Title: {}", metadata.title.unwrap_or("Unknown".to_string()));
println!("Artist: {}", metadata.artist.unwrap_or("Unknown".to_string()));
println!("Album: {}", metadata.album.unwrap_or("Unknown".to_string()));
println!("Year: {}", metadata.year.unwrap_or(0));
println!("Track: {}/{}",
metadata.track_number.unwrap_or(0),
metadata.total_tracks.unwrap_or(0));
if let Some(genre) = metadata.genre {
println!("Genre: {}", genre);
}
println!("Bitrate: {} kbps", metadata.bitrate.unwrap_or(0));
println!("Encoding: {}", metadata.encoding_info.unwrap_or("Unknown".to_string()));
if let Some(artwork) = metadata.artwork {
println!("Album art: {} bytes, format: {:?}",
artwork.data.len(), artwork.format);
std::fs::write("album_art.jpg", artwork.data)?;
}
Ok(())
}
Format-Specific Decoding
use moosicbox_audio_decoder::{
FlacDecoder, Mp3Decoder, AacDecoder, OpusDecoder, VorbisDecoder
};
fn decode_flac_advanced() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("audio.flac")?;
let mut decoder = FlacDecoder::new(file)?;
decoder.set_md5_checking(true);
decoder.set_metadata_callback(|metadata| {
println!("FLAC metadata block: {:?}", metadata);
});
let mut buffer = vec![0i32; 4096]; while let Ok(samples) = decoder.read_samples_i32(&mut buffer) {
if samples == 0 { break; }
process_high_resolution_samples(&buffer[..samples]);
}
Ok(())
}
fn decode_mp3_with_frames() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("audio.mp3")?;
let mut decoder = Mp3Decoder::new(file)?;
let mp3_info = decoder.mp3_info();
println!("MP3 version: {:?}", mp3_info.version);
println!("Layer: {}", mp3_info.layer);
println!("Bitrate mode: {:?}", mp3_info.bitrate_mode);
while let Ok(frame) = decoder.read_frame() {
println!("Frame: {} samples, bitrate: {} kbps",
frame.samples.len(), frame.bitrate);
process_mp3_frame(&frame);
}
Ok(())
}
fn process_high_resolution_samples(samples: &[i32]) {
}
fn process_mp3_frame(frame: &Mp3Frame) {
}
Error Handling and Recovery
use moosicbox_audio_decoder::{AudioDecoder, DecoderError, ErrorRecovery};
fn robust_decoding() -> Result<(), Box<dyn std::error::Error>> {
let file = File::open("potentially_corrupted.mp3")?;
let mut decoder = AudioDecoder::new(file)?;
decoder.set_error_recovery(ErrorRecovery::Aggressive);
let mut buffer = vec![0f32; 4096];
let mut total_errors = 0;
loop {
match decoder.read_samples(&mut buffer) {
Ok(0) => break, Ok(samples) => {
process_audio_samples(&buffer[..samples]);
},
Err(DecoderError::CorruptedData { position, recoverable }) => {
total_errors += 1;
eprintln!("Corrupted data at position {}", position);
if recoverable {
if let Ok(()) = decoder.recover() {
continue;
}
}
if let Ok(()) = decoder.skip_to_next_frame() {
continue;
}
break; },
Err(DecoderError::UnsupportedFormat { format, reason }) => {
eprintln!("Unsupported format {}: {}", format, reason);
break;
},
Err(e) => return Err(e.into()),
}
}
println!("Decoding completed with {} errors", total_errors);
Ok(())
}
Batch Processing
use moosicbox_audio_decoder::{BatchDecoder, BatchConfig, ProcessingMode};
use std::path::Path;
async fn batch_decode_directory<P: AsRef<Path>>(
input_dir: P,
output_dir: P,
) -> Result<(), Box<dyn std::error::Error>> {
let config = BatchConfig {
processing_mode: ProcessingMode::Parallel,
max_concurrent: 4,
output_format: OutputFormat {
sample_rate: Some(44100),
channels: Some(2),
bit_depth: Some(16),
sample_format: SampleFormat::S16LE,
},
error_handling: ErrorHandling::SkipAndContinue,
};
let mut batch_decoder = BatchDecoder::new(config);
for entry in std::fs::read_dir(input_dir)? {
let entry = entry?;
let path = entry.path();
if path.extension().map_or(false, |ext| {
matches!(ext.to_str(), Some("mp3" | "flac" | "aac" | "opus"))
}) {
batch_decoder.add_file(path);
}
}
let results = batch_decoder.process_all(output_dir).await?;
for result in results {
match result {
Ok(info) => {
println!("✓ {} -> {} ({:.2}s)",
info.input_file.display(),
info.output_file.display(),
info.processing_time.as_secs_f64());
},
Err(e) => {
eprintln!("✗ {}: {}", e.input_file.display(), e.error);
}
}
}
Ok(())
}
Programming Interface
Core Types
use moosicbox_audio_decoder::*;
pub struct AudioInfo {
pub format: AudioFormat,
pub sample_rate: u32,
pub channels: u16,
pub bit_depth: Option<u8>,
pub duration: Option<Duration>,
pub bitrate: Option<u32>,
pub is_lossless: bool,
pub is_variable_bitrate: bool,
}
pub struct DecoderConfig {
pub output_format: OutputFormat,
pub buffer_size: usize,
pub max_memory_usage: usize,
pub enable_simd: bool,
pub threading: ThreadingMode,
pub resampling_quality: ResamplingQuality,
pub error_recovery: bool,
}
pub enum SampleFormat {
F32LE, F32BE, S32LE, S32BE, S16LE, S16BE, U8, }
pub enum ThreadingMode {
Single, Multi(usize), Auto, }
Trait Implementations
use moosicbox_audio_decoder::{Decoder, DecoderResult};
pub struct CustomDecoder {
}
impl Decoder for CustomDecoder {
fn info(&self) -> &AudioInfo {
&self.audio_info
}
fn read_samples(&mut self, buffer: &mut [f32]) -> DecoderResult<usize> {
Ok(samples_written)
}
fn seek(&mut self, position: Duration) -> DecoderResult<()> {
Ok(())
}
fn current_position(&self) -> Duration {
self.current_pos
}
}
AudioDecoder::register_decoder("custom", Box::new(CustomDecoderFactory));
Configuration
Environment Variables
| Variable |
Description |
Default |
MOOSICBOX_DECODER_BUFFER_SIZE |
Default buffer size |
4096 |
MOOSICBOX_DECODER_MAX_MEMORY |
Maximum memory usage (bytes) |
67108864 |
MOOSICBOX_DECODER_SIMD |
Enable SIMD optimizations |
true |
MOOSICBOX_DECODER_THREADS |
Number of decoder threads |
auto |
MOOSICBOX_DECODER_QUALITY |
Default resampling quality |
high |
Feature Flags
[dependencies.moosicbox_audio_decoder]
path = "../audio_decoder"
default-features = false
features = [
"mp3",
"flac",
"aac",
"opus",
"vorbis",
"wav",
"aiff",
"m4a",
"wma",
"simd",
"threading",
"resampling",
"metadata",
"streaming",
]
Performance Optimization
Memory Usage
use moosicbox_audio_decoder::{AudioDecoder, MemoryConfig};
let memory_config = MemoryConfig {
max_buffer_size: 32 * 1024, max_lookahead: 8 * 1024, enable_memory_mapping: true, preload_threshold: 1024 * 1024, };
let mut decoder = AudioDecoder::with_memory_config(file, memory_config)?;
SIMD Optimizations
use moosicbox_audio_decoder::{SimdConfig, SimdLevel};
let simd_config = SimdConfig {
level: SimdLevel::Auto, enable_avx2: true, enable_neon: true, fallback_scalar: true, };
decoder.configure_simd(simd_config)?;
Threading Configuration
use moosicbox_audio_decoder::{ThreadConfig, ThreadPriority};
let thread_config = ThreadConfig {
decoder_threads: 2,
io_threads: 1,
priority: ThreadPriority::High,
affinity: Some(vec![0, 1]), };
decoder.configure_threading(thread_config)?;
Troubleshooting
Common Issues
-
Unsupported format errors
cargo build --features "mp3,flac,aac,opus,vorbis"
file audio_file.mp3
-
Memory usage too high
let config = DecoderConfig {
buffer_size: 1024,
max_memory_usage: 16 * 1024 * 1024, ..Default::default()
};
-
Poor performance
let config = DecoderConfig {
enable_simd: true,
threading: ThreadingMode::Auto,
..Default::default()
};
-
Seeking accuracy issues
decoder.seek(position, SeekMode::Accurate)?;
decoder.set_seek_mode(SeekMode::FrameAccurate);
See Also