ACIDCAT . FILE FORMAT REFERENCE

WAV Anatomy

RIFF / WAVE + familyPCM, metadata, large-file
rev 2026.07
magic "RIFF"
endian little
container chunked
lineage IFF 1985

A WAV is a RIFF container: the 12-byte RIFF header, then a run of chunks, each an id, a 32-bit little-endian size, and that many payload bytes (plus a pad byte if the size is odd). But WAV is one branch of a wide family — from the 1985 Amiga IFF it descends from, to the broadcast and large-file variants (BWF, RF64, Wave64) that outgrew its 4 GB ceiling, to its big-endian sibling AIFF. The tabs open each one, drawn byte by byte. Hover a field to light its bytes, click a field with a + for its table. Color marks kind (see the key).

The chunked container is not a WAV invention. It descends from Electronic Arts' IFF (dated 14 January 1985, six months before the Amiga it is remembered for): a FORM whose body is a form-type id followed by a run of chunks, each a 4-character id + a 32-bit size + payload, padded to an even boundary — so a reader that meets an unknown chunk knows exactly how many bytes to skip. The leading form type is load-bearing: it opens a local namespace, so a chunk id only has to be unique inside its own form. Microsoft's RIFF (1991) is that grammar with the byte order reversed for the little-endian PC; the name expands to Resource Interchange File Format, which its designer later glossed as “RIFF, the Resource IFF aka Reverse IFF” — one concession, two names. WAVE is the RIFF form for audio. Everything else on these tabs is a dialect layered on that one idea.

the family, dated
1985EA IFF 85 — the ancestor. FORM/LIST/CAT, big-endian 4CC+size chunks, even-byte pad.
1986Uhuru / DMCS (Mac) — Geoff Brown's format: 4CC + 32-bit big-endian length + payload, chained flat, no FORM, closed by a bare UEND carrying no size field. Version 2.0 moves the same chunks inside FORM ... DMCS, with UEND promoted to a proper zero-length chunk. Read off the shipped disks rather than reconstructed: all 31 Uhuru files on the v1.0 volume walk under that rule with zero errors. The 1985 spec's “Previous Work” names the Macintosh Scrap and Resource Managers, PostScript, PICT, DIF and InterScript — not Brown; Morrison's retrospective calls that omission his own: “I should've listed Geoff Brown of Uhuru Sound Software in the credits ... My screw-up.” The structure is settled; the chronology is not. Whether this design predates January 1985 unchanged is unresolved.
1984Macintosh Resource Manager — a precursor Morrison names directly, and to his knowledge the first file or database format to use 4-character codes: “an excellent tradeoff between compactness, fixed size, readability in the debugger, and mnemonic enough for distributed allocation.” He describes it as a flat database of tag + length + payload; strictly the 4CC lives in the resource map and the length sits with the data, joined by an offset — a split that makes IFF's single self-describing triple the larger step. He also recalls a “Chemical Abstracts” tag+length+payload format he never found documentation for.
1988AIFF (Apple, v1.1) — IFF for audio, big-endian, COMM + SSND, 80-bit sample rate. A child of IFF and WAVE's sibling: the spec declares its conformance to EA IFF 85 outright.
1991RIFF / WAVE (Microsoft + IBM) — IFF reversed to little-endian for the PC. fmt + data. The reversal was not the original author's preference: asked in 2026, Morrison said that had he known the format and its variants would still sit inside major media formats decades later, he would have pushed Microsoft to keep the same byte order — “with better industry coordination, these formats didn't need to diverge.”
1997BWF (EBU Tech 3285) — the bext chunk: originator, timestamp, coding history.
1999Wave64 (Sonic Foundry) — 128-bit GUID chunk ids, 64-bit sizes, 8-byte chunk alignment, to break 4 GB. Its two GUID suffixes were minted 8 April 1996 (container ids) and 7 December 1999 (audio-chunk ids).
2000DSDIFF (Philips; v1.0 February 2000, v1.5 2004) — the IFF side's 64-bit answer: FRM8 replaces FORM, sizes widen to 64-bit big-endian, the pad byte stays. DSD/SACD masters.
2006RF64 (EBU Tech 3306, first issued 2006; earliest surviving edition February 2007) — the ds64 chunk carries 64-bit sizes; magic flips to RF64 when a writer chooses, not only when 4 GB is crossed.
2015BW64 (ITU-R BS.2088-0) — RF64 generalized, tied to the Audio Definition Model (chna/axml).
the shared grammar. Every format on these tabs is the same atom: an id, a size, a payload, and padding to an alignment boundary. The padding is excluded from the size that owns it but counted inside the parent that contains it — an asymmetry that follows from the IFF grammar, where the pad sits outside a chunk's own braces and inside its parent's, and the classic source of off-by-one walks. Four things vary across the family: the byte order (IFF/AIFF big-endian, RIFF little-endian), the size width (32-bit, or 64-bit in RF64/Wave64/DSDIFF), the id format (a 4-character code, or a 16-byte GUID in Wave64), and the alignment modulus — 2 bytes everywhere, including DSDIFF, which widened its sizes but kept the IFF pad rule, except Wave64, which aligns to 8. Wave64 also counts its own 24-byte header inside its size, where every other member counts payload only.
the three normative WAVE rules. The 1991 specification guarantees three things and no more: programs must expect and ignore unknown chunks, as in every RIFF form; fmt and the wave data are both mandatory; and fmt precedes the wave data. The first is the load-bearing one. Because any reader must be able to skip a chunk it does not recognise, no reader may assume a fixed offset for anything — which is why the “44-byte WAV header” is a myth the grammar never supported, and why data can and does begin anywhere. Chunk order is otherwise free, with one exception: in RF64 and BW64 the ds64 chunk must come first. The declared RIFF size is often stale, and the wave data is normally a data chunk though the grammar also admits a wavl LIST.
sources for the quoted material. Jerry Morrison wrote the EA IFF 85 specification in January 1985. Remarks attributed to him on this page come from two public documents: his 2013 IFF retrospective, and his reply of 10 August 2026 to a question about Geoff Brown's pre-IFF chunk format, both at github.com/1fish2/IFF. The Uhuru byte evidence is read from the Macintosh Deluxe Music Construction Set v1.0 and v2.0 disk images. Chunk prevalence figures come from a census of 807,394 RIFF files, and the padding figures from a random sample of 58,153 of them.
magic"RIFF" .. "WAVE"endianlittlefmt16 / 18 / 40 Borderfmt before data

