Skip to content


Craig Campbell edited this page · 18 revisions
Clone this wiki locally

This documentation assumes that you are already somewhat familiar with MML. If you are not check out or for a good beginner's guide.

1. Language

MMLX is a superset of MML. This means anything written in MML is valid in MMLX but not vice versa.


MMLX supports 3 styles of commenting.

  • Block comments:
 * this is a block comment
  • Line comments:
# this is a line comment


; this is a line comment

1.2 Importing other MMLX files

If you would like to import other MMLX files into your current project you can use the @import command:

@import "filename.mmlx"
  • The extension .mmlx is optional.
  • The path should be a relative path from the current file.

1.3 Using variables

MMLX has support for variables. Variables can be any block of notes and definitions. For example you could store a chord arpeggio in a variable:

a_minor = a > c e a

A o3 [a_minor]4

1.4 Ignoring files

When you run the mmlx command from command line any mmlx file in the starting directory will be processed. If you want a file to be ignored you can prefix the file name with an underscore _.

For example if you would like to include a bunch of instruments in your current project, but you don't want the instruments to be processed on their own you can name the instruments file _instruments.mmlx

Global Variables


Transposing to other keys

MMLX has the ability to transpose a song to any key. To do this add a declaration at the top of your file with the number of semitones you want to transpose:



  • If you have note slides included using the / command they will be lost when you transpose. This will hopefully be resolved in a future version


Direct note targeting

You can compose using regular MML notation by defining your octave: o3 and then jumping up and down using > and <. Some people may prefer to compose by targeting notes directly and not worrying about octaves. To do this you must first add the following line to the top of your project:


After this you target every note using the following syntax:


For example:

g+4 g+3,8 a+3,8 b3 c+4,8 d+4,8 e4 g4 g+4,2

In traditional notation that would look like this:

o4 g+ < g+8 a+8 b > c+8 d+8 e g g+2
  • You cannot mix and match absolute notes with regular MML notation because of the ambiguity of something like c4 which could be interpreted as a quarter note c in the current octave or a c in the 4th octave with the current note duration.


Macro counters

When your MMLX is converted into MML the pitch/volume/vibrato macros for instruments are generated for you and begin at 0. This makes sense for a new MMLX project since you want to be able to create as many macros as possible so you don't run out.

It is possible, however, that you may already have a bunch of macros defined on your own. At the moment MMLX does not scan your files and skip macros that already exist in the file. A possible work around for this is to use the counter variable to tell MMLX to start counting at a number other than 0:


This means that all generated macros from your instruments will start at 20, for example, @v20 = { 12 } @v21 = { 8 } etc.

This is obviously not ideal and will be resolved in a future version.


Defining a global tempo

You can define a tempo in typical MML fashion by doing:

ABCD t100

This will work as expected, but if you are using expansion chips and don't want to have to think about the track names you can use:

#X-TEMPO 100

This will scan through your mmlx project, find all voices, and create a global tempo declaration for you.


Smooth portamento

This is a somewhat undocumented feature in PPMCK that improves pitch slides to make them smoother on channels A, B, and C.


Adding this to the top of your project will turn this on automatically every time you use the portamento command on channel A, B, or C.

2. Instruments

One of the main features of MMLX is that it lets you create instrument presets. This lets you quickly dial up sounds in your songs and switch between instruments.

2.1 Creating

To create an instrument patch add an instrument definition anywhere in your MMLX file in the format:

    key1: value1
    key2: value2
    key3: value3

If you wanted to create a simple square wave you would add the following:

    volume: 15
    timbre: 2
  • The indentations must be a tab character or 4 spaces and the instrument declaration must be followed by a linebreak.
  • The associated macros (timbre, volume, etc) are only generated if the instrument is actually used. This means you can define as many instruments as you want and even import a list of instruments into your MMLX file without having to worry about increasing the file size of the generated MML file.

2.1.1 Valid properties

Valid instrument properties are:


Volume envelope in MML format. Steps are separated by spaces and valid values are 0-15. Loop points are marked with |. For example this will fade in and out:

    volume: | 0 2 4 6 8 10 8 6 4 2


Timbre/duty envelope in MML format. Steps are separated by spaces and valid values are 0-3. Loop points are marked with |. These are the 4 duty cycles:

0     | |             |  12.5% thin raspy sound
      | |_____________|
1     |   |           |  25% thick fat sound
      |   |___________|
2     |       |       |  50% smooth clear sound
      |       |_______|
3     |           |   |  75% same as 25% but phase-inverted
      |           |___|

