<-- previous page     Table of Contents    Index    next page -->

Macros

Simple Macros (without parameters)

Macros can be defined to avoid retyping or to give mnemonic names to things. A macro is defined with the following syntax:

define  macro_name macro_text @

The macro_name consists of one or more uppercase letters, digits, and underscores, with the first character being a letter. The macro_text can be any text. It can be any length from empty to many pages. The "@" terminates the macro. A literal "@" can be placed in the macro_text by preceding it with a backslash. If you want a literal backslash in the macro_text, it also must be preceded by a backslash.

A macro is called by stating the macro_name in the input. The macro_name is replaced by the macro_text. A macro can be defined at any point in the input. It can be used as often as desired any time after it has been defined. A given macro_name can be redefined as many times as desired, with each new definition overwriting the previous definition.

As an example, suppose you are printing an orchestral score, and the oboe part happens to be on staff 5. Rather than having to remember which staff it is, you could define a macro:

define OBOE 5: @

Not only is the name easier to remember than a number, but if you later decide to move the oboe part to a different place in the score, only the macro definition and perhaps a few other things would have to be changed.

Another common use of macros might be if a musical motif occurs several times. You could define a macro for the motive:

define SCALE 8c;d;e;f;g;a;b;c+; @

then do something like:
OBOE SCALE

It is possible to remove the definition of a macro using the "undef" statement:

undef OBOE

It is possible to have parts of the input skipped over depending on whether certain macros are defined or not. This is done using "ifdef," "else," and "endif." The keyword "ifdef" is followed by a macro name. If a macro by that name is currently defined, Mup will continue reading and processing input normally. If it finds a matching "else," it will skip over input until the matching "endif." If the macro is not currently defined, Mup will skip over the input until it finds a matching "else" or "endif." There is also an "ifndef" command that uses the opposite logic: it will read the input up to the "else" or "endif" only if the macro is NOT defined.

The ifdefs can be sprinkled between other items in the input; they need not be on separate lines. They can be nested. Examples:

// make last c an octave higher if macro "FRED" is defined
1: c;e;g;c ifdef FRED + endif;

ifdef PIANO
    staff 1 visible=n
else
    ifdef VIOLIN
        staff 2 visible=n
        staff 3 visible=n
    endif
endif

Macros can also be set from the command line using the -D option. Only ordinary macros can be defined using the -D option, not macros with parameters.

Macro text cannot begin or end in the middle of a token, like a keyword or a number or a string. Usually it will be clear what things are tokens; generally tokens end where you are allowed to have white space. But there are a few obscure cases. For historical reasons that are now hard to change, "dashed tie" or "dashed slur" or the per-note versions of "dashed ~" and " dashed <>" (or the "dotted" counterparts of any of these) are treated as a single token. That means you can't define one macro for "dashed" and another for "tie" and put them together and have that be recognized.

Macros with parameters

Macros defined within Mup input can be defined to have "parameters." This may be useful when you have something that is repeated with small variations. When defining a macro with parameters, the macro name must be followed immediately by a ( with no space between the end of the name and the parenthesis. The opening parenthesis is followed by one or more parameter names, separated by commas, and ending with a close parenthesis. Parameter names have the same rules as macro names: they consist of uppercase letters, numbers, and underscores, starting with an uppercase letter. The parameter names can then appear in the text of the macro definition where you want a value to be substituted.

As an example, suppose you are doing a score with staffs 1 through 4 for vocal parts, and staffs 5 and 6 for a piano accompaniment, and that you frequently want to mark a dynamics change at the same point in time below each of the vocal scores and between the two piano staffs. You could typically do this with something like:

boldital below 1-4: 1 "ff";
boldital between 5&6: 1 "ff";

but if you needed to do this lots of times, it could get tedious. So let's define a macro with parameters:
define DYN( COUNT, VOLUME )
boldital below 1-4: COUNT VOLUME;
boldital between 5&6: COUNT VOLUME;
@

This macro has two parameters, which have been given the names COUNT and VOLUME. When you call the macro, you will give them values. For example,
DYN(1,"ff")

would give a VOLUME of "ff" at COUNT 1, whereas
DYN(3.5,"mp")

would give a VOLUME of "mp" at COUNT 3.5.

When calling a macro with parameters, the values to give the parameters are given inside parentheses. The values are separated by commas. The values in the parentheses are copied exactly as they are, including any spaces, newlines, macro names, etc. There are only a few exceptions to this: you can include a comma, closing parenthesis, or backslash as part of a parameter value by preceding it with a backslash, and a backslash followed by a newline in a parameter value will be discarded. Thus a macro call of

MAC(\\\,\))

