Skip to content
emma edited this page Nov 18, 2024 · 13 revisions

mfvitools MML reference

overview of the music systems of FF6

FF6, like most Square SFC games, uses a custom SPC700 music driver programmed in house at Square by Minoru Akao. All of these drivers share certain basic characteristics, but most aren't directly cross-compatible without major issues. Akao wrote four distinct drivers for SPC700, with various revisions; FF6 uses the third revision of the fourth Akao driver. We'll sometimes refer to this as AKAO4, AkaoSnes4, or similar.

data structures

Each song has three distinct types of data: an instrument table, a sequence header, and sequence data. The instrument table is stored separately in ROM, while the sequence is all one unit. Custom songs may be distributed as two binary files, "inst" and "data", or as a single MML file.

The instrument table tells the game which sample ids to load into SPC memory in which program slots. Custom samples go in program $20 through $2F (maximum of 16). This table is not stored in SPC memory, so it can't be directly extracted from an SPC dump.

sequence concepts

SPC700 has eight channels on which music and sound can play, one sound at a time per channel. In AkaoSnes, these channels are handled very simply: the header has eight pointers, one per channel. When a song starts, each channel looks up in the header where to start reading from. Whatever byte is found at that location is treated as a command, and whatever action that corresponds to gets performed. Then it just continues reading from there and performing commands. If the command is to play a note (including rests and ties), then it'll wait for however long that note lasts before continuing.

With a very few exceptions, then, each channel is just its own independent entity, unaware of anything going on in any other channels. Because of this, one of the most basic and important challenges of building a music sequence is keeping the channels synchronized - making sure one channel doesn't get ahead or behind the rest.

In most cases, sequence data will contain eight distinct regions (tracks), each corresponding to a channel, but other arrangements are possible and occasionally helpful.

Note that, also, the order in which tracks appear in a file has no bearing on which channels play those tracks. This is determined only by the track pointers in the header.

timing

timing is based on tick intervals of 192 ticks per measure. tempo is expressed in (a very close approximation of) beats per minute. (note: most other AKAO revisions use different tempo scales.)

Tempo must be set before any notes are played, or else the SPC700 will freeze.

core MML commands

mfvitools MML is based on rs3extool2 MML. MML files originally written for rs3extool2 should largely work in mfvitools as well, though some differences between RS3 and FF6 music formats may require adjustments.

