Skip to content

File Format: MUS and SAM

SalsaGal edited this page Aug 24, 2024 · 7 revisions

.mus/.sam files are a proprietary format developed by Crystal Dynamics. It stores sequences, custom attributes, etc.. It is similar in concept to Sony's seq, vh, vb, and vab files developed for the PlayStation, and MIDI files with their respective SoundFonts or DownLoadable Sounds (.mid/.sf2/.dls). It was used in Crystal Dynamics game that were developed from 2000 to 2007.

Names

Names can be up to 20 ASCII characters. If not all 20 are used then the name terminates when non-printable or junk ASCII characters are found. It is also possible that the name could be padded with spaces.

Mus

Header

There are currently three known versions of the mus header, 1.8 and 1.14 used in early 2000s Crystal Dynamics games while 1.20 is used in later games.

The magic number should be "Mus!".

1.8 and 1.14

Name Type Endianness
Magic number ASCII character x4 N/A
Header size Unsigned 32 bit integer Little
Bank version Unsigned 32 bit integer Little
Reverb volume Signed 32 bit integer Little
Reverb type Signed 32 bit integer Little
Reverb multiply Signed 32 bit integer Little
Number of sequences Unsigned 32 bit integer Little
Number of labels Unsigned 32 bit integer Little
Label offsets table start Unsigned 32 bit integer Little
Number of waves Unsigned 32 bit integer Little
Number of programs Unsigned 32 bit integer Little
Number of presets Unsigned 32 bit integer Little

1.20

Name Type Endianness
Magic number ASCII character x4 N/A
Header size Unsigned 32 bit integer Little
Bank version Unsigned 32 bit integer Little
Reverb volume Signed 32 bit integer Little
Reverb type Signed 32 bit integer Little
Reverb multiply Signed 32 bit integer Little
Number of sequences Unsigned 32 bit integer Little
Number of streams Unsigned 32 bit integer Little
Stream BPM Unsigned 32 bit integer Little
Stream Info Offset Unsigned 32 bit integer Little
Number of labels Unsigned 32 bit integer Little
Label offsets table start Unsigned 32 bit integer Little
Number of waves Unsigned 32 bit integer Little
Number of programs Unsigned 32 bit integer Little
Number of presets Unsigned 32 bit integer Little

Sequence Offsets

The number of sequences is specified in the header

Name Type Endianness
Sequence number Unsigned 32 bit integer Little
Sequence offset Unsigned 32 bit integer Little

Program Offsets

The header specifies the number of programs.

Name Type Endianness
Program Offset Unsigned 32 bit integer Little

Preset Offsets

The header specifies the number of presets.

Name Type Endianness
Preset Offset Unsigned 32 bit integer Little

Wave Entry

There are as many waves as specified in the header. The raw data is stored in a headerless sam file of the same name.

Wave size counts in 16-bit words, so to get the actual size in bytes, it needs to be multiplied by 2.

Wave loop start and Wave loop end refer to the 16-bit sample units when a wave is decoded into PCM.

Wave loop info can be one of the following values:

  1. No loop
  2. Loop
  3. Exit loop region after key is released

Wave fine tuning specifies both the note in which the sample is tuned to and the fine tuning at the same time. The note it's tuned to is integer part of the number divided by 256. Since the fine tuning uses a scale based on 256, we need to scale it down to 100 due to it usually being specified in cents. We lose accuracy doing this, but it is needed for compatibility. The equation is

(100 * (wave_finetuning % 256)) / 256

The use for Wave SFX handle is not well documented.

Name Type Endianness
Wave name Name N/A
Wave offset Unsigned 32 bit integer Little
Wave loop start Unsigned 32 bit integer Little
Wave size Unsigned 32 bit integer Little
Wave loop end Unsigned 32 bit integer Little
Wave sample rate Unsigned 32 bit integer Little
Wave fine tuning Signed 32 bit integer Little
Wave loop info Unsigned 32 bit integer Little
Wave SFX handle Signed 32 bit integer Little

Program Entry

There are as many programs as specified in the header. Inside each entry is a certain number of zones.

Name Type Endianness
Program name Name N/A
Number of program zones Unsigned 32 bit integer Little
Program zone Program zone array N/A

Program Zone

There are as many program zones as specified in the program entry.

