Skip to main content

How It Works

waxcut splits MP3 files without decoding or re-encoding any audio, and without shelling out to ffmpeg or any other external binary. This page explains why that's possible and how each piece works, so you can trust the output.

MPEG frames are self-describing

An MP3 file is a sequence of independent MPEG Audio Layer III frames, each with its own 4-byte header. That header starts with an 11-bit sync word (0xFFE), followed by fields for the MPEG version, layer, bitrate index, sample rate index, and padding bit. Critically, those fields are enough to compute the frame's exact length in bytes on their own — no need to decode the audio data that follows.

waxcut's scan_frames scans a file byte-by-byte looking for a valid sync word, decodes the header fields (waxcut.frames._parse_header), computes the frame length from them, and records a Frame(offset, length, start_ms, duration_ms). If the header decodes to a length that doesn't fit in the remaining data, or the sync word doesn't check out, the scanner advances one byte and keeps looking — this is what lets it skip over non-frame bytes such as a trailing ID3v1/APE tag without getting confused.

Because every frame's boundaries are derived directly from its own header, frame-accurate splitting is a byte-copy operation: slice_bytes doesn't need to touch or understand the audio payload at all, it just copies the byte range spanning frames[start_idx].offset through the end of frames[end_idx - 1]. The result is a valid, self-contained MP3 stream that is byte-identical to the corresponding span of the source file.

Leading ID3v2 tags

Files commonly start with an ID3v2 tag (artwork, metadata) before the first audio frame. id3v2_size reads the tag's syncsafe size field and returns how many bytes to skip, so frame scanning starts at the right offset instead of tripping over tag bytes that happen to look frame-like.

Xing/Info/VBRI exclusion

Many encoders write a special first "frame" that isn't audio at all — a Xing, Info, or VBRI header containing encoder metadata (total frame count, byte count, sometimes a seek table). It has a valid MPEG frame header so a naive scanner would treat it like any other frame, but including it in playback or duration calculations is wrong: it isn't sound, and its duration doesn't represent playback time.

load_audio_stream locates this tag by checking, immediately after the side info of the first parsed frame, for one of the three recognized 4-byte markers (_vbr_header_tag_offset). The side info size itself depends on the MPEG version and channel mode (mono vs. stereo/joint-stereo), since that changes where the tag would start. If a VBR header frame is found, it's dropped from the returned AudioStream.frames, and every remaining frame's start_ms is rebased so the first real audio frame starts at 0. If a file turns out to contain only a VBR header frame with no audio after it, load_audio_stream raises UnsupportedMp3Error rather than returning an empty, useless stream.

LAME gapless delay/padding

Real MP3 encoding pads output at the frame boundaries with a few hundred samples of silence at the start and end (needed because Layer III encoding require fixed-size frames). Players that want gapless playback need to trim that padding, and LAME encoders record exactly how much to trim in an extension appended after the standard Xing/Info tag fields (_parse_lame_gapless).

waxcut reads that extension defensively: it only trusts the delay/padding values if the 9 bytes at the expected offset literally start with the ASCII string LAME — the signature genuine LAME encodes write into that field. Other encoders (for example ffmpeg's native Lavc encoder) produce a Xing/Info header in the same position without this extension, so bytes read at that offset from a non-LAME file would be unrelated data. Even after confirming the LAME signature, the decoded 12-bit delay and padding values are range-checked before being trusted. If any of these checks fail, waxcut falls back to encoder_delay_samples = 0 and encoder_padding_samples = 0.

These values are informational: AudioStream.playable_duration_ms uses them to report the duration a real player would show (trimmed from the raw frame-derived duration_ms), matching what tools like mutagen compute independently. They don't change where splits can land — frame boundaries, and therefore valid cut points, are unaffected by gapless metadata, and split output carries no delay/padding semantics of its own since it's fresh audio starting exactly at a frame boundary.

Why Layer I/II are out of scope

"MP3" colloquially means MPEG Audio Layer III, but the MPEG Audio standard also defines Layer I and Layer II, which use different frame layouts, bitrate tables, and samples-per-frame counts. Virtually no real-world file extension .mp3 actually contains Layer I or II audio. Rather than partially support them with tables and logic that isn't validated the same way, waxcut's header parser only recognizes Layer III (_LAYER_III in _parse_header) — any other layer value is treated the same as an invalid sync, and a file containing no Layer III frames raises UnsupportedMp3Error. This is a deliberate scope boundary: rejecting clearly and loudly is safer than silently mis-parsing bytes as the wrong layer.

Validation

Because none of this involves an actual decoder, correctness is proven by cross-checking against tools that do decode: duration output is compared against mutagen's independent parser across CBR/VBR encodes, mono/stereo, and multiple encoder tags, and where ffmpeg/ffprobe are available, every split output is independently decoded to confirm it's a valid, playable MP3. The parser is also fuzzed continuously — see Security for details.