The basic idea of using MML for FF6 is to provide a human-readable and human-editable representation of music data; in many practical terms, writing music in MML can be thought of as similar to writing SNES code in ASM. Each basic MML symbol/command corresponds directly to a command in AKAO bytecode. (The structure of AKAO bytecode maps so plainly onto a standard MML format that it's clear that the original Square music team used MML, though they would have had different symbol mappings.)

Most MML commands follow this basic structure: one character to represent the type of command it is, then a number representing the first parameter, a comma, a number representing the second parameter, and so forth. The same symbol will represent different commands based on the number of parameters after it. For example:
m48,18,239 (enable vibrato, delay 48, period 18, depth 239)
m24,1 (pitch bend next note over 24 ticks by 1 semitone)
m12 (raise current transpose value by one octave)
m (disable vibrato)

Parameters can also be written as negative, as some commands like pitch bends accept negative values. The parser doesn't distinguish between these parameters and others, though: any parameter 0 ~ 255 will be represented as written, and any parameter -128 ~ -1 will be represented in two's complement. 255 and -1 as parameters produce the same output.

Whitespace is generally not meaningful, but it does signify the end of a command when encountered. (e 4 is not a valid way to write e4, and will produce e then ignore the 4 as it has no meaning on its own.)

0-byte declarations

  • #WAVE 0x20 0x1F - the WAVE declaration sets up the instrument table. The example puts the sample with id $1F into program slot $20. By convention, hex numbers are used, and must be marked with 0x. The only valid program slots for declaring samples are 0x20 through 0x2F. Any text that is not interpretable as a number, and any text after the first two numbers, is ignored / treated as a comment, so it is valid to write:
    #WAVE 0x20 0x1F -- ocarina

  • {1} - initial track pointers are set by placing numbers within braces. 1 through 8 will set the primary track pointers, and 9 through 16 will set an alternate start position if desired (if not present, the alternate position will be the same as the primary position). Any text other than numbers within the braces is not considered (allowing headings such as {--------------- Track 1 ---------------} to be used). More than one number within the same set of braces will still be counted ({1 3} will set a pointer for both channel 1 and channel 3). If more than one of the same track pointer are found, the last one is used. Braces can also be used as inline comments. To avoid numbers being counted as track pointers, also use single quotes: {'1st string section'}

  • $101 - jump pointers. Various commands allow jumping directly to a certain point in the sequence; rather than work directly with the byte locations, in MML we abstract this to these named (well, numbered) locations. Any number can be used, though it may help to be systematic about it, to avoid collisions. 101 could be used for the first measure of the first track, for instance, and 421 for the 21st measure of the fourth track. Like track pointers, in case of duplicates, the last declared in the file is used.

  • $ - infinite loop point. This is a special case of jump pointer: the next time the end track command is used (;), instead of stopping execution, it will jump to the infinite loop point and continue from there.

  • l16 - default length. When a note is written in MML without a specified length, the most recently set default length is used instead. When not specified, the default is 8. This is reset whenever the end track command is encountered. (This command's behavior when given a number that isn't a valid note length is undefined. Single dotted notes are valid.)

  • # - line comment. ignores everything after it in the line on parsing. preprocessor statements like #WAVE and #def also use this form, so it's good practice to disambiguate true comments from any possible preprocessor directives with one or more of these techniques: start the comment after the first character of the line; put a space directly after the initial #; pair the comment signs (##).

notes

A note on notes: Notes are single-byte commands that contain both a key and a length. Unlike some SPC formats, no octave data is stored -- there's one command for a C of a given length, one for a D, and so forth. Length is fully defined, with these options available:

  • 1 (192 ticks)
  • 2 (96 ticks)
  • 4. (72 ticks)
  • 3 (64 ticks)
  • 4 (48 ticks)
  • 8. (36 ticks)
  • 6 (32 ticks)
  • 8 (24 ticks)
  • 12 (16 ticks)
  • 16 (12 ticks)
  • 24 (8 ticks)
  • 32 (6 ticks)
  • 48 (4 ticks)
  • 64 (3 ticks)

Any of those can be represented in one byte. Other dotted combinations can be used, but these will use additional bytes and won't be able to increment in any durations smaller than a 64th note.

Notes are represented simply by the corresponding key (a b c d e f g).
Use + for sharp and - for flat. (a+, b-) Odd sharps and flats are allowed, but remember that octaves are NOT stored in note commands, only keys. c- writes a B to the file, and B is 11 semitones above C. For a duration other than the current default, add an appropriate number after the note: c4 d8.e16 g2 f1 Numbers that don't correspond to an allowed note (e.g. g17) cause the note to be ignored entirely.

In addition to the 12 standard keys, rests and ties are also available. Rests (r, r4, etc) stop all sound in the channel and leave the channel empty for the specified period. Ties (^, ^4) just advance time without stopping or starting any notes. This is useful for holding a note for a duration that's not on the standard list (g24^16 - 20 ticks), or for processing commands in the middle of a note, such as a volume fade or pitch bend.

bytecode-equivalent commands

Capital X, Y, and Z stand in for parameter values. Actual commands are given in lowercase (they are, however, not case sensitive).

basic channel state commands

MML Representation Bytecode # of bytes Description
@X DC 2 Program change. Sets the current channel to use sample X. May be written in decimal (@32) or hex (@0x20). Valid sample numbers range from 0 to 7 (fixed) and 32 / 0x20 to 47 / 0x2F (customizable, defined in instrument table).
|X DC 2 Program change (alternate). X is one hex digit only, and will choose the corresponding custom sample program: e.g. |0 = @0x20 = @32 => DC 20
vX C4 2 Set volume. 7 bit value; 0-127.
pX C6 2 Set pan. 7-bit value; 64 is center, 0 is left, 127 is right.
oX D6 2 Set octave. Middle C is usually C5 here, though it depends on the sample. A5 is the origin point for tuning (a sample with tuning value of 0 will play back at its native 32kHz at A5).
< D7 1 Increment octave / octave up. (No, this isn't a typo or backwards. It's not an arrow.)
> D8 1 Decrement octave / octave down. (Remember how greater than / less than symbols work? Yeah, that.)

global state commands

MML Representation Bytecode # of bytes Description
tX F0 2 Set tempo (in BPM, more or less). Affects all channels.
%xX F4 2 Set master volume (0-255).
%vX F2 2 Set echo volume to X. Affects all channels.
%bX,Y F7 3 Set echo feedback (with fade / slide). Gradually change echo feedback from current value to Y over X ticks. Use X = 0 for a plain "set echo feedback". Current value persists from previous songs. This affects all channels.
%fX,Y F8 3 Set echo FIR filter mode (with fade / slide). Gradually change echo filter parameters from current values to the target preset Y (0-3) over X ticks. Use X = 0 for a plain "set echo filter mode". Values persist from previous songs. This affects all channels. Filter modes other than 0 in FF6 tend to create audio pops and glitches.
tX,Y F1 3 Tempo fade / tempo slide. Gradually change tempo from current position to Y over X ticks. Affects all channels.
%vX,Y F3 3 Fade / slide echo volume from current value to Y over X ticks. Affects all channels.

flow control commands

MML Representation Bytecode # of bytes Description
[ E2 2 Loop start. Alias for [2.
[X E2 2 Loop start. Loop will iterate a total of X times. Up to four loops can be nested.
] E3 1 Loop end. Return to the current loop's start point unless you've already done so the appropriate number of times. If there's no active loop, then the track ends entirely.
; F6 or EB 3 or 1 End track. If an infinite loop point has been set, perform a hard jump to that point. If not, stop playback for this channel.
;X F6 3 Hard jump. Resets all segment-specific data (percussion state, infinite loop point, default note length) and continues playback from jump target X.
jX,Y F5 4 Loop break / conditional jump / jump to volta. If the current, top-level loop is on its Xth iteration, execution jumps to the jump pointer $Y. If the loop is on its last iteration, this will exit the loop entirely. Otherwise, the loop remains active. For example: [3 c j1,5 d j3,6 e] $5 f ] $6 g will play as c f, then loop and play c d e, then loop and play c d g. Be careful to make sure the correct number of ] are used; otherwise you can encounter undefined behavior or crashes.
jX F5 4 Loop break / conditional jump / jump to volta (shortcut). As above, but execution jumps to after the next "end loop" (]) command found in the MML. This is calculated naively -- if the next end loop command isn't the end of this loop (for example, as in [a j2 [b] c]) you'll need to use the full version of this command instead (jX,Y) -- corrected example: [a j2,5 [b] c] $5
:X FC 3 Conditional jump. If an event trigger to advance the song to the next section has been sent since the last time this was encountered, jump to target X. If not, continue. Used in Phantom Train and Dancing Mad pt. 1-3.

