Prof.Dr.Godfried-Willem RAES

<GMT> Library Reference Manual: Module 7: Midi related procedures and functions


<Index > <Introduction> <General Functions> <Midi Functions>
<Fuzzy Functions> <Analysis Functions> <Acoustical Functions> <Audio Functions>
<Visualisation Functions> <File Management> <Spectral Functions> <Rhythm functions>

 CHAPTER 7: GMT_MIDI

Public declarations for this module are included in gmt_lib.bi. The procedures are in our DLL: gmt_lib.dll.Most source code declarations for this module reside in g_midi.bi. The source code for the procedures itself is in g_midi.inc. If you do not find links to these specific files to work on our website, please download the zip files with the complete source code. Once unzipped, these links will be found on your pc. Scanning though the source code will likely clarify questions that we left -by lack of time and collaborators- unanswered in this brief reference guide. Note that all simple numeric parameters passed to library functions, unless explicitly mentioned otherwize, are passed by value.

The functions can be used by C programmers as well, if they just translate the declaration in g_lib.bi to C or C++ format.

Note that as an alternative to using MIDI with its very slow baudrate, we also support use of UDP/IP for networked PC's, since GMT version 6.1 and higher.

Procedures for handling midi I/O in <GMT> using the Win32Api


Alphabetical list of functions and procedures:
SUB PatternRecognize (noot?,velo?) ' - under development. Disregard..

SUB AfterTouch (k AS WORD, value?)

SUB AllNotesOff (k AS WORD)

FUNCTION B2S$ ( b%)

SUB Bend (k AS WORD,lsb?, msb?)

SUB BlockSysExReception(BYREF SXThread as GMT_SYSEX_THREAD)

SUB BypassTSR24 (k AS WORD)

SUB ClearDelayArrays ()

SUB ClearMiBuf ()

FUNCTION ExportSeqPtr ()

FUNCTION GetPitchBend% ( k AS WORD, flags AS INTEGER)
FUNCTION GetPitchBendRaw% (k AS WORD,flags AS INTEGER)

FUNCTION GetPressure% (k AS WORD, flags AS INTEGER)

FUNCTION GetProgChange% (k AS WORD, flags AS INTEGER)
FUNCTION GetMidiNote% (k AS WORD, flags AS INTEGER)
FUNCTION GetAfterTouch% (k AS WORD, flags AS INTEGER)

FUNCTION GetController% (k AS WORD, c?, flags AS INTEGER)

FUNCTION GetSongSelect (BYVAL mport AS WORD, BYVAL flags AS INTEGER) AS INTEGER

FUNCTION GetSysEx (Byref SxThread AS GMT_SYSEX_THREAD,flags AS INTEGER) AS STRING

SUB GetInstrumentParams (Ins AS Musician, konstant AS DWORD)

SUB InitP2M (Pitch2Midi$, k AS WORD)

SUB InstrumAllNotesOff (BYREF instrument AS musician, h AS LONG)
SUB InstrumPlay (BYREF instrument AS musician, h AS LONG)

SUB KeyPress (k AS WORD, noot?, value?)

FUNCTION MidiProc (LONG, LONG, DWORD, LONG, LONG) AS LONG

SUB MelodyPatternRecognize()

FUNCTION MidiStopStartCont (BYVAL mprt AS WORD, BYVAL flags AS WORD,BYVAL value AS WORD) AS WORD

SUB ModeMess (k AS WORD,ctrl?, value?) [you can also use the synonym procedure: Controller (k AS WORD, ctrl?, value?) ]
SUB NoteCentOn (k AS WORD, noot!, velo?)
SUB NoteCentOff (k AS WORD,noot!)
SUB NoteOff (k AS WORD,noot?)

FUNCTION OpenMidiInputDevice (devicenumber AS LONG) AS LONG
FUNCTION OpenMidiOutputDevice (devicenumber AS LONG) AS LONG
SUB Play (k AS WORD, noot?, velo?)
SUB PlayHar (H AS HarmType,k?)
SUB PlaySeq ()
SUB ProgChange(k AS WORD,value?)

SUB ProgChangeEx (k AS WORD, msblsb AS WORD, value?)
SUB Proteuspatch (k AS WORD, patchnumber AS WORD)

