Skip to content

Drop encoder priming and padding for gapless playback - #144

Draft
chrizzchrizz wants to merge 1 commit into
dimitris-c:mainfrom
chrizzchrizz:fix/gapless-trim
Draft

chrizzchrizz wants to merge 1 commit into
dimitris-c:mainfrom
chrizzchrizz:fix/gapless-trim

Conversation

@chrizzchrizz

Copy link
Copy Markdown

Problem

Decoders output the encoder delay (priming) and padding (remainder) of AAC and MP3 as if they were audio. processPacketTableInfo already reads kAudioFileStreamProperty_PacketTableInfo, but only uses it for the duration. So between two queued entries there is a short silence: for AAC 2112 priming frames plus the remainder of the previous file, about 50 ms. On continuous material (DJ mixes, live albums) this is audible as a stumble at every transition.

Change

  • processPacketTableInfo stores priming, valid and remainder frames on the entry.
  • While decoding from the start of the stream, only the decoded frames in [priming, priming + valid) are kept. Leading frames are dropped by moving the kept frames to the start of the decoded segment in the ring buffer.
  • After a seek the decoded position is not exact (e.g. MP3 bit reservoir), so instead the remainder is dropped from the end of the buffer at end of file (trimTrailingGaplessPadding(), called in endOfFileOccurred after the final flush; same lock order as the renderer).
  • Frame counts are converted from the source to the output sample rate.
  • Streams without packet table info are unchanged.

Measurement

Two files cut from one continuous 440 Hz sine (66273 + 66027 frames at 44.1 kHz), played with play + queue, output captured through frameFiltering:

Files Before After
AAC (afconvert) 2328 frames of silence right after the first file's 66273 valid frames no silence, phase jump 0.0 samples
AAC (ffmpeg) silence no silence, 0.0
MP3 (lame) silence no silence, 0.0
AAC 48 kHz (resampled to 44.1 kHz) silence no silence, 0.0
AAC / MP3 with a seek in the first file silence at the transition no silence at the transition

The short silence at the seek position itself is the same with and without this change (it varies between runs). The existing test suite passes. The measurement relies on real-time playback, so it is not included as a test; I can add it if you want it in the repo.

Also verified on device (iOS) in an app that uses AudioStreaming: the stumble between tracks of an AAC DJ mix is gone.

Decoders output the encoder delay (priming) and padding (remainder) of
formats like AAC and MP3 as audio. The packet table info was only used
for the duration, so consecutive entries were separated by a short
silence (2112 + remainder frames, about 50 ms, for AAC).

- Keep only the decoded frames in [priming, priming + valid) while the
  decoded position is exact (decoding from the start of the stream)
- After a seek, drop the remainder at the end of the stream instead
- Frame counts are converted to the output sample rate
@chrizzchrizz
chrizzchrizz marked this pull request as draft October 15, 2026 08:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant