Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Audio Formats

Audio resource families

DD1.DAT contains two independent audio families:

ExtensionMembersExpanded bytesPurpose
ABT41205,513Compressed unsigned eight-bit mono sound effects.
XMI3234,618IFF/XMIDI music sequences.

The scene interpreter prepares an effect with opcode 0x57, whose operands are an effect number and playback rate. Every nonzero invocation in the recovered command regions uses rate 9,000 and constructs D001.ABT through D041.ABT. Opcode 0x58 then starts or restarts that retained effect. A 0, 0 opcode-0x57 invocation stops active playback and releases the retained sample. Music opcode 0x52 selects a numeric track and play_music_resource constructs either a MUS###.XMI or IBM###.XMI name according to a runtime mode flag.

The decoded effect buffer belongs to the current scene. The common scene-initialization path calls release_sound_effect_buffer, stopping any active instance and discarding the retained sample before interpreting the new BIN. New-session initialization reaches the same cleanup path.

ABT header

The game contains its complete ABT decoder at unpacked load offset 0x92E0. Each member starts with this nine-byte header:

OffsetSizeInterpretation
0x002Decoded sample count, little endian.
0x022Playback rate in hertz, little endian.
0x041Samples emitted by each delta block.
0x051Codec identifier.
0x062Auxiliary field not used by the decoder.
0x081Initial unsigned PCM sample.

All 41 resources use a 9,000 Hz rate, delta blocks of 32 samples, and codec identifier 2. The auxiliary word is 128 in 35 files, 32 in four, 64 in one, and 320 in one. Its purpose remains unknown; the decoder explicitly consumes and discards it.

The decoded population contains 412,282 samples, or approximately 45.809 seconds at 9,000 Hz. Samples are unsigned eight-bit mono PCM. The smallest effect is 185 samples and the largest is 32,933 samples.

ABT command stream

The initial sample is followed by variable-length commands until the declared sample count has been produced:

Control byteEncoding
Bit 7 setAbsolute sample. The output byte is the control byte shifted left once.
Bit 7 clear, bit 6 setRepeat the previous sample by the low six-bit count.
Bits 7 and 6 clearAdaptive delta block selected by the high nibble with step size (low nibble + 1).

Delta mode 1 uses one-bit codes and the signed table [-step, +step]. Mode 2 uses two-bit codes and starts at -2 * step. Modes 0 and 3 use four-bit codes and start at -8 * step. Each table advances by step, skips zero, and keeps the executable’s signed-byte wraparound. Packed codes are read most significant bits first. Each delta is added to the preceding output and clamped to the inclusive range 0 through 255.

Across the archive, exact decoding encounters:

Command typeCount
Absolute sample71,094
One-bit delta block2,125
Two-bit delta block1,185
Four-bit delta block6,533
Run-length command1,699

Every member produces its declared sample count and consumes its compressed input exactly, with no unused trailer.

QEMU validation of D003.ABT

The startup sequence plays D003.ABT during the logo. QEMU was launched with the visible Cocoa display, silent audio backend, and GDB remote debugging. A breakpoint at physical address 0xA499 stopped the process at 0627:4229, immediately before the decoded effect is submitted to interrupt 66h.

The live state structure at DS:A0DE contained buffer pointer 5A45:0000, sample count 0x2368 (9,064), callback 14E1:79EC, and rate 0x2328 (9,000). Dumped 9,064 bytes from physical address 0x5A450. The live buffer and independently decoded host output are byte-for-byte identical:

ca97ad22acf3cc39d078b619168fa026deb1606082999bfb8b9a1aac4957422b

This validates the header, every command family exercised by D003.ABT, code bit order, signed delta table, clamping behavior, and final sample count against the running DOS program.

Converting ABT to WAV

Inspect an effect or write standard unsigned eight-bit mono PCM:

tools/convert_abt.py build/dd1/all/306_D003.ABT
tools/convert_abt.py \
  build/dd1/all/306_D003.ABT \
  --output build/audio/d003.wav