Program zone fine tuning follows the same logic as the variable Wave fine tuning found in the Wave entry structure.

Panning can continuously range from 0 (left) to 1 (right), with 0.5 being the center. To convert the Volume envelope attenuation value into units of decibels, you need to divide the value by 2.5.

If Program zone root key is -1, then it means there will be no root key applied to the zone. Otherwise it gets applied and overrides the note from the original Wave.

Program zone key high must always be greater or equal to Program zone key low. Same applies for the program zone velocity.

Rather than having a resolution of 100 steps between notes (as in cents), the following entries have 256 steps between notes. To convert to cents, the steps must be scaled to fit.

  • Keynum hold
  • Keynum decay
  • Modulation envelope to pitch
  • Vibrato to pitch
1.8

"Program zone velocity low/high" are present, however they are not used.

Name Type Endianness
Program zone fine tuning Signed 32 bit integer Little
Program zone reverb Signed 32 bit integer Little
Panning 32 bit float Little
Keynum hold Unsigned 32 bit integer Little
Keynum decay Unsigned 32 bit integer Little
Volume envelope Envelope N/A
Volume envelope attenuation 32 bit float Little
Vibrato delay 32 bit float Little
Vibrato frequency 32 bit float Little
Vibrato to pitch 32 bit float Little
Program zone root key Signed 32 bit integer Little
Program zone key low Unsigned 8 bit integer N/A
Program zone key high Unsigned 8 bit integer N/A
Program zone velocity low Unsigned 8 bit integer N/A
Program zone velocity high Unsigned 8 bit integer N/A
Wave index Unsigned 32 bit integer Little
1.14 and 1.20
Name Type Endianness
Program zone fine tuning Signed 32 bit integer Little
Program zone reverb Signed 32 bit integer Little
Panning 32 bit float Little
Keynum hold Unsigned 32 bit integer Little
Keynum decay Unsigned 32 bit integer Little
Volume envelope Envelope N/A
Volume envelope attenuation 32 bit float Little
Vibrato delay 32 bit float Little
Vibrato frequency 32 bit float Little
Vibrato to pitch 32 bit float Little
Program zone root key Signed 32 bit integer Little
Program zone key low Unsigned 8 bit integer N/A
Program zone key high Unsigned 8 bit integer N/A
Program zone velocity low Unsigned 8 bit integer N/A
Program zone velocity high Unsigned 8 bit integer N/A
Wave index Unsigned 32 bit integer Little
Base priority 32 bit float Little
Modulation envelope Envelope N/A
Modulation envelope to pitch 32 bit float Little

Envelope

All variables indicate seconds, except for sustain which indicates decibels in a volume envelope, and a percentage in a modulation envelope.

Name Type Endianness
Delay 32 bit float Little
Attack 32 bit float Little
Hold 32 bit float Little
Decay 32 bit float Little
Sustain 32 bit float Little
Release 32 bit float Little

Preset Entry

There are as many presets as specified in the header.

Name Type Endianness
Preset name Name N/A
MIDI bank number Unsigned 32 bit integer Little
MIDI preset number Unsigned 32 bit integer Little
Number of preset zones Unsigned 32 bit integer Little
Preset zones Preset zone array N/A

Preset Zone

Preset zone root key is handled the same way as Program zone root key, if equal to -1 don't use it. In version 1.8, "Preset zone velocity low/high" are present, however they are unused.

Preset zone key high must always be greater than or equal to Preset zone key low, the same applies to the Preset zone velocity.

Name Type Endianness
Preset zone root key Unsigned 32 bit integer Little
Preset zone key low Unsigned 8 bit integer N/A
Preset zone key high Unsigned 8 bit integer N/A
Preset zone velocity low Unsigned 8 bit integer N/A
Preset zone velocity high Unsigned 8 bit integer N/A
Program index Unsigned 32 bit integer Little

After the last preset zone has been loaded the header of the first sequence starts. Load and split all the sequences using the offsets provided in the header.

Label Offsets

There are as many labels as specified in the header. The use of labels is not well documented. If they are not needed they can be skipped. After loading them you should reach the end of the file.

Sam

Header

Occasionally can have a header if the magic number "Sam!" is detected. The body size is never correct and should not be used.

Name Type Endianness
Magic number ASCII character x4 N/A
Body size Unsigned 32 bit integer Little