This is a library that compiles scores written in Music Macro Language ("MML Codes" written for Mabinogi or Archeage) into the network packets for the "Perform" action in Final Fantasy 14.
This library was mainly created to make it a lot simpler to compose scores that
can be played in FFXIV. Currently, typing out notes in a format like
A (+0), Bb (-1), C# (+1)
is somewhat readable, but very verbose and there
aren't really any tools out there to visualize scores in this format. Also,
there's no standard way of specifying note length or tempo.
MML is a lot more convenient since there exist thousands of scores out there in MML format, and you can use 3ML Editor to convert MIDI to MML as well as to visualize the song.
See the For developers section for why the output format is network packets.
Performgen receives the input string from Stdin and returns comma separated values with the first column being the 32 byte segments written as hexadecimal strings and the second column being the duration of the segment in milliseconds.
- Download
performgen.exe
from the GitHub releases page. - On Windows, save the following MML string to a file like
song.mml
:t80o3a2b2c2d2e2f2g2
- Execute the following command in command prompt:
type song.mml | performgen.exe > segments.csv
- The contents of
segments.csv
should look like:data,duration(ms) 1d16ffbb15ffbb16ffbb18fffaff7d16ffbb18ffbb1bfffaff7d1affbb180000,1872 1dffbb16fffafffafffaffbb1ffffaff7d21fffaff7d22fffaff7d24fffa0000,2499 1eff7d0affbb09ffbb0affbb0cfffaff7d0afffaff7d0afffaff7d09ffbb0a00,1998 0cffbb0cfffaff7d0afffaff7d00000000000000000000000000000000000000,937
- These segments can be sent from a private server implementation of FFXIV or used with a FFXIV packet injector (there is no public one yet as far as I know).
Performgen can be used as a Golang library with no additional dependencies.
Simply add import "github.com/ff14wed/performgen"
and pass the MML string
to the perform.Generate()
call to receive byte data for an FFXIV performance,
split into segments.
The reason for the specific choice of output format is that the network protocol for the "Perform" action is actually way more powerful than the action itself. While using the "Perform" action manually generates about 1-3 notes per packet, the protocol allows you to play up to 10 notes per packet with at minimum a few milliseconds of delay in between them. It also allows you to specify exact number of milliseconds of delay to achieve a very high degree of accuracy with note length and tempo.
The network protocol format for a single segment of a performance is a
32 byte block structured as defined here. The data
for a segment is simply a list of either perform note IDs (1 byte), or delays
(2 bytes, first byte is 0xFF
, second byte is a number from 0-250 for number
of milliseconds).
When the FFXIV client receives a single segment from the server, it queues it for the performing character and reads the data from this queue as the notes play. If the client receives multiple segments at once, they will be read in order instead of overlapping.
This is useful because instead of sending one note at a time with delay in between, you can send an entire section of music and it will be played with the correct timing. However, this queue has a limited size buffer, so sending an entire song at once would result in only the last section of the song playing.
https://godoc.org/github.com/ff14wed/performgen
The MML understood by this compiler is a slight variation of what is understood by Mabinogi or generated by the 3MLE tool.
The major difference is that it can only understand one track at a time, so
while you can import a song in the MML@Melody,Harmony1,Harmony2,Song;
format
into 3MLE, this compiler can only understand each of Melody
, Harmony1
,
Harmony2
, or Song
without commas or semicolons or the MML@
header.
Most of these commands behave the same way as in other MML, so some of this reference is borrowed from existing documentation. The parser is case insensitive, so the symbols used could be uppercase or lowercase.
Symbols: A, B, C, D, E, F, G
A note command will produce a sound with the letters A
to G
corresponding
to the pitches.
A sharp note can be produced by adding a +
or a #
directly after the pitch
letter, and flat notes by adding a -
.
The length of a note is specified by appending a positive integer representing
the denominator of 1/x, which translates mean the length is this fraction a
whole note. For example, c8
would produce a C eighth note, and f+2
would
produce a F♯ half note. If the length of the note is not specified, the default
length specified by the length command will be assumed.
If the length is explicitly set to 0, this note will be used to form a chord
with the next note. For example, c0e0g0
forms a C Major triad. Implementation
wise, this is achieved by making an arpeggiated chord with 20 milliseconds
of delay in between each note.
Adding a dot or a period .
after the number specifying the length increases
the length of the note by 50%. For example f+8.
plays the an F# note for 3/16
of a whole note.
It's important to note that FFXIV doesn't currently support any sort of sustain for notes, so adding a length for a note is pretty much equivalent to a rest after the note.
Symbol: R
A rest is an interval of silence in the music. The length of the rest is
specified in the same manner as the length of the note. It also supports
the dot for increasing its length by 50%. For example, r8.
is an interval
of silence for 3/16 of a whole note.
Symbol: O
The octave can by set for all notes after this command by specifying o
followed by a number. Currently, FFXIV only supports octaves 3, 4, 5, or 6,
so setting any other octave generates an error in this tool. These octaves
correspond to the -1, 0, 1, and 2 numbers in-game. For example, o5 g a b g
plays G (+1), A (+1), B (+1), G (+1)
in game.
Keep in mind you can only play the C note on octave 6 (C (+2)
). Any other
note on this octave will generate an error.
Before using this tool, it is advisable to import the MML into an editor like 3MLE first to make sure it doesn't require going out of the 3-6 octave range, and if it does, change some notes around accordingly or shift the song up or down an octave.
Symbol: >, <
These commands don't take any arguments. The >
steps the octave up by one,
and <
steps the octave down by one. Shifting the octave outside of the
3-6 range will generate an error.
Symbol: L
The default length of all notes/rests without an explicit length after this
command can be set by specifying l
followed by a number. This number is
specified in the same manner as the length of the note. This number can also
be modified with the dot to make the default length 50% longer. For example,
l8.
makes the default length 3/16 of a whole note.
If this command is never called, notes without an explicit length will be quarter notes.
Symbol: T
The tempo of the song can be changed by specifying t
followed by the tempo
in beats per minute. For example t88
sets the tempo to 88 bpm. The default
tempo is 120 beats per minute.
Symbol: V
This command does nothing since FFXIV doesn't support volume. It is only
implemented to parse without error scores from other games. It must still be
followed by a number. For example, v120
would be parsed and ignored.
Symbol: &
This command is originally used to extend the duration of the previous note.
For example e-1&e-1&e-1&e-1
would extend the note E♭ to one full measure.
However, since FFXIV doesn't currently support sustained notes, this would
only translate into a single E♭ and a full measure of silence.
In order to support compatibility with MML used in other games, this command has the following behavior:
- It is used by specifying a
&
followed by a full note command or a full rest command. It doesn't matter whether there is anything before the&
or not. - It always adds a rest with the duration of the following note or rest
command. For example,
&a+8
is equivalent to ar8
, and&r4
is equivalent to ar4
.