Apparent bug in DOOM's DMXGUS instrument mapping process

Last modified: Sat Mar 12 20:07:27 EST 2016

Introduction

The music in the canonical DOOM and DOOM2 WADs uses the instruments of General MIDI (GM) with a few percussion instruments that were added in GS.  When DOOM or DOOM2 is paired with a Gravis Ultrasound ISA card, the sound sample to use for each instrument is specified by a mapping contained within the WAD file, called DMXGUS or DMXGUSC respectively.  These mappings were developed by Tom Klok to improve on default mappings that had been used earlier, and his modified mappings ultimately were incorporated into the final, published WADs.

Many people have noted some surprising behaviors.  On E1M8/E3M4/E4M1, what sounds like drums on any other sound card comes through on a GUS as bells.  On DOOM2 maps 1 and 15, the first few bars are marred by a dissonant clanging noise that again reproduces on no other sound card.  The latter annoyed me so much that I dumped the DMXGUSC lump from the WAD file to try to figure out what the plan was.  Unfortunately, I think the behavior was not planned but is rather a DOOM bug that has escaped capture for a very long time.

This sort of bug should have been discovered and discussed on Usenet in 1994 or 1995, and in fact may have been, but no such discussion is turned up by Google today.

Analysis

(Unless otherwise noted, all instrument numbers in this discussion use the convention of zero-based counting for non-percussion instruments and adding 128 for percussion instruments.)

The instrument heard in the first few bars of E1M8/E3M4/E4M1 is instrument 118, which GM calls Synth Drum and Gravis called syntom (patch file SYNTOM.PAT).  In the DMXGUS lump, for a GUS with 1024 KiB of RAM, this instrument has apparently been remapped to instrument 117, Melodic Tom (toms).

# patch#, 256K, 512K, 768K, 1024K, filename
118,115,117,117,117,syntom

Of course, that's not what we're getting.  Those bells are actually instrument 112, Tinkle Bell (carillon).  How does this happen?

Well, here are some more lines from DMXGUS.  Specifically, these are all of the lines in which the 1024 KiB mapping points to 117 toms.

# patch#, 256K, 512K, 768K, 1024K, filename
112,114,114,112,117,carillon
113,114,114,112,117,agogo
114,114,114,114,117,steeldrm
115,115,115,115,117,woodblk
116,115,117,117,117,taiko
117,115,117,117,117,toms
118,115,117,117,117,syntom

This is no simple off-by-one error.  Now, let's look at DOOM2 map 1.  The dissonant clang happens when instrument 117 toms is somehow replaced by 114 Steel Drums (steeldrm).  Here are all of the lines from DMXGUSC in which the 1024 KiB mapping points to 117 toms.

# patch#, 256K, 512K, 768K, 1024K, filename
114,114,114,114,117,steeldrm
115,115,115,115,117,woodblk
116,115,117,117,117,taiko
117,115,117,117,117,toms

Again, we have gotten neither the canonical GM instrument nor the one pointed to by the mapping, but rather the first instrument in the group that has the same mapping.

If we keep listening to map 1, a plain-sounding piano starts playing.  The original music called for instrument 18, Rock Organ (rockorg), which was supposed to be mapped to instrument 6, Harpsichord (hrpschrd).  Here is the context from DMXGUSC:

# patch#, 256K, 512K, 768K, 1024K, filename
3,2,1,1,6,honky
6,2,1,1,6,hrpschrd
7,2,1,1,6,clavinet
16,2,16,16,6,homeorg
17,2,16,16,6,percorg
18,2,16,16,6,rockorg
19,2,16,16,6,church
20,2,16,16,6,reedorg
21,2,16,16,6,accordn
22,2,16,16,6,harmonca
23,2,16,16,6,concrtna

The piano is plain-sounding because it is actually instrument 3, Honky-tonk Piano.

The pattern seems clear.  It sure looks like a bug, but maybe somebody was thinking that all instruments with the same mapping are equivalent to the point that we may as well just use the first one.

Bug or feature?

The steel drum on DOOM2 map 1 is so horrible that I can't believe it would have been done intentionally, but by the same token, it's hard to believe that such a glaring anomaly on the very first map would not have been noticed and fixed if it were unintentional.

To confirm this as an irrefutable bug, either Tom Klok would have to confirm that the observed behavior is not what was intended or someone with access to the documentation for the DMX sound library (assuming it was even documented) would have to confirm that the observed behavior is not what was specified.  I tried emailing Tom Klok at his 1994 address but it bounced.

Another thing:  arbitrarily dropped percussion instruments

The GUS module of the DMX sound library unconditionally drops instruments numbered 128 through 162 and 210 and up.  DOOM music is affected by this (e.g., DOOM2 map 2 uses 210, 213, and 214, which would have been shaker, castinet, and surdo1 respectively).

