Skip to content
YadeWira edited this page Jul 12, 2026 · 1 revision

The .pm3 format

File type is always detected by content (a 2-byte magic), never by extension — a .pm3 archive renamed to anything else still decodes.

Single-stream archive (magic "MS")

This is what -k1 (the default) produces: one archive = one serial arithmetic-coded stream.

byte 0-1   magic "MS"
byte 2     version byte (appversion, e.g. 20 = v2.0)
...        header (MPEG version/channels/bitrate flags, frame count)
...        arithmetic-coded: ID3/metadata, padding, block types,
           scalefactors, side info, and the main spectral data
           (Huffman big/small values re-coded with adaptive models)

Because it's one continuous arithmetic stream, decoding is inherently sequential — frame N's decode depends on the coder state left by frame N-1. This is what makes a single -k1 archive single-threaded.

Chunked container (magic "MK")

This is what -k<n> for n > 1 produces: the source file is split at MP3 frame boundaries into n independent byte ranges, each compressed as its own self-contained "MS" sub-archive (its own arithmetic stream, its own model state starting fresh). The container just concatenates them with an index:

byte 0-1        magic "MK"
byte 2          version byte
byte 3          chunk count N
byte 4..        N x 4-byte little-endian chunk sizes
...             N concatenated "MS" sub-archives, back to back

Because each chunk is a fully independent "MS" stream, chunks can be encoded and decoded on separate threads with no shared state. list detects "MK" and reports the aggregate (frames/format/channels/rate summed or checked for consistency across chunks, plus the chunk count).

Why chunking costs some ratio

MP3 Layer III has a bit reservoir — a frame's spectral data can borrow reserve bits from the physical space of previous frames. When a chunk boundary falls where a frame would normally borrow from data that's now in a different chunk, that frame is reconstructed through the same "bad first frame" recovery path packMP3 already uses for genuinely damaged real-world files (still 100% lossless, just slightly less efficient for that frame). This — plus each chunk's model statistics restarting from zero — is the entire cost of -k. See Benchmarks for the measured trade-off.

Version compatibility

  • The version byte in the header is a single number: v{n/10}.{n%10} (e.g. 20v2.0).
  • Archives are only guaranteed compatible within the same main version. v2.0 changed the entropy models and format entirely — v1.x .pmp archives are not decodable by v2.0 and vice versa. Trying to decompress an incompatible archive gives a clean error, never garbage output.
  • A sub-version string (when present, e.g. v1.0g) marks smaller, format-compatible changes within the same main version.

Clone this wiki locally