LW IT Solutions
« Blog Overview /Music Production / Gapless Playback: Encoder Delay and Padding in...

Gapless Playback: Encoder Delay and Padding in MP3, AAC, Opus and FLAC, Measured

Gapless Playback: Encoder Delay and Padding in MP3, AAC, Opus and FLAC, Measured
Contents
  1. How this was measured
  2. What the encoders add
  3. The seam: 37.57 ms of silence where none belongs
  4. AAC keeps its gap until the duration is honoured too
  5. With music instead of a sine tone
  6. What this means for a release
  7. What this covers and what it does not
  8. Questions and answers
  9. Sources

An album without pauses, a DJ mix, a live recording: the move from one track to the next is supposed to run through. That it often does not is rarely the fault of the edit and almost always the fault of the encoder. Lossy formats work in blocks and need a run-up, so they add samples before the music and after it which nobody recorded.

Whether that turns into an audible gap is decided twice: while encoding, where the numbers are written into the file or are not, and while playing, where a player honours those numbers or ignores them. The measurement takes both steps apart and puts figures on them.

Diagram in two parts. On the left horizontal bars for the silence at the seam between two separately encoded halves: FLAC, MP3, Opus and Vorbis sit at 0.02 ms with the fields honoured, AAC at 20.16 ms; with the fields ignored MP3 rises to 37.57 ms and AAC to 43.38 ms. On the right three windows of the waveform over 60 milliseconds: with MP3 and the fields honoured the tone runs through the seam, while MP3 without its Info frame and AAC with only the start honoured show a stretch without any excursion. Four figures below
The silence at the seam between two separately encoded halves, per format with and without the fields honoured, and the waveform at the same place.

How this was measured

  • Delay: three seconds of silence with a single impulse exactly at one second, encoded, decoded again, and then a search for where the impulse now sits. The shift is the delay the chain added.
  • Seam: a tone of 441 Hz at 44.1 kHz, which is exactly 100 samples per cycle, split into two halves of two seconds each. Both halves were encoded on their own, decoded again and joined. Measured were the longest run of silent samples in a window of 100 ms around the seam, the largest step between two samples and the level in a window of 20 ms.
  • Music: the same with a 32 second piece from the same workshop as the measurement on true peak after encoding, also cut in half.
  • Two modes: once the way ffmpeg does it, that is with the fields from the file honoured, and once the way a player that ignores them would get it. For MP3 the same file was rewritten without its Info frame, for MP4 it was read with -ignore_editlist 1.
  • Formats: FLAC as a control, MP3 at 320 and 192 kbit/s as well as VBR V0 through libmp3lame, AAC at 256 kbit/s through the encoder ffmpeg brings itself, Opus at 128 and 256 kbit/s through libopus, Ogg Vorbis at 192 kbit/s. All with ffmpeg 7.1.5 on a Raspberry Pi 4.

What the encoders add

Format Shift with the fields Shift without them What the file declares
FLAC 0 not applicable number of samples in the header
MP3 320 kbit/s 0 1105 (25.06 ms) Info frame: 576 delay, 756 padding
MP3 192 kbit/s 0 1105 (25.06 ms) Info frame: 576 delay, 756 padding
MP3 VBR V0 0 1105 (25.06 ms) Xing frame: 576 delay, 756 padding
AAC 256 kbit/s 0 1024 (23.22 ms) edit list: start 1024, duration 3.000 s
Opus 128 and 256 kbit/s 0 not applicable pre-skip 312 at 48 kHz (6.5 ms)
Ogg Vorbis 192 kbit/s 0 not applicable granule positions of the pages

The MP3 files carried the same entry regardless of bit rate: 576 samples of delay and 756 of padding. Without that frame the measured shift was 1105 samples, and the file ended up 1332 samples longer than the original. The difference between 576 and 1105 comes from the decoder: the technical FAQ of LAME records that every decoder tested introduces a delay of 528 samples. 576 and 528 make 1104, the measurement showed 1105; that single sample stayed unexplained.

With AAC the start is described by the edit list of the track, here with a start offset of 1024 samples. At the end 820 samples more remained than in the original. Opus always decodes at 48 kHz, so the shift was taken in time here; according to RFC 7845 the pre-skip sits in the header and amounts to 312 samples in these files, that is 6.5 ms. FLAC states the exact number of samples in its header, and Ogg Vorbis carries the length in the granule positions, so neither adds anything.

