ACIDCAT . FILE FORMAT REFERENCE

BFD .bfdlac Anatomy

FXpansion BFDBFDC . big-endian
rev 2026.07
magic "BFDC"
endian big
container IFF chunks
lineage BFD 2003

A .bfdlac is one drum hit for FXpansion BFD, the sampled acoustic-drum engine. The name is BFD + lac (a lossless audio codec), and the container is an IFF chunk grid — but a big-endian one, unusual for a Windows/Mac plugin. A BFDC magic and outer size, then four chunks: fmt (the audio descriptor), BFDi (a pack id), Indx (a block seek index for random access into the stream), and data (the compressed audio). Every known BFDC file is identical in shape: 24-bit, 44100 Hz, stereo. The container and the descriptor read cleanly; the lac codec itself is undocumented, so the audio stays opaque. The tabs open each chunk, drawn byte by byte from a real hit. Hover a field to light its bytes, click a field with a + for its table. Color marks kind (see the key).

BFD's sound library is enormous — thousands of hits per kit, every velocity and articulation captured — so each hit is its own small file, and it is compressed. A .bfdlac is an IFF container (magic BFDC, an outer size = file length − 8) holding four chunks in a fixed order: fmt describes the audio, BFDi tags the pack, Indx is a seek index into the compressed stream, and data is the lossless-coded audio. The hit below is a real one from BFD Crush.

a real hit, laid out
BFDC outer_size 906,302 — magic + (file - 8) ├─ fmt 24-bit, 44100 Hz, stereo, 220,577 frames (5.00s) ├─ BFDi "BFCRU-..." pack / kit identifier ├─ Indx 1024-sample blocks, 216 frames, u32 offset table — random access └─ data 905,342 bytes lac codec (undocumented) — the compressed audio
BFD, dated
2003FXpansion BFD — a deeply multisampled acoustic-drum instrument: many mics, many velocity layers, round-robin hits. The sheer sample count made per-hit compression a necessity.
2008BFD2 — the engine that popularised the modern .bfdlac library layout: one compressed file per hit, indexed for random-access streaming.
2013BFD3 — the current generation; expansion packs (Horsepower, Crush, ...) all ship as BFDC files, uniform in structure.
laca lossless codec. The data chunk holds losslessly-compressed PCM at roughly 7 bits/byte — enough entropy to be clearly compressed audio, but the codec is undocumented. The region and its geometry are readable, the samples are not.
big-endian, on an x86 plugin. BFD is a Windows/Mac VST, yet BFDC chunk sizes and the fmt/Indx integers are all big-endian — the reverse of RIFF/WAV. It is an IFF-family container in the Amiga/Kurzweil tradition, a design choice, not a platform constraint. Read every multi-byte field high byte first. One place it may part from strict IFF: chunks advance by 8 + size with no even-pad byte — but every known chunk size is even, so a pad byte would be invisible here either way.
the Indx is why it streams. A drum engine has to start a hit the instant a note lands and may layer dozens at once, so it cannot afford to decode a file from the top. Indx stores the byte offset of every 1024-sample block into the data chunk, so the player seeks straight to the block it needs. The frame count (216) is ceil(num_samples / block_size) — a checkable invariant.
uniform across the format. Every known BFDC hit carries exactly fmt/BFDi/Indx/data, 24-bit / 44100 Hz / stereo. That uniformity is what lets a reader trust the layout and flag any file that deviates — a truncated data, a wrong outer size — rather than guess.
magic"BFDC"outerfile - 8fmt5 big-endian u32audio24-bit / 44100 / 2ch

The file opens with the BFDC magic and a big-endian outer size = file length − 8, the IFF convention. The first chunk, fmt, is the audio descriptor: five big-endian u32s giving bit depth, an encoding tag, the frame count, the sample rate, and the channel count. Everything a decoder needs to interpret the data stream, minus the codec itself. Both maps are from a real BFD Crush hit.

BFDC header (8 bytes)

Hover outer_size — 906,302, exactly the file length minus 8.

fmt chunk (id + size + 20-byte body)

The descriptor, big-endian throughout. Hover any field to light its four bytes.

outer size is IFF, not RIFF. Like an IFF FORM, outer_size counts everything after the 8-byte magic+size pair, so it equals file length − 8. Checked against the real file length, a mismatch is a fast integrity tell for a truncated or appended file.
the encoding tag is a constant. The second fmt word is always 10 — a codec/encoding id for the lac stream, not a variable. The values that could vary between packs would be the frame count and rate, yet even those are pinned: 24-bit, 44100 Hz, stereo. A file that reports anything else is worth a second look.
block_size1024 samplesframes216tableu32 per blockpurposerandom access

The Indx chunk is a seek table over the compressed stream. It gives the block size (how many samples one compressed block decodes to) and the frame count (how many blocks), then a u32 offset for each block: the byte position of that block inside the data chunk. With it, the engine jumps straight to any point in the hit without decoding from the start. The map is the chunk header plus its first two table entries.

Indx chunk (id + size + block/frame + first offsets)

Hover frame_count — 216 = ceil(220,577 / 1024).

compression breaks random access, so the index restores it. A raw PCM file lets you seek by arithmetic: byte = frame × bytesPerFrame. A compressed stream does not — block N starts wherever block N−1 happened to end. Indx stores those unpredictable boundaries so the decoder can start at any block. The first offset is always 0 (block 0 opens the data payload); each next entry is larger.
frame_count is a checkable invariant. With block_size and the fmt's num_samples, the frame count must be ceil(num_samples / block_size) — here ceil(220577 / 1024) = 216. Recompute it and reject a stored count off by more than one, catching a corrupt or mis-sized index before you trust its offsets.
contentlac-coded PCMsize905,342 bytesratio~7 bits/bytecodecundocumented

The data chunk is the hit itself — the 24-bit stereo audio, losslessly compressed by BFD's lac codec. It is the last and by far the largest chunk (905,342 of the file's 906,310 bytes here). There is no public specification for the codec, so the payload is opaque: its position and size are known, its samples are not. The map is the chunk header and the first bytes of the stream.

data chunk (id + size + first stream bytes)

The payload past the 8-byte header is the compressed stream — high-entropy, no readable structure.

~7 bits of entropy per byte. The stream is clearly compressed audio: its entropy sits near 7 bits/byte, well above readable structure but below the ~8 of encryption or random data — the signature of a lossless audio coder. That alone is enough to recognise the region as coded audio without knowing the codec.
opaque by design. The lac codec is unpublished, so a structural read ends at the chunk boundary: name the region, check the outer size and the index, stop. Extracting the data chunk yields the literal compressed stream, byte-for-byte — useful for archival or for feeding a real BFD engine, not decodable to WAV.