Enable seamless transitions between audio tracks without silence or gaps
Gapless playback removes encoder-added silence at the beginning and end of audio tracks, enabling seamless transitions between tracks. This is essential for albums, DJ mixes, and audiobooks.
Most lossy audio codecs add padding frames at the start (encoder delay) and end of the audio stream due to how the encoding algorithm works. This creates unwanted silence between tracks.
Encoder Delay
Encoder delay refers to silent frames added at the beginning of an encoded stream. This happens because encoders need “look-ahead” samples to initialize their internal state.For example:
MP3: Typically 576 or 1152 samples of delay
AAC: Usually 1024 or 2112 samples
Vorbis: Variable delay depending on codec settings
Encoder Padding
Padding refers to silent frames added at the end of a stream. This occurs because encoders produce output in fixed-size blocks, and the last block needs to be padded if the original audio doesn’t align perfectly.Example:
When gapless is enabled, packets contain trim information:
use symphonia::core::audio::Signal;loop { let packet = format.next_packet()?; if packet.track_id() != track_id { continue; } // Check trim information let trim_start = packet.trim_start(); let trim_end = packet.trim_end(); println!("Trim start: {}, end: {}", trim_start, trim_end); let decoded = decoder.decode(&packet)?; // Process decoded audio (trimming is reflected in timestamps) // ...}
3
Apply trimming (optional)
The decoder handles trimming automatically, but you can also trim manually:
use symphonia::core::audio::{AudioBufferRef, Signal};let decoded = decoder.decode(&packet)?;let trim_start = packet.trim_start();let trim_end = packet.trim_end();match decoded { AudioBufferRef::F32(mut buf) => { // Manual trim if needed if trim_start > 0 || trim_end > 0 { buf.trim(trim_start as usize, trim_end as usize); } // Process trimmed audio let samples = buf.chan(0); } _ => { /* Handle other formats */ }}
When gapless is enabled, packet timestamps and durations automatically account for trimming. You typically don’t need to handle trim values manually.
// Gapless enabledlet fmt_opts = FormatOptions { enable_gapless: true, ..Default::default()};// First packet - delay is trimmedlet packet = format.next_packet()?;assert_eq!(packet.ts(), 0); // Still starts at 0assert_eq!(packet.dur(), 1152 - 576); // Duration reduced by delayassert_eq!(packet.trim_start(), 576); // Trim 576 samplesassert_eq!(packet.trim_end(), 0); // No end trim yet// Last packet - padding is trimmedlet packet = format.next_packet()?;assert_eq!(packet.trim_start(), 0); // No start trimassert_eq!(packet.trim_end(), 768); // Trim 768 padding samples