ACIDCAT . FILE FORMAT REFERENCE

Kurzweil Anatomy

K2000 / K2500 / K2600VAST . big-endian
rev 2026.07
magic "PRAM"
endian big (68k)
container object db
lineage K2000 1991

A .KRZ file is not a chunked container — it is a flat object-database dump. A 32-byte PRAM header, then a run of length-prefixed object blocks (Sample, Keymap, Program, Setup, Studio-FX...), an int32 = 0 end marker, and one contiguous region of raw 16-bit PCM the samples point into by absolute word offset. Everything is big-endian — the K-series is a Motorola 68000 platform. Objects cross-reference by numeric id: a Program layer names a Keymap, a Keymap entry names a Sample. The tabs open each structure, drawn byte by byte from a real bank. Hover a field to light its bytes, click a field with a + for its table. Color marks kind (see the key).

Where a WAV or AIFF is a self-describing tree of chunks, a Kurzweil bank is a database dump: a header, a flat list of independent objects, an end marker, then the audio. There is no preset/voice/zone hierarchy inside an object — instead a bank is three kinds of object that reference each other by numeric id. A Program (a VAST synth patch) names a Keymap; a Keymap's key entries name Samples; the Samples point by absolute word offset into one shared PCM blob at the end of the file. The object below is a real Sweetwater bank, e3_bass.krz.

a real bank, laid out
PRAM osize=1028 OS v2.08 — 32-byte file header ├─ Sample #200 "UNNAMED WS" 39101 Hz loop, root C2 — KSample + Soundfilehead ├─ Keymap #200 "E3 BASS C2-C4" -> sample 200, 127 keys ├─ Keymap #201 "E3 BASS -L2" -> sample 200 └─ Program #200 "E3 BASS" 1 layer -> keymap 200 int32 = 0 — object-section end marker PCM 0x0404 .. EOF 205,610 bytes 102,805 samples — raw 16-bit BIG-endian
the K-series, dated
1991K2000 — Kurzweil's VAST engine (Variable Architecture Synthesis): sample playback into a DSP block chain. The .KRZ bank format is its disk-save.
1996K2500 — expanded VAST, more DSP, same bank model. Reads K2000 banks.
1999K2600 — the last of the line; the format is stable across all three, disambiguated by the OS version in the header.
68kbig-endian throughout. The K-series runs a Motorola 68000, so every size, id, and sample word is big-endian — the opposite of the little-endian RIFF/WAV world and of E-mu's E4B.
the type-in-the-hash trick. Every object's 16-bit hash packs both a type and an id: hash = (type << 10) | id. So 0x98C8 is (38 << 10) | 200 — type 38 (Sample), id 200. The reader recovers type = hash >> 10 and id = hash & 0x3FF. Hardware-confirmed types: 36 Program, 37 Keymap, 38 Sample; inferred from object names (tentative): 28 Studio/FX, 39 Setup, 25 Master. The three id spaces (sample/keymap/program) all start at 200 and can collide harmlessly — the type qualifies the id.
walk by the negative length. Each object opens with a signed 32-bit blocksize stored negative — a reader advances to the next object by adding −blocksize to its position. The walk ends at an int32 = 0. After that marker sits one contiguous PCM region, word-addressed as osize + word_index × 2. A forged blocksize can point anywhere, so each step must be bounded to the file.
RAM ≠ file. The K2000's live SysEx object dump returns a fixed-layout RAM object, not this tagged-segment disk format — the firmware converts on load and save. These bytes describe the .KRZ file, the form a librarian or converter sees on disk.
magic"PRAM"size32 bytesendianbigosizepoints at PCM

The 32-byte file header. Four things matter: the PRAM magic, the osize pointer to where the PCM sample region begins (which is also the end of the object section), and the software version that dates the bank. The remaining words are reserved — the mpc2emu writer leaves them zero, but hardware-saved banks carry non-zero values whose meaning is not pinned down.

PRAM header (32 bytes)

Example: e3_bass.krz — a small 4-object bank, K2000 OS v2.08.

osize is a pointer, not a size. The name is misleading: osize is the absolute byte offset where the PCM begins (written last, patched in after all objects are laid out). It doubles as the end of the object section. A file where osize == filesize has objects but no samples (a program-only or keymap-only bank).
blocksizenegative i32hashtype << 10 | idname≤ 16 ASCII

Every object — whatever its kind — opens with the same framing: a negative blocksize to walk by, a hash carrying type and id, an obj_size, a name offset, the ASCII name, and word-alignment padding. Only after this common header does the type-specific body begin. The map below is the header of the Sample object from the bank on the Overview tab.