The core WAVE: a fmt chunk describing the encoding and a data chunk holding the samples, with a tail of optional chunks. The regions below list them; the fmt (PCM), data, acid, and smpl loop chunks are drawn byte by byte. Order varies by writer — only fmt-before-wave-data is guaranteed.

chunk regions
RIFF header0x00 . 12 bytes
magic"RIFF" (see the RIFF tab)
riff_sizeu32 LE = file size - 8 (often stale; linted, not trusted)
form type"WAVE"
fmtformat . drawn below

The audio encoding. 16 bytes for PCM (the byte map below), 18 with a cb_size field at 0x10, 40 for extensible (tag 0xFFFE) — see the EXTENSIBLE tab.

factrequired for non-PCM . 4+ bytes
when requiredmandatory for every compressed format, and for any file whose audio sits in a wavl LIST even when that audio is PCM. Optional only for PCM in a plain data chunk
dwSampleLengthu32 sample count, the authoritative length for non-PCM. The 1991 grammar names the field dwFileSize and never states whether it counts per channel or in total; per channel is the usual reading, and the ambiguity is on the record
chunk size4 bytes in practice, but the spec reserves the right to append fields and tells readers to use the chunk size to see what is present
0xFFFFFFFFRF64 sentinel, resolved via the ds64 sample_count
datathe audio payload . drawn below
contentswhatever fmt declares, interleaved frame by frame: every channel of frame 0, then every channel of frame 1
sample sign1 to 8 bits unsigned, so silence is 0x80; 9 or more bits signed two's complement, so silence is 0. The rule is a threshold, not a list of depths
odd sizes8-bit mono with an odd frame count makes data odd, so it takes a pad byte. This is the one chunk where writers commonly omit it, because data sits last and nothing follows to desynchronise
durationdata_size / avg_bytes_per_sec in general; for PCM, data_size / (rate * block_align). For compressed audio the fact chunk sample count is authoritative, not the byte count
declared sizea size past EOF is a corruption tell; the bytes present are the ground truth
LIST wavlthe other wave data
grammarwave data is either a data chunk or a LIST of type wavl holding an alternating run of data and slnt chunks. This is why the normative rule is fmt before wave data, not fmt before data
slntu32 count of silent sample frames. Silence, not zeroes: the spec is explicit that the previous sample value should be held, since dropping to zero can click
side effectsa wavl file requires a fact chunk even when its audio is PCM, and cue point offsets become relative to the wavl list, with fccChunk naming data or slnt
in the wilda census of 807,394 RIFF files found no wavl and no slnt. The machinery is fully specified and effectively unused
acidloop metadata . drawn below

