a Basic multitasking development system for experimental real time interactive and algorithmic musical composition under Windows 32bit.

version 9.16


Prof.Dr.Godfried-Willem RAES

Introduction The GMT Cockpit: User interface GMT Users Guide Language Library Users Guide

Manual for GMT program developers / composers

Installation instructions

Main runtime setup screen:This screen will always open first on starting the compiled GMT software. The menu bar allows the user to select all required settings for his application. The 'Cockpit' window opens after selecting start GMT in this main menu. If any menu item is grayed out, either you do not have the required hardware, or the compilation does not included the required source code or libraries.

Writing code within the GMT in a minimum of time:

Step 1:

Power Basic DLL compiler V8.02 :

- start the PBEdit-editor and load our source code file gmt.bas

This source code includes automatically all code contained in our libraries (g_lib.dll, g_mus.dll, g_har.dll, g_indep.dll, g_noh.dll, g_mus.dll, gh_indep.dll...) as well as some code for windows callbacks required for GMT.

In any case, the source-code file is:

Since power basic has no real restrictions with regard to program size, we have now included all essential GMT code in a single module. (There might be a problem though with source code modules requiring over 600kByte, since the power basic editor is in fact still a 16 bit application...). Please check it out and report any bugs found. Users are advised to add their composition and application code in a separate module, such that they can easily upgrade to future versions of <GMT>. Demonstration code for GMT apps can be found in other *.BAS or *.INC modules such as demo.bas. You can load those in the editor after GMT.BAS or by editing your g_meta.inc file.

Step 2:

Suppose you have something in mind you want to see handled as a task. We will handle this step by example, since this seems to be the most pragmatic way of explaining things in this context. Suppose the task we want to have performed consists of sounding a note using midi. We would use the procedure Play to send the note information to the port:

Play 0, 36, 64 ' suppose channel 0, note is low C and midrange velocity

In order to make this happen, we have to declare a task, preferably in a newly created code module or procedure of your own, but you can also add it in the gmt.bas module.(Later you can easily move procedures from this module to your own...). If at this point you decide to add a module of your own, start by inserting its reference in the header of gmt.bas with a $INCLUDE command. Note that you can only include source code and/or declarations of procedures and functions to be used in a named DLL file. (The use of compiler metastatements is highly advised for building applications).

So we go on and write

SUB MyNote
Play 0, 36, 64

Now we have written a task. It wont do anything yet if we launch the multitasker. So we have to declare it, by adding a declaring statement in the corresponding *.BI file or in the procedure declaration section of gmt.bas.


Note that the procedure does not take parameters, so we leave the brackets empty. Procedures we want to see as tasks should never have parameters, since we use a code pointer mechanism inside the taskscheduler. Later we can pass all parameters we want to our tasks either by using semaphore variables, or by using the Windows message mechanism, thus making things like varying reschedule intervals (periods), varying dynamics, controller values, algorithms... possible.

After having done this, still it wont run. We have to initialize the reschedule values for our task and give it a name so that it appears on our screen. Let us choose to make it task nr. 15 (Tasks 0-15 and a few others are reserved for internal GMT-use, so we should not use these numbers. Look into the procedure InitTasks to find out which numbers are free).

Go to the procedure InitTasks in GMT.Bas and lets fill in some values for our newly created task:

Task(15).name = "MyNote"

Then we set an initial reschedule frequency if required:

Task(15).freq = 5               ' 5 times a second this would be

You will already have noticed that the typed array variable Task().--- has many fields. It's prototype is declared in TYPES.BI and referenced in the header of the GMT.BAS file. The task() type looks like this:

