Subsections


Variables, Conditionals and Jumps

To make the processing of your music easier, MMA supports a very primitive set for variable manipulations along with some conditional testing and the oft-frowned-upon GOTO command.

Variables

MMA lets you set a variable, much like in other programming languages and to do some basic manipulations on them. Variables are most likely to be used for two reasons:

To begin, the following list shows the available commands to set and manipulate variables:

Set VariableName String
Mset VariableName ...MsetEnd
UnSet VariableName
ShowVars
Inc Variablename [value]
Dec Variablename [value]
Vexpand ON/Off

All variable names are case-insensitive. Any characters can be used in a variable name. The only exceptions are that a variable name cannot start with a ``$'' or a ``_'' (an underscore--this is reserved for internal variables, see below) and names cannot contain a ``['' or ``]'' character (brace characters are reserved for indexing, details here).

Variables are set and manipulated by using their names. Variables are expanded when their name is prefaced by a space followed by single ``$'' sign. For example:

Set Silly Am / Bm /
1 $Silly

The first line creates the variable ``Silly''; the second creates a bar of music with the chords ``Am / Bm /''.

Note that the ``$'' must be the first item on a line or follow a space character. The variable name must be terminated by a space or the end of a line. For example, the following will NOT work:

Set Silly 4a;b;c;d;
1 Am {$Silly } // needs space before the $

Nor will:

1 Am { $Silly} // needs space at the end of the name

However:

1 Am { $Silly }

will work fine.

Following are details on all the available variable commands:

Set

Set or create a variable. You can skip the String if you do want to assign an empty string to the variable. A valid example is:

Set PassCount 1

You can concatenate variables or constants by using a single ``+''. For example:

Groove Rhumba
Repeat
...
Set a $_Groove + Sus
Groove $a
...
Groove Rhumba1
Repeatend

This can be useful in calling GROOVE variations.

NewSet

The NEWSET command works the same as SET with the exception that that it is completely ignored if the variable already exists. So,

NewSet ChordVoice JazzGuitar

and

If NDef ChordVoice
Set ChordVoice JazzGuitar
Endif

have identical results.


Mset

This command is quite similar to SET, but MSET expects multiple lines. An example:

MSet LongVar
1 Cm
2 Gm
3 G7
MsetEnd

It is quite possible to set a variable to hold an entire section of music (perhaps a chorus) and insert this via macro expansion at various places in your file.

Each MSET must be terminated by a ENDMSET or MSETEND command (on its own separate line).

Be careful if you use an MSET variable in a PRINT statement ... you'll probably get an error. The PRINT command will print the first line of the variable and the remainder will be reinserted into the input stream for interpretation.

Variables are not expanded when creating an MSET macro (lines are read verbatim from the input path)--this makes MSET subtly different from SET. Variables are expanded when the macro is executed.

Special code in MMA will maintain the block settings from BEGIN/END. So, you can do something like:

Mset Spam
  Line one
  Line 2
  333
EndMset
Begin Print
  $Spam
End


RndSet

There are times when you may want a random value to use in selecting a GROOVE or for other more creative purposes. The RNDSET command sets a variable from a value in a list. The list can be anything; just remember that each white space forms the start of a new item. So,

RndSet Var 1 2 3 4 5

will set $VAR to one of the values 1, 2, 3, 4 or 5.

You could use this to randomly select a GROOVE:

Groove $var Groove1 Groove2 Groove3

Alternately,

RndSet Grv Groove1 Groove2 Groove3

will set $GRV to one of ``Groove1'', ``Groove2'' or ``Groove3''.

Then you can do the same as in the earlier example with:

Groove $Grv

You can also have fun using random values for timing, transposition, etc.

UnSet VariableName

Removes the variable. This can be useful if you have conditional tests which simply rely on a certain variable being ``defined''.

ShowVars

Mainly used for debugging, this command displays the names of the defined variables and their contents. The display will preface each variable name with a ``$''. Note that internal MMA variables are not displayed with this command.

You can call SHOWVARS with an argument list. In this case the values of the variables names in the list will be printed. Variables which do not exist will not cause an error, e.g.:

