Language…
15 users online: 35TCB77, Buflen, caioskii, crm0622, crocodileman94, fanfan21, Green Jerry, Hayashi Neru, Josuke Yoshikage, KoJi, Maw, Metakabe, Neuromancer, signature_steve, toady - Guests: 243 - Bots: 351
Users: 64,795 (2,377 active)
Latest user: mathew

M64 documentation

Here's some stuff about how to get rid of echo in your ports by ergazoobi, here's the features of M64, M64 Parser.

In M64, we have the header, the track and music event table, the header is the beginning of the M64, what all M64s must have, it contains necessary information about the M64, like the NLST, amount of channels, tempo and master volume.

HEADER FORMAT AND COMMAND TABLE
 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
Sequence type 80 for variable sequence, 0x00 - 0x25 for bank 00 to 37or 20. 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-length (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





TRACKER FORMAT AND COMMAND TABLE
 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)
0xD4Echo (1 byte)
 Amount of echos for the track (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-length (1 or 2 bytes)
Timestamp (used between track chunks and to ensure right looping).
0xFF
No Param
End of Track Data





MUSIC FORMAT AND COMMAND TABLE
 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"
 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



I have yet to figure out the exact units but "duration" is not a timestamp as far as I can tell. It may actually be relative to the note's timestamp. It's basically how much before the actual end of the note to stop playing the sample. 0x7F seems to always just stop notes pretty much as soon as they start, 0x00 plays for the full length of the timestamp. Also I think the 0x40-0x7F play note commands don't use the previous duration but actually just use a duration of 0 (or something close to it).




Okay so one of the more poorly explained things in frauber's specs is what the timestamps are for and how they work, so let's go.

Basically timestamps, wherever they show up, tell the game to wait a certain amount of time before moving on to the next command. Whether it's the timestamp parameter for the play note command, the rest command in note data (0xC0), or the timestamp command in track and sequence headers they all work the same way. Since it basically just tells the game to wait, they're all relative to the last timestamp. For example, FD 01 FD 01 and FD 02 are equivalent, the timestamps add up. It's all cumulative.

The actual value of a timestamp can be either 1 or 2 bytes. The very first bit is used to indicate if a second byte should be read, and is not part of the actual value. What this means is that a single byte timestamp can only go up to 0x7F, if you try FD 80 for example it's going to read the next byte as a part of the timestamp which will break things. if you want a timestamp value of 0x80 or greater, you need to add 0x8000 to the value. So if you want a timestamp with a value of 0x80, it would be FD 80 80. As you probably guessed, this means that FD FF FF is actually a timestamp with a value of 0x7FFF, so that's the largest value you can use.

So what does the value actually mean? The unit for the timestamp values is called ticks. The length of a tick is relative to the tempo of a song, one beat = 48 ticks (0x30 in hex). What this actually means in music notation is up to you depending on the time signature, though the standard is that 1 beat is a quarter note.

So what are the timestamps actually for? It's pretty obvious with the play note command and rests, that's just the duration of the note. The 0xFD command is less obvious. Basically, in either sequence or track headers, when you use a load command (0x90-0x9f), the game doesn't wait for the data you loaded to finish before it moves on. This means at the very least you need to follow up load commands with a timestamp indicating the length of what you loaded to stop the game from jumping back to the start of the loop before it actually plays anything. But the perk to doing things this way is that you can use other commands while the music events are being played.

Say I have a track header where I load some note data at an offset XX into layer 0. This data is two beats (96 or 0x60 ticks). So I have 90 00 XX FD 60. What if I want to change the panning at the start of the second beat? No problem. It doesn't matter how many timestamps I use as long as they add up to 96 ticks, so I can split FD 60 into two FD 30 timestamps. Now I can insert a panning command (0xDD) in between these timestamps as follows:
90 00 XX FD 30 DD 01 FD 30
What this means is the game will wait one beat after it loads the data, and then it will pan the channel all the way to the left. This is independent of the data actually being loaded, it doesn't matter if it happens in the middle of a note, the panning will change. This is true of any of the track commands except for program change (0xC1), you can't change instruments mid-note. You can have a program change after a timestamp but it won't actually take effect until the next note.

So yeah by doing this you can change track volume, tuning, panning, reverb, etc. mid-note. Using a lot of these commands separated by very short timestamps is how volume fades and pitch-bends are done so yeah timestamps are pretty key to doing more complicated stuff in the m64 format.




So Frauber's notes on the instrument set mapping table don't include some information that he actually discovered so I'm gonna just update it here. A sequence can use multiple instrument sets in theory, I'm just not sure how to actually access the additional sets if it can actually be done. For the sfx sequence (sequence 0) the game actually loads 11 banks, and for whatever reason the big boo's haunt sequence also loads the merry go round instrument bank. I intend to do some experimentation to see what happens if i change that to a different bank.

For now though, this is how the instrument sets for each sequence are actually stored:

The table that determines which instrument set is used in each sequenced is located at 0x7cc620. Each entry is actually
a relative pointer to a variable length list of the instrument banks used by that sequence. To read the table, just use the sequence
number * 2 (halfword) + 0x7cc620 (start of the table). The byte at this address is the number of banks the sequence uses, and whatever that value is, that's how many of the next bytes you need to read to get the full list of banks.

example:
Code
Big Boo's Haunt Music (sequence 0x0A)
= 0x0064 (value in the table for 0x0A at offset 0x7cc634)
+ 0x7cc620 (start of the table)
---------- 
value at 0x7cc684 = 0x02
so we read the next two bytes:
value at 0x7cc685 = 0x21 (Nlst 33: merry-go-round)
value at 0x7cc686 = 0x10 (Nlst 16: big boo's haunt)


So the sequence loads both of those banks.

As I said at the start, I still need to look into how significant and/or useful this actually is but any inaccuracies in the existing documentation are worth correcting. So far I've had no luck actually using two banks but I'm gonna keep experimenting and see what i can discover.

  Does this mean, if this is correct, that we could use more than one instrument bank per sequence?...

...Man, that would be amazing.

    See my hack development here!: Super Mario and the missing memories.



That's the best case scenario. I'm trying to figure out how you can actually access the other instruments, if you can at all. It might just be a trick to get it to preload the merry-go-round samples so it can fade between them on the fly or something. As far as I know there's nothing that really seems like a bank change command, so it's unlikely that it's as simple as just straight up being able to access both banks in their entirety. The sfx sequence definitely does actually load and use 11 different banks but it has a different sequence type which hasnt been documented, I don't know how that format actually works. Maybe looking into that would shed some light on how to use different banks in music sequences too but it's unclear right now.

Basically don't get your hopes up. It's definitely worth looking into but the results may not end up being particularly useful.

What about using a whole bunch of PLAYSOUND functions through a separate C/GS or ASM code, rather than the actual sequencing of a separate song using the normal sequencing or a sequencing format that is unfamiliar; because they can play any sounds, even when music is playing,(Example: Sand Ambience in SSS that plays in the background inside the pyramid, while the normal music plays), so hence maybe sampling is an order? After all, it's probably safer to take baby steps rather than just going out and eating the whole meal. Or you could just have a bunch of instruments that can't be played/ or aren't in the game at all loaded into the sample, so that you can loop the sample and add more samples, potentially making tunes that are longer than a normally imported .m64 file would allow in a stable form that wouldn't crash the game. (Just throwing that out there, pls don't kill me). Problem is though doing it the way I just mentioned would probably be a lengthy process as you would have to do a lot more manually, (assuming you're trying to create a separate sequence and not just loading a sample, but actually composing a mixture of sounds that are already in the game and not a sample imported from an external source), and if a mistake was made, you would have to go back from where you started.
I promise to never cancel my hacks
Originally posted by ergazoobi
That's the best case scenario. I'm trying to figure out how you can actually access the other instruments, if you can at all. It might just be a trick to get it to preload the merry-go-round samples so it can fade between them on the fly or something. As far as I know there's nothing that really seems like a bank change command, so it's unlikely that it's as simple as just straight up being able to access both banks in their entirety. The sfx sequence definitely does actually load and use 11 different banks but it has a different sequence type which hasnt been documented, I don't know how that format actually works. Maybe looking into that would shed some light on how to use different banks in music sequences too but it's unclear right now.

Basically don't get your hopes up. It's definitely worth looking into but the results may not end up being particularly useful.

Maybe there's more to 0xC1 than we think.



Ya maybe. I'll try using larger or negative values for it and see what happens I guess. None of the nonsfx banks have more than 16 instruments but I doubt it's s simple as C1 10.

Have you noticed those 0x6* commands in the haunted house and merry go round sequence data? hmmmm.....

For 0xC1;
May be like C1 7F loads drums, but I doubt it.



Originally posted by MrGreenThunder
Have you noticed those 0x6* commands in the haunted house and merry go round sequence data? hmmmm.....

I'm pretty sure sure the 0x6* are to make other track commands only apply to certain layers. I havent been able to figure out how to actually make it work but there are track headers with multiple panning or volume commands that aren't separated by timestamps that use these, and there's always multiple layers being loaded in those tracks, so this is the logical conclusion. It's definitely noticeable in some songs.

see: hazy maze cave drum channel. 0x353, 0x35e, and 0x361. Three volume commands, no timestamps between them, and a bunch of the 0x6* commands.




the 0xDC track header command seems to set the channel priority. The original sm64 sequences always set it to 0 on drum channels so that if there's too many channels in use at once, sfx take priority over drums. I'm not sure what the default value is but yeah this explains why it had no audible effects when i first tried to figure out what it was.