The project's namesake: Sonic Foundry ACID tempo, beats, meter, and root note. No official spec; layout field-verified against real ACIDized packs. See the byte map below.

smplsampler root + loops . record drawn below
sample_periodnanoseconds per sample; tunes finer than the integer sample rate
midi_unity_noteroot key 0-127, 60 = middle C per the MMA. No value is reserved: readers that treat 0 as unset follow a convention, and a genuine C-1 sample is indistinguishable from an unset field
midi_pitch_fractiona 32-bit binary fraction of one semitone above the unity note, not a count of cents. 0 is no offset, 0x80000000 is half a semitone (50 cents); cents = fraction / 2^32 x 100
num_sample_loopscount of 24-byte loop records after the 36-byte header
loop recordid, type (0 fwd / 1 ping-pong / 2 back), start, end (inclusive), fraction, play count
instinstrument tuning . 7 bytes
base_noteMIDI root, usually duplicates the smpl unity note
detune / gaini8 cents (-50..+50) and i8 dB adjustment
note / velocity rangelow..high playable keys and trigger velocities, one u8 each
odd length7 data bytes, so inst always forces a pad byte — a free conformance test for a reader's padding logic
cue / LISTmarkers, text
cueu32 count (clamped to payload), then 24-byte points
LIST/INFOINAM title, IART artist, ICMT, IGNR, ...
bextbroadcast wave . 602-byte fixed area
description256-byte free text
originator / reference32 + 32-byte creator name and unique reference
time_referenceu64 sample count since midnight, two u32 halves
version0, 1, or 2; the fixed area is 602 bytes in every version
umid / loudnessv1+ 64-byte UMID at 0x15C; v2 five i16 loudness at 0x19C
coding_historyunrestricted ASCII from 0x25A to the end of the chunk, in CR/LF-terminated rows, one per coding stage. EBU R 98 defines a recommended row syntax, not a mandatory one
fmt chunk (PCM, 16 bytes)

Example: 16-bit stereo at 44100 Hz.

32-bit float (tag 0x0003)
Samples become little-endian IEEE-754 binary32 (or binary64 at 64-bit), nominal full scale ±1.0. The exponent is the whole point: values past ±1.0 (0 dBFS) are legal and survive in the file, so a take recorded too hot is recovered by pulling the gain down after the fact — only clipping at the analog or converter stage is irreversible. Eight exponent bits buy a ~1500 dB theoretical range while the 24-bit mantissa keeps ~24-bit precision inside any 6 dB window. Being non-PCM it formally needs a fact chunk and a cbSize-bearing fmt; some players (Windows Media Player) refuse a float file without them. Float also rides under EXTENSIBLE as the sub-format GUID 00000003-....
the non-unit float variant
Not every 32-bit float agrees on full scale. Cool Edit, and editors after it, declare 24 bits/sample with a 32-bit container and store the floats normalized to full scale 223 (8,388,608) rather than 1.0 — a reader that assumes unit scale is off by ~138 dB. No flag marks it; you infer it from the sample statistics. The community PEAK chunk exists partly to settle this: it records each channel's peak so a reader can normalize and draw the waveform without a full scan, whatever the convention. (GoldWave carries the same non-unit-float reputation; the documented case is Cool Edit.)
data chunk (the first four frames)

Example: 16-bit stereo, the opening 16 bytes of the payload. Silence, then a one-count pair, then a matched pair, then full scale in both directions.

where data actually begins
The diagram everyone learns puts fmt at 0x0C and data at 0x24, with the first sample at 0x2C — byte 44. That is one legal arrangement, not the format. Any chunk may sit in front of data. Take a minimal conformant file carrying a single LIST INFO with a 9-byte ISFT string: the list runs 0x24 to 0x41, its odd-length child forces a pad byte at 0x41, the data header lands at 0x42, and the first sample is at 0x4A — byte 74. Nothing in that file is unusual and nothing in it is non-conformant. A reader that seeks to 44 lands in the middle of a software name. The only lawful way to find the samples is to walk the chunks.
the pad byte, measured
The even-alignment rule is inherited from IFF and is the family's most-discussed conformance point. Across a random sample of 58,153 RIFF files, a third contain at least one odd-length chunk and 99.35% of those chunks are correctly padded. The failures are not where folklore puts them: odd lengths are overwhelmingly text metadata (ISFT, ICMT, ICRD, bext), and those pad correctly. Of 231 unpadded chunks, 168 were data — the chunk that sits last, where omitting the pad desynchronises nothing because nothing follows. The rule holds wherever it is load-bearing and decays only where its absence is invisible.

