The default scorefile parsing/interface language for RTcmix
Minc is the default parsing interface for RTcmix. Invoking the command
CMIX < some_score.file
or
CMIX -f some_score.file
will use Minc in “standalone” mode to parse the score and make the appropriate function calls to run RTcmix. The following are other ways of invoking the Minc parser:
There are other parser options, including the ability to run RTcmix from Perl and Python.
Minc is also used to parse buffer-scripts in the Max/MSP rtcmix~ object and in the iRTcmix package for iOS devices.
Minc is an interpretive (non-compiled), interactive language. It takes much of its functionality from the C programming language, including flow-of-control features such as for and while loops, if decision-constructs, etc. In addition to the many examples below, see the RTcmix tutorial (especially the later sections about algorithmic composition) for examples of Minc use.
One of the main differences between Minc and C is the ability to
leave out semicolons at the end of lines (NOTE: semicolons are still required
in “for(…)” statements and a couple of other places). Semicolons may be used
at the end of Minc lines, but they are not required. The advantage of using them
is that the parser can more easily pinpoint the offending line number in your score - very
useful for large, complex scores! The examples in this document sometimes use semicolons
and sometimes do not. A special note: If you are running Minc interactively on the command
line, many command lines will not execute upon hitting
Many of these are also available in the form ‘+=’, ‘-=’, etc. Operators can be used with Minc data types other than float, as well. See the descriptions of the data types below.
Minc Functions may be calls to Instruments, built-in, or score-defined functions used to perform score file calculations. Return values are assigned to Minc variables with the ‘=’ assignment operator:
aval = somecalculation(anotherval, something_else)
thisone = thatone + those
ANINSTRUMENT(with, many, different, parameters)
In addition, operations and functions may be embedded (or ‘nested’):
ANINSTRUMENT(somefunction(var1, var2), val^3.2, aval, 3.1654, func1(func2(array[3])))
Several C conditional-branching statements are included in Minc (“if/then/else”, “while”). The standard C comparison operators (‘==’, ‘&&’, ‘||’, ‘!=’, ‘>’, ‘<’, etc.) are used for the conditional comparison. Using parentheses to group and determine precedence of evaluation is also supported:
if ((val > 2.0) && (val2 != 0)) { ... }
Note that the “do…while” and “switch” statements are not supported in Minc. Postfix (x–) operators are also not supported.
Comments may be included in Minc scripts using C comment syntax:
/* a comment may be between two slash-asterisk markers */
/* and it
may also span
several lines
*/
// anything after two backslashes on a line will be construed as a comment
Data types are different “flavors” of variables used to store different kinds of information. One of the other major differences between C and Minc is the set of built-in data types available. Variables of the following types can be created in your scores, either by declaring them directly or having them automatically declared.
floats are all numbers such as -15.4 and 0.0125, and includes whole number (integer) values like 3 or 99. All are stored and passed around as floating point. They can be declared and then set:
float instVolume;
instVolume = get_inst_volume()
or auto-declared:
instVolume = 7
instPitch = some_function_which_returns_a_float();
Note that all Minc numerical values are floating-point. Care is taken to prevent round-off errors from causing problems for counting variables that would normally use integers. The trunc and round built-in functions can be used to guarantee a correct ‘integer’ value.
strings are any form of text, like comments, file names, names of instruments, etc. These are stored in string variables. Like floats, they can be declared:
string soundPath;
or auto-declared:
soundPath = “/tmp/recording.wav"
soundPath = some_function_which_returns_a_string();
Strings can be concatenated using the ‘+’ and ‘+=’ operators:
sentence = “hello" + “ “ + “world!"
print(sentence)
“hello world!"
word = "foo";
word += "bar";
print(word)
"foobar"
Numeric values can also be appended to a string:
version = "version";
version += 2; // appends the character for '2'
print(version)
"version2"
Individual characters in a string may be accessed using the [] operator:
s = "abcde";
print(s[2])
"c"
lists or, as they are often called, Minc arrays, store a set of variables of any type which can be accessed by indexing into the list using the [] operator. These are extremely useful in a score. The following are acceptable Minc constructions:
// auto-declare an array filled with floats
a = { 1, 2, 3, 4, 5 }
// use various indices into that array as inst args
INSTRUMENT(a[3], a[0], ...)
b = {} // this auto-declares an empty array
for (i = 0; i < 10; i += 1) {
b[i] = i * 3 // the array will grow as elements are added
}
For more information, see More About Arrays below.
handles are a type used to store references to special objects returned from other Minc functions such as maketable() and makeinstrument(). Quite often variables of this type will be handed to Instrument calls to set up dynamic p-fields. Handles to RTcmix tables have the extremely powerful ability to be combined with each other and with other arithmetic operators to create new tables which can then be accessed using functions like samptable():
// create a line table between 0.0 and 1.0
line = maketable(“line", “nonorm", 10000, 0,0, 1,1)
// ‘line’ is now a Minc handle referring to that table
print(samptable(line, 10000))
1 // last value in table
lineTimes7 = 7 * line; // it’s this simple
print(samptable(lineTimes7,10000))
7 // last value in table, now 7 x what it was
structs allow you to define and use custom data types which are very much like the “struct” concept in the C and C++ languages: It allows you to group a bunch of variables together into a named container which you can pass around. In Minc, like in C, a struct type must be named as part of its definition:
struct MyData { ... }
What goes between the curly braces is called a member list, and it consists of a comma-separated list of variable declarations:
struct MyData { string name,
float number,
list array
}
These members can be any Minc data type, and there is no limit on the number of member variables. Their names must all be unique within the struct definition. For now, structs cannot be nested within each other. There are several ways to declare a variable in your score to be a struct:
// You can simply declare it. This will initialize all its members to 0 or NULL.
struct MyData instrumentData;
// You can auto-initialize a variable by assigning to it from the struct's built-in *constructor* function:
initializedInstrumentData = MyData("Loud Instrument", 1, {});
NOTE: The struct constructor is a new feature as of version 5.4.0. The arguments to the constructor are the initial values for all members in the struct, in order that they are declared. If there are fewer arguments than members, the remaining members will be set to 0 or NULL.
If you chose the first option, you can assign to the struct variable’s members using the “dot” syntax:
instrumentData.name = "Loud Instrument"
instrumentData.number = 1
instrumentData.array = {}
To read from the members, always use the “dot” syntax:
printf("Instrument %f: %s\n", instrumentData.number, instrumentData.name)
For more details, see More About Structs below.
maps in Minc allows you to use one piece of score information as a key to access another. A simple example should help explain. Imagine you have a set of sound files that you wish to use in your score, but you have specific durations you want to read from them (i.e., not their full duration). You can create a map to associate the paths to the files to the durations you want:
map durationMap; // creates empty map
// Assign three duration floats into the map using three string keys
durationMap[“lion_roar.wav"] = 3.0
durationMap[“bird_tweet.wav"] = 8.2
durationMap[“gunshot.wav"] = 0.5
// later, open and play one of these using one of these mapped durations
currentSound = “lion_roar.wav"
rtinput(currentSound)
MIX(inskip=0,
outskip=0,
dur=durationMap[currentSound], // get duration by looking up via the key
gain=1000,
0, 1)
Minc maps are extremely flexible; both the stored item and the key can be any type supported by Minc.
mfunction supports an advanced feature which allows a score to pass score-defined Minc functions around as variables - which could then be passed as an argument to another function, or stored in a Minc array, etc. See the section Score-defined Minc Functions, below.
Minc differs from C in how variable declarations work. In the latter, the declaration of a variable and the assignment of a value to it can happen in a single statement:
// C ALLOWS (AND PREFERS) THIS
float volume = 10000;
in Minc, when you auto-declare a variable, the parser “knows” what type it is by what you assign to it:
x = "A string" // x is now a string variable
y = { 1, 2, 3 } // y is now a list variable
Explicit (non-auto) declarations in Minc must be just a bare declaration:
float x, y, z, q // x, y, z, q all declared to be float variables
string s = "Hello" // THIS WILL GENERATE A COMPILER ERROR
The purpose of the explicit declarations will be discussed later.
Minc arrays can contain ‘mixed’ data types:
afloat = 1.2345
astring = "hey hey!"
b = { 123, afloat, "ho ho", astring }
Arrays can contain pfield-handle handles for tables, etc.:
e = {}
x = 1
for (i = 0; i < 10; i += 1) {
e[i] = maketable("random", 1000, "linear", min=x, max=x*2, seed=x*3)
x *= 2
}
The len built-in function is used to determine the length of an array (as well as lengths of other Minc data-types).
f = 90
str = "hello"
mylist = { 1, 2, 3 }
print(len(f))
1
print(len(str))
5
print(len(mylist))
3
Arrays can be passed as an argument to any built-in function or instrument call. When this is done, the elements in the list become the next N arguments to the function:
// NOTE: To be passed as function arguments, all the array's items must be floats
remaining_args = { 0,0, 1,1, 3,1, 5.5,0 }
// This next line is the same as calling maketable(“line", 1000, 0,0, 1,1, etc.
env = maketable("line", 1000, remaining_args)
Array elements may also be other arrays, and can be of different lengths. To access the sub-arrays, you “double-index” the “parent” array:
arr1 = { 1, 2, 3, 4, 5, 6, 7 }
arr2 = { 77, 87, 97, 107 }
superarr = { arr1, arr2 }
for (i = 0; i < len(arr1); i += 1) {
print(superarr[0][i])
}
Arrays can be concatenated using the ‘+’ operator:
first = { 1, 2, 3 }
second = { 4, 5, 6 }
third = first + second
print(third)
[1, 2, 3, 4, 5, 6]
There is now an ‘append’ method which will allow a single new element of any type to be added to the end of an array, increasing its length:
l = {}
l.append(999)
l.append("hello")
print(l)
[999, "hello"]
Similarly, there is a ‘remove’ method which will allow an element to be deleted from an an array, decreasing its length:
l = {11, 12, 13, 14}
l.remove(13)
print(l)
[11, 12, 14]
Also, there is an ‘insert’ method which will allow an element to be add into an array, increasing its length:
l = {11, 12, 13, 14}
l.insert("poof!", atIndex=2); // Local variables like 'atIndex' to help remember argument order
print(l)
[11, 12, "poof", 13, 14]
All the elements of an array can be modified via operators +, -, *, /, %, and ^ followed a float variable:
arr = { 1, 2, 3 }
arr = arr + 10 // add 10 to all elements
print(arr)
[11, 12, 13]
arr = arr * 100 // multiply all elements by 100
print(arr)
[1100, 1200, 1300]
The unary negation operator ‘-‘ flips the sign on all an array’s float elements:
arr = { -2, -1, 0, 1 }
print(-arr);
[2, 1, 0, -1 ]
any element which is not a float will be left as-is.
All struct member variables are set to 0 or NULL by default. You cannot auto-declare struct variables unless you are assigning from one to another or from a call to the struct’s constructor:
OK:
struct MyData someData;
// initialize members of 'someData'...
someData.someMember = 9;
copyOfMyData = someData // 'copyOfMyData' and 'someData' now point at the *same* struct object
NOT OK:
autoStructData.name = "something" // cannot auto-declare 'autoStructData'!!
Struct variables may be used as arguments and return types for custom functions. This is a very convenient way to get lots of different variables into custom function calls without having to create long lists of arguments.
struct EasyArgs { float outskip, float inskip, float dur, float amp }
float myMixWrapper(struct EasyArgs theArgs)
{
return MIX(theArgs.outskip, theArgs.inskip, theArgs.dur, theArgs.amp, 0, 1)
}
args = EasyArgs(15.4, 0, DUR(), 10000) // 'args' auto-declared via assignment from constructor
myMixWrapper(args)
args.outskip = 22.8; // tweak value of one arg
myMixWrapper(args) // call it again with new outskip
The named elements within a struct can be accessed and used the same way a simple variables are used:
struct FloatAndString { float fElement, string sElement };
fas = FloatAndString(99, "hello"); // variable auto-declared via call to constructor
fas.fElement += 1; // increments the value of 'fElement'
mystring = fas.sElement + " world";
print(fas)
{ 100, "hello" }
print(mystring)
"hello world"
Minc supports the ability to create custom functions right in your score. The format for these functions is nearly identical to that used to define functions in the C programming language:
return_type function_name(arg0_type arg0_name, arg1_type arg1_name, ..., argN_type, argN_name)
{
<function body>
}
Rules for Creating and Using Custom Functions:
Here is an example of some of these rules:
varDeclaredAboveFun = 89; // this is visible to the function
// A custom function declared to take a list and return a float count of its items
float countList(list inListArgument)
{
// Here we reference the var declared below the function to show it works
printf("%s - counting the list\n", varDeclaredBelowFun);
// declaring these here guarantees that these variables are local to this function
float listLen, itemCount;
listLen = len(inListArgument);
itemCount = 0 // **Minc** always zeros variables, but this is good practice
// If the list contains lists, the function is called recursively on those.
for (i = 0; i < listLen; ++i) {
item = inListArgument[i];
// Use the type() function to look for list items
if (type(item) == "list") {
itemCount += countList(item);
}
else {
++itemCount; // "++" operator is a shorthand for "+= 1"
}
}
return itemCount;
}
varDeclaredBelowFun = "hi there"; // this is *also* visible to the function
listToCount = { 3, 19, 4, { 1, 2, 3 } }; // list containing a list
// Here I include the complete printout from **Minc** so you can see the recursive call
// happening
listCount = countList(listToCount);
============================
countList: [3, 19, 4, [1, 2, 3]]
hi there - counting the list
============================
countList: [1, 2, 3]
hi there - counting the list
print(listCount);
6
varDeclaredBelowFunctionCall = {} // but this variable is not visible to the function
Custom functions can be stored into variables, lists, maps and structs using the mfunction data type discussed above:
// a custom function
float pluralFunction(string arg) {
combinedString = "Many " + arg + "s"; // Making use of the "+" operator to concatenate strings
printf("%s\n", combinedString);
return 0; // dummy return
}
// call the function to see it work
pluralFunction(“cat")
Many cats
// assign the function to a variable
funVar = pluralFunction // ‘funVar’ is now an mfunction
// call the function on the newly-created mfunction variable
funVar(“meatball")
Many meatballs
The include statement can be used to embed one (or more) Minc score files within another. This can be very helpful in creating a single file where parameters (like sampling rate and audio device) are set and then included by multiple other scores. Changing the values in the one file will then affect all files which include it.
Here we create a score file to be included in other files. This file calls rtsetparams for us, and makes two global variables available to any score which includes this one. We’ll name this “setup_48K_stereo.sco”.
sample_rate = 48000
channel_count = 2
rtsetparams(sample_rate, channel_count)
When we include that score in another score, the Minc parser will automatically run the commands in the included score and set those variables.
include "setup_48K_stereo.sco"
rtinput("some_file.wav"); // open a file
pan = 0; // default value
// Adjust the pan value only if we are running in stereo. If you changed your include to, perhaps,
// "setup_48K_monaural.sco", you would set channel_count to 1 in there, and this score would automatically
// do the right thing.
if (channel_count == 2) {
pan = 0.5;
}
STEREO(inskip=0, outskip=0, DUR(), 1, pan)
Number and string values can be specified on the ‘CMIX’ command line and used as named variables within the score. This lets you create one score with named argument variables for all its important parameters, and then you can experiment with changing those parameters on the command line. So, one score instead of 25 separate scores!
With a command line execution like this:
CMIX —-sample_rate=48000 —-filename=“input.wav" < scorefile
and a score set up like this:
// ‘$’ marks a variable as something set via the command line
rtsetparams($sample_rate, 2);
rtinput($filename);
Minc will set $sample_rate to 48000 and $filename to “input.wav”. It’s an error to use a $variable you have not specified on the command line, but you can check if it has been set by using the boolean expression ?variable:
rate = 44100; // this will be the default
if (?sample_rate) { rate = $sample_rate } // use '$sample_rate' if set
You can even pass in a list via named argument! You just have to remember to double-quote the entire list. With a this score:
printf("The passed-in list was: %l\n", $listargument);
This command runs as follows. Note that the string element is also not internally quoted:
CMIX -f listarg.sco --listargument="{1, 2, Buckle My Shoe}"
The passed-in list was: [1, 2, "Buckle My Shoe"]
The type command returns a Minc string which specifies the Minc data type of a particular variable:
myList = { 1, 2, 3 }
print(type(myList))
“list"
The index command is also very useful with Minc arrays., and reports the particular index in the array which holds an item.
foo = "foo";
bar = "bar"
afloat = 1.2345;
astring = "blah"
b = { 123, afloat, "blabber", astring, foo + bar } // mixed array
// also note the string concat with '+' operator for the last item
idx = index(b, astring) // Return a zero-based index into array (arg 1) of item (arg 2)
print(idx)
3
index returns -1 if the specified item is not found.
The contains command returns 1 if the specified item is being stored in the given container. This also works for searching for substrings.
mylist = { 1, 2, "hello" };
print(contains(mylist, "hello"));
1
print(contains(mylist, "world"));
0
mystring = "hello, world!";
print(contains(mystring, "world"));
1
Minc supports a simplified version of the formatted printf() as found in C and C++
foo = "foo";
bar = "bar"
afloat = 1.2345;
astring = "blah"
b = { 123, afloat, "blabber", astring, foo + bar } // mixed array
numitems = len(b) // get length of array
// format for floats and strings
printf("afloat=%f, astring=%s\n", afloat, astring)
// format to tream a float as an integer value
printf("array b: %d items: %l\n", numitems)
// format lists (i.e., arrays), structs, maps, and function pointers with special character "%z"
printf("array b: %z\n", b)
When Minc encounters a parsing error, it generally exits (for command-line CMIX) or returns a FATAL_ERROR value (embedded apps like rtcmix~ or iRTcmix) with an attempt to identify the number of the line with the syntax error. As mentioned above, supplying semicolons at the end of score lines will assist in accurately determining the line location for the error.
If you wish to do your own additional error checking in your score, you can use the error() function:
if ($entered_sample_rate < 8000)
{
error("entered sample rate (%f) is an illegal value!", $entered_sample_rate)
}
The parsing of your score will stop at that point and FATAL_ERROR will be returned.
Minc has a set of extremely powerful and flexible features which may take a bit of extra work to understand if you do not have a programming background. Some of these are adaptations of features found in the C++ programming language. There are an infinity of things you can do with the system as already described, so if you are interested in these, study the provided examples.
For programmers, the term “object” usually refers to a chunk of information (the “state”) which moves around together as a unit (like the structs discussed above), but also which provide a set of object-specific functions (often called “methods”) which allow a user to get and modify that “state”. Minc supports the ability to expand the definition of a struct to include “method functions”. These functions are different than other “user-defined” function because:
// Simple struct object which contains a count
struct Counter
{
float _count, // all member variables and method function decls are comma-separated
method float set(float initialCount)
{
this._count = initialCount; // member variables are always accessed as "this.membername"
return this._count;
},
method float increment()
{
this._count = this._count + 1;
return this._count;
}
}
struct Counter myCounter // declare one of these objects
myCounter.set(100) // counter will start at 100
print(myCounter.increment())
print(myCounter.increment())
print(myCounter.increment())
101
102
103
If you define a method in your struct named “_init”, this function will be called automatically after you create an instance of your struct using the constructor function (see above). This allows you to do any other setup work you want to do in your struct before you use it:
struct MyStruct {
float fMember,
string sMember,
list listMember,
method float _init() { // must return float and take no arguments
for (n = 0; n < this.fMember; ++n) {
this.listMember[n] = n;
}
return 0;
}
}
// Create using the built-in constructor
ms = MyStruct(5, "a string", {}); // this will call _init() internally
// Note that the list has been initialized
print(ms);
{ 5, "a string", [0, 1, 2, 3, 4] }
Several utility functions have been described in this document: len(), type(), print(), and others. The built-in data types in Minc have the ability to act as “objects” (see above) and these utility functions may be called as methods on those objects. Here is an example of the traditional and the new use of len():
myString = "hello world" // declare a string
string_len = len(myString) // traditional way
string_len = myString.len() // new way using a method call
When a variable of any type is first declared or auto-declared in a Minc score file, its visibility (i.e. what other parts of the score can use it) is determined by its scope. Two important scopes are Global and Function. Variables declared at Global scope can be used by any part of the score, including inside user-defined functions. Variables declared inside a function block (i.e., between the opening and closing curly brace) can only be “seen” by code inside the function which follows that declaration. In addition, any set of enclosing curly braces can define a scope. The following score snippet shows examples of all of these.
aGlobalNumber = 7 // This variable is Global scope and visible/usable by any code in a score.
float myCustomFunction()
{
string myPrivateString; // This one is Function scope and ONLY visible inside this function.
print(aGlobalNumber); // Global variables are accessible inside functions
return 0;
}
if (aGlobalNumber > 0) { // This opening curly brace defines a new scope.
float privateVariable; // Declaring a variable this way puts it in this scope.
privateVariable = 10;
} // The new scope ends here.
// trying to use 'privateVariable' here would generate an error
Due to legacy concerns, variables which are auto-declared inside if and if/else blocks are treated as if they were declared at the enclosing scope (the scope immediately outside of the new {}):
x = 10;
if (x > 0) {
newvariable = 7;
}
print(newvariable); // 'newvariable' visible due to legacy behavior for auto-declared vars
NOTE: This is not true if the declaration falls anywhere inside a custom function; in this case any variable declared inside ‘{}’ is only visible inside that scope.
The original Minc parser was coded by Lars Graf. Additional modifications were made by John Gibson, Doug Scott and others.
The following is from the original cmix Minc documentation. Some is a bit dated, but the basic concepts are still valid. For a more complete and current grammar, see the RTcmix/src/parser/minc/minc.y file
Minc (Minc is not c) is the data specification language for cmix. It was written by Lars Graf. Minc almost follows a C-like syntax. Arithmetic, conditional and logical expressions are permitted, as well as looping. Any statement of the form foo(val, num, etc) results in the execution of foo() if it is a cmix procedure and declared as such, either in the user’s profile.c or in cmix’s ug_intro.c. If it is neither the evaluated terms within parentheses will simply be printed and returned. The arguments within the parentheses are passed to the cmix procedure in its float *p array. All variables must be declared as float. The sign ; has the meaning ‘clear out any syntax errors’. cmix routines such as open, input, output which have a file name as a first argument, must pass that argument between “ signs. Minc, has two modes of operation: batch and interactive (the default). The only difference between them is that loops cannot be executed in interactive mode. To run in batch mode a -b flag must be specified on the Cmix command line, and all data must be entered either by redirecting input from a file, or by typing all data followed by a
The following is Lars Graf’s formal statement of the language:
Order of precedents:
||
&&
= !=
< > <= >=
+ -
* /
** ^
CASTS
Syntax:
prg: stml
stml : stmt
| stml stmt
| stmt ;
| stml stmt ;
stmt: float idl /* declare ids */
| id = exp
| IF bexp stmt
| IF bexp stmt ELSE stmt
| WHILE bexp stmt
| FOR ( stmt , bexp , stmt ) stmt
| id ( expl ) /* a call to a cmix function */
| { stml }
idl: id /* like: fnct_name(arg1,arg2,arg3) */
| idl , id
id: any letter/digit/"_" combination, but start with letter
expl: exp
| exp ',' expl
| or no function argument
bexp: exp
| ! bexp
| bexp && bexp
| bexp || bexp
| bexp = bexp /* note the comparison of booleans */
| exp < exp
| exp > exp
| exp != exp
| exp <= exp
| exp >= exp
| TRUE
| FALSE
exp: exp ** exp
| - exp
| exp * exp
| exp / exp
| exp + exp
| exp - exp
| ( bexp )
| any number: e.g. 34 -645.34 234.E23 ...
Note:
1) Semicolon and are optional.
2) '=' is used for assignments and boolean expressions.
3) False = 0; True = 1(or nonzero).
Samples:
/* this is a legal comment */
float foo,x;
if i=3 && foo>4 say_mumble( foo**i , i-foo*3)
else mumble_foo()
while x=3 {
this_is_a_cmix_function_call()
x= 5 * (foo=i) /* this is slime */
while true nested_loops_are_possible()
}
for (x=1, x <= 10,x=x+1) dispatch(x)
Minc also has the following facilities:
1) function calls can return values
e.g. i = rand(j)
NOTE: those are calls to your own C-functions.
You may adjust your dispatcher and your functions accordingly
The function must be introduced in the profile, and must
be of type double.
2) assignment statements have the value of the assignment.
e.g. i = j = 5
while (i=j) == 5 { mumblings .. }
3) strings can be passed as arguments.
e.g. file_id = open("name of file",2)
printf("some string %f %f0,i,j)
/* of course, the dispatcher has to know about printf */
NOTE: the string pointer is passed as a double; to convert
back, you have to first cast it into an integer, and then
you have to cast the integer into a char*.
4) use '=' for assigment; and '==' as the booleam equal operator.
Here is a simple example file:
open("sf/bigsound",0,0)
open("sf/bigmix",1,2)
sfclean(1)
float a,b,c,d
a=2 b=3 c=28000 d=-4
sound(a,b,c,1,22*19,d=d-a)
sound(a=a+2, b+.1, 99, d=d-a+2)