ShowVars xXx Count foo
   $XXX - not defined
   $COUNT: 11
   $FOO: This is Foo

Inc and Dec

These commands increment or decrement a variable. If no argument is given, a value of 1 is used; otherwise, the value specified is used. The value can be an integer or a floating point number.

A short example:

Set PassCount 1
Set Foobar 4
Showvars
Inc FooBar 4
Inc PassCount
ShowVars

This command is quite useful for creating conditional tests for proper handling of codas or groove changes in repeats.

VExpand On or Off

Normally variable expansion is enabled. These two options will turn expansion on or off. Why would you want to do this? Well, here's a simple example:

Set LeftC Am Em
Set RightC G /
VExpand Off
Set Full $LeftC $RightC
VExpand On

In this case the actual contents of the variable ``Full'' is ``$LeftC $RightC''. If the OFF/ON option lines had not been used, the contents would be ``Am Em G /''. You can easily verify this with the SHOWVARS option.

When MMA processes a file it expands variables in a recursive manner. This means that, in the above example, the line:

1 $Full

will be changed to:

1 Am Em G /

However, if later in the file, you change the definition of one of the variables ... for example:

Set LeftC Am /

the same line will now be ``1 Am / G /''.

Most of MMA 's internal commands can be redefined with variables. However, you really shouldn't use this feature. It's been left for two reasons: it might be useful, and, it's hard to disable.

Not all commands can be redefined. The following examples will work, however in most cases we recommend that you do not redefine MMA commands.

Set Rate Tempo 120
$Rate
Set R Repeat
$R

Set B Begin
Set E End
$B Arpeggio Define
...
$E

Set A Define Arpeggio
Begin
$a ...
End

Even though you can use a variable to substitute for the REPEAT or IF directives, using one for REPEATEND, ENDREPEAT, REPEATENDING, LABEL, IFEND or ENDIF will fail.

Variable expansion should usually not be a concern. In most normal files, MMA will expand variables as they are encountered. However, when reading the data in a REPEAT, IF or MSET section the expansion function is skipped--but, when the lines are processed, after being stored in an internal queue, variables are expanded.

VEXPAND only has an effect when creating a macro using SET. It has no effect when using MSET.


StackValue

Sometimes you just want to save a value for a few lines of code. The STACKVALUE command will save its arguments. You can later retrieve them via the $_StackValue macro. For example (taken from the stdpats.mma file):

StackValue $_SwingMode
SwingMode On
Begin Drum Define
  Swing8 1 0 90 * 8
End
  ...
SwingMode $_StackValue

Note that the $_StackValue macro removes the last value from the stack. If you invoke the macro when there is nothing saved an error will occur.


Predefined Variables

For your convenience MMA tracks a number of internal settings and you can access these values with special macros.21.1 All of these ``system'' variables are prefaced with a single underscore. For example, the current tempo is displayed with the variable $_TEMPO.

There are two categories of system variables. The first are the simple values for global settings:

  $_AutoLibPath
Current AUTOLIBPATH setting.

  $_BarNum
Current bar number of song.

  $_ChordAdjust
The chord adjustment table values (see here). Note, you cannot use this value to reset the table.

  $_CTabs
List of the time-set chord tabs.

  $_Debug
Current debug settings.

  $_Groove
Name of the currently selected groove. May be empty if no groove has been selected.

  $_Groovelist
List of all currently defined GROOVE names.

  $_KeySig
Key signature as defined in song file. If no key signature is set the somewhat cryptic 0# will be returned.

  $_IncPath
Current INCPATH setting.

  $_LastDebug
Debug settings prior to last DEBUG command. This setting can be used to restore settings, e.g.:

Debug Warnings=off
...stuff generating annoying warnings
Debug $_LastDebug

  $_LastGroove
Name of the groove selected before the currently selected groove.

  $_LastVolume
Previously set global volume setting.

  $_LibPath
Current LIBPATH setting.

  $_LineNum
Line number in current file.

  $_Lyric
Current LYRIC settings.

  $_MMAPath