This one seems intentional because the function is exactly to filter out any percussion instruments that are outside the range that is defined by GM (lower than 35 or higher than 81 in the GM numbering system for channel 10).  This would have made sense if we were using the generic GM driver because XG and GS disagree on these extended instruments.  However, since the GUS mapping of the affected instruments agrees with GS, this is a misfeature when applied to the GUS.

Here are the parts of DMXGUS and DMXGUSC that get nullified as a result.  The fact that some non-blank mappings were done suggests that the author(s) of the mappings were unaware of the filtering.

DMXGUS DMXGUSC
128,128,128,128,128,blank
155,128,128,128,128,highq
156,128,128,128,128,slap
157,128,128,128,128,scratch1
158,128,128,128,128,scratch2
159,159,159,159,128,sticks
160,128,128,128,160,sqrclick
161,128,128,128,160,metclick
162,128,128,128,162,metbell
210,128,128,128,128,shaker
211,128,128,128,128,jingles
212,128,128,128,128,belltree
213,128,128,128,128,castinet
214,128,128,128,128,surdo1
215,128,128,128,128,surdo2
 
128,128,128,128,128,blank
155,128,128,128,128,highq
156,128,128,128,128,slap
157,128,128,128,128,scratch1
158,128,128,128,128,scratch2
159,159,159,159,128,sticks
160,128,128,128,128,sqrclick
161,128,128,128,128,metclick
162,128,128,128,128,metbell
210,128,128,128,210,shaker
211,128,128,128,209,jingles
212,128,128,128,209,belltree
213,128,128,128,213,castinet
214,128,128,128,214,surdo1
215,128,128,128,214,surdo2

Aside from blank, the patch names are in agreement with the list of "Additional percussion notes" that Wikipedia identifies in the delta of GS versus GM.

Patching and emulating vanilla DOOM

The DMXGUS[C] mapping of DOOM or DOOM2 can be patched by loading a modified lump with the -file switch, just like a PWAD.  The dropping of non-GM percussion cannot be fixed this way, but it is possible to work around the wrong-instrument problem for all other instruments by replacing the patch name for the first instrument in each mapping "group" with the intended target for the whole group.  A limitation of this approach is that only one of the memory-size mappings can be unscrewed at a time, since the groups are different in different mappings.

Here is DMXGUS-fix.rb:  a Ruby script that, given a DMXGUS or DMXGUSC lump extracted from a WAD file, generates Timidity config files to replicate the observed and expected Gravis Ultrasound instrument mappings of DOOM/DOOM2, plus a modified DMXGUS[C] lump that works around the apparent bug in vanilla DOOM/DOOM2.

DMXGUS-fix.rb by David Flater 2016-03-06, license GPLv3
Usage: DMXGUS-fix.rb DMXGUS-orig DMXGUS-out expected.cfg observed.cfg u|l path [1|2|3|4]
DMXGUS-orig:  (input filename)  DMXGUS[C] lump extracted from a WAD file
DMXGUS-out:   (output filename) modified DMXGUS[C] lump
expected.cfg: (output filename) timidity config to replicate expected behavior
observed.cfg: (output filename) timidity config to replicate observed behavior
u|l:   specify uppercase or lowercase filenames for patch file collection
path:  full path of directory containing the patch files
[1-4]: select 256, 512, 768, or 1024 KiB mapping (default 1024)

Note that the script does not replicate the discarding of instruments that occurs if the requested patches do not fit into the GUS's RAM.  It remains unclear whether this is occurring with DOOM and DOOM2.

Generated output for all four mappings is in FIXDMXGU.zip.  Each of the four subdirectories, named 256, 512, 768, and 1024 per the selection of memory size, contains:

In the root directory are the original DMXGUS and DMXGUSC lumps for reference.

Samples

All of the below samples use the 1024 KiB mapping.  "Actual GUS" means recorded line output from a Gravis UltraSound "Classic" Rev. 2.4 while vanilla DOOM was playing the music.  "Timidity" means TiMidity++ 2.14.0.  The particular levels were selected because the following differences were readily apparent during testing:

GameLevelActual GUS unpatchedActual GUS with patchTimidity "observed"Timidity "expected"
DOOME1M8
DOOM2Map 1
DOOM2Map 2
DOOM2Map 5
DOOM2Map 20

If your browser is being dumb about the audio links, try this dumbed-down version:

GameLevelActual GUS unpatchedActual GUS with patchTimidity "observed"Timidity "expected"
DOOME1M8 Clicky Clicky Clicky Clicky
DOOM2Map 1 Clicky Clicky Clicky Clicky
DOOM2Map 2 Clicky Clicky Clicky Clicky
DOOM2Map 5 Clicky Clicky Clicky Clicky
DOOM2Map 20 Clicky Clicky Clicky Clicky

Acknowledgment

Thanks to Fraggle for helpful tips and information.


KB
Home