has one parameter, the text of which is 3 characters long: a backslash, comma, and closing parenthesis. If you backslash other characters, they will be copied without the backslash, but doing this on anything other than a double quote will produce a warning, because it seems unlikely the backslash was really needed.

If in a macro definition a parameter is used inside backticks, as in `NAME`, the value of the parameter will be placed inside double quotes. Thus, another way to do the example above would be:

define DYN( COUNT, VOLUME )
boldital below 1-4: COUNT `VOLUME`;
boldital between 5&6: COUNT `VOLUME`;
@

DYN(1,ff)
DYN(3.5,mp)

Conceptually, when the macro is expanded, the backticks are replaced by double quote marks, but in addition, any double quote mark found in the value being passed to the parameter will have a backslash inserted before it, and any backslash that occurs within double quotes in the value will also have a backslash inserted before it. Thus, for example:

// If we define a macro like this:
define QUOTED(X) `X` @

// then for input    value passed is    `X` would be    which would print as

print QUOTED(hello)       hello          "hello"          hello
print QUOTED("hello")     "hello"        "\"hello\""      "hello"
print QUOTED(\\n)         \n             "\n"             a literal newline
print QUOTED("\\n")       "\n"           "\"\\n\""        "\n"

Sometimes it can be a little tricky to get the number of backslashes right, or other details like that. The -E Mup command line option shows how macros will expand, which may help you figure out what to do.

Concatenating macro names

Inside the ` ` it is possible to use ## to concatenate the values of two or more macros to form a macro name, whose value is then converted to a string. This is probably easiest to understand from an example. This example shows a way to print capo and real chords.

// Define some guitar chords
define  D              d0 g2 b3 e2      @
define  F       e'1 a3 d3 g2 b1 e1      @
define  A           a0 d2 g2 b2 e0      @
define  C           a3 d2 g0 b1 e0      @

// Define the mapping of F and C transposed by a minor third
define TR_F D@
define TR_C A@

// This is used by the K macro below to derive a pasted-together macro name
define TR TR_@

// Define a macro that will print both real and capo chords,
// when given a count at which to print, and the real chord name.
define K(COUNT, NAME)
   bold chord all align 2: COUNT `TR##NAME`;  // capo chords
   ital(9) chord all align 1: COUNT `NAME`;   // real chords
@

score
  staffs = 3
  key = 1&
staff 2
  transpose = up min 3
staff 3
  stafflines = tab
music
  // Use the K macro to print the real and capo chords
  K(1,F) 
  K(3,C)
  1: 2f;c;
  // Enter the chords as you would think about them, but transpose them.
  3: 2 TR_F;TR_C;
  bar

Picture of Mup output

The capo chords line is the one of interest here, which has the `TR##NAME`. The TR macro will be evaluated, and found to have a value of TR_. At the first place the K macro is called, the value being passed to the NAME parameter is F. Those two values (TR_ and F) are concatenated to form TR_F and that macro is then looked up, and found to have the value D. That is then made into a string (because of the ` ` enclosing the construct) and printed. On the second call to K, a C is passed to NAME, so the pasted-together name will be TR_C, which then yields "A" to be printed. On the second-last line, the TR_F gets replaced by the correct notes for a transposed F, namely a D chord, and the TR_C gets replaced by the correct notes for an A chord. Thus the various TR_F macros, along with the K macro, adjust the music for having capo. In a real example, there would likely be more chords, with an additional TR_ macro to map each one.

It is possible to paste more than two macro names together, as in `AA##BB##CC##DD` but each component must represent a valid defined name, and the result of pasting them all together must also yield a valid defined macro name.

A complex macro example

Let's look at an example of much more complicated use of macros. This example demostrates the use of arithmetic functions described in the section on location tags. We will draw a line with an arrow between notes on two different staffs. While it may be possible to write the expressions directly, the result would be very hard to read and understand, so using macros to build up the pieces of the expression can be very helpful.