envelope and LFO commands

MML Representation Bytecode # of bytes Description
vX,Y C5 3 Volume fade / volume slide. Gradually change volume from current position to Y over X ticks.
pX,Y C7 3 Pan fade / pan slide. Move pan from the current position to Y over X ticks.
mX,Y C8 3 Pitch slide / pitch envelope / portamento. The next played note (including continuing the current note with a tie) will start at its existing pitch and then slide upward or downward by Y semitones over X ticks. Cancelled if the note ends early.
mX,Y,Z C9 4 Vibrato / pitch LFO. Vibrato starts only after a note is held for X ticks. Y is the rate/period (units unclear, not directly scaled to tempo, smaller is faster). Z is a bitfield containing the mode and depth: mmdd dddd. Modes: unipolar down (0/2), unipolar up (1), bipolar (3). Max depth is half a semitone (?). (To calculate Z, pick a depth between 0 and 63, then add 0, 64, or 192 to that number for down, up, and bidirectional, respectively. For vibrato, there is rarely a reason to use modes other than bidirectional.)
m CA 1 Disable vibrato / pitch LFO.
vX,Y,Z CB 4 Tremolo / volume LFO. Tremolo starts only after a note is held for X ticks. Y is the rate/period (units unclear, not directly scaled to tempo, smaller is faster). Z is a bitfield containing the mode and depth: mmdd dddd. Modes: unipolar down (0/2), unipolar up (1), bipolar (3). Max depth ???. (To calculate Z, pick a depth between 0 and 63, then add 0, 64, or 192 to that number for down, up, and bidirectional, respectively.)
v CC 1 Disable tremolo / volume LFO.
pX,Y,Z C8 3 Pansweep / pan LFO. Pan shifts left and right repeatedly by Z units over Y ticks. X is ignored.
p CE 1 Disable pansweep / pan LFO.
%a, %y, %s, %r E1 1 Reset ADSR envelope to sample default.
%aX DD 2 Set ADSR attack rate. range 0-15, slow to fast, 15 is instant.
%yX DE 2 Set ADSR decay rate. Range 0-7, slow to fast.
%sX DF 2 Set ADSR sustain level. Range 0-7, low to high. 0 stops decay at 12.5% original volume. 7 stops decay at 100% volume (effectively eliminating the decay step).
%rX E0 2 Set ADSR "release" (sustain rate). Range 0-31, slow to fast, 0 is infinite.