object block header (Sample #200)

The 22 bytes from block start to the object-specific data. Hover the hash to see the type/id split.

the name offset. name_ofs is the distance from its own field to the start of the object data: name_len + 3 for an odd-length name, name_len + 4 for even (the extra byte word-aligns). "UNNAMED WS" is 10 chars (even), so ofs = 14 and the data starts 14 bytes past the field — after the name and its two null pad bytes.
KSample12 BSoundfilehead32 Bloop bit0x80 invertedratefrom period

A Sample object body is a 12-byte KSample header (mono/stereo, header count) then a 32-byte Soundfilehead per channel: root key, loop flag, the word offsets into the shared PCM blob, and the sample period from which the rate is derived. Two envelope structs follow (not drawn). The whole thing points into the PCM region — it carries no audio itself.

KSample + Soundfilehead (44 bytes)

Example: Sample #200, 16-bit mono, 39101 Hz, looped, root C2. Hover sf_flags for the loop encoding.

the inverted loop bit (HW-confirmed). Soundfilehead.flags is 0x70 when looped and 0xF0 when one-shot — the loop on/off bit 0x80 is inverted (set = one-shot). The 0x10|0x20|0x40 bits are the load + playback-enable set; 0x40 alone loads the sample but produces no sound (the original "silent KRZ" bug). For a looped sample sampleEnd is the loop end word, not the PCM end.
rate from period. There is no sample-rate field. samplePeriod is a big-endian u32 = round(1e9 / rate) in nanoseconds, so rate = round(1e9 / period). Here 0x000063E7 = 25575 ns → 39101 Hz.
header28 Bentries128 keysmethod0x03 / 0x13mapskey -> sample

A Keymap is one voice's key-to-sample map: a 28-byte header, then up to 128 fixed-size key entries. Each entry names the Sample id to play for that key (the K2000 auto-transposes it from the sample root). The entry width is set by the method field — and it is not the same in every bank, so the stride must be read, never assumed.

KKeymap header (28 bytes)

Example: Keymap #200 from e3_bass.krz. Hover method and entry_size.

key entry (3 bytes, method 0x03)

One key's mapping. This bank uses method 0x03 — a 3-byte entry with no per-key tuning.

two entry layouts in the wild. The modern K2000 (and the mpc2emu writer) save method 0x0013: a 5-byte entry, tuning[i16] | sampleID[u16] | SSNr[u8]. But other banks use method 0x0003: a 3-byte entry, sampleID[u16] | SSNr[u8] with no tuning prefix — so the Sample id sits at offset 0, not offset 2. A reader that hard-codes the 5-byte layout reads the wrong bytes here (it lands on 0x0100 = 256 instead of the real 0x00C8 = 200). The entry_size field is the authoritative stride; the method low nibble bit 0x10 is what adds the tuning word.
encodingtagged segmentsseg lenby tag & 0xF8endsint16 = 0layers1 LYR each

A Program is a VAST patch: a stream of tagged segments, each a 1-byte tag then a fixed number of data bytes, terminated by int16 = 0. The segment length is keyed by tag & 0xF8 (a family), with one exact-tag exception. The shape is PGM FX once, then per layer a run of LYR ... ENC ENV ENC CAL HOB HOB HOB HOB — one LYR and one CAL (which names the layer's Keymap) per voice.

program stream (first 24 bytes: PGM + FX)

Example: Program #200. Hover pgm_tag / fx_tag to see the two segment boundaries.

segment lengths (data bytes after the tag). 0x08 PGM / 0x09 LYR = 15; 0x0F FX = 7 (the exact-tag exception — it lives in the 0x08 family but is short); 0x18 FUN = 3; 0x10 ASR / 0x14 LFO / 0x68 KDFX = 7; 0x20 ENC / 0x50 HOB = 15; 0x40 CAL / 0x78 KB3 = 31. Counting LYR (0x09) tags gives the layer count; the per-parameter VAST decode (filter, envelopes, LFO) is undocumented.
CAL holds the keymap link. Each layer's CAL (0x40, 31 bytes) references its Keymap id in bytes [11,12] only — a second keymap slot at [7,8] must stay zero, or the K2000 claims two keymaps per layer and goes silent past four layers. This is the Program → Keymap edge of the reference graph.
magic"SROM"notPRAMcontenteffects / sample ROM

Not every .krz is a PRAM bank. Effects and sample-ROM files use a different, simpler container that opens with SROM instead of PRAM — a magic, a declared size, then a zero-filled body. The header must be read on its own terms rather than parsed as an object bank; the SROM body layout is undocumented.

SROM header (8 bytes)

Example: verbhall.krz, a reverb-hall effects file.

two magics to sniff. A .krz is Kurzweil on either PRAM or SROM at offset 0. Files like flangefx, gatedvrb, verbhall, and chorus are SROM; the rest of a soundset is PRAM. Anything else at offset 0 is not a Kurzweil bank.