Minc

The default scorefile parsing/interface language for RTcmix

Introduction to the Minc Parser

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 unless they have been terminated with a semicolon (this is how the interpreter knows there is not more text to come).

Some useful features of Minc

Minc Data Types

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.

float

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.

string

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"

list

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.

handle

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

struct

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. Currently, you must provide arguments for all members in the struct - there are no default values.

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.

map

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

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.

A note about declaring variables

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.

More About Arrays

More About Structs

Score-defined (custom) Minc Functions

More Useful Minc Commands and Features

The ‘include’ statement

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)

Command-line “named arguments” (“standalone” mode only)

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"]

type()

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"

index()

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.

printf()

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)

Minc error handling

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.

Advanced Topics in Minc

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.

Structs as “objects”

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

Built-in Methods

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

Minc Variable Visibility and Lifetime: Scopes

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.

History of the Minc Parser

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 . Comments are inclosed by /\* and \*/ as in C. Any user function which returns a value can return that value to Minc if it is declared as a type double and introduced in the profile.c file.


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)