channel modes

MML Representation Bytecode # of bytes Description
%e0, %e1 D5, D4 1 Disable (%e0) or enable (%e1) echo for this channel.
%g0, %g1 E7, E6 1 Disable (%g0) or enable (%g1) gapless mode for this channel (legato without slur). Normally the last tick or so of any note is muted to prevent the clicks or pops caused when a sample stops playback suddenly at an arbitrary point. This mode disables these gaps, allowing smoother execution of very rapid notes at the cost of possible audio pops.
%l0, %l1 E5, E4 1 Disable (%l0) or enable (%l1) legato mode for this channel (with slur). When new notes are encountered, instead of muting the existing note and then starting a new note, the current note continues and changes pitch to the note specified in the new note command. Sometimes functions similarly to a tie and sometimes not -- for instance, ADSR envelopes will continue the same as with ties, but vibrato timing will restart.
%p0, %p1 D3, D2 1 Disable (%p0) or enable (%p1) pitch modulation mode for this channel. This uses the prior channel's output (unaffected by volume, but affected by ADSR) as an FM operator to distort the current channel.

other useful commands

MML Representation Bytecode # of bytes Description
&X E8 2 Set note duration. The next played note will last X ticks instead of whatever duration is specified by the note command. This only triggers once; &48[c1] would play a quarter note and then a whole note.
%kX D9 2 Set transpose. X is a signed number of semitones.
mX DA 2 Add to transpose. X is a signed number of semitones.
kX DB 2 Set detune. X is signed and uses units of 1/16 semitone.

Some additional, rarely used commands can be found in the alphabetical MML command reference

macro definitions

Macros are one of the core concepts of MML and the source of its name. A macro stores a series of commands under a name, and then that name can be used anywhere within the main sequence as a shorthand for that series of commands. This can be used to improve readability ('clarinet' instead of |A), to duplicate a recurring pattern, or to allow editing one definition to propagate to many places within the sequence. If you always want trumpets panned somewhat to the left, you can create a macro like:
#def trumpet= |4 p48 and use this instead of writing the commands out manually each time: 'trumpet'
Then if you decide that 48 isn't quite far enough left, you can just change the line to: #def trumpet= |4 p32 and every trumpet will change accordingly.

The format is: #def name= commands. name is any text you like, with a few caveats: # can't be used because it comments out the remainder of the line; leading and trailing spaces are removed; single quotes are removed; and any names using *, +, or - can be defined but cannot be called, so are effectively unusable. commands is a set of text that is written into the MML in place of the macro call before the main parser process. (Whitespace is added before and after it.) Everything between the = and the end of the line (by newline or # comment) goes into the macro. Macro calls can be included in commands (avoid making infinite loops).

