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.
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).
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. For example, the following will NOT work:
Set Silly 4a;b;c;d;
1 Am {$Silly} |
However:
1 Am { $Silly} |
will work fine.
Following are details on all the available variable commands:
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.
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.
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.
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 |
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.
Removes the variable. This can be useful if you have conditional tests which simply rely on a certain variable being ``defined''.
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. Eg:
ShowVars xXx Count foo
$XXX - not defined $COUNT: 11 $FOO: This is Foo |
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.
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.
However, not all commands can be redefined. The following is short list of things which will work (but, again, not all suggestions should be used!):
Set Rate Tempo 120
$Rate Set R Repeat $R |
But, the following will not work:
Set B Begin
Set E End $B Arpeggio Define ... $E |
This fails since the Begin/End constructs are expanded before variable expansion. However:
Set A Define Arpeggio
Begin $a ...End |
is quite alright.
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.
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.
For your convenience MMA tracks a number of internal settings and you can access these values with special macros.19.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:
Debug Warnings=off
...stuff generating annoying warnings Debug $_LastDebug |
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:
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 |
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. 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:
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 functions19.2:
for, in, str(), .join(), .split()
and values and parentheses.
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 IFEND19.3 directive. An optional ELSE statement may be included.
The first set of tests are unary (they take no arguments):
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 (they take two arguments):
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 GT $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 $$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 either string in a comparison is a numeric value, both strings are converted to values. Otherwise they are compared as strings.19.4
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.
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:
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.