Without the ability to alter the values in variables, a program wouldn't be of much use.
It is with this in mind that we now introduce the various ways that we can manipulate the
variables listed in the previous section. Remember, all of the "operations" you
will be looking at in this section are bonafide functions. That means that the way the
elements of the operation are ordered will follow the rules of function syntax and as such
probably look a bit weird to you at first. Just keep in mind that as with all functions,
the function keyword (or in most of these cases, key symbol) comes first followed IN
PROPER ORDER by the arguments that will be operated on.
Declaration Statements
(int name value)
This declares an integer that you can name whatever you want. In this example it is
called "name". You should use names that help remind you of the variable purpose
such as "highnote" or "counter". It's a good idea to conserve memory
by keeping the name simple and short. After the name comes the value it will be
initialized to. If nothing else, initialize it to zero. You can also initialize a variable
to the value of another variable so long as the latter has been declared and initialized.
Suppose you declare a variable like (int range 12) and you want to declare another
variable later that equals "range". Also suppose that in the meantime,
"range" has been subjected to other functions and so it's value is unknown. You
could do it like this:
(int lowpoint range)
and now "lowpoint" will be initialized to whatever value currently resides in
"range". You could also have declared "lowpoint" and initialized it to
zero at the same time you declare "range" and then when the time comes just set
"lowpoint" equal to "range" like
(= lowpoint range)
which is the usual course of action. If you are one of those efficiency freaks that must
use the absolute minimum number of statements to perform a task, you can save yourself the
extra "equal" function by declaring at the time of assigning the value instead.
Another way to save a few bytes is to chain the declaration of like variables. This means
that you can list several variable names on the line after the "int" keyword,
each separated ("delineated" in geek speak) by a space, and then at the end
given an initial value. All of them will be initialized to that same value. Such a
statement might look like this:
(int first second last loop lownote highnote maxvel minvel temp 0)
In this statement, all of the variables listed from "first" through
"temp" would be declared as integers and initialized to zero. I don't like to do
this because as luck would have it, at some point I might need to change the initial value
of a variable as I'm shaking down my program and would have to edit this statement to
remove the offending variable and then write a new one just for it. I like to keep my
variables separate so all I have to do to change them is alter one statement. It also
makes them easier to read and keep track of. Remember that an "int" has a range
of 32676 to negative 32767 so be sure all of your integer variables will stay in that
range throughout the running of your program.
(word name
value)
This statement declares a word variable named "name" with an initial value of
"value". Everything mentioned above applies here. Its range is limited to
positive numbers and can go from 0 to 65536.
(long name
value)
This declares a long variable. It is able to swing plus or minus 2.14 billion. Again,
the rules are the same as noted above.
(dword name
value)
This declares a double word called "name" initialized to a value of
"value". These variables are positive only and can range from 0 to
4,294,967,295. Everything said about "int" applies here as well.
(string name
"This is a bunch of text")
And now for something completely different! Here we have a variable declared as a string
called "name" and initialized to the text enclosed in quotes. Strings can be
very useful when we want our CAL program to generate track names or some such. Like it's
kin above, you can chain declare multiple string variables and initialize them all to the
text in quotes, but I see little use for such an occurrence. There's a limit to the size
of a string variable, 126 characters. Rarely would a string of more that a two dozen
characters be needed, so that issue will probably never come up in general use.
(undef name)
This function "undefines" or erases from memory the pocket occupied by a
variable and deletes its name from the list of declared variables. It works on any
variable you have declared using any of the above data types. This is a way of freeing up
memory for new variables after a current one has outlived its usefulness. It can come in
handy for erasing large variables like double words to make room for more integers for
example. From time to time I have run into the limits of CAL's ability to keep up with too
many variables. The use I tend to put this function to the most is undefining variables
that an included program declares but doesn't need to pass back to the parent program. See
the section describing the "include"
function.
Input Functions
Once we declare a variable, it's fair game! However, there will be many times when the
operation of a CAL program will depend on the ability of the user to set some of the
parameters. Thus, we come to the "get" statements. There is a "get"
statement for the integer and word and a special way of entering a double word as we will
see shortly. There is no input statement for the string. Shame, this, as it would be nice
to input strings from time to time. But, alas, we have not the luxury. As far as the
numerical variables go, we are free to query the user for input at any time using the
following statements:
(getInt name
"Prompt the user with this text" minimum maximum)
(getWord name
"Prompt the user Etc..." minimum maximum)
As should be obvious, each statement is meant for a specific data type. Note how the
"get" part is in lower case and then without any space comes the name of the
data type with it's first letter capitalized. Next comes the name of the variable we are
giving the user the opportunity to change. After that comes some text in quotes that will
serve as the prompt for the user. It can be anything you wish to convey, just limit it to
less that a screen's width because the resulting dialog box will spill off the screen.
Last come the restrictions you can place on the user's response in the form of two values.
This is optional but recommended. The first is the smallest, or in the case of an
"int", the most negative value you will allow. The second is the largest value
allowed. As I mentioned earlier in this document, I like to leave a space or two at the
end of the prompt before closing the quotes. The reason is that the dialog box displays a
small window after the prompt containing the current value of the variable and a blinking
cursor. The spaces allow a bit of esthetic room between the text and that little window.
The user can just hit ENTER or click OK and accept the current value or can enter a new
value and then hit ENTER or click OK. If they attempt to enter a value outside the limits
you have set with "minimum" and "maximum", an error box appears
displaying those limits and allows the user to try again until an acceptable value is
entered. Your choice of "minimum" and "maximum" will depend on the
nature of the variable. If you are requesting a MIDI channel number, the limits should be
1 and 16. If you are requesting they input a pitch, you would use 0 and 127 as your limits
and so on.
(getTime name
"prompt text requesting a time in M:B:T format")
This is a bit different in that the user is to enter a time value in measures, beats and
ticks which CAL will convert into raw time and assign to the double word variable
"name". This variable MUST be of type "dword" and have already been
declared. Keep in mind that you can have a "tick" value of zero, but
"beat" and "measure" must be a value of at least 1. Also, if you enter
a beat that doesn't exist, such as 6 in 4/4 time, or a value of "tick" greater
than the value set in "TIMEBASE", CAL will convert the values anyway by
multiplying the number of "beats" by "TIMEBASE" and adding to the
result the value in "ticks". If you reconstruct the raw time back into M:B:T
time (see "makeTime" in the Musical Time Functions page), all of the values will be
corrected to display a proper time given the "meter" and "TIMEBASE"
values in use during that Cakewalk session. The other difference is that CAL will not
allow the use of "minimum" and "maximum" limits for
"getTime". Attempting to use limits results in a "wrong number of
arguments" error at run time.
IMPORTANT
NOTE: This feature has a bug in it for versiosn 7 and 8. The (getTime) dialog, which is
supposed to request user input in M:B:T time, instead will only accept raw time. While
this new ability to input double words is a great addition to the instruction set, it
comes at the cost of an already well established feature. As a result, all CAL programs
that depend on the user entering time in M:B:T format are totaly hosed for versions 7 and
8.
Assignment Functions
The following are mathematical assignment functions. They start with the most basic, the
"equals" function, but then slide into a gray area between assignment and math.
I will list them here before the mathematical functions they emulate because they do
possess assignment characteristics.
(= variableA
variableB)
This "equal" function assigns the value of "variableB" to
"variableA". Take a close look at the sentence you just read! It is
"variableA" that will be assigned and "variableB" that holds the
master value that will be passed on by the "=" function, not the other way
around. In English it would read "Make variableA equal to variableB". Don't get
confused on the direction of flow in this and all following math functions, as it is easy
to do given the odd syntax of CAL. Not surprisingly, you can replace "variableB"
with a constant like "From", or even another function. For example, you could
have something like
(= lowrange (- Note.Key base))
Here, the variable "lowrange" is set equal to the result of subtracting
"base" from "Note.Key". Notice the brackets. The function
"-" with its two arguments must be enclosed by its own set of brackets, but this
does not lessen the necessity of enclosing the "=" function with a complete set
of brackets as well.
(+= variableA
variableB)
This is an assignment function that acts as a form of shorthand for making a variable
equal to itself as modified by some other variable. In English, this would read "Make
variableA equal to the result of adding variableB to it". As with any equation,
variableB can also be a constant or another function. In CAL longhand, it would read
(= variableA (+ variableA variableB))
By the way, the double use of "variableA" in this longhand example is legal. You
can use the same variable as both destination and source because CAL first does the
calculations and then passes the result to the destination variable. This way, the
contents of variableA are safely preserved until after it is used in the calculation, and
only then it is changed to its new value. The "+=" function is simply a bit more
efficient.
(-= variableA
variableB)
(*= variableA
variableB)
(/= variableA
variableB)
These are just like the first example except they represent subtraction, multiplication
and division respectively.
(%= variableA
variableB)
This fellow may take some explaining. The percent sign means find the modulo value. A modulo value is the remainder left over
after dividing the first variable by the second. CAL does not see fractions, fractional
amounts or decimal values. It deals strictly in whole numbers. Therefore, if you divide 9
by 8, you will get an answer of 1. You will get the same result for dividing 9 by 7, 9 by
6, or 9 by 5. The result of dividing 9 by 4 would be 2. The remainder left over by any of
these divisions is only available by obtaining the modulo value. The modulo value from
dividing 9 by 8 is 1, as that is the remainder from dividing them. For 9 by 7 the modulo
value would be 2, for 9 by 6 it would be 3 and so on. We can use this value after
performing a "divide" function for various purposes such as rounding. Using the
modulo assignment function is a bit more obscure than the rest of our math assignments
described above. We are more likely to use a modulo value in the conventional math
functions.
(++ variable)
Here is a special case of a combo math / assignment function. This simply adds 1 to the
value of "variable". This is referred to as "incrementing" a variable
and often happens in association with counting the occurrence of some kind of condition.
Such things as counting the number of times a "while" loop has executed or
tallying the number of notes encountered are handled by the "increment"
function.
(-- variable)
This is the same thing as incrementing except it subtracts 1 from a variable. This is
known as "decrementing" and has a lot of the same uses including countdown
timers and so on. Keep in mind that it's a bad idea to let a variable decrement below zero
unless the variable can handle negative numbers.