momentaneous values for the last time the task was performed. This value should not be set by the user. It is continuously recalculated inside the timer callback function in the dll. Do not write anything to this field.
momentaneous values in calls per seconds (Hz) .This parameter should be set by the user. For active tasks it should never be %False.
tempo AS WORD
tempo specified in M.M. units. This determines the global periodicy of tasks, so that .freq can be used to let them perform even to most complicated rhythms
switch AS BYTE
used to receive switching flags for the tasks. The on-off flag is %TASK_ONOFF. The %TASK_BUSY flag is set the the callback. You should not change these flags in you code. Use the procedures StartTask en StopTask instead.
starttime AS LONG
if your task has to run only from a given moment on, you can specify its starting value in Promil of the total duration of the project, at this place. The total duration of a project is contained in a global variable as part of the strtucture App : App.Komposduur, expressed in seconds. As a tast is started the starttime value is updated by the procedure with the promil value of its starting time in proportion to App.Komposduur.
stoptime AS LONG
if your task should stop after a certain moment, set the promil value here.As a tast is stopped the stoptime value is updated by the procedure with the promil value of its stopping time in proportion to App.Komposduur.
naam AS STRING * 8
place for a 8-character name for your task. This easies the design of a user interface. It also helps debugging...
level AS BYTE
used to set a controller value for the task (i.e. velocity byte value), or a parameter in audio applications.
channel AS BYTE
used to assign a midi-channel for the task. Of course it can also be used as a track-pointer in a real-audio context.
patch AS BYTE
used to hold a patch byte, can of course also be used as a pointer to sound files of sound-generating tasks
used to hold a panning byte, for midi oriented tasks. It can of course also be used as a pointer to sound files of sound-generating tasks
duur AS LONG
used for timestamping, automatic switching off of tasks and such more.
Har AS HarmType
used with HarmLib from version 2.0 on. This structure keeps track of the momentaneous harmonic situation for a musical task.The HarmType structure is documented in detail in the manual for our harmony library. The structure consists of following fields:
Har.vel : string containing 127 velocity bytes
Har.C(0 TO 11) : single precision type fuzzy shepard chord descriptor
Har.Dis : single precision fuzzy dissonance value
Har.Kon : single precision fuzzy consonance value
Har.Iprop(0 TO 6) : single precision interval property strenghts
Rit AS RitmType
used with HarmLib and GMT from version 2.0 on. With this structure it is possible to implement autonomous meters and rythmical structure for each musical task. The structure consists of following fields:
Rit.pattern(0 TO %patternsequencelenght) : 256 single precision values. Positive values are sound durations, negative values represent silences, the zero value is a delimiter for the end of the pattern
Rit.minduur : shortest possible/allowable timeduration expressed in seconds. (single precision value)
Rit.maxduur : longest possible/allowable timeduration expressed in seconds. (single precision value)
this field is crucial to the working of our preemptive multitasker. It should contain a codepointer pointing to the start of the code for the taskprocedure.
The value can easily be calculated with the PB statement CODEPTR(taskprocedurename)

This typed variable array is shared across modules. It is dimensioned in GMT.Bas, and known by its pointer in the DLL. Task() is declared as global. (cfr. g_GLOB.BI for a complete declaration of all global variables).

So your code can change the values at any time. The use of the Task().level, Task().channel, Task().switch, Task().patch, Task().duur variables is not restricted to their names within our code-framework. So if you are writing code for tasks not requiring certain fields provided in the type, you may use them for other purposes. However, for code clarity, it is a better idea to add specific typed variables of your own. If you need more task linked data, we also provided the TaskEX() type. Herein we store handles for extra real time controlls and dialogs, etc. Details are documented in G_TYPE.BI The Task() prototype contains a single field which the user should not set/reset nor interfere with in any respect. This is: Task().rsi.

From this, it will be clear that our procedure can already take profit of some of the features of the Task().- variable. Let's implement it right away, and change our procedure as follows:

SUB MyNote
Play Task(15).channel, 36, Task(15).level
'Task(15).level  is the velocity as calculated in a procedure or function.

and set the appropriate fields in InitMT:

Task(15).channel = 0
Task(15).level = 64

Since we can already foresee from now that we forgot to switch the sounding note off, we may as well implement this on entry by writing:

SUB MyNote
STATIC Oldnote%
Newnote%= 36 :' low C
IF Oldnote% THEN
NoteOff Task(15).channel, Oldnote%
Oldnote%= %False
Play Task(15).channel, Newnote%, Task(15).level
Oldnote% = Newnote%

It will be clear that we have now prepared our task already to accept the note value as a parameter, or to expand the procedure such as to calculate new notes every time it is invoked. Lets make our note go up and down using the cosine function:

SUB MyNote
LOCAL Newnote%
STATIC Oldnote%
Newnote%= 48 + (COS(Promil!) *12)           ' down to 36 and up top 64
IF Oldnote% THEN
NoteOff Task(15).channel,Oldnote%
Oldnote%= False
IF Newnote% <> Oldnote% THEN
Play Task(15).channel, Newnote%, Task(15).level
Oldnote% = Newnote%

In order to use the Promil% function, (as well as the derived functions Tprop! and Tang!) you have to set a value for the global variable Komposduur%. This value should be expressed in absolute duration units, in seconds. The value is found in the GMT.INI file, so the user can change it easily using notepad or the PB editor itself. Or, better, make up an application specific ini file where you set a suitable value.