If you would like to create a plucked effect you can do something like:

    timbre: 2 0


ADSR envelope. This should be 4 numbers separated by spaces:

  • Attack - The number of frames it will take for your instrument to go from zero to the maximum volume. String and wind instruments would have a higher value than a plucked or keyboard instrument.

  • Decay - The number of frames it will take for your instrument to go from the maximum volume to the sustain volume

  • Sustain - The amplitude the note should be held at (value from 0 - 15). For example the initial hit could be 15, but the note might be held at 8 before dropping off to 0.

  • Release - The number of frames that it will take for your instrument to go from the sustain amplitude to 0.

A sustained piano might look something like this:

    adsr: 0 10 10 30

This is the same thing as doing:

    volume: 15 15 14 14 13 13 12 12 11 10 10 10 10 9 9 9 8 8 8 7 7 7 6 6 6 5 5 5 4 4 4 3 3 3 2 2 2 1 1 0
  • By default the maximum volume is 15. It cannot be higher than 15 but you can change it by updating the max_volume property.
  • If the decay value is 0 the sustain value will be used as the maximum amplitude.


This property allows you to limit the maximum volume of a specific ADSR envelope. For example if you wanted to create a sound that swells from 0 to 12 and then is held at 8 you could do something like this:

    adsr: 10 10 8 0
    max_volume: 12

That will produce a volume envelope that looks like this:

    volume: 0 2 3 4 6 7 8 10 11 12 12 12 12 11 11 10 10 9 9 8

By contrast, the same thing without max_volume would look like this:

    volume: 0 2 4 5 7 9 10 12 14 15 15 15 14 13 12 12 11 10 9 8

You can see the second one rises up to 15 before dropping to 8 whereas the first one rises up to 12 before dropping.


Pitch bend macro in MML format. If you want to make an instrument that slides up or down when a note is pressed you can use the pitch property in the instrument definition.

For example this would be a gradual slide down:

    pitch: -20

This would be a rapid initial slide down then small waves of increasing pitch:

    pitch: -20 | 0 2 0

In this last example the 0 2 0 is repeated until the end of the note so it will continue to slowly slide up.


Arpeggio macro in MML. This uses relative pitches in a sequence. For example a major chord arpeggio would look something like:

    arpeggio: 0 | 0 0 0 4 0 0 0 3 0 0 0 5 0 0 0 -12

This stays on the root note for 4 frames then moves up 4 semitones to the major third, up 3 semitones to the fifth, up 5 semitones to the octave, then down to the root and repeats the pattern.


Vibrato macro in MML. It contains 3 values: delay, speed, and depth.

    vibrato: 8 4 4


Quantize value for this instrument. Value can be from 1 to 8. The default is 8.

    q: 4

This means that this note will be held for half of the full duration. This is useful for staccato or snappy bass lines.

If you want to make a q value that does not depend on the note duration but is instead a fixed number of frames you can use this alternate notation. Let's say you want every note to last 30 frames (half a second):

    q: 0, 30

2.1.2 Expansion chip properties

Valid properties for instruments using expansion chips are:


Expansion chip for this instrument. Currently N106 is the only option (for the NAMCO 106/NAMCO 163 chip), but the others are coming soon.

This is used so the instrument can be handled differently if it belongs to a chip other than the default (2A03). Also this is used to make sure that you are not applying the instrument to a voice that will not support it.

Leave this blank for any instrument that doesn't belong to an expansion chip.


This is used specifically for the N106 wavetable synth. This macro lets you define the waveform that you would like to use for this synth.

For example this will be a square wave sound:

    waveform: 15 15 15 15 0 0 0 0

    // # # # #     # # # #
    //       #     #
    //       #     # 
    //       # # # #

This will be a sawtooth wave:

    waveform: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

    //          #        #
    //       #  #     #  #
    //    #     #  #     #
    // #        #        #

The number of samples must be a multiple of 4 and the octave will get lower with the more samples you use. The value for each sample can be 0 to 15.

  • It is recommended that you use 4, 8, 16, or 32 samples cause otherwise the instrument will not be in tune and you will have to play around with the K (transpose) command.


This is used specifically for the N106 wavetable synth. MMLX will default the buffer for each N106 instrument to 00, but this attribute lets you override that and force it to a specific buffer.

  • Depending on the number of samples in your waveform only a certain number of buffers will be available. MMLX will throw an exception if you try to set the buffer to an invalid value

2.2 Switching

