ACIDCAT . FILE FORMAT REFERENCE

VOC Anatomy

Creative Voice FileSound Blaster . 1990
rev 2026.09
magic Creative Voice File
header 26 bytes
lengths u24
endian little

The sample format of the DOS era, written by the Sound Blaster's own tools and carried inside almost every Build-engine and early-90s game archive. A 26-byte header, then a chain of typed blocks: a type byte, a 24-bit little-endian length, and a payload. Three things about that chain send a reader quietly wrong rather than stopping it: the terminator has no length, the continuation block has no format, and the length is three bytes where the eye expects four.

the three ways to get it wrong

The terminator has no length field. Every block is a type byte and a three-byte length -- except type 00, which is one byte and ends the file. A reader that reads four bytes for every block header consumes three bytes past the end of the data, and on a file that ends exactly at its terminator it reads past EOF. This is the single most common way to walk the format wrong.

Block 02 carries no format bytes. It continues whichever sound block came before it, so a reader that treats every sound block as self-describing loses the rate on every file longer than one block. A long sound is commonly one block 01 and a run of 8,192-byte continuations; the byte map below shows one, with the samples either side of its header so the join can be read rather than taken on trust.

The length is 24 bits, not 32. Three bytes, little-endian, so a block caps at 16 MB and the fourth byte of what looks like a u32 is the next field. Read it as a u32 in a block 01 and the time constant is swallowed into the length, taking the sample rate with it.

the header

Twenty-six bytes, and the last four are worth reading rather than skipping: a version, and a checksum derived from it as (~version + 0x1234) & 0xFFFF. It validates nothing about the audio, but it is the only integrity check the format has, and a mismatch means the header was not written by something that knew the rule.

block 01 -- the original sound block

A time constant and a codec byte, then unsigned 8-bit samples. The constant is a hardware divisor rather than a rate, and the rate it produces is deliberately not round.

block 02 -- the one that carries no format

A sound longer than one block is split, and every block after the first is a type 02 that states nothing about itself. No time constant, no codec, no rate. It inherits all of it from the sound block before it, so a reader that asks each block what it is gets an answer from the first one and silence from the rest.

block 09 -- what 1.20 added

The same audio, described directly: an explicit u32 rate, a bit width, a channel count and a format code. Block 09 exists because the time constant could not express a rate exactly, and it is the version worth preferring when a file offers both.

block 05 -- the file labelling itself

Game data names its own sounds more often than you would expect, which makes the text block worth reading rather than skipping past as metadata.

the block types
typewhat it isnotes
00terminatorone byte, no length field
01sound data: time constant + codec, then samplesthe original, and the common case
02sound continuationno format bytes of its own
03silence: a duration and a time constantoccupies time, carries no samples
04markera u16 id
05ASCII textNUL-terminated, inside the length
06repeat starta u16 count
07repeat endcloses the nearest 06
08extended formatprecedes a block 01 and overrides it
09sound data with an explicit u32 rateadded in v1.20

Types 03, 04, 06, 07 and 08 are defined by the format and seldom written. A reader should frame them from their documented layouts so the chain does not derail on a file that has them, and should not claim to have verified what it has never seen.

the rate, and why it is two numbers

A block 01 stores a divisor, not a rate, and the rate is 1000000 / (256 - tc). The constants in use produce values like 5988, 8000, 10870, 10989, 11111, 21739 and 22222 Hz -- clustering around the era's 11025 and 22050 in exactly the way a coarse integer divisor would, and never landing on them.

A file is not required to state its rate twice, so nothing inside a block 01 confirms the formula. That is the argument for reporting the constant the file holds alongside the rate derived from it: the constant is a fact about the bytes, and the rate is an inference from a documented formula. Block 09 removes the question entirely by writing the rate as a plain u32, which is why a file that offers both is better read from the 09.