Step 3:

Now we are done with the coding. The next and last step prior to test running, is enabling the multitasker to recognize your task. Go to the InitTasks procedure and search where we fiind a reference to Task(15). The we add:

Task(15).cPtr = CODEPTR(MyNote)

Now save all, and make sure you have added the declare statement to either GMT.BI, to the header of Gmt.Bas in the appropriate section, to your own declaration file. (In the latter case, insert it in the header of GMT, following the other #INCLUDE statements.)

COMPILE IT and/or RUN IT !!! (Do not attempt to run it using the PBcc debugger: this does not work at all for real time apps like this one!).

When you startup the program, it will display two windows: a 'Cockpit' style controll window and a 'welcome' and initialisation window. Make the latter active and initialize the I/O dives you want to use (Click on the menu items Input-devices and Output devices). Follow the instructions displayed in the messageboxes on screen. If you finished this step, select Start in the menu and the win dow will minimize itself leaving you only with the cockpit window, which is the main command window for GMT. If you have your midi module connected, or if you have selected internal sound devices, the program should start running. In order to activate your newly created task, use your mouse and click on its name in the corresponding check box. (Your task name should appear in the Cockpit window to the right of a checkbox).

So we helped you through the very first steps... The rest is left to you imagination! You might be helped by studying the sample procudures provided in the debug module (One does an upwards scale, another one a downwards scale in a different tempo).

Note that this GMT-framework is basically capable of all things such a program as Max or better, PD, can do. Only with these differences:

1.-it's a whole lot faster! (Max is originally developped for the legacy Macintosdh system and was only later translated for Intel platsform PC's) . On the PC platform, PD is in fact a much better alternative. PD is public domain and free!

2.-it's open architecture and public domain!

3.- it's for free...

You can remap whatever incoming information into whatever going out, feeding it to and transforming it by any algorithm of your liking. Although in this Basic version, its a bit harder to take profit of many new hardware features offered on the Windows platform, it is not at all impossible as we have shown in many of our GMT applications. Users have to know and understand the Win32Api in order to add these features..

People interested can obtain hardware specific GMT-libraries supporting a range of ADC/DAC converter boards (ISA, USB, PCMCIA...). We have used the Contec and Arcom boards to good success in our compositions <A Book of Moves>, <Songbook>, <Mach'96>, <Winter'97>. This is really the best way to go for real-world interfacing, and at the same time the reason to go for PC's for this kind of applications. We have libraries for FFT-analysis, complex math and filtering, robot-controll… available as well. Much of it is actually already a part of the code contained in our libraries. On purpose we did not include any of these extra features in the GMT basic package, that we wanted to keep as much bare bones as possible. Contributions from people writing Basic code or public DLL's are welcomed! Support for USB-based data acquisition hardware is available. Sofar we support devices by National Instruments. Since august 1998, only the version written in PowerBasic DLL will be available from the logos website.Comments relative to previous versions can be found in our archive file http://www.logosfoundation.org/logos/gmt/gmt_archive.html. Since october 1998, we stopped all support for the Dos-box only versions of this package. Now we exclusively use Power Basic's dll compiler version 8.02.

After the proper devices have been selected, the opening screen displays the users choices in a list as follows:

The GMT-Cockpit : User Interface

Users Guide to public declared Tasks, Functions and Procedures

List of GLOBAL variables

All main procedures and functions are now to be found in the code modules: GMT.BAS or, for the largest part, are contained in the DLL libraries.

Here we present them in categories. We do not have the time to regularly synchronize the contents of this manual with the contents of the code. So please, look into the source code for the most up-to-date and accurate information. Often we move source code from the main GMT module to the dll libraries. So if you cannot find something referred to in this section in GMT.BAS, please have a look in its libraries, g_lib.bas/bi, g_nih.bas/bi, g_noh.bas/bi,g_indep.inc... . There is usually a lot of comment embedded in our source code. New since version 2.1 is also that we are now including all procedures and functions in a single hyperlinked index covering both our trusted HarmLib and <GMT>.. Since version 2.2 we integrated complete support for the Win32Api. This ought to make us more independent from common but very contingent hardware intrinsics of users machines. The drawback is that our code now absolutely needs a 32-bit OS as well as a fast Pentium processor. MMX support is very desirable now. There are, since version 2.3 a lot more midi functions and procedures. Since version 3, there is also suppport for real time audio processing. Version 3.3 brings support for data acquisition hardware: this module can be requested on demand. It makes no sense including it in the main GMT package, since most users do not have the required hardware.


