VMM - Manual

Last update : 03-10-2006

Table Of Contents





VMM Virtual MIDI Machine, is a MIDI (Musical Instruments Digital Interface) programming language written by Mark Meeus, in Visual C++. The main purpose, is to provide the possibility to program algorithms, that control MIDI hardware and or software.

VMM is freeware and used at own risk. The creator of VMM, and me, the author of this manual, are in no way responsible to where you (ab)use VMM for. If you destroy your computer, it is your own fault (or another, but not VMM's) :-) . By using VMM, you agree with this. The truth is: If you are going to do some MIDI routing or programming without care, MIDI feedback could freeze your computer or applications. Make sure you save everything a lot.

About this manual

Welcome to the VMM manual. This manual has the purpose to give you, the reader, a comprehensive background knowledge, to start programming and coding in VMM: Virtual Midi Machine. It doesn't include information about programming in general. There are plenty of excellent tutorials and books about that.

Because VMM was, is, and remains in progress: this manual is always incomplete. If you have comments, found a spelling error or anything you want to mention, contact us at http://vmm.audionetwork.be/contact/.
If VMM crashes we are always interested in the code what caused that, so don't hesitate to e-mail it.

The official and most recent version of the VMM manual and software can be found at:

VMM Features


This is a list of hardware and software minimum requirements:

VMM is compatible with any Microsoft Windows product except 3.1. It has been succesfully tested on Windows98SE, 2000 and XP.
However, the recommended Operating System to use is Windows XP. Mainly, because WindowsXP has some needed dll files already included. This possibly saves you some installation trouble. If you think you have a compatibility problem on your Operating System, let us know.

A soundcard. Any SBLive soundcard is recommended for most users.

You will need a MIDI input and output. If you don't use external equipement and therefore haven't got a MIDI Interface, you can simulate this by using a MIDI router.

Note that in conjunction of MIDI Yoke, you will need MIDIOX. This is highly recommended because it gives you multi-client options and much more.

The VMM Compiler is a programming editor, where you type your source code, save it, and eventually, compile it. This will create a VMM excecutable file.

The needed DLL libraries are mfc42do.dll and mfc42d.dll and can be found here:

The VMM Runtime is needed and used to run/play your compiled program.

Download VMM

Download Virtual MIDI Machine
Use winrar or winace to extract the file.


First of all, you will have to download VMM*.rar (or .zip) from the download section. The most recent version is always named VMM.rar. There might be older versions too, that can be recognized by version in the form vmm-n.n.n.zip where n is a number.

After you have downloaded VMM. Extract it to the directory where you want to install VMM. Any directory will do. If you don't know how to extract a .rar file, download winrar at http://www.rarlab.com/.

Now, make sure you have correctly installed the needed libraries. To avoid multiple programs using the same dll at the same time, it's best that you put them in the directory where you have VMM installed. eg. C:\VMM\ . If the files aren't allready there, put the dll's in your %systemroot%\system32\ directory (2000/XP). On a windows98 computer this would be c:\windows\system\ .

The VMM compiler can be started by clicking vmm.exe. If you don't get any errors, you can start coding. You can listen to your result, by playing the generated file in the VMM runtime, vmmrt.exe.

IMPORTANT: If you want to use VMM's MIDI functions, you will have to set the path to your include folder. Do this by clicking Options - Settings in the compiler and set the path to c:\vmm\include\ (if you would have installed VMM in c:\vmm\). Note the "\" (backslash) at the end of your path.

To upgrade VMM, no uninstall is needed. You can overwrite your files, or install the new VMM in another directory and run multiple VMM versions on one machine.

VMM Syntax

The VMM syntax looks a lot at C syntax. Most of the time they are not identical, however, many similarities can be noticed.

The program itself always starts with:

  proc main()

A VMM excecutable can not be succesfully compiled without the proc main. Everything between the {ldelim} {rdelim} will be excecuted. If you write a function outside the brackets, but don't call it within the proc main, nothing will happen.

All expressions must end with a ";".

Comments are started with "//". Everything what follows on the same line will be ignored. Block comments can be used this way:

  /* This is a block comment */

Both methods can be used at any place in your program.

Data Types

There is only one type available: numeric, and can be an integer or decimal.


Variables have to be declared before they can be used. This _must_ be done in the beginning of your program and may not be declared after any statement. If there is no value given to a variable, the variable remains unknown. The name of a variable can be both upper and lower case. A variable may not start with a number.
Underscores are allowed.

Global Variables

  global <name>;
  global var;  // defines the variable "var" as global.
  global MyVar; // is also correct.
  global 2test; // is wrong.

Local Variables

  var2test; // declares "var2test" as a local variable.
  a;  // a variable can be a single letter.
  b;c;d;e;  // and they can be more than one declared on the same line.

To assign a value to a variable:

  <name> = <value>;
  a = 10; // assign 10 to a.
  var = 5;  // assign 5 to var.
  newvar = oldvar; // would copy oldvar to newvar.
  <value> can be a number, or another variable.


VMM provides the possibility to use arrays. An array, can have infinite dimensions. Similar to a variable, an array also has to be declared (in the beginning of your program), before it can be used.

  <MyArray> [ <n elements> ];

"MyArray" can be any word upper and or lower case.
"n elements" must be a number that defines the total elements in MyArray. Note that this number can be a result from a function, another variable or array, etc.
Thus, declaring array[10]; means there are 10 elements available in array.
The array index starts counting from zero. This means that to fill the whole array, you have to use the range from array[0] till array[9].

  /* syntax */
  ThisArray[6]; // declares "ThisArray".
                // "ThisArray" has 6 elements. [0-5]

  /* example 1 */
    arr[2] = 5; // gives an Error: Out of Range

  /* example 2 */
  arr[0] = 10; // is correct.
  arr[1] = 25; // arr has 2 elements with the values 10 and 25.

  /* example 3 */
  notes[0] = 64;
  notes[1] = 72;
  notes[2] = 56;
  notes[4] = 80;
  Debugging all keys in this array will have the result:
  ThreadId : 1 => 64.000000
  ThreadId : 1 => 72.000000
  ThreadId : 1 => 56.000000
  ThreadId : 1 => 0.000000
  ThreadId : 1 => 80.000000

Global Arrays

  global <GlobArr> [ <total elements> ]; 

Global arrays have the same syntax as local arrays. Except global arrays are used in the global scope of a program.

Multi Dimensional Arrays

  <MultiArr> [ <n elements> , <x elements> ];

MultiArr is a 2 dimensional array. "n elements" are the number of elements in the first dimension. The dimensions are seperated by a comma ",".
"x elements" declares the second dimension.

  MultiArr is a three dimensional array.
  The first dimension has 2 elements.
  The second dimension has 3 elements.
  The third dimension has 1 element.
  MultiArr[0,0,0] = 30;
  // Would assign 30 to the first possible key in the array.

  MultiArr[1,2,0] = 64;
  // Would assign 64 to the last possible key in the array.

  MultiArr[1,2,123] = 64;
  // Would result in the Error: Out of range.

To assign a value to an array key element:

  TwoDim[3,3]; // first declare "TwoDim"
  // then assign a value to each key
  <MultiArr> [ <key1>, <key2> ] = <value>;  
  TwoDim[0,0] = 20;
  TwoDim[0,1] = 30;
  TwoDim[0,2] = 45;
  TwoDim[1,0] = 12;
  TwoDim[1,1] = 65;
  TwoDim[1,2] = 78;
  TwoDim[2,0] = 64;
  TwoDim[2,1] = 19;
  TwoDim[2,2] = 38;

In a graphical table, it would look like this:

      |0 |1 |2 |
     -|--|--|--|-> key1
    0 |20|12|64|      
    1 |30|65|19|
    2 |45|78|38|

(note: In this figure you could also switch key1 and key2's X and Y directions, on condition that you also change the resulting values.)

But, VMM goes much further than that. These examples are also correct:

  MyArray[(var + bar())+10];

Important: if you declare a variable again, all previous information of that variable in memory is cleared.


Mathematical functions in VMM:

  +   plus
  -   minus
  %   modulo
  =   equals
  /   divide
  ++  add by one
  --  substract by one

Control - Structures


  if (expression) { do something }

Optional, you can make use of "else" in the statement.

  if (expression) { do something } else { do something else }


To make a loop in VMM:

  loop{ }

If you want to break a loop, you can use a condition what returns true or false, followed by:



  i; i = 1;
    do somethings...
    if(i > 10){
    i = i + 1;
    // would loop somethings 10 times, and exit.


Identical in purpose but different in syntax from loop(), you can use while().

  while ( <condition> ){
    do something


An expression can be anything that returns a value, function, variable. Control structures can be:

  == equals
  <  smaller
  > greater
  <= smaller or equal
  >= greater or equal
  || OR
  && AND
  != NOT

  var = 20;
  if (var == 10) { } // returns FALSE
  if (var == 20) { } // returns TRUE

NOTE: In previous versions there wasn't a NOT "!=" operator.
Of course you are encouraged to use the latest VMM release, however, there is a way around this:

  proc main()
  a = 10;
  b = 20;
  true = 1;
  false = 0;
   if(0 == (a < b)) // if a is not smaller than b..

  // returns false because a _is_ smaller than b
  // ThreadId : 1 => 0.000000
  // note: 
  // if(0 == (a < b))
  // is exactly the same as
  // if (a > b)

Functions - Procedures

  proc MyFunction ( <parameter> ) { do something }

The function or procedure is started by "proc", followed by a "name". There can be no, one, or more parameters. More than one parameter must be seperated by a comma.

To break a function:


In the example, MyFunction has to be called"MyFunction(somevalue)", before it will produce any output.

A simple function could look like this:

  proc a(var){
    b = var + 3;
    a(30); // returns 33

Of course, VMM has also functions all ready build in.

Currently, the core functions you can use are:


These functions are expandable by your VMM Function Library. You can create your own, or use the default VMM MIDI function Library. More information is in the MIDI Functions section.


To send data to a midi output, use the "out()" function. Between the brackets come the bytes you want to send to your MIDI hard or software. The bytes are interpreted as hexadecimal values.

  out ( <portnumber> <amount> <n_nr> <n_kind> );

That means, if you would like to send a note event, it can be explained like this:

  out ( <portnumber> <velocity> <pitch> <note> );

or a controller event:

  out ( <portnumber> <amount> <ctrl_nr> <control> );

or a shorter alternative:

  out ( <message> );

  // eg.:
  proc main()

Would send a note event on MIDI channel 1 with a pitch of 60 and a velocity of 127.


  sleep ( <time> );

<time> can be anything that returns a value. (in milliseconds).

sleep(10); // delays the (further) execution of your program with 10 milliseconds.

Of course, time can be variable:

  var = 10;

  var = var + 5;
  sleep(var); // "var" will be 15 here.



rand(); returns a value.

  pitch; pitch = rand(); // makes "pitch" random.
  pitch = (pitch % 50); // modulo from pitch divided by 50.

In this example, pitch is always bigger than 0 and smaller than 50.


  srand ( <parameter> );

srand() is used to initialise the random generator. Random generators calculate their first value from a seed. The default seed sits somewhere in the system, but you can add one by yourself using srand().



Everything between a "critical" function gets executed before anything else.


"in" reads MIDI input. This input can be used as realtime variable, or sent to another output. It is read as a global variable, therefore it can be used in the whole script.


  proc main()
      msg = in(); // "msg" is input
      out ( msg + ((rand()%3) * 0x100));

The pitch on the output becomes a random value 3 semitones higher or lower than the pitch on the input.


In VMM Runtime, you can debug the value of a variable. Its current value will be displayed in the VMM Debug Window.


  proc main()
    a = 13;
    b = 12;
    c = a + b;
  ThreadId : 1 => 25.000000

Another example:

  proc main()
    a = 7;
    b = 7;

will show:
ThreadId : 1 => 8.000000
in the debug window.
If "a" would be 5 (and smaller then 7), it will show nothing.



The int function rounds a value to an integer number. It is only supported when you make use of the default VMM library stdMidi.vmm.

  #include "stdMidi.vmm"
  proc main()
    n = 7/4;
    n = int(n);

  ThreadId : 1 => 1.000000

Look at the difference when int isn't included:

  #include "stdMidi.vmm"
  proc main()
    n = 7/4;
  ThreadId : 1 => 1.750000

NOTE: This is important when doing mathematical functions on MIDI Data!



GetSystemTime gets the computer system time.
It starts counting at midnight from 0, and the value it returns is in seconds.

  // when it is 01:00am, and you do
  // the value it returns is:
  ThreadId : 1 => 3600.000000

MIDI Functions

To make VMM a bit more user friendly, MIDI Channel Messages are included for you, and all have their own Functions. They are free to use, deleted or changed. But keep in mind that when you make your own MIDI commands, other users cannot help you out of trouble. Above that, when you don't include your own library, they are not possible to run your programs. To avoid confusing syntax and code, please always use the default MIDI commands.

These are:

AfterTouch() is not implemented yet.
System Common, System Real Time, and System Exclusive functions aren't supported in the default library. But if you really need them you can create your own.

ALL MIDI data is send to the MIDI output port you activate in Options - Settings in VMM Runtime.


To make VMM's default MIDI functions available, you must have the correct include file in your script, before one of the MIDI functions gets called. It's recommended to start every program you write, with the include file that you want to use.

The default MIDI functions are in the file stdMidi.vmm in your %vmm%\include\ directory. Be sure to set the path in Options - Settings to the right path as described in the Installation part of this manual.


  #include "stdMidi.vmm"

NOTE: there may NOT be a ";" at the end. You may not use comments in stdmidi.vmm.


NoteOn is the command that sends a NOTE ON. All flags are needed. A NOTE ON in MIDI language always comes with some bytes that are needed to play a note. These parameters are also needed when playing traditional instruments so there is nothing weird about that. The only difference is that you must define a MIDI channel. And that's good, because you can use 16 channels, that means 16 times more fun. The other parameters to play a NOTE ON are PITCH (the frequency of the note), and VELOCITY (the volume of the note).




The values must be in these ranges:

  <Channel>   1 - 16
  <Pitch>     0 - 127
  <Velocity>  0 - 127

So, if you want to play a note with pitch C3 (Middle C) with full velocity on channel 1:


OK that's fun, but now the NOTE ON doesn't stop. To make it stop use NoteOff();

To make the note play for one second, use sleep() between the NOTE ON and OFF;



To stop a note from playing use a NOTE OFF. This is similar to the NoteOn() function. Actually a NOTE OFF in MIDI, is a NOTE ON but with a Velocity of 0. Notice that the NOTE OFF pitch value must be the same as the NOTE ON pitch value, else you will stop a note that even wasn't playing.



That would be the same as:


Of course, the first example, with NoteOff, is the most obvious to use. There are other ways to send a NOTE OFF (with a release velocity) but that is not implemented at the moment. If you want a NOTE OFF function with release velocity you create one yourself.


With VMM you can send Control Change Message data to every parameter you want.





Is a PAN (ctrl.10) controller event, on channel 1, with a value of 127. That means the sounds would be fully panned to the right.
For a list of all controller numbers and their purpose look here.


With a pitchbend you can change pitch with fine or coarse resolution.




eg.: PitchBend(1,64,50);


A Program Change changes the current patch in a bank to the new value.
Most synthesizers and samplers have 127 programs in one bank.




On a General MIDI synthesizer this would set the sound on MIDI Channel 1 to patch/program 1 (piano).

Thread - MultiTask

VMM can run different tasks at the same time. These tasks in VMM are called "threads". A thread can have it's own functions. You can run as much threads you want!

  thread <function> ( <parameters> ) { }

VMM Runtime

The VMM Runtime is the program where you load your compiled executables. VMM executables have the extension ".vmx".
After setting the MIDI OUT port, you can "play" your code and actually listen to what you have coded.
(if you like to test an example script, you can use, play, and modify test.vmx from the scripts directory).
If you hear sound, congratulations :-)
If you here silence, make sure your MIDI port settings are correct.
If everything fails, make sure you can play MIDI at all.

VMM Networking

To call a function externally use "extern".

  extern MyFuntion(<value>, [valueN]){

Read more in the VMM Networking section. You can also download vmm_networking.pdf 314KB.


This is an example of a raw MIDI message, note on and off. A short delay of 500ms between the 2 events is inserted.

  proc main()
    out(0x007F3b90); // note on chan1
    sleep(500); // sleep 500
    out(0x007F3b80); // note off chan1
  Is the same as:

  proc main()
    note_on = 0x007F3b90;
    note_off = 0x00003b80;

  Then a dump in MIDI-OX looks like:

    1     90    3B    7F    1  B 3  Note On               
    1     80    3B    00    1  B 3  Note Off              

I hope this has helped to get you starting with the Virtual MIDI Machine.
If you have further questions, comments, tips, corrections, ..., contact us
VMM Team