Why it exists at all is on the record from IFF's author: the even bound was for the 68000, which needed the alignment for in-memory work, whereas “byte-oriented copying or stream I/O takes care of it”. Asked in 2026 what he would change, Morrison listed omitting the pad byte for simplicity among three regrets — alongside pushing Microsoft to keep the same byte order, and publishing the spec more widely. The measurement above is the rule outliving the machine that required it.
acid chunk (24 bytes)

Example: an 8-beat 4/4 loop at 120 BPM, root C4, root-set flag.

smpl loop record (24 bytes)

Example: one forward loop, a full second at 44.1 kHz. The 36-byte sampler header (root note, pitch, sample period) precedes these records; num_sample_loops says how many follow.

loops in the wild
The spec's own loop machinery is cue points plus a plst playlist chunk carrying a repeat count. Almost nothing writes it: across a census of 807,394 RIFF files from one production sample library, plst appeared 48 times and smpl 154,697. The sanctioned system lost outright to the sampler chunk. The other real loop carriers are the acid chunk (104,880 — ACIDized tempo + loop flags) and SyLp (7,564), an undecoded ACID-family chunk. One corpus, skewed toward DAW and sampler writers, so read these as prevalence inside production libraries rather than across WAV files at large. Want a loop point? Read smpl.
magic"RIFF" / "RIFX"sizeu32, 4 GB cap1991MS + IBM

The container itself: 12 bytes that every WAV opens with. "RIFF", a 32-bit little-endian size, and a form type — "WAVE" for audio. RIFF wraps IFF's grammar with the byte order flipped for the Intel PC, the concession its designer later glossed as “RIFF, the Resource IFF aka Reverse IFF.” A rare big-endian variant, RIFX, keeps everything else the same and flips only the number encoding. The riff_size field claims the file length minus 8, but writers routinely leave it stale, so it is a hint rather than a bound.

RIFF header (12 bytes)

Example: a small WAVE file, 2,092 bytes total.

RIFX — big-endian RIFF. Identical layout, but the size fields and all multi-byte values are big-endian, and the magic is "RIFX". Microsoft defined it; almost nothing writes it. Macromedia Director used big-endian RIFF internally — whether that was registered RIFX or an independent reinvention is unrecorded.
other RIFF forms. WAVE is one of many form types after the size field: AVI (video), RMID (RIFF-wrapped MIDI), RDIB (bitmap), PAL (palette), RMMP (Director), sfbk (SoundFont), DLS (Downloadable Sounds). The form type at 0x08 is what a reader branches on.
tag0xFFFEfmt size40 bytessub-format16-byte GUID

The modern fmt descriptor. When the format tag is 0xFFFE, the fmt chunk grows to 40 bytes: the 16-byte PCM core, then a cb_size, then three extension fields — the real bit depth (valid_bits, so 24-in-32 padding is explicit), a channel mask naming which speakers each channel drives, and a 16-byte sub-format GUID whose leading 2 bytes hold the actual codec tag and whose fixed tail identifies it as a standard KSDATAFORMAT subtype. This is how multichannel and high-bit-depth PCM are described unambiguously.

fmt EXTENSIBLE (40 bytes)

Example: 24-bit-in-32 stereo at 48000 Hz, front L/R, PCM sub-format.

magic"RF64"sizesu64 in ds642007EBU Tech 3306

The large-file answer that stays RIFF. A file over 4 GB flips its magic from RIFF to RF64, writes 0xFFFFFFFF as a sentinel in the 32-bit size fields, and adds a ds64 chunk as the first chunk carrying the real 64-bit sizes. A clever writer reserves a JUNK chunk of exactly the ds64 size up front while the file is small, then overwrites it in place with ds64 if the recording crosses 4 GB — so the file never has to be rewritten. (ffmpeg does it slightly differently: under -rf64 auto it puts its 28-byte JUNK placeholder after fmt rather than in the spec's first-chunk slot, then on promotion swaps the two, writing ds64 into the old fmt slot and fmt into the old JUNK slot — so the finished RF64 file is conformant, with ds64 first. A 28-byte JUNK immediately after fmt in a sub-4 GB file is the fingerprint of that mode; ffmpeg's default is -rf64 never, which reserves nothing.)

