User Tools

Site Tools


super_mario_64:music

SM64 Music

Specification

These are the specifications for the data formats to store music in the SM64 ROM. They are common across many first-party Nintendo 64 titles, such as Mario Kart 64, Wave Race 64, Starfox 64, Zelda 64.

Work on reversing this specification roughly took place in three phases:

  • Early 2000s: Messaien64 and Deathbasket. This is the version of the format shown on this page. Basic structure and most music-related commands. There wasn't enough information known to reliably export sequences, but sequences could be imported without too many errors. There was early software written to import sequences with one voice per MIDI channel.
  • 2014-2017: Sauraen. Developed full MIDI importer/exporter, https://github.com/sauraen/seq64 . The definitions of the sequence file format is user-editable within the program, not hard-coded, so you can just view all the command definitions (like they are listed below on this page) within the GUI. Began to reverse engineer the scripting capabilities of the file format but was not able to figure out many of the scripting-related commands.
  • 2019: Simon Lindholm. Complete list of all scripting commands: https://hackmd.io/opEB-OmxRa26P8h8pA-x7w Most are fully understood but some still are not fully.

Sequence Bank

The sequence bank starts at 0x7B0860. The format of the sequence bank header is shown in the following table.

Offset Length Description
0x00 2 Revision number?
0x02 2 Sequence count
0x04 + N*8 4 Start offset of sequence N
0x08 + N*8 4 Length of sequence N

Header Format and Commands Table

The sequence header consist of pointers for all tracks, tempo, volume and loop settings. Each sequence is usually divided in chunks (i.e., eight 4/4 bars) separated by variable-length timestamp commands (0xFD). The first command is always 0xD3, followed by parameter 80, 60 or 20.

Byte Parameters Description
0x90 - 0x9F Offset (2 bytes) Points to track data for a specific channel, indicated by the second nibble of status byte
0xD3 M64 flags (1 byte) Used to enable specific flags, 0x80 for M64 to pause when the music does.
0xD6 Channels (16 bits) Disable channels flag. Each bit = one channel
0xDB Master Volume (1 byte) Master Volume (unsigned integer)
0xDD Tempo in BMP (1 byte) Tempo setting (BPM)
0xD5 16 bits Channel priority set for SFX to overtake channel when played
0xD7 Channels (16 bits) Enable channels flag. Each bit = one channel
0xFD Variable-length (1 or 2 bytes) Timestamp (used between track chunks and to ensure right looping), for two bytes long, add 0x80 to the first bit, for example, timestamp at 10 50 would be FD 90 50
0xFB Offset (2 bytes) Loop from the specified offset
0xFF No param End of header

Tracker Format and Commands Table

The track data commands are very similar to the header ones.

Byte Parameters Description
0x90 - 0x9F Offset (2 bytes) Loads music data (divided in “layers”). Since simultaneous notes (using a “0” timestamp) aren't possible, many layers are loaded and played simultaneously. The second nibble of the status byte indicates the layer number
0xC1 Program (1 byte) “Set program” related. There isn't a master index for instruments, this seems to refer to the current set of instruments, which is not specified in the sequence data but loaded by something external to it. Drums tracks usually use program 0x7F (127).
0xC2 Transposition (1 signed byte) Transposition in semitones
0xC4 No Param Mark the beginning of track data
0xD3 Pitch bend (1 byte) Pitch Bend (signed integer)
0xD4 Reverb (1 byte) Dry/Wet
0xD8 Vibrato (1 byte) Vibrato range
0xDC unknown (1 byte) Drum-related?
0xDD Pan (1 byte) Set pan for this track (01 = left, 64 = center, 127 = right)
0xDF Track Volume (1 byte) Volume for this track (unsigned integer)
0xFD Variable-length (1 or 2 bytes) Timestamp (used between track chunks and to ensure right looping)
0xFF No Param End of Track Data

Music Events Table

This is the format for the data pointed by the Track Data 0x90 - 0x9F commands.

Byte Parameters Description
0x00-0x3F Timestamp (1 or 2 bytes). Velocity (1 byte). Duration (1 byte) “Play Note” commands ('Type 0'). Unlike MIDI, 0 = A0. Timestamp is variable length (see below). Duration byte seems to be timestamp before “NoteOff” (I'm not sure about this)
0x40-0x7F Timestamp (1 or 2 bytes). Velocity (1 byte). “Play Note” commands ('Type 1') Duration = previous specified (producing more compact data)
0x80-0xBF Velocity (1 byte). Duration (1 byte) “Play Note” commands ('Type 2'). Timestamp = previous specified (producing more compact data)
0xC0 Timestamp (1 or 2 bytes) Timestamp used for rests
0xC2 Transposition (1 byte) Transposition in semitones. Since the 'PlayNote' commands just cover a range of 0x40 notes (as opposed to 0x7F possible MIDI notes), this is used to shift the range when needed
0xE0 - E8 ? ?
0xFC 2 bytes Jump (used for loops). There isn't a loop counter, so the command is repeated. Offset is relative to individual sequence start
0xFF No Param End of music track / Return from jump

Timestamp Parameter

The variable length timestamp parameter comes in two forms:

  1. Fixed values:
    • 0x10 = Triplet
    • 0x18 = Eight-note
    • 0x30 = Quarter note
    • 0x60 = Half-note
  2. Variable length timestamp. If most significant bit (0x80) is set, an additional byte is read, e.g.:
    • 0x8C00 = 0xC00 timestamp
    • 0x9C00 = 0x1C00 timestamp

Instrument Sets

The table that determines which instrument set is used in each sequence is located at 0x7CC620.

References

super_mario_64/music.txt · Last modified: 2019/08/16 18:14 by sauraen