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.
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.−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.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.
Example: e3_bass.krz — a small 4-object bank, K2000 OS v2.08.
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).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.
The 22 bytes from block start to the object-specific data. Hover the hash to see the type/id split.
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.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.
Example: Sample #200, 16-bit mono, 39101 Hz, looped, root C2. Hover sf_flags for the loop encoding.
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.samplePeriod is a big-endian u32 = round(1e9 / rate) in nanoseconds, so rate = round(1e9 / period). Here 0x000063E7 = 25575 ns → 39101 Hz.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.
Example: Keymap #200 from e3_bass.krz. Hover method and entry_size.
One key's mapping. This bank uses method 0x03 — a 3-byte entry with no per-key tuning.
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.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.
Example: Program #200. Hover pgm_tag / fx_tag to see the two segment boundaries.
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.[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.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.
Example: verbhall.krz, a reverb-hall effects file.
.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.