Skip to content
tanoku edited this page Apr 11, 2012 · 18 revisions

This documentation assumes that you are already somewhat familiar with MML. If you are not check out http://nullsleep.com/treasure/mck_guide/ or http://nesdev.parodius.com/mckc-e.txt 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.

1.1 Comments

MMLX supports 3 styles of commenting.

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

or

; 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"
Notes
  • 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

#X-TRANSPOSE

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:

#X-TRANSPOSE +2

or

#X-TRANSPOSE -3
Notes
  • 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

#X-ABSOLUTE-NOTES

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:

#X-ABSOLUTE-NOTES

After this you target every note using the following syntax:

[note][octave],[duration]

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
Notes
  • 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.

#X-COUNTER

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:

#X-COUNTER 20

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.

#X-TEMPO

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

Smooth portamento

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

#X-SMOOTH

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:

{instrument-name}:
    key1: value1
    key2: value2
    key3: value3

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

square:
    volume: 15
    timbre: 2
Notes
  • 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

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

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

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
Notes
  • 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.

max_volume

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

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

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

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

    vibrato: 8 4 4

q

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:

chip

Expansion chip for this instrument. Currently N106, FDS, and VRC6 are supported, but the other chips 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.

waveform

This is used specifically for the N106 wavetable synth and the FDS (Famicom Disk System) 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

    //          #        #
    //       #  #     #  #
    //    #     #  #     #
    // #        #        #
Notes
  • For N106 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.
  • For N106 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.
  • For FDS the number of samples must be 64 and the value for each can be 0 to 63.

buffer

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.

Notes
  • 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:

cello:
    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.

Notes
  • 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:

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

cello-soft:
    @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:

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

soft:
    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
square:
    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. Expansion chips

Currently supported chips in MMLX are N106, FDS, and VRC6.

Using expansion chips in your project is pretty straight forward. Each expansion chip has its own set of voices that can be used for it. MMLX will create those voices for you so you don't have to remember. Take a look at the following examples for an idea of how it works.

You can also see working examples at https://github.com/ccampbell/mmlx/tree/master/files.

4.1 N106

The N106 chip supports 8 channels (P, Q, R, S, T, U, V, W).

You can use the expansion the same way as you would using regular MML or you can use special MMLX notation.

First define an instrument:

sawtooth:
    volume: 12
    chip: N106
    buffer: 0
    waveform: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

For more information about setting up an instrument and extra properties check out expansion chip properties.

Now use the instrument on a channel:

N106-A o2 @sawtooth g a b > g

You can use N106-A to N106-H to define the 8 N106 channels. If you go beyond this an exception will be thrown. When you use expansion channels the expansion declaration will be added automatically to the top of the MML:

#EX-NAMCO106 1

The number of channels will be filled in automatically depending on which channels you are using.

You can use the X-TEMPO declaration to make sure these special voices still have the tempo set on them.

Notes
  • The octave changes depending on the number of channels you use as well as the number of samples in your instrument's waveform declaration
  • The number of channels used will either be 1,2,4, or 8 to make sure that the channels stay in tune.

4.2 FDS (Famicom Disk System)

The FDS chip is used for channel F. Usage is similar to the N106 except waveforms have to be 64 samples long. You can use FDS-A or F for the FDS channel.

Notes
  • The volume range on this channel is 0-63 instead of 0-15
  • FDS pitch modulation support @MW and @MH coming soon.

4.3 VRC6

The VRC6 chip supports 3 channels (M, N, O). The first two channels work pretty much the same way as the default 2A03 pulse wave channels. The main difference is that there are 8 possible duty cycles instead of 4.

The range is 1/16th (6.25%) to 8/16th (50.00%) and these correspond to timbre settings 0 and 7.

vrc6-square:
    chip: VRC6
    volume: 12
    timbre: 7

VRC6-A o3 @vrc6-square g a b c d c b a g1

This expansion #EX-VRC6 will be automatically added to your project.

The third channel (VRC6-C or O) is the sawtooth channel and does not accept a timbre attribute:

vrc6-sawtooth:
    chip: VRC6
    volume: 12
    q: 4

VRC6-C o2 @vrc6-sawtooth [g]8 g1

5. 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.

Notes
  • 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.

6. 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).