A BW64 file is one outer chunk that holds a list of inner chunks, as a WAV file is. Every
chunk has the same eight-byte header, so a reader can step over chunks it does not know.
What differs is the size rule: a size that does not fit in 32 bits is written as
0xFFFFFFFF, and the real one is in the ds64 chunk. All numbers
are little-endian, least significant byte first.
The BW64 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 | BW64 (42 57 36 34) |
| 4 | 4 | u32 | ckSize | 0xFFFFFFFF; the real size, the file size minus 8, is the first field of ds64 |
| 8 | 4 | char[4] | formType | WAVE (57 41 56 45) |
The ds64 chunk must come next, at offset 12. libbw64, the EBU’s reference
library, refuses a BW64 file whose first chunk is not ds64 (“mandatory ds64
chunk for BW64 or RF64 file not found”), and FFmpeg’s format probe gives a BW64 file
full marks only when ds64 is at offset 12.
No BW64 file was among the files examined for this site. This example was built for the
page from a real RF64 file, SciPy’s 3-channel, 24-bit, 8,000 Hz test file of five frames,
with BW64 in place of RF64:
00000000 42 57 36 34 FF FF FF FF 57 41 56 45 64 73 36 34 BW64....WAVEds64
00000010 1C 00 00 00 76 00 00 00 00 00 00 00 2D 00 00 00 ....v.......-...
00000020 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 ................
00000030 66 6D 74 20 10 00 00 00 01 00 03 00 40 1F 00 00 fmt ........@...
00000040 40 19 01 00 09 00 18 00 64 61 74 61 FF FF FF FF @.......data....
| Offset | Bytes | Meaning |
|---|---|---|
| 0 | 42 57 36 34 | BW64 |
| 4 | FF FF FF FF | the outer size: look in ds64 |
| 8 | 57 41 56 45 | WAVE |
| 12 | 64 73 36 34 | ds64 chunk |
| 16 | 1C 00 00 00 | 28 bytes: three 64-bit sizes and an empty table |
| 20 | 76 00 00 00 00 00 00 00 | the outer size, 118: the file is 126 bytes |
| 28 | 2D 00 00 00 00 00 00 00 | the data size, 45 bytes |
| 36 | 05 00 00 00 00 00 00 00 | the frame count, 5 |
| 44 | 00 00 00 00 | no table entries |
| 48 | 66 6D 74 20 10 00 00 00 | fmt chunk, 16 bytes |
| 56 | 01 00 03 00 | PCM, 3 channels |
| 60 | 40 1F 00 00 | 8,000 frames a second |
| 64 | 40 19 01 00 | 72,000 bytes a second |
| 68 | 09 00 18 00 | 9 bytes a frame, 24 bits |
| 72 | 64 61 74 61 FF FF FF FF | data chunk, its size in ds64 |
| 80 | 45 bytes | the samples, then a pad byte, since 45 is odd |
What the explorer reports
The explorer turns down only a file whose first 12 bytes are wrong. Each of these is reported on the chunk where it occurs, and the rest of the file is still read:
- an outer size that is not
0xFFFFFFFF; - a first chunk that is not
ds64, or nods64chunk at all; - a
ds64outer size that disagrees with the length of the file; - a chunk whose size is
0xFFFFFFFFbut whose real sizeds64does not give; it is taken to run to the end of the file; - bytes, or whole chunks, after the end of the outer chunk.
The chunk layout
After the header, 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 chna |
| +4 | 4 | u32 | ckSize | the size of the chunk’s data, not counting this header or the pad byte; 0xFFFFFFFF means the real size is in ds64 |
| +8 | size | bytes | ckData | the chunk’s contents |
| +8+size | 0 or 1 | u8 | pad | a zero byte, present only when the size is odd |
The size rule, as libbw64 applies it: a chunk whose 32-bit size is
0xFFFFFFFF takes its size from ds64, the data chunk from its
dataSize field and any other chunk from the table. Every other size is read
as it is. A chunk
ID that is not printable ASCII, where the next chunk should start, means the sizes
before it are wrong.
The pad byte
The pad byte keeps every chunk on an even offset. It follows a chunk of odd size,
whether the size is in the chunk header or in ds64, and is not counted in
either. The same rule applies to the small chunks inside a LIST.
Some writers leave the pad out. 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
ds64 comes first. The format chunk must come before the data chunk,
because the data cannot be read without it. EBU Tech 3285 supplement 7 places the
chna chunk before the data and axml chunks. Other chunks can
appear before or after the data, in any order, and a reader should skip any it does not
recognise, using the size and the pad rule.
| Chunk | Holds | Required |
|---|---|---|
ds64 | the real 64-bit sizes | yes, first |
fmt | how the audio is encoded | yes |
data | the samples | yes |
chna | which ADM track each channel carries | with ADM metadata |
axml | the ADM metadata, as XML | with ADM metadata |
fact | the number of sample frames | for formats other than PCM |
JUNK | reserved space, ignored | no |
Finding the samples
- Read 12 bytes and check for
BW64andWAVE. - Read the
ds64chunk at offset 12, and keep its sizes and its table. - Read an 8-byte chunk header. If its size is
0xFFFFFFFF, take the real size fromds64. - If the ID is
fmt, read the format. If it isdata, note where the samples start and how long they are. - Skip the size, plus one more byte if the size is odd, and go back to step 3 until the end of the outer chunk or of the file.
Offsets past 4 GB need 64-bit arithmetic all the way through.