define HEAD_LENGTH             5 @
define HEAD_WIDTH              3 @
define ANGLE(X1,Y1,X2,Y2)      atan2((Y2) - (Y1), (X2) - (X1)) @
define COS(X1,Y1,X2,Y2)        cos(ANGLE(X1,Y1,X2,Y2)) @
define SIN(X1,Y1,X2,Y2)        sin(ANGLE(X1,Y1,X2,Y2)) @
define HEAD_X(X1,Y1,X2,Y2)     (HEAD_LENGTH * COS(X1,Y1,X2,Y2)) @
define HEAD_Y(X1,Y1,X2,Y2)     (HEAD_LENGTH * SIN(X1,Y1,X2,Y2)) @
define HB_X(X1,Y1,X2,Y2)       ((X2) - HEAD_X(X1,Y1,X2,Y2)) @
define HB_Y(X1,Y1,X2,Y2)       ((Y2) - HEAD_Y(X1,Y1,X2,Y2)) @
define THICK_LEN               (HEAD_WIDTH / 2) @
define THICK_X(X1,Y1,X2,Y2)    (THICK_LEN * SIN(X1,Y1,X2,Y2)) @
define THICK_Y(X1,Y1,X2,Y2)    (THICK_LEN * COS(X1,Y1,X2,Y2)) @
define FEATH_UP_X(X1,Y1,X2,Y2) (HB_X(X1,X2,Y1,Y2) - THICK_X(X1,Y1,X2,Y2)) @
define FEATH_UP_Y(X1,Y1,X2,Y2) (HB_Y(X1,X2,Y1,Y2) + THICK_Y(X1,Y1,X2,Y2)) @
define FEATH_DN_X(X1,Y1,X2,Y2) (HB_X(X1,X2,Y1,Y2) + THICK_X(X1,Y1,X2,Y2)) @
define FEATH_DN_Y(X1,Y1,X2,Y2) (HB_Y(X1,X2,Y1,Y2) - THICK_Y(X1,Y1,X2,Y2)) @
define ARROW(X1,Y1,X2,Y2)
  medium line (X1, Y1) to (X2, Y2)
  medium line (X2, Y2) to (FEATH_UP_X(X1,Y1,X2,Y2), FEATH_UP_Y(X1,Y1,X2,Y2))
    medium line (X2, Y2) to (FEATH_DN_X(X1,Y1,X2,Y2), FEATH_DN_Y(X1,Y1,X2,Y2))
@

score
	staffs = 2
staff 2
	clef = bass
music
	1: c =h; r; 2;
	2: r; g =k; e; g;
	bar

ARROW(h.x + 2, h.y - 1, k.x - 2, k.y + 1)

Picture of Mup output

Expression macros

There is a special kind of macro definition, where the value is the result of evaluating a numeric expression. It uses "eval" rather than "define" as the keyword, along with an equals sign. At its simplest, the value is just a single number, so

 eval X=1 @

is effectively the same as
  define X 1 @

But the value can be an expression, like
  eval T=(3+5)*5/20 @

which would be evaluated, setting the value of T to 2. Using all literal numbers is not likely to be very useful, but using other macros as variables can be. For example:
  ifndef SIZE define SIZE 12 @ endif
  eval WITHSIZE = SIZE + 2 @
  eval LYRSIZE = SIZE-1 @
  score
    size=SIZE
    withsize=WITHSIZE
    lyricssize=LYRSIZE

By default, that would set size to 12, withsize to 14 and lyricssize to 11, but with a single command line override, like -DSIZE=10, all three values would adjust.

Another common usage would be to increment a value. Suppose you were making a booklet with many songs, and want to automatically number them. At the beginning you could do something like:

eval SONGNUM = 0 @
  define NEWSONG(TITLE)
    eval SONGNUM = SONGNUM+1 \@
    block
    title bold (16) `SONGNUM`+ ". " + `TITLE`
    music
  @

and then begin each song with:
  NEWSONG(This is the title of the song)

New songs could then be inserted anywhere, and later songs would automatically be renumbered appropriately. See the section on Converting a value to a string for more information on how to turn a number into a letter or Roman numeral string to be printed.

Another use for incrementing might be for staff numbers. You might start with something like:

  eval VIOLIN = 1 @
  eval VIOLA = VIOLIN + 1 @
  eval CELLO = VIOLA + 1 @
  eval BASS = CELLO + 1 @