Once you have defined an instrument you can use it in your track.
For example let's start with a cello sound:

    adsr: 20 20 10 0
    timbre: 3

If you want to have a voice use this you can apply the instrument like so:

A o3 @cello c2 d e-2 d c2 g c2.

You can change the instrument at any point during your song by adding an @{instrument-name} call.

  • Changing the instrument on a voice will automatically turn off the previous instrument, but occasionally you may want to stop an instrument on your own. An example of this would be if you have an instrument used purely as a pitch slide that you want to apply to just a single note. To do this you can use the @end command which is reserved for ending the most recent instrument.

2.3 Extending

There are two ways to extend an existing instrument. The first is to create a new instrument with the @extends tag, for example:

    adsr: 20 20 10 0
    vibrato: 60 3 2
    timbre: 3

    @extends "cello"
    adsr: 20 20 5 0
    max_volume: 10

This allows you to avoid having to duplicate rules. The vibrato macro and timbre envelope from cello is applied to cello-soft automatically.

A o3 @cello-soft c2 d e-2 d @cello c2 g c2.

The second way is to create a new instrument definition like so:

    adsr: 20 20 10 0
    vibrato: 60 3 2
    timbre: 3

    adsr: 20 20 5 0
    max_volume: 10

Now you can use the cello instrument but add a modifier on top of it.

A o3 @cello +@soft c2 d e-2 d @cello c2 g c2.

This is useful if you want to create generic instrument modifiers for commonly used things (such as volume changes) and then apply them to any instrument without having to create a new version of that instrument for each one. The next instrument declaration without a + sign will clear out any active instruments and modifiers.

3. Portamento

Portamento is when a note slides smoothly from one note to another. This is something that is doable using MML, but it is a pain. You have to manually calculate how far the note has to move and then create a pitch macro that slides that far over a given amount of time.

In MMLX applying portamento is simple. If you wanted a C in octave 3 to slide up to a G in octave 5 you would do the following:

A o3 c / > g2.

The / command is used to designate a slide. The slide begins when the next note is played similar to how portamento works on a synthesizer. The / has to come directly after the previous note. For example the following would not work:

A o3 c > / g2.

3.1 Adjusting slide duration

By default the slide duration is the equivalent of a 16th note at whatever tempo you are at. If you want to change the duration of the slide you can do so by adding a number after the slash.

For example if you want the slide to happen over an quarter note instead of a 16th note you would do:

A o3 c /4 > g2.

To see this example in action let's take a complete MMLX project:

#TITLE Slide Demo

// 120 bpm
A t120

// simple square wave instrument
    volume: 15
    timbre: 2

// slide from c to g an octave up
A o3 @square c /4 > g2.

To get an idea of what is happening here we can look what happens when this is coverted to MML:

#TITLE Slide Demo
@0 = { 2 }
@v0 = { 15 }
@EP0 = { 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 19 0 }
A t120
A o3 @@0 @v0 c EP0 c2. EPOF >

The pitch change is calculated and a pitch macro is created for you. 120 BPM is the equivalent of 2 beats per second. Therefore a single beat is given 1/2 a second. MCK macros run at 60 frames per second, so therefore a quarter note slide should take half of this or 30 frames. If you count there are 30 frames in the generated MML.

Also note that the octave shift moves to the end. This is because it has to start from the same note in the same octave to achieve the slide effect therefore once we reach the new note the octave needs to reflect where that note actually is.

4. DPCM samples

As of right now DPCM samples work basically the same way as they do in MML. The only difference is that you don't have to worry about defining the samples relative to the path that your script is running in command line. Instead you define the DPCM samples with a path relative to the MMLX file that you are editing.

For example if you have an mmlx file you are editing at /home/username/songs/mmlx/song1.mmlx and you want to reference a sample at /home/username/songs/samples/kick.dmc you would add the following to song1.mmlx:

@DPCM0 = { "../samples/kick.dmc", 15 }

You would then reference this sample as the c note on the DPCM channel E.

  • In the future it is likely that MMLX will support converting .wav samples to .dmc files on the fly. Also it is likely thats samples on channel E will be accessible via some command other than using a note.

5. NSF files

NSF files are created on save when you are watching a directory or when you manually run the mmlx command on a specific file. The script will throw an exception if it fails to create an NSF file.

If you do not wish to create NSF files you can pass create-nsf 0 into your mmlx call from command line.
If you would like to automatically open the created NSF file on save you can pass open-nsf.

This will open the NSF file using whatever the default application on your system for opening NSF files is (for example Nestopia).

Something went wrong with that request. Please try again.