SUB ProtOFF (k AS WORD)
SUB ProtON (k AS WORD)

FUNCTION ReadDelayLine% (instance AS BYTE, delaytime??, speed!)

FUNCTION ReadMidiFlagsFromFile (f AS STRING, Sx AS GMT_SYSEX_THREAD) AS LONG
FUNCTION ReadSeqFile$ (track%, speedfactor!)
FUNCTION RecognizeNoteDur% ( nv%, duration&, tokennote?, tolerance?)

FUNCTION SongPositionPointer (BYVAL mport AS WORD, BYVAL flags AS WORD,BYVAL value AS WORD) AS WORD

SUB StartSysExThread (SxThread AS GMT_SYSEX_THREAD)

FUNCTION SxIn (LONG) AS LONG
SUB SysEx ( hMidiOut AS LONG, STRING)

SUB WriteDelayLine (noot?, velo?)
SUB WriteSeqScore ()


Users & Reference guide

1. Midi-related functions and procedures

CALLBACKFUNCTIONS:

MidiProc (BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, BYVAL dwInstance AS DWORD, BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG

This callback function is part of the Windows message handling code and thus essential to the functioning of GMT. Note that <GMT> version prior ti version 4.00 did not allow users to sysex messages. This had been changed with version 4.00. However, midi time-code, is not implmenented as yet. At a later time, we will implement support for tmed messages and timecode as well. Normally users should not call this function nor interfere with it in any respect. Contributions/ improvements to the code are welcomed however.

Initialization functions:

FUNCTION OpenMidiInputDevice (devicenumber AS LONG) AS LONG
FUNCTION
OpenMidiOutputDevice (devicenumber AS LONG) AS LONG

These functions return a handle for a midi device on your pc. These function are internally called from the main set up window in <GMT>. They return a windows handle for the midi-in and midi out device used in GMT respectively.

FUNCTION SxIn (LONG) AS LONG

This DLL function is the thread for sysex reception in GMT. It should never be called by the user.

FUNCTION StartSysExThread (SxThread AS GMT_SYSEX_THREAD)

This DLL functions starts the sysex reception thread in the DLL. It is called on selecting a midi-input device, with OpenMidiInputDevice. Normally users do not need to call this function from their own code.

SUB BlockSysExReception (SxThread AS GMT_SYSEX_THREAD)

This procedure should be called after the user is done with any sysex reception via midi and just before starting his real time application. Leaving the midi-sysex thread in an active state may cause excessive jitter in GMT.

FUNCTION ReadMidiFlagsFromFile (f AS STRING, SxThread AS GMT_SYSEX_THREAD) AS LONG

This function is called in ReadInifile in GMT to retrieve and set the followinf flags in SxThread:
SxThread.flags bit 0 = if set, received midi sysex string will be append to a file (cfr, declared constant %SYSEX_TO_FILE = 0)
SxThread.flags bit1 = if set, received midi sysex string will be stored in a buffer array (cfr. declared constant %SYSEX_TO_SXB = 1)
SxThread.flags bit2 = if set, reception of midi sysex strings is blocked in the callback (cfr. declared constant %SYSEX_BLOCK = 2)
Setting SxThread.flags to -1 (= %SYSEX_STOP constant) disables midi sysex reception and kills the thread function. If this flag is set, you cannot restart the thread in your application.
The flags can be part of you application ini file, in which case you use this function to set the flags according to your application. The string constants used in the inifile are: RESEIVE_SYSEX, SYSEX_TO_FILE and SYSEX_TO_ARRAY. These entries should be contained in the Flags data block. The function returns %True if succesfull, %False otherwize. The code resides in the DLL.

Sequence & time related procedures and functions:


FUNCTION

ReadSeqFile$ (track%, speedfactor!)

  • This DLL function is used for reading existing *.SEQ files within <GMT>. The function returns a harmony string (128 bytes) as defined in the Harmony library as read from a opened *.SEQ file if one is found on the timetickcount the function was called. If no string was available, the string returned wil be a null-string (= "" ,containing nothing that is). This should not be confused with the empty string (= STRING$(128,0), consisting of 128 ascii-null characters).
  • A speedfactor (any single precision value larger than zero, can be used) can be given as a playback speed parameter. Value 1 means: play at original speed. 2= twice as fast, 0.5 = half speed.
  • WriteSeqScore ()

  • This task, defined as task nr. App.WriteSeqScoreTaskNr in GMT, allows the user to write generated midi-information to a sequential file. This file can later be used for conversion to a midi-file, or to a musical score. A conversion utility from the generated format to standard midi file format can be obtained from the author. It is also available on the Logos website under the \logos\exe directory.
  • The timing precision is in centiseconds.
  • The user should put the name of the output file in the App.SeqOutFileName field and should set the task(tasknr).flag to %SCORE_TASK of the tasks that have to be monitored during initialisation. The task will monitor the task(tasknr).Har field of all tasks that have this flag set and will write all changes to the seq file. The usere should NOT set the App.SeqOutFileNr field! A coding example can be found in gmt_demo.inc
  • ExportSeqPtr()

  • This DLL function returns the pointer to the DLL's Sequecertype structure. You need this to initialise seqfile playback. See the PlaySeq() function for more details.
  • PlaySeq ()

  • This DLL task plays existing *.SEQ files. The user should not call this function directly. To play a *.seq file, fill in it's name in App.SeqFileIn. Then call the function ExportSeqPtr. This returns a pointer to a sequencertype structure. In the flags field of this structure set a bit for every track from the seq-file you wish to play, set for every track the map(track) field to the midi channel you wish this track to be played on (make sure you don't assign the same channel to different tracks). In the seq.Speedfactor field you can determin the speed at wich the file should be played. 1 = normal, < 1 is slower, >1 faster. Finally start task App.ReadSeqScoreTaskNr. A coding example can be found in gmt_demo.inc. Note that we can, by creating multiple instances of this code, easily playback different tracks contained in the file, at individually different speeds! Ever heard of a sequencer capable of doing that?
  • ClearDelayArrays ()

    This procedure erases the midi delays (globals) from memory. This can happen either under program control, by calling the procedure, or via the user interface: the default Cockpit-button.


    SUB WriteDelayLine (BYVAL noot?, BYVAL velo?)


    SUB InputPlayer
    LOCAL nv%, noot?, velo?, tasknr%

    tasknr% =21
    ' suppose this task is defined as task nr. 21
    ' code for real time midi input - this removes the notes from the buffer:

    nv% = GetMidiNote% (Task(tasknr%).channel, %REMOVE OR %OLDEST)
    IF nv% = -1 THEN EXIT SUB
    :' if no note came in, exit the task
    velo? = LOBYT(nv%)
    noot? = HIBYT(nv%)
    ' write it into the delayline:
    WriteDelayLine noot?, velo?
    ' lets implement full polyphonic midi-thru now:
    IF velo? = %False THEN
    DelNote2Har Task(tasknr?).Har(0), BYCOPY noot?
    PlayHar Task(21).Har(0), Task(tasknr?).channel
    ELSE
    DelNote2Har Task(tasknr?).Har(0), BYCOPY noot?
    PlayHar Task(tasknr?).Har(0), Task(21).channel
    AddNote2Har Task(tasknr?).Har(0), BYCOPY noot?, BYCOPY velo?
    PlayHar Task(tasknr?).Har(0) , Task(21).channel
    END IF

    END SUB


    FUNCTION ReadDelayLine% (BYVAL instance AS BYTE, BYVAL delaytime??, BYVAL speed!)


    MelodyPatternRecognize()

    This task attempts to recognize melody patterns from a real time midi-input. The documentation is still to be written. For now, we hope the source code to be clear enough. This task makes extesive use of RecognizeNoteDur%, documented further.

    RecognizeNoteDur% (BYVAL nv%, BYVAL duration&, BYVAL tokennote?, BYVAL tolerance?)

    Use multiple calls to this function in order to recognize a sequence of notes and durations. Uses the global Follow. typed variable for handles.
    nv% is coded as: msb=note, lsb=velo and this is the input to the function. Duration is the timeduration it has to look for with regard to the note to compare to. Tokennote is the note to recognize. The function returns true if the note & duration match The tolerance margin in % is the last parameter. All parameters passed BYVAL !!!


    FUNCTION GetController% (BYVAL k AS WORD, BYVAL c?, flags AS INTEGER)


    FUNCTION GetMidiNote% (BYVAL k AS WORD, flags AS INTEGER)

  • This function reads note information from the midi-in buffer and returns an integer value composed of the note and velocity information as two Msb%-Lsb% bytes (Note-byte & Velocity-byte). The function does delete the data it returns from the buffer, if the %remove flag is set. The low byte of parameter k stands for the midi-channel from which to read a byte. Thus legal values of k should be limited to the range 0 - 15. The high byte may contain the midiport number opened and used. If no data was found in the buffer, the functions returns -1.
  • The function retrieves resp. the most recent information found in the buffer or the oldest information present in the midi input buffer, depending on the bits set in the flag parameter. Normally you should use %REMOVE OR %OLDEST. More information on flags used with midi-input functions.
  • Example:
    nv% = GetMidiNote%(k?,%REMOVE OR %OLDEST)
    IF SGN(nv%)= %True THEN
    noot? = HIBYT(nv%)
    velo? = LOBYT(nv%)
    ELSE
    '... no note available
    END IF

  • FUNCTION GetPressure% (BYVAL k AS WORD, flags AS INTEGER)

  • Analogue to previous functions, this one returns key pressure information (160 + channel,note, value byte sequences). The value returned by the function is an integer packed with 2 bytes. If no data was found in the buffer, the function returns -1. The note byte is the msb, the value byte is the lsb.
  • The flags used with this function are as described previously.More information on flags used with midi-input functions.

  • FUNCTION GetPitchBend% (BYVAL k AS WORD, flags AS INTEGER)
    FUNCTION
    GetPitchBendRaw% (BYVAL k AS WORD,flags AS INTEGER)

  • The first function operates just like reading the bendwheel information but returns the bend value in cents directly instead of the awkward shifted 14-bit msb-lsb format defined in the midi standard, and returned by the second function.
  • The function reads pitch-bend information from the midi-in buffer for the channel passed in the low byte of k (range=0-15) and returns a value in CENTS (+/- 100) based on the bendrange set in your equipment. Bendrange is supposed to be set to be +/- 1 semitone on you hardware prior to using this functions.
  • The second function returns the two pitchbend bytes packed into the returned integer. You should use the LOBYT and HIBYT functions to retrieve both bytes separately. (cfr. GetMidiNote%())
  • More information on flags used with midi-input functions.

  • FUNCTION GetAfterTouch% (BYVAL k AS WORD, flags AS INTEGER)

  • Analoguous to previous functions, this one returns aftertouch information from the midi input buffer. (Status bytes &HD0-&HDF). The value returned by the function is a single byte packed in an integer. If no data was found in the buffer, the function returns -1.
  • More information on flags used with midi-input functions.
  • FUNCTION GetProgChange% (BYVAL k AS WORD, flags AS INTEGER)

  • Analoguous to previous functions, this one returns program change information from the midi input buffer. (Status bytes &HB0-&HBF). The value returned by the function is a single byte packed in the LOBYT of an integer. If no data was found in the buffer, the function returns -1.
  • More information on flags used with midi-input functions.
  • FUNCTION GetSysEx (BYREF SxThread AS GMT_SYSEX_THREAD, BYVAL flags AS INTEGER) AS STRING

    Since version 4.0, we implemented reception of sys-ex messages within the midi input callbackfunction. Received sysex messages may be retrieved with this function residing in the dll. Subsequent calls to the function will return a sysex string consisting of &HF0 ..... up to the final &HF7 byte. (EOX). The buffersize is determined by the declared constand %SysExBuffer and defaults to 16kB. It can be extended to maximum 64kB, a limit set by the windows Api. If no sysex data is found in the buffer, it will return an empty string ("").

    If you plan using sysex reception in you program, make sure you start the task/thread calling this function right after the start of you program. If the buffer overflows, data may get lost or disorganized. Normally this will be done automatically as soon as the user selects a midi-input device, unless the appropriate flags in the SxThread structure are different than the defaults.

    Note that handling for excessive sysex input data may endanger the timing precision of GMT. So you better perform your sysex I/O on initialization of your application or composition. In our example code, pushing the start button in the cockpit blocks reception of sysex messages in the callback.

    Flags to be used with this function are as with all other midi related get-functions.More information on flags used with midi-input functions.

    FUNCTION GetSongSelect (BYVAL mport AS WORD, BYVAL flags AS INTEGER) AS INTEGER

    The real time midi message Song Select can be retrieved from the midi-input buffer in the DLL. Although it is a real time message we decided to place these data in the buffer, allowing users to misuse the message for more clever purposes than selecting songs from sequences. By doing so you can use the message for instance to select many pieces at the same time (Ives would have loved this...).

    The first parameter is the number of the midiport (0 to 15) you want to retreive data from. Of course the port should have been opened prior to calling this function. Note that this message is not a channel message, so do not confuse mport with the midi channel. There simply is no channel data here. The flags to use are as with other Get... functions used to retrieve midi information.More information on flags used with midi-input functions.

    The value returned is either -1 (when no song select message was found in the buffer), or else a value between 0 and 127.

    FUNCTION SongPositionPointer (BYVAL mport AS WORD, BYVAL flags AS WORD,BYVAL value AS WORD) AS WORD

    When the real time midi message songposition pointer is received, the data is not written to the midibuffer for the port specified in mport, but updates the SongPositionPointer value held in this function/procedure. The return value is a unipolar 14 bit value.

    To set/reset the songpositionpointer call this function as a sub:
    SongPositionPointer midiportnr, %G_SPP_SET, value
    If you want to set it to a new value and retrieve the old value, code as:
    oldvalue = SongPositionPointer (midiportnr,%G_SPP_SET,newvalue)
    To retrieve the latest song position pointer value code as:
    value = SongPositionPointer (midiportnr, %G_SPP_GET, %False)
    'The songpositionpointervalues are set from within the midi-in callback as soon as they are received, but can be reset/set by the user.

    Note that the flags use different constants then those used in other midi-in procedures. Only the values %G_SPP_SET and %G_SPP_GET are legal.

    FUNCTION MidiStopStartCont (BYVAL mprt AS WORD, BYVAL flags AS WORD,BYVAL value AS WORD) AS WORD

    This function returns the midi real time flags song-start, song-stop, song-continue and tuning request as soon as received via the opened midiport specified and passed in mprt. (0-15). Next to the standard messages defined in the official midi standard, we also implemented the retrieval of undefined bytes in the protocol (bytes 241,244,245,249,253). So you can experiment using these for any non-standard purposes in your own coding. The flags can only assume following constant values:

    The value passed to the function must be one of the following declared constants:

    Note that bits 0 to 2 toggle each other when set. All other bits have to be reset individually, unless %False is specified for value in combination with %G_SSC_SET or %G_SSC_GETANDRESET. With %G_SSC_GET, you can pass any value for value since it is disregarded anyway.

    The value returned by the function will be an OR-ed combination of all these (bit) constants as well. If you use the function to set the flags, the value returned will reflect the previous state the flags were in. Normally the function is called directly from the midi-in callback procedure and the user is assumed to read the value returned. There is no penalty for setting the flags in your own code however.


    OUTPUT PROCEDURES

    general note: the first parameter always contains the midi-channel in the low nibble of its low byte. Valid midi channels always range from 0 to 15. The high byte of the first parameter (mostly indicated as k or kanaal) allows you to specify the selected midi port.


    AfterTouch (BYVAL kanaal AS WORD, BYVAL value?)

    Bend (BYVAL kanaal AS WORD,BYVAL lsb?, BYVAL msb?)

    &HE0 pitchbend (raw, 14bits)

    ClearMiBuf ()

    erase the midi input buffer. This is faster than flushing the midi input buffer by reading and removing all contents byte by byte.

    KeyPress (BYVAL kanaal AS WORD, BYVAL noot?, BYVAL value?)

    ModeMess (BYVAL kanaal WORD ,BYVAL ctrl?, BYVAL value?)


    NoteCentOff (BYVAL kanaal AS WORD,BYVAL noot!)

    NoteCentOn (BYVAL kanaal AS WORD, BYVAL noot!, BYVAL velo?)

    Coding: the integer part of noot is the note, the fractional part, the deviation in cents. Negative means: cents below the specified note, positive is always note + cents above it. The NoteCentOff procedure switches the note off and resets the pitchbend.


    NoteOff (BYVAL kanaal AS WORD,BYVAL noot?)

    Play (BYVAL kanaal AS WORD, BYVAL noot?, BYVAL velo?)

    ProgChange(BYVAL kanaal AS WORD ,BYVAL value?)

    ProgChangeEx (BYVAL kanaal AS WORD, BYVAL msblsb AS WORD, BYVAL value?)

    This is the extended version of the programm change procedure. It implements the often encountered selection of patches by using bank change commands using controll changes with controllers 0 and 32. Thus the msb for the selected bank should be packed in the high byte of msblsb, whereas the lsb for the bank should be packed in its low byte. The patch number should be passed in the parameter value?. On some synths this value should be the same as the value packed in the lsb of msblsb. Refer to your synths manual if in doubt.

    Proteuspatch (BYVAL kanaal AS WORD, BYVAL patchnumber AS WORD)

    Midi patch procedure for EMU Proteus 1/2/3/2000. Supports patches 0 ---> 191 or more, depending on what your EMU module has on board.

    BypassTSR24 (BYVAL k AS WORD)

    This macro switches the digitech TSR24 dsp processor (with PPI2 board dual DSP processor), to bypass mode using a sysex command.The k? parameter should correspond to the channel the processor is setup to listen to.


    ProtOFF (BYVAL k AS WORD)

    This macro switches off the midi channel passed, on a Proteus 1,2,3,2000 synthesizer.


    ProtON (BYVAL k AS WORD)

    This macro enables midi reception on the midi channel passed, for a Proteus 1,2,3,2000 synthesizer.


    InitP2M (Pitch2Midi$, BYVAL k AS WORD)

    This macro initializes a Roland GI10 pitch to midi convertor. It supports only roland GI10 sofar, but we count on generalizing the code to support more (and better...) devices.


    SysEx (BYVAL hMidiOut AS Long, Sysexstring$)

  • This procedure should be used to send long midi-messages -most of the time, these will be sys-ex, via the midi device driver. Prior to sending a sysex, the user should have selected and opened a midi-device. In other words, the public midi out handle (hmo) should exist. This handle should be passed to the procedure as the first parameter. The procedure code resides in the DLL.
  • The size of the string, and thus of the sysex message, is limited to 64kB. This is a limitation of the Win32Api, not of our code. If your application needs larger sys-ex's to be transmitted, split them in shorter chunks.
  • Do never declare the string to be send as ASCIIZ, because midi sysex strings may very well contain zero's, which would break of the string, and hence never transmit your sysex properly.
  • Building the string to be passed to this procedure is very straightforward, thanks to PowerBasic's extended CHR$() function:
  • Example:
    Sx$= CHR$(&HF0,&H43,&H20,&H15,&H10,127,&HF7)
    SysEx hmo, Sx$

  • AllNotesOff (k AS WORD)

  • This procedure sends the midi command to switch all notes off on a channel passed in the low nibble of k to the midi-output buffer. At the same time it performs a reset all controllers command. Of course the function will only work if your midi-system supports the command. Many older FM-synths don't...(Yamaha's infamous FB01 is just one example). The procedure does not reset the Task().Har().vel fields in the task structure. A more general procedure is offered in InstrumAllNotesOff().


  • SUB PlayHar (H AS HarmType, BYVAL k AS WORD)

    SUB InstrumAllNotesOff (BYREF instrument AS musician, BYVAL h AS LONG)


    SUB
    InstrumPlay (BYREF instrument AS musician, BYVAL h AS LONG)

    SUB GetInstrumentParams (Ins AS Musician, konstant AS DWORD)


    Filedate: 990301/last update:2004-10-30


    <Index > <Introduction> <General Functions>
    <Fuzzy Functions> <Analysis Functions> <Acoustical Functions>
    <Visualisation Functions> <File Management> <Spectral Functions>
    To homepage dr.Godfried-Willem RAES