The root directory used by MMA . The modules, library, etc. are in this directory. This is set at startup and cannot be modified by the user.

  $_MIDISplit
List of SPLITCHANNELS.

  $_MIDIPlayer
Current MIDIPLAYER setting, including options.

  $_NoteLen(n)
Returns value of the duration in MIDI ticks of ``n''. Note: No spaces are permitted. Examples:
$_NoteLen(8.)
returns 144T, the duration of a dotted eight note,
$_NoteLen(5:4+16)
returns 86T, the duration of a 5:4 tuplet plus a 16th.

  $_OutPath
Current OUTPATH setting.

  $_Plugins
A list of registered simple plugins.

  $_Seq
Current SEQ point (0 to SEQSIZE). Useful in debugging.

  $_SeqRnd
Global SEQRND setting (on, off or track list).

  $_SeqRndWeight
Global SEQRNDWEIGHT settings.

  $_SeqSize
Current SEQSIZE setting.

  $_SwingMode
Current SWINGMODE setting (On or Off) and the Skew value.

  $_StackValue
The last value stored on the STACKVALUE stack.

  $_Tempo
Current TEMPO. Note that if you have used the optional bar count in setting the tempo this will be the target tempo.

  $_TickPos
Helpful in debugging, this variable is set to the current tick position in the generated MIDI file.

  $_Time
The current TIME (beats per bar) setting.

  $_TimeSig
The last value set for TimeSig.

  $_ToneTr
List of all TONETR settings.

  $_Tracklist
List of all currently defined TRACK names.

  $_Transpose
Current TRANSPOSE setting.

  $_VExpand
VExpand value (On/Off). Not very useful since you can't enable VEXPAND back with a macro.

  $_VoiceTr
List of all VOICETR settings.

  $_Volume
Current global volume setting.

  $_VolumeRatio
Global volume ratio (track vrs. master) from ADJUSTVOLUME Ratio setting.

The second type of system variable is for settings in a certain track. Each of these variables is in the form $_TRACKNAME_VALUE. For example, the current voice setting for the ``Bass-Sus'' track can be accessed with the variable $_Bass-Sus_Voice.

If the associated command permits a value for each sequence in your pattern, the macro will more than one value. For example (assuming a SEQSIZE of 4):

Bass Octave 3 4 2 4
Print $_Bass_Octave
...
3 4 2 4

The following are the available ``TrackName'' macros:

  $_TRACKNAME_Accent
  $_TRACKNAME_Articulate
  $_TRACKNAME_Channel
Assigned MIDI channel 1-16, 0 if not assigned.
  $_TRACKNAME_Compress
  $_TRACKNAME_Delay
  $_TRACKNAME_Direction
  $_TRACKNAME_DupRoot
(only permitted in Chord Tracks)
  $_TRACKNAME_Harmony
  $_TRACKNAME_HarmonyOnly
  $_TRACKNAME_HarmonyVolume
  $_TRACKNAME_Invert
  $_TRACKNAME_Limit
  $_TRACKNAME_Mallet
Rate and delay values (only valid in Solo and Melody tracks)
  $_TRACKNAME_MidiNote
Current setting
  $_TRACKNAME_MOctave
  $_TRACKNAME_MIDIVolume
  $_TRACKNAME_NoteSpan
  $_TRACKNAME_Octave
  $_TRACKNAME_Ornament
(all options)
  $_TRACKNAME_Plugins
(track registered plugins, the same for all tracks)
  $_TRACKNAME_Range
  $_TRACKNAME_RDuration
  $_TRACKNAME_Rpitch
  $_TRACKNAME_Rskip
  $_TRACKNAME_Rtime
  $_TRACKNAME_Rvolume
  $_TRACKNAME_SeqRnd
  $_TRACKNAME_SeqRndWeight
  $_TRACKNAME_Sequence
  $_TRACKNAME_Span
  $_TRACKNAME_Sticky
  $_TRACKNAME_Strum
  $_TRACKNAME_StrumAdd
  $_TRACKNAME_Tone
(only permitted in Drum tracks)
  $_TRACKNAME_Trigger
  $_TRACKNAME_Unify
  $_TRACKNAME_Voice
  $_TRACKNAME_Voicing