The seam: 37.57 ms of silence where none belongs

A continuous tone, cut in the middle and joined again, shows the damage most clearly. The column “gap” gives the longest run of silent samples at the seam; the level refers to the same place in the unsplit signal:

Format Gap with the fields Level with the fields Gap without them Level without them
FLAC (control) 0.02 ms 0.00 dB not applicable not applicable
MP3 320 kbit/s 0.02 ms 0.00 dB 37.57 ms −85.08 dB
MP3 192 kbit/s 0.02 ms −0.26 dB 37.57 ms −78.32 dB
AAC 256 kbit/s 20.16 ms −3.02 dB 43.38 ms −61.43 dB
Opus 128 kbit/s 0.02 ms −0.02 dB not applicable not applicable
Ogg Vorbis 192 kbit/s 0.02 ms 0.04 dB not applicable not applicable

The 0.02 ms are a single sample and therefore the limit of this measurement: where the tone crosses zero it dips below the threshold for a moment. MP3, Opus and Vorbis therefore join seamlessly as long as the fields are honoured. Play the same tone without its Info frame and 37.57 ms of silence sit in the seam, with the level dropping by 85 dB. That is no longer a detail, that is a pause.

AAC keeps its gap until the duration is honoured too

The 20.16 ms in the AAC row stand out, because there the fields were honoured. A counter-check in three stages shows where it comes from:

Stage Gap at the seam Length per half
no field honoured 43.38 ms 90,112 samples
only the start of the edit list honoured 20.16 ms 89,088 samples
start and duration honoured 0.02 ms 88,200 samples

Two seconds at 44.1 kHz are 88,200 samples. The edit list describes both the start offset and the duration of the track; honouring only the offset trims the start cleanly and keeps the padding as silence. That is exactly what the ffmpeg documentation describes for advanced_editlist: with both options switched off, only the start of the stream index is modified to reflect the initial dwell time or starting timestamp described by the edit list. As soon as the duration comes along, the gap disappears entirely.

With music instead of a sine tone

Format Gap with the fields Level with the fields Gap without them
FLAC (control) 0.32 ms 0.00 dB not applicable
MP3 320 kbit/s 0.16 ms 0.00 dB 39.21 ms
AAC 256 kbit/s 21.77 ms −0.94 dB 26.08 ms
Opus 128 kbit/s 0.25 ms −0.10 dB not applicable
Ogg Vorbis 192 kbit/s 0.29 ms 0.04 dB not applicable

With music the threshold for silence also triggers briefly on the control, because the signal contains quiet moments anyway; the values are therefore to be read against FLAC at 0.32 ms. The picture stays the same: MP3 with the fields honoured, Opus and Vorbis sit at the level of the control, MP3 without the fields tears a hole of 39.21 ms, and AAC keeps its good 20 ms.

What this means for a release

Publishing a continuous recording leaves three options. The safest is not to split at all and deliver one file. The second is a format that knows its own length exactly: FLAC for the archive, Opus or Vorbis for delivery. The third is MP3 or AAC with complete fields, and then it depends on the player.

In practice that means cutting before encoding, never after. Every file gets its Info frame or its complete edit list, and before uploading, a look inside the file shows whether the fields are really there. That is what the gapless playback checker is for: it reads the header of a file, names the values and works out how long the gap would get with a player that ignores them. What else belongs in the files is covered by the tutorial on release metadata.

What this covers and what it does not

  • Encoding used the encoders ffmpeg brings. The LAME program itself, Apple’s encoder and other implementations write different values into the frame; the order of magnitude holds, the exact numbers need not.
  • The player was ffmpeg. How a particular program or a particular phone handles the fields is not part of this measurement.
  • The 0.02 ms in the tables are a single sample and therefore the resolution of the method, not a measured defect.
  • Opus is always decoded at 48 kHz. The values therefore hold in time, not in samples of the source file.
  • The difference between the 1104 samples from 576 and 528 and the measured 1105 is noted but not resolved.

Questions and answers

Why was a tone of exactly 441 Hz chosen for the seam?

Because one cycle is then exactly 100 samples long and a two-second half contains exactly 882 whole cycles. The second half therefore starts in the same phase in which the first one ends, and the unsplit signal runs through without a step. Any gap or step at the seam thus comes from the encoder and decoder chain, not from the cut.

