Small command-line player for the MIDI music in Little Big Adventure 1 (1994):
point it at MIDI_MI.HQR, MIDI_SB.HQR, or Midi_mi_win.hqr from your game
files and it plays a chosen track through your speakers.
- An HQR file from the game (e.g.
MIDI_MI.HQR) — next to the binary or pass a path. - A General MIDI
.sf2soundfont. The HQR only holds MIDI (note data); the soundfont defines the instruments. Without a font there is no audio.
make
./lba-midi-play MIDI_MI.HQR 3- Linux: install a font package (e.g.
timgm6mb-soundfonton Debian/Ubuntu — see Soundfonts) so a system.sf2can be found automatically. - macOS: put
FluidR3_GM_GS.sf2in/Library/Audio/Sounds/Banks/, or pass any.sf2as the third argument (Soundfonts). - Windows: build with MSYS2 MinGW (
make→.exe), then pass a.sf2path (Build → Windows).
If the program prints that no soundfont was found, pass the font explicitly:
./lba-midi-play MIDI_MI.HQR 3 ./MyFont.sf2Playback exits when the track ends; press Enter to stop early.
Usage · Which HQR file? · Track index · How it works
# Clone or copy this directory alongside your game files, then:
makeThe Makefile picks linker flags from uname: macOS (Core Audio), Linux
(-lpthread -ldl -lm), MSYS2 MinGW (-lpthread -lm — no libdl on Windows).
No configure; only cc, make, and the vendored headers.
Use a MinGW environment (MINGW64 or UCRT64 in MSYS2), not the old MSYS-only
shell, so you get a normal Windows .exe and uname reports MINGW… (what
the Makefile expects).
MSYS maps drives as /d/..., /c/.... Examples:
# Project on D: → D:\lba2-hacking\lba-midi-play
cd /d/lba2-hacking/lba-midi-play
# Or under your MSYS home (often on C:), e.g. /home/User-PC/lba2-hacking/lba-midi-play
cd ~/lba2-hacking/lba-midi-play
make
./lba-midi-play.exe MIDI_MI.HQR 3 C:/path/to/some-font.sf2Pass a .sf2 path as the third argument (Soundfonts); this build
does not search default paths on Windows.
One-time MSYS2 setup (from a MinGW shell):
pacman -S mingw-w64-x86_64-gcc makeRequirements: a C99 compiler (cc / gcc), make, and tsf.h / tml.h /
miniaudio.h (vendored). TinySoundFont and miniaudio — nothing else to link
beyond the OS. Soundfonts load at runtime; see Soundfonts.
# List tracks in an HQR file
./lba-midi-play MIDI_MI.HQR
# Play a track (uses the first system soundfont it finds)
./lba-midi-play MIDI_MI.HQR 3
# Play with a specific soundfont
./lba-midi-play MIDI_MI.HQR 3 /path/to/soundfont.sf2
# Play the Sound Blaster version of the same track
./lba-midi-play MIDI_SB.HQR 3
# Play the LBAWin Windows port version (native SMF, richer arrangements)
./lba-midi-play Midi_mi_win.hqr 3Playback exits when the track ends; press Enter to stop early.
LBA1 exists in three MIDI variants, all 33 tracks (indices 0–32 — see TRACKS.md):
| File | Version | Format | Target hardware |
|---|---|---|---|
MIDI_MI.HQR |
DOS | XMIDI IFF | General MIDI over MPU-401 / Wave Blaster (e.g. Roland Sound Canvas SC-55) |
MIDI_SB.HQR |
DOS | XMIDI IFF | Sound Blaster OPL2/OPL3 FM synthesis |
Midi_mi_win.hqr |
LBAWin port | Native SMF | General MIDI, Windows multimedia |
For the two DOS HQRs, MIDI_MI is the full General MIDI soundtrack — aimed at
MPU-401 / Wave Blaster / external GM hardware. MIDI_SB is the Sound Blaster FM
version: simplified arrangements for the OPL chip on typical machines. Same
music in spirit; MI is the less-constrained arrangement, SB the hardware
compromise.
MIDI_MI follows General MIDI (GM): fixed program numbers (e.g. piano, bass).
Some DOS menus offered Roland MT-32, but MT-32 uses a different instrument map
than GM, so the same file often sounds wrong there. A GM module (e.g. Roland
SC-55) matches what this MIDI expects.
This tool plays all three.
Intended for General MIDI on typical 1990s setups (MPU-401, Wave Blaster, or an external module such as Roland Sound Canvas SC-55). Stored as XMIDI IFF (Miles), converted to SMF on load. Richer than the FM version: more voices, instruments like fretless bass, harp, synth brass, piccolo, layered pads. Files are usually larger (e.g. track 8: 21 980 bytes vs 10 540 in MIDI_SB).
Vachey described early LBA work on an Atari 1040 with samplers, later studio work at Delphine Records — not the same pipeline as the shipped DOS GM files, but in line with MIDI_MI as GM-targeted game audio (MO5.COM interview, 2019).
Intended for the OPL2/OPL3 FM synthesis chip on Sound Blaster cards. The
original source (PERSO.C) selects midi_sb.hqr when MidiFM is set (FM
driver active) and midi_mi.hqr otherwise. Arrangements are simplified: fewer
voices, simpler patches. OPL FM chips have
limited polyphony and cannot reproduce complex pads or layered strings, so
tracks were re-arranged accordingly.
Shipped with the LBAWin Windows port (dated 2001), by Sébastien Viannay (Adeline
— story coding on LBA1, programming on LBA2). Not a commercial release;
distributed via Magicball Network (magicball.net). GOG and Steam ship the DOS
files only; Midi_mi_win.hqr comes from LBAWin separately. It differs from the
DOS HQRs:
- Native SMF — no XMIDI wrapping
- SMF format 1 (multi-track), up to 14 instrument tracks per song
- Higher PPQN: mostly 384 (some 96 or 120) vs 60 for converted DOS XMIDI
- Real tempo per track (about 64–133 BPM) vs forced 120 BPM in this tool’s DOS path
- Longer arrangements: most tracks ~30–50% larger decompressed
- Tracks 24/25: DOS had a unique ~3 kB pair; WIN replaced both with a copy of track 3
Track 31 (Adeline logo jingle) is slightly shorter in WIN (139 vs 166 bytes) and track 26 (FLA flute) is nearly identical.
There is no single “right” soundfont for the GM HQRs; swapping .sf2 files
changes the result a lot. That is separate from which HQR you open: MIDI_MI
and MIDI_SB are different arrangements (GM vs FM-targeted), as above.
Playing MIDI_MI or Midi_mi_win today is largely about trying different GM
soundfonts — same MIDI, different bank, different character.
LBA1 uses 33 MIDI indices (0–32) across the three HQR variants. Scene assignments, sizes, duplicates, CD vs MIDI routing, and naming caveats (vs Philippe Vachey’s separate OST) are in TRACKS.md.
HQR (High Quality Resource) is Adeline's general-purpose asset container.
File layout
───────────
Offset 0x00 uint32 Offset to entry 0's data (= num_entries * 4; doubles
as the index table size, used to compute entry count)
Offset 0x04 uint32 Offset to entry 1's data
…
Offset N*4 uint32 Offset to entry N's data
Offset (N+1)*4 ← sentinel — equals the file size
At each data offset
───────────────────
+0 uint32 Decompressed (real) size
+4 uint32 Compressed size
+8 uint16 Compression mode
+10 bytes Compressed (or raw) data
Compression modes
─────────────────
0 Uncompressed — data is read directly
1 LBA LZ mode 1 — back-reference length = (offset & 0x0F) + 2
2 LBA LZ mode 2 — back-reference length = (offset & 0x0F) + 3
All MIDI entries in the DOS HQR files use compression mode 1.
The compression ratio is roughly 2:1 (e.g. MIDI_MI track 3: 8 612 bytes real,
4 790 bytes stored). Midi_mi_win.hqr also uses mode 1 for most entries;
track 31 and track 26 use mode 0 (uncompressed) in both DOS and WIN.
LBA uses XMIDI from Miles Sound System (MSS) — John Miles / Miles Design (later RAD Game Tools; Wikipedia). XMIDI is an IFF FORM container around MIDI event data.
IFF structure
─────────────
FORM <length>
XDIR
INFO <2> num_tracks (little-endian uint16)
CAT <length>
XMID
FORM <length>
XMID
TIMB (optional: custom timbres — ignored)
EVNT <length> ← MIDI event stream starts here
Key differences from Standard MIDI File (SMF):
| Property | XMIDI | SMF |
|---|---|---|
| Delta time encoding | Custom VLQ: sum non-high-bit bytes (readVLQ2) |
Standard VLQ |
| Note duration | Stored inline after Note-On velocity | Explicit Note-Off event |
| Tempo | Forced to 500 000 µs/beat during conversion | As written |
| Controllers 0x6E–0x78 | Miles-specific (loop, branch, timbres) | N/A |
The converter (xmidi.c, from ScummVM/Exult via TwinEngine) outputs SMF format 0
at 60 PPQN, tempo 500 000 µs/beat (= 120 BPM).
Auto-discovery only checks a few paths where Linux and macOS often put a default GM bank. Anything else (FluidR3, GeneralUser, a download) — pass it as the third argument.
Search order:
- Third argument if given:
./lba-midi-play MIDI_MI.HQR 3 my.sf2 /Library/Audio/Sounds/Banks/FluidR3_GM_GS.sf2(macOS — if you placed that file there)/usr/share/sounds/sf2/default-GM.sf2(Debian/Ubuntuupdate-alternativesdefault GM)/usr/share/sounds/sf2/TimGM6mb.sf2(e.g.timgm6mb-soundfontpackage)/usr/share/soundfonts/default.sf2(Arch symlink — see Arch Linux below)
If nothing matches, pass any .sf2 path (absolute or relative), e.g.
./FluidR3_GM.sf2 next to the binary.
The program reads .sf2 directly — no FluidSynth, TiMidity++, or MIDI daemon.
On Debian/Ubuntu, sudo apt install timgm6mb-soundfont installs TimGM6mb and
registers default-GM.sf2 via update-alternatives, which matches the search
list above.
Larger banks (FluidR3, etc.): install the package (e.g. fluid-soundfont-gm)
and either point update-alternatives --config default-GM.sf2 at FluidR3 or
pass the full path to the .sf2 as the third argument.
Arch: see MIDI /
FluidSynth wikis (e.g.
soundfont-fluid). You can symlink a preferred bank to
/usr/share/soundfonts/default.sf2 so auto-discovery finds it.
Put a GM .sf2 in /Library/Audio/Sounds/Banks/ as FluidR3_GM_GS.sf2 for
auto-discovery, or pass any path as the third argument. ~/Library/Audio/...
is not searched.
No default paths — lba-midi-play … HQR index C:\path\to\font.sf2 (or MSYS
paths).
| Soundfont | Rough size | Notes |
|---|---|---|
| TimGM6mb | ~6 MB | Small; many Linux distros (Debian). |
| FluidR3 GM | ~140 MB | Common reference; Debian, often FluidR3_GM.sf2 elsewhere. |
| GeneralUser GS | ~30 MB | Free GM/GS; author’s site. |
| FreePats General MIDI | varies | Open instruments — FreePats. |
Pass the path as the third argument if it is not your distro’s default-GM
target.
main.c CLI, argument parsing, miniaudio setup, playback loop
hqr.c/h HQR decompression — adapted from TwinEngine (twin-e/src/hqr.c)
xmidi.c/h XMIDI → SMF conversion — adapted from TwinEngine via ScummVM/Exult
tsf.h TinySoundFont — SF2 synthesiser (single-header, Bernhard Schelling)
tml.h TML — SMF parser (single-header, Bernhard Schelling)
miniaudio.h Cross-platform audio I/O (single-header, David Reid / mackron)
The tool auto-detects format by checking the first four bytes of each entry
(FORM = XMIDI, MThd = native SMF):
HQR file (on disk)
│ hqr_get_entry_alloc() — LZ decompress
▼
raw bytes (XMIDI or SMF)
│ if XMIDI: convert_to_midi() — XMIDI IFF → SMF format 0
│ if SMF: used directly (Midi_mi_win.hqr)
▼
SMF bytes (in memory)
│ tml_load_memory() — parse events, build linked list
▼
tml_message list
│ audio callback: advance time, fire tsf_channel_* per event
▼
TSF rendered frames (float32, 44 100 Hz stereo)
│ miniaudio data callback
▼
Speaker output
The callback renders audio in blocks between MIDI events: when the internal
clock reaches event->time, it dispatches to TinySoundFont, then renders samples
up to the next event (or the end of the buffer).
GPL-2.0-only — see LICENSE. Same spirit as the adapted HQR and
XMIDI code (TwinEngine / ScummVM / Exult). TinySoundFont, TML, and miniaudio
keep their own licenses in the headers (MIT or public domain); bundled in a
GPLv2-compatible way.
- LBA MIDI music — Adeline Software International / Philippe Vachey (1994)
- TwinEngine (HQR + XMIDI code) — The TwinEngine team (GPL v2)
- XMIDI conversion — ScummVM / Exult lineage (GPL v2)
- TinySoundFont / TML — Bernhard Schelling (MIT)
- miniaudio — David Reid (MIT / public domain)