Macro calls are simply the name of the macro in single quotes. Both definitions and calls are case insensitive.

macro scaling

Consider the case of macros used to set up instrument changes, as briefly mentioned above. It can be very convenient to have one place to change all sorts of things about how a conceptual instrument is represented in the sequence - volumes, octaves, vibrato, and so on. But this falls apart quickly if we need to use the instrument broadly in various octaves, at various volumes. Macro scaling is a solution to that problem.

If we have the macro
#def flute= |0 o4 v48 p64 %e1 m48,18,223 %r7
then scaling might look like this:
'flute+o1'
This macro call would write this into the sequence: |0 o5 v48 p64 %e1 m48,18,223 %r7

Scaling is performed in a macro call by adding either a +, -, or * after the macro's name but before the closing quote, then the symbol of the command to change, then one or more numbers separated by commas. The scaled command is determined by the symbol and the number of commas: 'flute+m1' would only affect relative transpose commands, 'flute+m0,1' would only affect pitch bends, 'flute+m0,0,1' would only affect vibrato, and so on. (The first two of these three examples would not affect the output at all, since those commands don't appear in this macro.) Numbers can be omitted entirely in order to leave parameters unchanged: 'flute+m,,1' would work just like 'flute+m0,0,1', changing the vibrato depth to 224.

With multiplication, decimals can be used, e.g. 'flute*v0.5' for |0 o4 v24 p64 %e1 m48,18,223 %r7. Leading zeros can be omitted ('flute*v.5') Scaling will cap out at appropriate maximum values: 'flute*v10' -> |0 o4 v127 p64 %e1 m48,18,223 %r7. Multiple scales can be chained together on the same macro call for different commands: 'flute+o1*v2' -> |0 o5 v96 p64 %e1 m48,18,223 %r7. Multiple scale calls for the same command won't currently work (e.g. 'flute*v2+v8' will only perform the '+v8', ignoring the '*v2'. Last declared wins.)

"percussion" macros

While standard and scaled macros do well to allow a "mixing board" for standard instruments, percussion tracks introduce several additional problems that those methods do not solve, and may even exacerbate. Tuning percussion and auditioning different samples may require changing the played note, not just its octave. And rapid sample changes in percussion tracks makes macros such as the flute example above impractical due to all the wasted bytes. You don't want to use bytes on things like octave or volume or pan or echo unless you actually need them to change. But you also want the ability to differentiate different drum types and articulations by changing any or all of these. The percussion macro system is an attempt to solve this problem. It has many issues of its own, and can be difficult to deal with, but it does more or less work.

