ACIDCAT . FILE FORMAT REFERENCE

RF64 / BW64 Anatomy

EBU Tech 330664-bit RIFF . .wav
rev 2026.07
magic RF64 .. WAVE
endian little
sizes 64-bit in ds64
sentinel 0xFFFFFFFF

RF64 is RIFF/WAVE grown past 4 GB. A plain WAV stores its sizes in 32-bit fields, which top out at 4 GB. RF64 keeps the exact WAV chunk layout but writes the sentinel 0xFFFFFFFF into the RIFF and data size fields, and adds one mandatory chunk right after the header: ds64, carrying the real sizes as 64-bit integers. A reader that sees 0xFFFFFFFF must go read ds64 instead. BW64 (the newer name, EBU Tech 3306) is the same structure. This matters: trust the 32-bit field and you compute a 4.29 GB size for every RF64 file, and any scan bounded by that size silently covers nothing. The record drawn below is a real ds64 body. Hover any field to light its bytes; click a field with a + to open its table. Color marks kind (see the key).

file regions

Identical to WAV, with two twists: the magic is RF64 (or BW64) not RIFF, and ds64 must be the first chunk. The 32-bit sizes are placeholders; the truth is in ds64.

▸RF64 header0x00 . 12 bytes

'RF64' (or 'BW64'), then a u32 size field that holds the sentinel 0xFFFFFFFF, then the form type 'WAVE'. The sentinel is the flag: it says "my real size does not fit here, read ds64".

magic"RF64" (or "BW64") at 0x00, "WAVE" at 0x08
riff sizeu32 at 0x04 = 0xFFFFFFFF (sentinel); the real 64-bit size is in ds64
readeron seeing the sentinel, must read ds64 for the true end before scanning anything
▸ds64 chunk0x0C . 64-bit sizes . drawn below

The mandatory first chunk. It carries the real riff size, the real data size, and the sample count as 64-bit little-endian integers, then a table that overrides the size of any other chunk that also overflowed 32 bits. The body below is 28 bytes: three u64 and a u32 table length of zero (no overrides needed for this file).

layoutriffSize(u64) . dataSize(u64) . sampleCount(u64) . tableLength(u32) . then tableLength override entries
override tableeach entry is a 4cc + u64: the real size of another chunk that also holds the 0xFFFFFFFF sentinel
cross-checkriffSize + 8 should equal the file length; this is the real container end
▸fmt + data + the reststandard WAV

From here it is ordinary WAV: fmt, data, and any LIST/bext/cue the writer added. The one difference is the data chunk's 32-bit size, which is also 0xFFFFFFFF when the audio exceeds 4 GB; its real size is the dataSize in ds64.

fmtsame as WAV: format tag, channels, sample rate, bits
dataaudio; size field is 0xFFFFFFFF when it overflows, real size from ds64
everything elsewalked exactly as a WAV; RF64 changes only how sizes are read
reading it safely

The same as WAV, plus the ds64 override. The important part is defensive: never trust the 0xFFFFFFFF sentinel as a real size.

▸ds64-aware decoderead . scan . repair
readthe 64-bit riff/data sizes and sample count from ds64, and the WAV fmt/data as usual
container endds64's riffSize is the real end; a trailing-data or polyglot scan must bound itself there
the trapwithout ds64 the sentinel makes the declared end ~4.29 GB, so a scan past the declared end silently finds nothing
repairRF64 sizes live in ds64, so a WAV size-cascade repair must rewrite them there, not at the sentinels