Skip to content

SMUS — the music score format

War in Middle Earth’s music is 22 .smus files in EA IFF 85 “SMUS IFF Simple Musical Score” (Jerry Morrison, Electronic Arts, February 1987) — a published standard, not a bespoke format. Each score references its instruments by name; those are SampledSound .instr files.

Divisions shorter than a 16th note are silently played as rests. That is not a decoder bug and not a data bug — the Sonix player’s own duration table maps 32nd, 64th and 128th notes to $FF, which takes the silence path. A faithful playback engine has to reproduce it.

FORM <size> SMUS
SHDR <8 bytes> score header (required)
NAME <variable> score name (optional)
INS1 <12+> instrument reference (zero or more)
TRAK <variable> track event stream (one or more)
+0 u16 tempo 128ths of a quarter note per minute
+2 u8 volume master volume, 0–127
+3 u8 ctTrack number of tracks

Divide tempo by 128 for BPM: Aragorn.smus stores 0x397C (14,716) = 115 BPM; Title.smus stores 0x2FA0 (12,192) = 95.25 BPM.

+0 u8 register instrument register index (0, 1, 2 …)
+1 u8 type instrument type code
+2 u8 data1
+3 u8 data2
+4 varies name null-terminated filename stem, e.g. "Piano"

The register field is what TRAK’s instrument-select events refer to; the name field matches a .instr file on disk.

Names can be truncated, and that’s normal. The name field uses chunkSize − 4 bytes; a name longer than the space available is simply cut short — "Bass1Shorter" appears as "Bass1Shor" in Aragorn.smus. Bytes after a null terminator are garbage padding. This is standard SMUS behaviour, not corruption.

A flat sequence of 2-byte events: a type code, then a data byte.

sID Event Data byte means
0–127 Note The sID itself is the MIDI pitch; data is the duration field below
128 Rest Duration field below
129 Instrument Select instrument register
130 Time signature Bits 7–3 = numerator − 1, bits 2–0 = log2 denominator
131 Key signature Key signature code
132 Dynamic Volume / velocity, 0–127
133 MIDI channel Channel assignment
134 MIDI preset Program change
135 Clef Clef type
136 Tempo Tempo change, quarter notes per minute
7 6 5-4 3 2-0
+------+------+------+------+------+
| rest | tie | grp | dot | div |
+------+------+------+------+------+
  • rest (bit 7) — silence for the given duration
  • tie (bit 6) — the note sustains into the next event
  • grp (bits 5–4) — voice group select; when non-zero, Sonix skips the event
  • dot (bit 3) — dotted
  • div (bits 2–0) — 0 whole, 1 half, 2 quarter, 3 eighth, 4 16th, 5 32nd, 6 64th, 7 128th

Duration in quarter-note units:

base = 4 / (1 << division)
if dot: base *= 1.5
if triplet: base *= 2/3
if quintuplet: base *= 4/5
if septuplet: base *= 4/7

Confirmed by reading the player’s own lookup table (LAB_01B0 in Sonix.code). Each entry is a CIA tick count at 8 ticks per quarter note — exactly 8× the quarter-note units above:

Index (div) Division Value Quarter notes
0 whole $20 (32) 4.0
1 half $10 (16) 2.0
2 quarter $08 (8) 1.0
3 eighth $04 (4) 0.5
4 16th $02 (2) 0.25
5–7 32nd–128th $FF rest — unsupported

That last row is the hardware limitation in the callout above: three of the eight encodable divisions have no playable duration on this player at all.

The reimplementation interprets SMUS directly through a Web Audio renderer rather than pre-converting the scores to another tracker format — the build step now emits only a manifest, and no audio files are generated ahead of time.

docs/formats/smus.md in the main repository (not part of this site), with WIME’s own playback notes in docs/wime/amiga/music-playback.md and the known decoder issues in docs/wime/amiga/smus-decoder-issues.md.