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:
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 |
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 |
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 |
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 |
The variable length timestamp parameter comes in two forms:
The table that determines which instrument set is used in each sequence is located at 0x7CC620.