Super Mario 64 Music Format Specification



These are the specifications I've used for Mario 64 Sequenced Music Parser v0.07. They are in most part valid also for
all first-party Nintendo 64 titles, such as Super Mario Kart 64, Wave Race 64, Starfox 64, Zelda 64, Yoshi's Island
nad others 1080 Snowboarding and a few others.There might be a few variations between each game.

Thanks to rstewart215804
for and DJBouche for some earlier notes posted at the Jul SM64 Hacking Forum.

All offsets are for the Super Mario 64 1.0 US ROM. Other versions may use slightly different commands.


Sequence Bank

The sequence bank starts at 0x7B0860. First four bytes may be some sort of "revision number" (2 bytes) and sequence count (2 bytes).
After this, follow a list of pointers. Format is: start offset (4 bytes) + lenght (4 bytes). You can use the seq_rip.exe utility to extract all
sequences from a Mario 64, Mario Kart 64 or Wave Race 64 ROM.


"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 (ie, eight 4/4 bars)
separated by variable-lenght timestamp commands (0xFD). The first command is always 0xD3, followed by parameter 80, 60 or 20.


 Status 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
1 byte
Sequente type? Usually 80 (Mario64) or 20 (all other games). 60 is used for the 'Mario sounds' sequence.
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
 Not sure. Used in the very beggining of sequences (before channel enable flag).
0xD7
Channels (16 bits)Enable channels flag. Each bit = one channel.
0xFD
Variable-lenght (1 or 2 bytes)
Timestamp (used between track chunks and to ensure right looping).
0xFB
Offset (2 bytes)
Loop from the specified offset.
0xFF
No param
End of header


"Track" Format and Commands Table

A different set of commands is used when processing the track data, but they are very similar to the header ones.


 Status 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).
0xC2Transposition (1 signed byte)
Transposition in semitones.
0xC4No Param.
Mark the beggining of track data.
0xD3Pitch bend (1 byte)
Pitch Bend (signed integer).
0xD4unknown (1 byte)
 ??