The decoder rejects truncated commands, output overruns, invalid block geometry, sample-count mismatches, and unused input bytes. The WAV rate comes from the ABT header rather than being hard-coded.

XMI: IFF/XMIDI music

The archive has matching numeric families MUS001.XMI through MUS016.XMI and IBM001.XMI through IBM016.XMI. Every file has two top-level IFF containers. IFF sizes are big endian, while the two-byte sequence count in INFO is little endian:

FORM XDIR
  INFO  01 00
CAT  XMID
  FORM XMID
    TIMB
    EVNT

All files declare exactly one sequence and contain one FORM XMID. TIMB is an even-length list of two-byte patch/bank pairs. EVNT uses XMIDI’s MIDI-like event representation:

  • bytes below 0x80 before an event are additive delay values;
  • channel statuses carry fixed-size parameters;
  • note-on events add a variable-length duration after note and velocity;
  • system-exclusive and meta events use variable-length payload sizes; and
  • meta event FF 2F 00 ends the track.

Eleven EVNT chunks include one zero padding byte after end-of-track. One track also contains controller parameter 0xFF, so the validator preserves the game data instead of imposing a generic seven-bit MIDI parameter check.

The 32 sequences contain 7,087 events, including 6,608 duration-bearing note events and 173 TIMB entries. MUS001.XMI, loaded during startup, contains 12 timbres, 446 events, 432 notes, eight meta events, and total additive delay 3,016.

Inspect and validate a sequence with:

tools/inspect_xmi.py build/dd1/all/267_MUS001.XMI

The tool validates container sizes and padding, directory counts, form and chunk order, event boundaries, variable-length quantities, and end-of-track.

Executable routines

Load offsetCurrent nameEvidence
0x4091play_music_resourceBuilds MUS###.XMI or IBM###.XMI, loads it, and starts the music driver.
0x4155release_sound_effect_bufferReleases the retained decoded PCM allocation.
0x417Fplay_sound_effect_resourceStops/releases the preceding sample, builds D###.ABT, decodes it, and asks DIGPAK to preformat the retained PCM state without starting it.
0x4235start_prepared_sound_effectStops any active instance, resets completion state, and submits the retained preformatted sample for playback.
0x92D0abt_get_sample_rateReturns the little-endian word at ABT offset 2.
0x92E0decode_abtReads the header and dispatches absolute, run, and delta commands.
0x93BEabt_decode_1bit_delta_blockExpands eight one-bit delta codes per byte.
0x94CBabt_decode_2bit_delta_blockExpands four two-bit delta codes per byte.
0x956Eabt_decode_4bit_delta_blockExpands two four-bit delta codes per byte.

Offsets use the unpacked load-module convention documented elsewhere in this book.

The separate sound-driver chapter maps the DIGPAK/MIDPAK interrupt interface, the exact playback structure receiving decoded ABT data, and the installed OPL timbre library.

The Rust SDL frontend parses the same EVNT stream at XMIDI’s 120 Hz timebase and renders it on a music stream separate from digital effects. Its portable two-operator synthesizer uses the transpose, operator multiplier, total-level, envelope, waveform, and connection fields from the original SOUND.4 timbres. It intentionally approximates the installed driver’s analog OPL output rather than emulating a YM3812 chip cycle by cycle. Completed PCM is cached per music identifier and queued repeatedly until a stop or replacement request.

An exact event inventory of the 16 MUS resources found only note-on, program-change, and controller events—no pitch-bend or embedded XMIDI loop controllers. Controller 0 selects banks, controller 7 sets volume, and controller 10 supplies stereo pan which the mono OPL renderer may ignore. MUS016 additionally contains standard RPN/NRPN setup events but no pitch bends for those settings to modify. Percussion uses bank 0x7F; its directory patch selects the drum timbre and the timbre record’s signed note byte supplies the oscillator pitch.