All midi related procedures and functions have been placed in a separate section of the DLL file since 03.03.1999... The manual, covering all midi related functions is also in a separate file now:


2. Internal GMT Main-module tasks functions and procedures

Code module: DLL


StartTask tasknumber%

StopTask tasknumber%

SUB CleanUp ()

SUB InitTasks ()

SUB PrepareMenu ()

FUNCTION CheckTimerResolution LIB "gmt_harm.dll" () AS WORD


3. Debugging-related tasks, functions and procedures (the reside in the DLL)

SUB ShowMTspeed ()

SUB ShowTime ()

SUB ShowPromil () :

SUB wPrint (param AS DWORD, BYREF gList() AS ASCII * 80)

DEMO task code:

SUB ScaleUP (): ' test task - example for the coding of a task playing an upwards scale (in module gmt_demo.bas)
SUB ScaleDOWN () :' test task - example downwards scale

4. User-related tasks functions and procedures, application examples

DEMO code:

DECLARE SUB VoiceProtoType (tasknr%):' test task 18 - example code from boxing
DECLARE SUB Player () :' test task 21 - example code for use of midi input
DECLARE SUB GlobHar () :' task 28 - example code from Boxing
DECLARE SUB DelayPlayerF (tasknr%) :' - example code for delay lines
DECLARE SUB DelayPlayerS (tasknr%)
DECLARE SUB DelayPlayerN (tasknr%)
DECLARE SUB DelayPlayerBackN (tasknr%)

5. Time-related tasks functions and procedures, application examples

FUNCTION GetPromil% ()

FUNCTION Tprop! (k%)

FUNCTION Tang! (k%)

6. Window & Win32Api tasks functions and procedures, application examples

Centers a window on the screen. You have to pass the handle. (LONG)

for upper left corner you pass "UL" or "BL" or "1"
for upper right corner you pass "UR" or "BR" or "2"
for lower left corner you pass "DL" or "OL" or "4"
for lower right corner you pass "DR" or "OR" or "3"

This procedure, residing in the DLL was added to the library to overcome the lack of easy to use, non-modal text displaying tools both in the WinApi and in PowerBasic. The MSGBOX function in PB as well as the MessageBox function in Windows both confront you with minimal modal dialogs that require user interaction and halt the flow of your programm. So out procedure displays the string passed in a autowrapping and autosizing window that kills itself after the time set in duration_in_ms is passed. This duration has to be passed as a value expressed in milliseconds.

Sorry but the manual isn't complete and ready as yet…We will update and complete it as soon as we can. Reading through the source may help you out in this early stage!.For quick references, please consult the language reference: index-harmlib.html This is always the first file we update as new functions become available.

Visit Logos' Homepage Visit Godfried-Willem Raes' pages Get the Harmony Library for Basic
Compositions realized using this software: <Fall'95>, <Sincs>, <Counting Down to minus 747>,<Mach'96>,<Winter'97>, <Boxing>, <CoHiBa>, <Gorgonio>, <OboTek>, <CelloPi>,<LickStick>,<Hidden {C} Harrns>,<Climbing FujiSan>,<FriRick>,<Fidel-C>, <TechnoFaustus>,<Gestrobo>,<PicRada>, <Quadrada>, <TransiTrance>...
Other composers and musicians using this real time software: Joachim Brackx, Karin De Fleyt, Marc Maes, Luc Brewaeys, Champ d'Action, Kristof Lauwers,Yvan Vander Sanden, Kris De Baerdemacker...

Note for Students in Godfried-Willem Raes composition class at the Ghent Royal Conservatory:

Meer kommentaar en lesmateriaal wordt aangereikt in de paragrafen 1134 en 1135 van de kursus. Paragraaf 1135 behandelt meer in het bijzonder de behandeling van ritmiek onder multitaskers zoals GMT.

Vroegere versies van <GMT>, voor Microsoft Professional Basic evenals voor Turbo Basic, voor PowerBasic 3.5 etc... zijn nog steeds beschikbaar op de klaskomputer (voorzover die het nog doet...) maar worden niet langer van updates voorzien.

<GMT> wordt in detail behandeld in de kursus algoritmische kompositie en behoort tot de obligate leerstof.

Last updated: 2007-01-20