0xD8Vibrato (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 bytes)
Volume for this track (unsigned integer).
0xFD
Variable-lenght (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.


 Status 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 lenght (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.
 0xC2Transposition (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 ? ?
 0xFC2 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 (Variable-lenght - 1 or 2 bytes)

Fixed values:

0x10 = Triplet
0x18 = Eight-note
0x30 = Quarter note
0x60 = Half-note

The first bit indicates variable lenght timestamp. If any valuer higher or equal to 0x80 is used, an additional byte is read, ie:

0x8C00 = 0xc00 timestamp.
0x9C00 = 0x1c00 timestamp.


Instrument sets:

The table that determines which instrument set is used in each sequenced is located at 0x7cc620:

0x007CC620 0046 0052 0054 0056 0058 005A 005C 005E
0x007CC630 0060 0062 0064 0067 0069 006B 006D 006F
0x007CC640 0071 0073 0075 0077 0079 007B 007D 007F
0x007CC650 0081 0083 0085 0087 0089 008B 008D 008F
0x007CC660 0091 0093 0095 0B0A 0908 0706 0504 0302
0x007CC670 0100 0122 0111 0122 010E 0113 010F 0112
0x007CC680 010B 010D 0221 1001 1401 1501 1601 1701
0x007CC690 1801 1201 1901 1F01 2101 1A01 0E01 1B01
0x007CC6A0 1A01 1C01 1D01 2501 1401 2001 1E01 1B01
0x007CC6B0 1A01 2301 2401 1B00 0000 0000 0000 0000


The blue bytes consist of 0x23 halfwords (2 bytes), which is the same number of sequences in the ROM. Each entry is actually
a relative pointer to the 'instrument set' used by that sequence. To read the table, just use the sequence
number * 2 (halfword) + 0x7cc620 (start of the table).

For instance --> Title Screen Music (sequence 0x02)
                 = 0x0054 (value in the table for 0x02 at offset 0x7cc624)
                 + 0x7cc620 (start of the table)
                 ----------
                 value at 0x7cc674 = 0x0111


0x01 and 0x11 are actually two separate bytes, not an halfword. I'm not sure if the first one is also an instrument set, but the second
one certainly is. So, this sequence uses instrument set 0x11 (in decimal = 17).

This number relates to the "NInst" instruments in the .ctl file. Using srip (download from dextrose.com), you can extract all (or most)
sounds in the ROM and it outputs a control.h file which includes information about the sound structs used in the game in a
sort of reconstructed .ctl file.

There are 37 NInst entries (0x25). Each one of these contries contain an index to the individual instrument sounds.
SRIP outputs all sounds in the format A00 + Ninst number (00 to 37) + sound index.

Here is NInstrument NInst17 (0x11), the Title Screen SMB Music instrument set:

Sound1700, 0x00 = Snare Drum
Sound1701, 0x01 = Fingered Bass
NULL, 0x02 = NULL = same as last one (0x01)
Sound1703, 0x03 = Organ
Sound1704, 0x04 = Steel Drum
Sound1705, 0x05 = Trumpet
Sound1706, 0x06 = Slap Bass
Sound1707, 0x07 = Synth
Sound1708, 0x08 = Clavinet
NULL, 0x09 = NULL = same as last one (0x08)
Sound1710, 0x0A = Drum Sample (hi-hat)
Sound1711, 0x0B = Drum Sample
Sound1712, 0x0C = Drum Sample
Sound1713, 0x0D = Drum Sample

Other instrument sets:


NInstrument NInst 15
Used in: Lethal Lava Land (sequence #6)

00: Simple percussion loop
01: Voice 'Uhs'
02: Sitar drone notes
03: Sitar melody

NInstrument NInst14
Used in: Inside Castle (sequence #6)
Sound1400, 0x00 = Strings
Sound1401, 0x01 = ?
Sound1402, 0x02 = ?
Sound1403, 0x03 = Pizzicatto string
Sound1404, 0x04 = Cello? (or low and deep woodwind?)
Sound1405, 0x05 = Eletric Piano
Sound1406, 0x06 = ?

This is the index used inside the Title Screen Music (for the 0xC1 instrument command, or '@' in mml2m64).

Default instrument sets

Sequence #00. Pointer = 0046 NInst = 11 and 10 (Mario Sounds)
Sequence #01. Pointer = 0052 NInst = 01 and 34
Sequence #02. Pointer = 0054 NInst = 01 and 17
Sequence #03. Pointer = 0056 NInst = 01 and 34
Sequence #04. Pointer = 0058 NInst = 01 and 14
Sequence #05. Pointer = 005a NInst = 01 and 19
Sequence #06. Pointer = 005c NInst = 01 and 15
Sequence #07. Pointer = 005e NInst = 01 and 18
Sequence #08. Pointer = 0060 NInst = 01 and 11
Sequence #09. Pointer = 0062 NInst = 01 and 13
Sequence #0a. Pointer = 0064 NInst = 02 and 33
Sequence #0b. Pointer = 0067 NInst = 01 and 20
Sequence #0c. Pointer = 0069 NInst = 01 and 21
Sequence #0d. Pointer = 006b NInst = 01 and 22
Sequence #0e. Pointer = 006d NInst = 01 and 23
Sequence #0f. Pointer = 006f NInst = 01 and 24
Sequence #10. Pointer = 0071 NInst = 01 and 18
Sequence #11. Pointer = 0073 NInst = 01 and 25
Sequence #12. Pointer = 0075 NInst = 01 and 31
Sequence #13. Pointer = 0077 NInst = 01 and 33
Sequence #14. Pointer = 0079 NInst = 01 and 26
Sequence #15. Pointer = 007b NInst = 01 and 14
Sequence #16. Pointer = 007d NInst = 01 and 27
Sequence #17. Pointer = 007f NInst = 01 and 26
Sequence #18. Pointer = 0081 NInst = 01 and 28
Sequence #19. Pointer = 0083 NInst = 01 and 29
Sequence #1a. Pointer = 0085 NInst = 01 and 37
Sequence #1b. Pointer = 0087 NInst = 01 and 20
Sequence #1c. Pointer = 0089 NInst = 01 and 32
Sequence #1d. Pointer = 008b NInst = 01 and 30
Sequence #1e. Pointer = 008d NInst = 01 and 27
Sequence #1f. Pointer = 008f NInst = 01 and 26
Sequence #20. Pointer = 0091 NInst = 01 and 35
Sequence #21. Pointer = 0093 NInst = 01 and 36
Sequence #22. Pointer = 0095 NInst = 01 and 27



Seq. Bank. related code (DMA)
-----------------------------
 
80319714: LUI A0, 0x007B
80319718: SW A1, 0x0000 (V0)
8031971C: ADDIU A0, A0, 0x0860
80319720: JAL 0x80318040            # BlockDmaCopy function
80319724: ADDIU A2, R0, 0x0010

80319768: LUI A0, 0x007B
8031976C: SW V0, 0x0000 (V1)
80319770: ADDIU A0, A0, 0x0860
80319774: OR A1, V0, R0
80319778: JAL 0x80318040            # BlockDmaCopy function
8031977C: OR A2, S0, R0

80319780: LUI A0, 0x8022
80319784: LUI A1, 0x007B
80319788: ADDIU A1, A1, 0x0860    # alSeqFileNew function
8031978C: JAL 0x80325CD8


This code process Track Header commands:
----------------------------------------

You can place a breakpoint at 0x8031DC74 to see how some of the header commands
are processed. This specific point checks if the byte is 0xFF (end of header).
You can step through the code to see how some commands are processed.

8031DC74: ADDIU AT, R0, 0x00FF
8031DC78: ANDI A3, V0, 0x00FF
8031DC7C: BNE V0, AT, 0x8031DCB8    # V0 = current byte from sequence header
8031DC80: OR A0, V0, R0
8031DC84: LBU V1, 0x0018 (S0)

This is one function that reads Variable Lenght values (for timestamps):

8031C080: LW V0, 0x0000 (A0)       ; ReadVarLen() function  (1 or 2 bytes)
8031C084: LBU V1, 0x0000 (V0)
8031C088: ADDIU T6, V0, 0x0001
8031C08C: SW T6, 0x0000 (A0)
8031C090: ANDI T7, V1, 0x0080
8031C094: BEQ T7, R0, 0x8031C0BC
8031C098: OR A1, V1, R0
8031C09C: LBU T0, 0x0000 (T6)
8031C0A0: SLL V1, A1, 0x8
8031C0A4: ANDI T9, V1, 0x7F00
8031C0A8: OR V1, T0, T9
8031C0AC: ANDI T1, V1, 0xFFFF
8031C0B0: ADDIU T2, T6, 0x0001
8031C0B4: SW T2, 0x0000 (A0)
8031C0B8: OR V1, T1, R0
8031C0BC: JR RA
8031C0C0: OR V0, V1, R0


Comments