A RIFX WAVE file is one outer chunk that holds a list of inner chunks. Every chunk has the same eight-byte header, so a reader can step over chunks it does not know. All numbers are big-endian, most significant byte first, which the 1991 specification calls Motorola format.
The RIFX header
The file starts with the outer chunk’s header and its form type, 12 bytes in all.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| 0 | 4 | char[4] | ckID | RIFX (52 49 46 58) |
| 4 | 4 | u32 | ckSize | the size of everything after this field: the file size minus 8 |
| 8 | 4 | char[4] | formType | WAVE (57 41 56 45) |
The first 58 bytes of a real file, SciPy’s 32-bit float stereo test file:
00000000 52 49 46 58 00 00 0D FA 57 41 56 45 66 6D 74 20 RIFX....WAVEfmt
00000010 00 00 00 12 00 03 00 02 00 00 AC 44 00 05 62 20 ...........D..b
00000020 00 08 00 20 00 00 66 61 63 74 00 00 00 04 00 00 ... ..fact......
00000030 01 B9 64 61 74 61 00 00 0D C8 ..data....
The RIFX size is 00 00 0D FA, 3,578, and the file is 3,586 bytes. The
format chunk is 18 bytes: format code 3 (float), 2 channels, 44,100 Hz, 352,800 bytes a
second, 8 bytes a frame, 32 bits and a cbSize of 0. The fact chunk says 441
frames, and the data chunk holds 3,528 bytes of samples, 441 frames of 8 bytes.
The outer size
Readers do not agree on the byte order of the outer size. libsndfile reads it big-endian, like every other number in the file. FFmpeg reads it little-endian (“The RIFF header size field is always read little-endian”), though it reads the chunk sizes and the format and fact chunks big-endian. The explorer reads it big-endian and checks it against the file size; in every RIFX file examined for this site the big-endian value matched the file.
A writer should use big-endian, as the specification says for every integer.
As for a RIFF file, the explorer reports an outer size larger than the file, one equal
to the whole file size (8 too many), bytes after the end of the outer chunk, and a size
of 0 or 0xFFFFFFFF, after which it reads the chunks to the end of the
file.
The chunk layout
The rest of the file is a run of chunks, back to back. Offsets are from the start of the chunk.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
| +0 | 4 | char[4] | ckID | four printable ASCII characters naming the chunk, padded with spaces, such as fmt and cue ; the same bytes as in a RIFF file |
| +4 | 4 | u32 | ckSize | the size of the chunk’s data, big-endian, not counting this header or the pad byte |
| +8 | ckSize | bytes | ckData | the chunk’s contents |
+8+ckSize | 0 or 1 | u8 | pad | a zero byte, present only when ckSize is odd |
A chunk size read in the wrong byte order is usually far too large: the 18-byte format chunk above would read as 301,989,888 bytes. A chunk ID that is not printable ASCII, where the next chunk should start, means the sizes before it are wrong; the explorer reports it and stops reading chunks there.
The pad byte
The pad byte keeps every chunk on an even offset. It is easy to forget: an 8-bit mono
file with an odd number of samples has an odd data size, and the next
chunk starts one byte later than the size alone suggests. The same rule applies to the
small chunks inside a LIST.
Some writers leave the pad out. libsndfile always skips a pad byte. The explorer decides by looking ahead: after an odd chunk it takes the pad byte, unless a chunk header that fits starts straight away and none starts one byte later (it looks one chunk further to settle a tie), and then it reports the missing pad.
Chunk order
The 1991 specification gives the WAVE form as
RIFF('WAVE' <fmt-ck> [<fact-ck>] [<cue-ck>] [<playlist-ck>]
[<assoc-data-list>] <wave-data>)
with RIFX in place of RIFF for this format, and says that
“<fmt-ck> must always occur before <wave-data>, and both of these chunks are
mandatory”. Other chunks may appear before or after the data, in any order. A reader
should look for chunks by ID and skip any it does not recognise, using the size and the
pad rule.
| Chunk | Holds | Required |
|---|---|---|
fmt | how the audio is encoded | yes |
data | the samples | yes |
fact | the number of sample frames | for every format but PCM |
LIST | text tags, or labels for cue points | no |
cue | markers at sample positions | no |
smpl | root note and loop points for samplers | no |
bext | broadcast production details | no |
JUNK | reserved space, ignored | no |
A complete file
A 60-byte file holding four frames of 16-bit stereo audio at 44,100 Hz. It is the complete file of the RIFF reference written in RIFX byte order, built for this page rather than taken from a file seen.
00000000 52 49 46 58 00 00 00 34 57 41 56 45 66 6D 74 20 RIFX...4WAVEfmt
00000010 00 00 00 10 00 01 00 02 00 00 AC 44 00 02 B1 10 ...........D....
00000020 00 04 00 10 64 61 74 61 00 00 00 10 00 00 00 00 ....data........
00000030 7F FF 80 01 FF FF 00 01 01 00 FF 00 ............
| Offset | Bytes | Meaning |
|---|---|---|
| 0 | 52 49 46 58 | RIFX |
| 4 | 00 00 00 34 | 52 bytes follow: the file is 60 bytes |
| 8 | 57 41 56 45 | WAVE |
| 12 | 66 6D 74 20 | fmt chunk |
| 16 | 00 00 00 10 | 16 bytes of format data |
| 20 | 00 01 | format code 1, PCM |
| 22 | 00 02 | 2 channels |
| 24 | 00 00 AC 44 | 44,100 samples per second |
| 28 | 00 02 B1 10 | 176,400 bytes per second |
| 32 | 00 04 | 4 bytes per frame |
| 34 | 00 10 | 16 bits per sample |
| 36 | 64 61 74 61 | data chunk |
| 40 | 00 00 00 10 | 16 bytes of samples: 4 frames |
| 44 | 16 bytes | the samples, decoded on Sample data |
Finding the samples
A reader walks the chunks:
- Read 12 bytes and check for
RIFXandWAVE. - Read an 8-byte chunk header, taking the size as big-endian.
- If the ID is
fmt, read the format. If it isdata, note where the samples start and how long they are. - Skip
ckSizebytes, plus one more ifckSizeis odd, and go back to step 2 until the end of the outer chunk or of the file.
A data size of 0 or 0xFFFFFFFF is a placeholder from a writer that could
not go back and fill it in. The explorer then takes the samples to run to the end of the
file and reports it.