Percussion symbols are defined with the #drum statement:
#drum "c"=5a |A v64 p64 %e0
Some key differences here between this and standard macros:

  • The name of a percussion definition is limited to one symbol (here, c) and an optional following + or -. (Using sharps and flats after symbols allows easier translation from other formats that have their own key-based percussion mode, such as later AKAO4 revisions or the SuzukiSnes driver). The symbol can be any letter and is not case sensitive.
  • The symbols are also defined with a delimiter ("). This can theoretically be any character, but should only be set to a character with no other semantic meaning (e.g. ", (, ), ~, /, _, `). Double quotes are used by convention. If necessary, multiple delimiters can be used in a file, each having its own set of symbol definitions.
  • The first block of text after the = must contain a number followed by a note symbol. This sets the pitch of this symbol: this is an A on octave 5.
  • The remainder of the line is a list of commands that should be applied before playing the note itself. Unlike a standard macro, these will only be written to the file if the MML parser determines that they represent a change from existing state.

Percussion macros are called by using the symbol within its delimiters as you would use a note: "c8c8c16.c32" -> |A v64 p64 %e0 o5 a8a8a16.a32

After a character is found that is defined as a percussion delimiter, anything afterward is treated as a percussion macro-call symbol except:

  • the delimiter again (which returns parsing to normal)
  • # comments
  • ties (^) work as normal
  • a handful of characters with special meaning in percussion mode, defined below

It's important to understand the limitations of this mode in order to use it effectively. MML parsing goes from top to bottom in each step: first definitions and other #-commands are read, then 'macros' are converted to their equivalent MML, then the MML is converted from top to bottom into bytecode. Percussion calls are calculated during this last step. Since it's a linear top-to-bottom process, the percussion parser is not aware of any jumps, loops, or other flow changes. In fact, it is completely unaware of anything that happens outside itself, other than ; (which resets the percussion state). Examples:
|A "c8" -> |A |A v64 p64 %e0 o5 a8 (wasted bytes)
"c8" |B %e1 o4 g8 "c8" -> |A v64 p64 %e0 o5 a8 |B %e1 o4 g8 a8 (wrong octave, program, and echo state)
With #drum "d"=6d |A v64 p64 %e1:
"cdc" -> |A v64 p64 %e0 o5 a %e1 < d %e0 > a (ok)
"c"[4"d"]"c" -> |A v64 p64 %e0 o5 a [4 %e1 < d ] %e0 > a (runaway octave increase during loops, ending with an A8)

To help address these issues, several special commands are available in percussion mode that manipulate the percussion parser's stored state.

  • ! - remove an element from the state, so that the next call that uses this element will set it. Elements are defined by their MML symbol with 0 standing in for any parameters.
    "c !v0 c" -> "|A v64 p64 %e0 o5 a8 v64 a8"
    There are a few special cases of this. !! resets state entirely except for octave (o0). A few elements can be accessed using an abbreviated form without the 0s: |, @, o (program change and octave).
    While these allow significant flexibility, the most generally useful technique is simply to perform a full state reset ("!!!o") whenever the state becomes ambiguous, such as after playing non-percussion parts, at jump targets, and at the start of loops. These can then be optimized further if saving the space becomes necessary.
  • \ - Lock or unlock the state. In this mode, only note and octave count; everything else is ignored, both in terms of setting the percussion state and in terms of writing to the bytecode sequence. This is typically used when something needs to be set manually outside percussion delimiters, such as a volume fade for a roll, and you want to avoid the volume being reset and the fade cancelled.
    with #drum "c+"=5b |A v96 p64 %e0
    "ccc+c" v24,96 "\c32c32c32c32" -> |A v64 p64 %e0 o5 aa v96 b v64 a v24,96 b32b32b32b32
    Locked state persists until either another \ or the end delimiter ("):
    "cc\c+\cc+" -> |A v64 p64 %e0 o5 aaba v96 b
  • : - The source code calls this "silent mode", but it's probably better thought of as "set state mode", or "what should I pretend just happened?" mode. In this mode, any symbols will affect the percussion state, but not write anything to the sequence. Examples:
    ["cccc+"j2"cc"]"c4" -> [ |A v64 p64 %e0 o5 aaa v96 b j2 v64 aa ] a4. The naive parser thinks the c4 comes after a c, so it does not set volume and we end up with the c+ volume (96).
    ["cccc+"j2"cc"]":c+:c4" -> [ |A v64 p64 %e0 o5 aaa v96 b j2 v64 aa ] v64 a4. The :c+ instructs the parser to act as if a c+ just happened, without writing anything. This gives better information to the c4 which now ends up with proper volume (64).
    "cc"[4"cc+"] -> |A v64 p64 %e0 o5 aa [4 a v96 b ]. The cs played inside the loop will have the wrong volume.
    "cc"[4":c+:cc+"] -> |A v64 p64 %e0 o5 aa [4 v64 a v96 b ] The cs inside the loop have the correct volume.
    This last technique is often useful, but should not be used without thinking carefully about what its effects will be, as it can create its own problems.

other miscellaneous directives

  • #REPLACE <> >< - Before any parsing, simply replaces any character found in the first string with the equivalently positioned character in the second string. The intended purpose of this command, and the effect of this specific example, is to allow using backwards octave markers for those who can't stop thinking of them as arrows pointing in a direction on a piano, or for those who have forgotten how greater than and less than signs work, or simply for converting from or transitioning from formats whose designers are that way. It may also function as a way to alias other commands to more familiar bindings, but do that at your own risk -- if you change 'r' to 'p' (another common mml variation), you'd also have to define #dpums instead of #drums (or use #DRUM instead), as well as finding an alias for the panning commands.

  • #cdef ( %l1 - Character macro. Essentially similar to a standard macro, but is called by a single character instead of a quoted name. Useful for reducing visual clutter and making typing easier in situations when a sequence is used repeatedly mixed in with other content. A common use is turning legato on and off within phrases; other possible uses include weaving primary notes and echo notes into one channel MOD-style (using different volume and panning), changing articulation with ADSR, etc. If the symbol used is already used for a command, this will supersede that, so be careful. This is less drastic than #REPLACE, though, since #cdef macros are parsed as symbols, not replaced beforehand. #cdef k %k0 would prevent using kX (detune), but still allow using %kX normally (transpose). (You can't use parameters with a cdef, however -- k7 with the previous cdef would produce %k0 7, and a lone 7 has no semantic meaning and is ignored.)

  • #VARIANT - This is used to produce multiple versions of a piece from one file. This can be useful if producing versions for different sets of samples in different romhacks, or if the game needs different variations to use at different times, such as a version with more dramatic timbres, a version with a smaller sample loadout to reduce loading times, or a version with and without an intro, outro, or bridge. The Beyond Chaos randomizer has several built-in situations in which it checks for defined variants, including the smaller sample version (as 'vic') and versions that vary if the song is used on the Phantom Train ('tr') or Zozo or the World of Ruin ('sfx').

    Syntax is the text #VARIANT, followed by a string of characters, and then a name (optional, but only one variant in a file can be nameless). For example, #VARIANT ~ vic or #VARIANT / rotds. The characters (~, here) essentially act as variable comment delimiters. In the variant vic, here, anything between ~ characters is included, but anything between / characters (because / is a variant delimiter for at least one variant in this file, but is not listed for vic) is completely ignored. A more complicated example is the random Dancing Mad segments for Beyond Chaos:
    #VARIANT ~_
    #VARIANT ~* tier1
    #VARIANT ?* tier2
    #VARIANT ?_ tier3
    Depending on whether the song is used as tier 1, 2, or 3, it should have either an intro, an outro, or both. Tier 1 and 2 both have an outro, and tier 2 and tier 3 have intros. As such, we enclose the intro inside a pair of ? ? and the outro inside * *. We might also want to do something unique if we don't have an intro or outro, such as making sure the global song settings, channel states, and so forth are set the way we want them at the beginning, or using the extra space saved by not having those sections to add more dynamics. No-intro stuff goes inside ~ ~, and no-outro stuff goes inside _ _.

    The variant delimiters, like cdefs, are processed during command parsing, so using those characters inside commands, macro calls, or percussion delimiters will not function as a variant delimiter. This means that characters like * and !, which have semantic meaning in macro calls and percussion respectively, are still safely "unused" and can be used as variant delimiters.

  • #SFXV ` * - Sound effect variant. This is an alternate form of the variant concept that was designed for certain use cases within Beyond Chaos. #SFXV is followed by two character strings, and either the first or the second of these strings is added to the list of variant delimiters -- so that anything within a pair of those characters is ignored unless a variant definition specifically includes it. In nearly all cases, it is the second set of characters (* in the example) that is ignored. When the MML parser is embedded into an external program, instead of being called directly, that program can request the sfx variant version of the sequence. Only in that case will the *-marked text be included and the `-marked text be ignored. Beyond Chaos does this in tracks to which it adds either the rain (Zozo) or wind (World of Ruin) sound effects.

  • #TITLE, #ALBUM, #COMPOSER, #ARRANGED: These have no meaning within the MML parser but can be used by external programs to pull metadata (Beyond Chaos currently does this).