(only permitted in Chord tracks)
  $_TRACKNAME_Volume

The ``TrackName'' macros are useful in copying values between non-similar tracks and CHSHARE tracks. For example:

Begin Bass
  Voice AcousticBass
  Octave 3
  ...
End
Begin Walk
  ChShare Bass
  Voice $_Bass_Voice
  Octave $_Bass_Octave
  ...
End


Indexing and Slicing

All variables can have an option slice or index appended to them using ``[]'' notation. The exact syntax of the data in the ``[]''s is dependent on the underlying Python interpreter. But, as a summary:

[2] - selects the 3rd item in the list,
[1:2] - selects the 2nd to 3rd item (which means only the 2nd),
[0:2] - selects items 1 and 2,
[-1] - selects the last item.

It is possible to use the step option as well, but we don't know when you would.

When indexing or slicing a variable, the following should be kept in mind:

The ``[]'' must follow the variable without any space characters. The expression inside the ``[]'' must not contain any spaces.

The index or slice expression cannot be a variable.

An example:

Groove bossanova
Bass Volume m mf p mp
print $_Bass_Volume
print $_Bass_Volume[1:3]
print $_Bass_volume[2]

will display:

100 110 40 70
110 40
40

Mathematical Expressions

Anywhere you can use a variable (user defined or built-in) you can also use a mathematical expression. Expressions delimited in a $(...) set are passed to the underlying Python interpreter, parsed and expanded. Included in an expression can be any combination of values, operators, and MMA variables.

Here are a couple of examples with the MMA generated values:

Print $( 123 * (4.0/5) )
98.4

Tempo 100
Set V $( $_Tempo + 44)
Print $v
144

How it works: MMA first parses each line and expands any variables it finds. In the second example this means that the $_Tempo is converted to ``100''. After all the variable expansion is done a check is made to find math delimiters. Anything inside these delimiters is evaluated by Python.

You can even use this feature to modify values stored in lists.21.2 A bit complex, but well worthwhile! In the following example we add ``10'' to the current ARTICULATE setting. It's split into three lines to make it clearer:

set a $( ' $_Chord_Articulate '.split() )

Note the use of single quotes to convert the MMA ``string'' to something Python can deal with. You could just as easily use double quotes, but do note that the spaces before the ``$'' and before the final `` ' '' are needed. The result of the above is that the variable ``$a'' now is set to something like: ``['100', '100', '90', '80']''.

set b $([str(int(x)+10)for x in $a ] )

Next we use a list comprehension to add ``10'' to each value in the list. Our new list (contained in ``$b'') will be: ``['110', '110', '100', '90']''. Notice how the strings were converted from strings to integers (for the addition) and then back to strings.

set c $( ' '.join( $b ) )

The new list is now converted to a string which MMA can deal with and store it in ``$c''. In this case: ``110 110 100 90''.

Chord Articulate $c

Finally, CHORD ARTICULATE is modified.

Now, that that is clear, you can easily combine the operation using no variables at all:

Chord Articulate $(' '.join([str(int(x)+10)for x in' $_Chord_Articulate '.split()]))

Some additional notes:

Conditionals

One of the most important reasons to have variables in MMA is to use them in conditionals. In MMA a conditional consists of a line starting with an IF directive, a test, a series of lines to process (depending upon the result of the test), and a closing ENDIF or IFEND21.4 directive. An optional ELSE statement may be included.

The first set of tests are unary (they take no arguments):

Def VariableName
Returns true if the variable has been defined.

Ndef VariableName
Returns true if the variable has not been defined.

In the above tests you must supply the name of a variable--don't make the mistake of including a ``$'' which will invoke expansion and result in something you were not expecting.

A simple example:

If Def InCoda
  5 Cm
  6 /
Endif

The other tests are binary. Each test requires a conditional (symbolic or mnemonic as detailed in the following table) and two arguments.

Mnemonic Symbolic Condition
     
