Variables, Conditionals and Jumps
Keywords: mma

Subsections

- Variables
  - Set
  - NewSet
  - Mset
  - RndSet
  - UnSet VariableName
  - ShowVars
  - Inc and Dec
  - VExpand On or Off
  - StackValue
- Predefined Variables
- Indexing and Slicing
- Mathematical Expressions
- Conditionals
- Goto

#
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:

- For use in setting up conditional segments of your file,

- As a shortcut to entering complex chord sequences.

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 } // ¡¡Wrong!! needs space before the $

Nor will:

1 Am { $Silly} // ¡¡Wrong!! 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 can be retrieved using the $_TEMPO variable.

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. The positions in this list indicate the offsets used for chord placement on a chord data line. The values are set with TIME and TIMESIG changes.

$_DateDate
  The current date in yyyy-mm-dd format.

$_DateTime
  The current time in hh-mm-ss format.

$_DateYear
  The current year in yyyy format.

$_Debug
  Current debug settings.

$_FileName
  The name of the file being processed as entered on the command line (returns an empty string if input is from STDIN).

$_FilePath
  Absolute (expanded) name of the file currently being processed. This is not necessarily the same as the $_FileName macro since the current file might be a library file. An empty string is returned if input is from STDIN. If MMA added a “.mma” suffix to the filename that will be included.

$_Env(n)
  Returns the value of an environment variable. For example:

  $_Env(PATH)

  will return the current setting of the shell PATH variable. If the variable is not defined or if it has no value an empty string will be returned. The case of the argument must match that of the variable in the shell.

$_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.

$_MIDIassigns
  A list of all in-use MIDI channels and the MMA tracks assigned to them. Useful for debugging channel overflow issues.

$_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.

$_DataPlugins
  A list of registered data plugins.

$_TrackPlugins
  A list of registered track plugins (same as $_TRACKNAME_PLUGINS, below).

$_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.

$_SongPath
  Absolute (expanded) filename of the file being processed as entered on the command line. This is the expanded version of $_FileName. It is set to an empty string if input is from STDIN. The filename suffix, “.mma” will be added if it was supplied by MMA .

$_Tempo
  Current TEMPO. Note that if you have used the optional bar count, offset or restore settings the tempo this will be the one at the current point in the MIDI track, not 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:

- For simple variables which contain only one element (e.g., $_Tempo) any index other than “[0]”, “[-1]”, etc. will return an empty string.

- Variables containing multiple values (e.g., $_Bass_Volume) are treated as list. Slicing and indexing is useful to extract a single value.

- Variables created with MSET are treated a list of lines. Slicing returns multiple (or single) lines. This can be useful in selecting only a portion of a previously created variable.

- Using an empty “[]” will return the number of elements in the variable.21.2 This can be useful in writing SUBROUTINES where you need to know the number of arguments which have been passed. For example:

  defCall Count Args print $Args[] args passed EndDefCall Call Count Am Bm Cm Dm

- The “[]” must follow the variable without any space characters. The expression inside the “[value[s]]” 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.3 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:

- To keep your computer safe from malicious scripts, only the following operators and functions are permitted.

  The unary operators:

          - + ~

  the basic operators:

          + - / // % * **

  the bitwise operators:

          & | ^ << >>

  the constants:

          e pi

  the functions:

          ceil() fabs() floor() exp() log() log10() pow()
          sqrt() acos() asin()  atan() atan2() cos() hypot()
          sin()  tan()  degrees() radians() cosh() sinh()
          tanh() abs()  chr()  int()

  the miscellaneous functions:21.4

          for, in, str(), .join(), .split(), randint()

  and values and parentheses.

- For details on the use/format of the above please refer to the Python documentation.

- $(...) expressions cannot be nested.

- There must be a whitespace character before the leading $.

- Any MMA variables must be delimited with whitespace. For example $( $_Tempo + 44) will work; however, both $($_Tempo + 44) and $( $_Tempo+ 44) will cause an error.

- The supplied file egs/misc/math.mma shows a number of examples.

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

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:

- A duplicate label generated with a LABEL command will generate an error.

- A line number label duplicating a LABEL is an error.

- A LABEL duplicating a line number is an error.

- Duplicate line numbers are permitted. The last one encountered will be the one used.

- All label points are generated when the file is opened, not as it is parsed.

- Line numbers (really, just comments) do not need to be in any order.

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.
... variable.21.2
  This is not a standard Python convention, but can be handy in MMA .
... lists.21.3
  this was written before the introduction of slices, (details here). Slices make this much easier, but let's leave the hard stuff in just to show what can be done.
... functions:21.4
  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.5
  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.6
  For this comparison float values are used. Rounding errors can cause equality comparisons to fail.