Then if later you decided to add a second violin part, you could just replace the second line with:
  eval VIOLIN2 = VIOLIN + 1 @
  eval VIOLA = VIOLIN2 +  1 @

and the other instruments would move down automatically.

Numbers in expressions can either be whole numbers, like 1 or 42, or they can be numbers with a decimal point, like 0.7 or 3.14. If arithmetic needs to be done involving a whole number and a decimal number, the whole number will be "promoted" to a decimal number, with the result being a decimal number. Decimal arithmetic is done at the precision of your system, but the results (i.e., the ultimate value that the macro takes on) is limited to 6 decimal places.

Supported operators are listed in the table below, with those on a line being higher precedence than those on lines below it:
operators operations associativity
( ) grouping left to right
! ~ - + not, one's complement, unary minus, unary plus right to left
* / % multiply, divide, modulo left to right
+ - add, subtract left to right
<< >> left shift, right shift left to right
< <= > >= less than, less or equal, greater than, greater or equal left to right
== != equal, not equal left to right
& bitwise AND left to right
^ bitwise XOR left to right
| bitwise OR left to right
&& logical AND left to right
|| logical OR left to right
? : interrogation right to left

The not, one's complement, modulo, left and right shift, bitwise and, bitwise or, bitwise exclusive or, logical and, and logical or operators are only allowed on whole numbers. Comparision operators result in either 0 for false, or 1 for true.

There are also some functions supported. Most take a single argument, but a few take two, in which case there is a comma between them.
Name Argument(s) Result Result type
sqrt number square root of the number decimal
sin angle (degrees) sin of the angle decimal
cos angle (degrees) cosine of the angle decimal
tan angle (degrees) tangent of the angle decimal
asin number arc sine of the number (degrees) decimal
acos number arc cosine of the number (degrees) decimal
atan number arc tangent of the number (degrees) decimal
atan2 number,number arc tangent of the ratio of the numbers (degrees) decimal
hypot number,number square root of sum of the squares of the numbers decimal
round number number rounded to nearest whole whole
floor number number rounded down to whole number whole
ceiling number number rounded up to whole number whole

Here are some examples of expressions:

 eval PI=3.1415 @
 eval R = (1 + 0.6) / hypot(3.1, 1.896)@
 eval CIRC = PI * R * R@
 eval S = CIRC > R * 5 ? round(R) : ceiling(CIRC) @ 
 eval ANGLE=asin(0.625) @

Saving and restoring macros

You can take a snapshot of all your current macro definitions by using the savemacros command, and then restore that set of definitions later using the restoremacros command. These are both followed by a quoted string, which is a name you give to the saved definitions. This might be useful if, for example, you had a multi-movement piece, and wanted to use one set of macros on the first and third movements, but a completely different set on the second movement. You could save the macro state at the very beginning under one name, and after the first movement under a different name. Before the second movement, you could restore to the original state with no macros defined, and define your second set. Before the third movement, you could restore the macros as they had been at the end of the first movement.

Another possible use would be if you have several standalone files, each containing a complete song, and you want to "include" those files in another file. By putting a save/restore around each include, you can prevent any macro definitions in one file from interacting with those from another file.

Here is a very simple example, using single measures rather than a whole movement or song.

// Define one "global" macro that will stay around,
// by defining it before doing any savemacros.
define ONE_MEAS
score
	BSTYLE
music
1: 8c;;e;;A 2g; 
bar
@

// Save just that one macro as the "default."
savemacros "default"

// Now define a couple other macros and save the macros.
define BSTYLE beamstyle=2,2 @
define A [with >] @
// Note that the name can be any arbitrary text string.
savemacros "2,2 >"

// Restore the default setting and set macros
// with the same names to different values.
restoremacros "default"
define BSTYLE beamstyle=4,4,4,4 @
define A [with ^] @
savemacros "4,4,4,4 ^"

// Now use the "global" macro with each saved macro state.
restoremacros "2,2 >"
ONE_MEAS
restoremacros "4,4,4,4 ^"
ONE_MEAS

Picture of Mup output

If you do another savemacros using the same name as you had already used, the old state is forgotten and replaced with the new.

Often, though not always, you may want to use saveparms/restoreparms at the same places as you use savemacros/restoremacros.


   <-- previous page    Table of Contents    Index    next page -->