LT < True if Str1 is less than Str2
LE <= True if Str1 is less than or equal to Str2
EQ == True if Str1 is equal to Str2
NE != True if Str1 is not equal to Str2
GT > True if Str1 is greater than Str2
GE >= True if Str1 is greater than or equal to Str2

In the above tests you have several choices in specifying Str1 and Str2. At some point, when MMA does the actual comparison, two strings or numeric values are expected. So, you really could do:

If EQ abc ABC

and get a ``true'' result. The reason that ``abc'' equals ``ABC'' is that all the comparisons in MMA are case-insensitive.

You can also compare a variable to a string:

If > $foo abc

will evaluate to ``true'' if the contents of the variable ``foo'' evaluates to something ``greater than'' ``abc''. But, there is a bit of a ``gotcha'' here. If you have set ``foo'' to a two word string, then MMA will choke on the command. In the following example:

Set Foo A B
If GT $Foo abc

the comparison is passed the line:

If GT A B abc

and MMA seeing three arguments generates an error. If you want the comparison done on a variable which might be more than one word, use the ``$$'' syntax. This delays the expansion of the variable until the IF directive is entered. So:

If GT $$foo abc

would generate a comparison between ``A B'' and ``ABC''.

Delayed expansion can be applied to either variable. It only works in an IF directive.

Strings and numeric values can be confusing in comparisons. For example, if you have the strings ``22'' and ''3'' and compare them as strings, ``3'' is greater than ``22''; however, if you compare them as values then 3 is less than 22. The rule in MMA is quite simple: If both strings can be converted to a value, a numeric comparison is done; otherwise they are compared as strings.21.5

This lets you do consistent comparisons in situations like:

Set Count 1
If LE $$Count 4
  ...
IfEnd

Note that the above example could have used ``$Count'', but you should probably always use the ``$$'' in tests.

Much like other programming languages, an optional ELSE condition may be used:

If Def Coda
  Groove Rhumba1
Else
  Groove Rhumba
Endif

The ELSE statement(s) are processed only if the test for the IF test is false.

Nesting of IFs is permitted:

If ndef Foo
  Print Foo has been defined.
Else
  If def bar
    Print bar has been defined. Cool.
  Else
    Print no bar ...go thirsty.
  Endif
Endif

works just fine. Indentation has been used in these examples to clearly show the nesting and conditions. You should do the same.

Goto

The GOTO command redirects the execution order of your script to the point at which a LABEL or line number has been defined. There are really two parts to this:

  1. A command defining a label, and,

  2. The GOTO command.

A label is set with the LABEL directive:

Label Point1

The string defining the label can be any sequence of characters. Labels are case-insensitive.

To make this look a lot more line those old BASIC programs, any lines starting with a line number are considered to be label lines as well.

A few considerations on labels and line numbers:

The command:

Goto Point1

causes an immediate jump to a new point in the file. If you are currently in repeat or conditional segment of the file, the remaining lines in that segment will be ignored.

MMA does not check to see if you are jumping into a repeat or conditional section of code--but doing so will usually cause an error. Jumping out of these sections is usually safe.

The following example shows the use of both types of label. In this example only lines 2, 3, 5 and 6 will be processed.

Goto Foo
1 Cm
Label Foo
2 Dm
3 /
Goto 5
4 Am
5 Cm
6 Dm

For an example of how to use some simple labels to simulate a ``DS al Coda'' examine the file ``lullaby-of-Broadway'' in the sample songs directory.



Footnotes

... macros.21.1
The values are dynamically created and reflect the current settings, and may not be exactly the same as the value you originally set due to internal roundings, etc.
... lists.21.2
this was written before the introduction of slices, (details here). Slices make this much easier, but lets leave the hard stuff in just to show what can be done.
... functions:21.3
It is possible that the following functions could be used to do ``bad'' things. If you see code using these commands from a suspect source you should be careful.
...IFEND21.4
MMA 's author probably suffers from mild dyslexia and can't remember if the command is IfEnd or EndIf, so both are permitted. Use whichever is more comfortable for you.
... strings.21.5
For this comparison float values are used. Rounding errors can cause equality comparisons to fail.
Bob van der Poel 2017-03-25