Prof.Dr.Godfried-Willem RAES
<GMT> Library Reference Manual:
Module 7: Midi related procedures and 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?)
- This procedure receives note information and stores it in
a delay line with time values. The data can be retrieved using the function
ReadDelayLine (instance, deltatime, speed). The data is kept in the arrays
named and declared as DelayTimeArray() AS DWORD, DelayNoteArray() AS WORD:
these are and have to be global! Note that these arrays grow forever, once
started and fed with realtime data...
Make sure you have enough memory installed! It's a wise idea to erase the
arrays from memory if the program does no longer require them. A simple REDIM
DelayTimeArray(0 TO 0) does the job.
An example of a taskprocedure that writes to the delay line and takes its
input from real-time midi may look like this:
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!)
instance : 0 to 255 - there can be many simultaneous
readers active (up to 255 !)
delaytime in milliseconds,
speed: 1= normal, <1= slower (0.5= halfspeed). >1 faster ( 1.5= DOUBLE
speed).
The function will allow you to read backwards in time also. This is achieved
by
specifying negative values for speed.
For example, -1 reads backwards at original speed starting on the moment the
function is first called.
This function returns a note/velo couple packed in an integer (msb-lsb format)
read from the delayline at the position delaytime?? from NOW (real time).
The delaytime should be given in milliseconds.
If no note was found in the delayline at the given position, -1 is returned.
The procedure uses the arrays DelayTimeArray() and DelayNoteArray() that are
global, but only dimensioned on their first use in WriteDelayLine%. Hence
a fatal error will occur if you attempt to call ReadDelayLine before a WriteDelayline
call has happened. If speed! = 1! the reads will we time synchronous and we
can realize a constant time delayline. If the delaytime specified is larger
than the earliest data in the delayline, the function will return -1 until
the line contains enough data.
If speed! > 1 the reads will be faster than original real-time. When now
is reached, the functions returns -2. It is up to the user to either stop
the function or to reset it.
If speed? < 1 the read will be slower than the original time. As a consequence,
this type of delay never ends, unless the user stops calling the function
with a reset.
Resetting the values for a given instance of a delayread procedure should
be performed by specifying zero values for both the delaytime and the speed.
So, dummy%= ReadDelayLine% (instancenr?, 0??, 0!) causes a complete reset.
There can be many delayline readers (max. 255 instances) active at the same
time. This makes implementing multitaps, loop feedback etc. possible.
Example of a
taskprocedure using a delayline:
SUB DelayPlayerBackN ()
' this taskprocedure demonstrates how to build
a delay-line for midi input. This code plays backwards.
LOCAL nv%, noot?, velo?, tasknr%
STATIC oldnoot?, oldvelo?
- tasknr%= 25
' defined as task nr. 25
nv% = ReadDelayLine%(4, 6000, -1!) :' instance 4
of a delaylinereader
:' 6 second delay = 6000 milliseconds
:' > 1 speed-up
:' < 1 slow down
:' -1! is negative, so we do backwards playing
SELECT CASE nv%
CASE %False,%True : EXIT SUB
CASE -2
' read past present.
' This can only happen if speed! > 1!
nv% = ReadDelayLine%(2, 0, 0) :' force a reset
'Task(tasknr%).switch = %False
'Menuscreen tasknr%
EXIT SUB
CASE -3
' read past delay in backwardsmode
' we can force a reset:
nv% = ReadDelayLine%(4,0,0)
Task(tasknr%).Har(0).vel= STRING$(128,0)
PlayHar Task(tasknr%).Har(0), Task(tasknr%).channel
' we could of course also switch the task
off...
'Task(tasknr%).switch = %False
END SELECT
velo? = nv% AND &H007F
SHIFT RIGHT nv%, 8
noot? = nv% AND &H007F
IF velo? = %False THEN
DelNote2Har Task(tasknr%).Har(0), BYCOPY noot?
PlayHar Task(tasknr%).Har(0), Task(tasknr%).channel
ELSE
DelNote2Har Task(tasknr%).Har(0), BYCOPY noot?
PlayHar Task(tasknr%).Har(0), Task(tasknr%).channel
AddNote2Har Task(tasknr%).Har(0), BYCOPY noot?, BYCOPY velo?
PlayHar Task(tasknr%).Har(0) , Task(tasknr%).channel
END IF
END SUB
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)
- k = requested midi-channel packed on the low nibble. The
midiport is packed in HIBYT(k)
c?= controller number to trace
flags: %OLDEST ,%NEWEST, %REMOVE combined with OR.
The first function reads either the oldest or the most recent
midi-controller information from the midi-in buffer. The channel to read
controller information from, should be passed to the function (k parameter,
word). The range for k is limited to the range for valid midi-channels:
0-15. The number of the controller to read should also be passed to the
function as parameter c?. All legal midi controller numbers can be accepted.
Check you midi equipment to know what controllers it sends out. Controller
nr.1 is generally mapped to reflect the position of the modulation wheel
on keyboard synths.
FLAGS USED WITH MIDI INPUT FUNCTION:
The last parameter -common to all Get... functions used
for midi input- serves as a bit flag, and makes use of the predefined flag
constants:
- %OLDEST = &H8000% (setting bit 15 in an integer,
and thus making the number negative)
- %NEWEST = 1 (setting bit 0 in an integer)
- %REMOVE = 2 (setting bit 1 in an integer)
If flags = %REMOVE OR %OLDEST [binary: 1000 0000 0000 0010]
- The function removes the data returned, from the midi
input buffer. Any subsequent call, will return a new and more recent value,
until no more midi messages from the given channel are found in the buffer.
If flags = %OLDEST [binary: 1000 0000 0000 0000]
- The function leaves the midibuffer intact. A second call
to the function will return the same value. This mode can be used to peek
in the buffer, without actually doing something with the data.
If flags = %NEWEST [binary 0000 0000 0000 0001]
- The function leaves the midibuffer intact.A second call
to the function will return the same value, unless new data have flown
in. This mode can be used to return always the most recent message in
the buffer. The user should realize that this way, information may get
skipped or lost! without actually doing something with the data.the function
retrieve resp. the most recent information found in the buffer
If flags = %REMOVE OR %NEWEST [binary: 0000 0000 0000 0011]
- The function removes the data returned, from the midi
input buffer. Any subsequent call, will return a new value, until no more
midi messages from the given channel are found in the buffer. Be carefull
using this mode, since it may very well reverse the chronologic order
or midi messages: return values can be either the most recent message
that was received, or the message one older than the message read previously!
The function returns a single Midi-byte value (7-bit number)
as an integer. If no information was found in the midi input buffer, the
function returns -1. Thus the data can be retrieved by looking into the
low byte of the return value:
GetController% (k?,c?,%REMOVE OR %OLDEST)
If LOBYT(nv%) AND %d7 THEN
... no byte available
ELSE
... the data byte in in LOBYT(nv%)
END IF
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:
- %G_SSC_SET
- %G_SSC_GET
- %G_SSC_GETANDRESET
The value passed to the function must be one of the following
declared constants:
- %False
- %MIDI_STOP_RECEIVED 'bit 0
- %MIDI_START_RECEIVED 'bit 1
- %MIDI_CONT_RECEIVED 'bit 2
- %MIDI_TRQ_RECEIVED 'bit 3
- %MIDI_UD241_RECEIVED 'bit4
- %MIDI_UD244_RECEIVED 'bit 5
- %MIDI_UD245_RECEIVED 'bit 6
- %MIDI_UD249_RECEIVED 'bit 7
- %MIDI_UD253_RECEIVED 'bit 8
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)
- plays contents of har.vel via channel LOBYT(k). This is a
fully polyphonic procedure. It internally handles the switching off of notes
as well as the restarting of notes with a new velocity value.
SUB InstrumAllNotesOff (BYREF instrument AS musician, BYVAL h AS LONG)
- This procedure sends midi note-off commands for all notes
that may be ON within the range set in the lowtes and hightes field of the
structure 'musician' passed via instrument. The channel should also be set
in the appropriate field. (cfr. gmt_types.bi for a complete structure declaration.
The procedure does reset the Instrument.Har(0).vel and Instrument.Har(1).vel
fields in the musician structure. If the Har(0) vel contained a harmony string
on init, the Instrument.Dur(0) field will contain the duration of the harmonic
constellation just switched off, expressed in milliseconds. Instrument.Dur(1)
will return the clocktime in ms the procedure was called. Note that the user
has to pass the midi output handle to the procedure. If the handle is invalid,
the procedure will fail. The procedure resides in the gmt dll library.
SUB InstrumPlay (BYREF instrument AS musician, BYVAL h AS LONG)
- This procedure plays all different notes found in instrument.Har(1).vel
(the notes to play/change) using instrument.Har(0).vel as a comparison base
(previous chord/notes). When done, it moves the played notes and thus the
sounding situation to instrument.Har(0).vel, returning a blank instrument.Har(1).vel.
The procedure also fills the Dur(0), Dur(1) fields . Only notes within the
range defined for the instrument passed will be considered. This range is
passed in the fields instrument.lowtes and instrument.hightes. The midi channel
used is passed in instrument.channel.This is a fully polyphonic procedure.
It internally handles the switching off of notes as well as the restarting
of notes with a new velocity value.
Dur(0) will contain the time in ms. that the Har(0) passed on input has been
sounding. (absolute duration, not clocktime)
Dur(1) will contain the clocktime in ms. when Har(1)passed on input was switched
on.
If the Har() string was identical to the previous one, the values will not
change, as no output has to take place.
- The instrument structure should have been initialized and
declared prior to calling the procedure. Note that you also have to pass the
windows handle for the midi output device. If the handle is invalid, the procedure
will fail.
- The procedure resides in the dll library.
SUB GetInstrumentParams (Ins AS Musician, konstant AS DWORD)
- This procedure replaces the former (before version 4.0) general
call to InitInstruments in GMT main. Instruments are no longer declared as
global, this way we could save quite some memory.
Now a composition needing these instrumentation data, has to do:
DIM yourinstrument AS GLOBAL musician
GetInstrumentParams yourinstrument, instrument-constant
- After this call the structures fields naam, lowtes, hightes,
centr, minvel, minduur, maxduur, polyphony, will be filled with the 'normal'
compositional defaults. You have to set the midi channel in instrument.channel.
If the procedure returns a number in the patch field, it will be either the
general midi patch number, or a number used by EMU proteus synths. Of course
users are free to fill in any values for their own applications. The instrument.dur(1)
field will contain the clocktime in ms the procedure was called.
You should use the constants as defined in GMT_kons.bi (cfr. instrumental
section in the file). The procedure resides in the DLL library to use with
GMT.
Filedate: 990301/last update:2004-10-30