ds64 fixed area (28 bytes)

Example: a 4.3 GB recording — just past the u32 ceiling, which is the whole point.

the override table. After the 28-byte fixed area, table_length records follow, each a 4-byte chunk id + a u64 size, for any chunk other than data whose own size exceeds 4 GB. Usually zero: normally only data is that large. Note what the spec does not say — the sentinel rule is scoped to exactly three fields (the RIFF size, the data size, and the fact chunk's sample count), and nothing requires an overflowing chunk to write 0xFFFFFFFF in its own header. Doing so is convention.
BW64. ITU-R BS.2088 generalizes RF64 as BW64 (magic "BW64"), adding the Audio Definition Model via the chna and axml chunks for object and immersive audio. The ds64 wire layout is unchanged — same 28-byte fixed area, same sentinel rule, same JUNK promotion — but the third 64-bit slot is retired. BW64 has no fact chunk, so sample_count becomes a dummy that shall be ignored on read and written as zero, and riff_size is renamed bw64Size. A writer that carries RF64 semantics straight across emits non-conformant BW64.
ids16-byte GUIDssizesu64SonySonic Foundry

Sonic Foundry's take on the 4 GB problem, and the odd one out. Wave64 abandons 4-character codes entirely: every chunk id is a 128-bit GUID and every size is a 64-bit value that includes the 24-byte chunk header, where RIFF counts only the payload. It also aligns chunks to 8-byte boundaries rather than 2, and that padding is excluded from the size — so the next GUID sits at the size rounded up to a multiple of 8, and a reader carrying RIFF's 2-byte habit desynchronises on the first chunk whose payload is not a multiple of 8. The original Sonic Foundry document is lost, but a four-page Sony Wave64 specification published by VCS AG with Sony's cooperation survives and agrees with libsndfile's implementation throughout.

Wave64 header (40 bytes)

The RIFF GUID + 64-bit file size + WAVE GUID. Watch the first four bytes of each GUID.

the GUID trick. For every chunk Wave64 inherits from RIFF, the first four bytes on disk are the FOURCC in lowercase ASCII — 72 69 66 66 is riff, 77 61 76 65 is wave — followed by one of two constant 12-byte suffixes: one for the container ids (riff, list), one for the audio-chunk ids (wave, fmt , data, fact and the rest). Because those four bytes are read back as the GUID's little-endian first field, the printed form shows them reversed. These are genuine version-1 UUIDs, version and variant nibbles intact, with only the 32-bit time_low overwritten. That destroys the low 32 bits of the 60-bit timestamp and leaves the top 28, which still fixes each mint to a 7.16-minute window: 8 April 1996 for the container pair, 7 December 1999 for the audio batch. Wave64's two native chunks, MARKER and SUMMARYLIST, carry no FOURCC and keep their full timestamps, decoding exactly to August and September 1998.
magic"FORM" .. "AIFF"endianbig1988Apple

The mirror image. AIFF is IFF-for-audio done by Apple, version 1.1 dated January 1988 — the same FORM/chunk grammar as RIFF, but big-endian throughout (the 68k Mac's byte order, the one IFF kept and RIFF reversed). The one exception is AIFF-C's sowt, where the container stays big-endian and the samples flip. Its COMM chunk is the analogue of fmt, and it stores the sample rate in the strangest field in the whole family: an 80-bit IEEE-754 extended-precision float, a format no mainstream language unpacks natively, so it has to be rebuilt by hand.

COMM payload (18 bytes, FORM AIFF)

Example: 16-bit stereo at 44100 Hz, 88200 frames.

the 80-bit rate. Where WAV writes the sample rate as a plain u32, AIFF uses a 10-byte extended float: 1 sign bit, a 15-bit exponent biased by 16383, and an explicit 64-bit significand (no hidden leading 1, unlike float64). 44100 encodes as 40 0E AC 44 00 00 00 00 00 00. The AIFF spec itself does not give this layout: it names the type as SANE Extended and points at the Apple Numerics Manual. An all-ones exponent is IEEE infinity or NaN; neither is a usable rate, so readers substitute a sentinel, but that substitution is a reader's choice and not a rule of the format.
AIFF-C. A file whose form type is AIFC rather than AIFF extends COMM with a 4-character compressionType and a Pascal-string compressionName, making ckSize 22 + the padded string instead of 18. This is not a fringe case: sowt, the little-endian sample variant macOS has written for two decades, is an AIFF-C compression type. A reader that hard-codes 18 misparses every one of them.