Is the gap of just over 20 ms with AAC a fixed value?

No, it depends on the length of the track. The AAC encoder works in blocks of 1024 samples, puts 1024 samples of delay in front and fills the last block with silence. A player that honours only the start offset plays exactly this padding as silence, and depending on the length it lies between 0 and 1023 samples, that is between 0 and just over 23 ms at 44.1 kHz.

The figures in the article can be recalculated on this basis:

  • Sine tone, halves of two seconds: 88,200 plus 1024 of delay gives 89,224; rounded up to 88 full blocks that makes 90,112 samples, exactly the value in the table. The padding comes to 888 samples or 20.14 ms; the measurement showed 20.16 ms, one sample more, as occurs elsewhere at the resolution limit too.
  • Impulse, three seconds: 132,300 plus 1024 gives 133,324; rounded up to 131 blocks that makes 134,144. After subtracting the delay and the original, 820 samples remain, exactly the surplus the article reports at the end.
  • Music, the 32-second piece cut in half: 705,600 plus 1024 gives 706,624; rounded up to 691 blocks that makes 707,584. The padding comes to 960 samples or 21.77 ms, exactly the measured value.

Another track may therefore join almost seamlessly or lose nearly 23.2 ms, depending on how many samples it lacks to fill the next full block. Only the complete edit list is reliable.

Why is there no measurement without the fields for Opus, Vorbis and FLAC?

Because no file without those fields can be produced there without breaking the format. With MP3, the Info frame is an addition by the encoder; a file without it remains a valid MP3 file, and that is exactly how it was rewritten for the measurement. With Opus, by contrast, RFC 7845 makes the pre-skip a fixed part of the header, to be subtracted from the granule position, and with Vorbis the granule positions carry the length. FLAC adds no samples at all, so there is nothing a player could skip over.

A player that reads these formats correctly therefore has the information in front of it by necessity. That does not rule out an error, but it would be an error in the player, not a missing field in the file.

Can the Info frame of an MP3 file still get lost after encoding?

Yes, wherever a file is rewritten by a tool that does not know the frame or does not carry it over. The measurement made use of exactly that: for the mode without the fields, the same MP3 file was rewritten without its Info frame while the audio stayed unchanged. Editors that split or join files without re-encoding, and tools that repackage files, are therefore the places where a complete frame can disappear or end up with wrong values.

That is why the look inside the file belongs at the end of the chain, immediately before uploading, and not straight after the encoder. Whether a platform re-encodes the file itself after the upload cannot be checked from outside; in that case its own encoder decides.

Sources

  • RFC 7845: Ogg Encapsulation for the Opus Audio Codec: pre-skip as 16 bits in the header, counted at 48 kHz, to be subtracted from the granule position; the last page may contain less audio than the final packet would yield.
  • RFC 9639: Free Lossless Audio Codec (FLAC): STREAMINFO with sample rate, channels, bit depth and the total number of interchannel samples, where 0 means unknown.
  • LAME: technical FAQ: encoders and decoders add samples, every decoder tested 528 of them; LAME embeds the padding in the ancillary data of the first frame, the INFO tag.
  • ffmpeg: Formats Documentation: ignore_editlist ignores the edit list atoms, advanced_editlist reflects them in the stream index; with both off, only the start is modified.
Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Get in Touch

Briefly describe your project or inquiry for a tailored response. This site is protected by reCAPTCHA.

Write a comment

Experience with other DAWs and plugins, differing readings and questions are welcome here.

The email address is not published. Required fields are marked with an asterisk.

Articles & categories

CCTV

Follow this category by RSS

Cloud & AI

All 11 articles in this category Follow this category by RSS

Data Privacy

All 18 articles in this category Follow this category by RSS

Digital Analytics

All 58 articles in this category Follow this category by RSS

Digital Marketing

All 37 articles in this category Follow this category by RSS

IT & Networks

All 18 articles in this category Follow this category by RSS

Music Production

All 17 articles in this category Follow this category by RSS

Raspberry PI

Follow this category by RSS

SaaS & Internet Earning

Articles coming soon

Smart Home

All 18 articles in this category Follow this category by RSS

Web Development

All 11 articles in this category Follow this category by RSS

WordPress Plugins & Tricks

All 13 articles in this category Follow this category by RSS