HOW IT WORKS

MIDIVIEW is written in Megamax C, the language of choice among ST
developers, though it can be easily modified to work with any C that
has hooks to GEM and the XBIOS functions.  The program was my first
attempt at programming in C, as well as my first serious ST MIDI
program (can any ST BASIC program be considered serious?), a double
challenge that was made much simpler by the convenience of the ST's
built in MIDI ports, and much tougher by the lack of good
documentation on using GEM and accessing the MIDI ports.  The two
toughest parts of the program were modifying the ST's MIDI buffer and
figuring out which GEM calls to use to start and end the program, but
once these chores were done, the program practically wrote itself,
according to the rules set down in the MIDI Detailed Specification.

The function main() calls a series of preparatory functions before
calling view_midi(), which is the heart of the program.  init_gem()
does the minimal amount of work needed to get a simple program like
this started from the desktop, and is not much different than what's
used it other programs of this sort.  midi_buffer(), which establishes
a new and larger buffer for the ST's MIDI input is a much more
interesting function, and is explained in detail below.  The calls to
form_alert(), v_hide_c(), and v_clrwk() are standard GEM syntax, and
are explained fully in the Megamax documentation.  The two Cconout()
calls send an "Esc H" sequence to the ST's keyboard processor, which
positions the text cursor in the upper left corner of the screen, and
flush_buffer() clears out the contents of the MIDI buffer before
view_midi() is called.  When you're done examining MIDI data, the
program restores the original MIDI buffer, and ends with the usual GEM
calls.

Since the program displays data much more slowly than it can be
received via MIDI, a relatively large input buffer is needed to avoid
an overflow situation.  The ST's default MIDI buffer is a paltry 128
bytes, hardly up to the task, but it is possible to modify the size
and location of this buffer using the XBIOS function Iorec().
midi_buffer() handles the task of allocating memory for the new
buffer, saving the location and size of the old buffer, and telling
the op system to use the new buffer.  Similarly, old_buffer() undoes
the work of midi_buffer().  MIDIVIEW uses the ST's maximum 32K MIDI
buffer size, though you can probably get by with less in most cases.

view_midi(), which comprises the program's main loop, is made up of
two nested while() loops, one of which watches for the Control key to
be pressed, while the other looks for the Alternate key.  The GEM
function graf_mkstate() modifies the external variable gr_mkkstate,
which holds the status of the Control, Alternate, and Shift keys, as
well as the left and right mouse buttons.  As long as the Control key
is not pressed, the outer loop is executed, and if the Alternate key
is pressed, the program goes into a very tight loop and waits for the
key to be released.  Next the status of the Shift keys are checked,
and if one was pressed, the flag for the Active Sense/Clock filter is
toggled.  This part of the function also checks to make sure that the
current keyboard state is not the same as it was the last time through
the loop, which would result in the filter flag being toggled
repeatedly and unpredictably.

With the keyboard thoroughly scanned, the program checks the ST's MIDI
input for unprocessed data.  If a byte has been received, from_midi()
is called, which gets a word from the input buffer and masks the upper
byte, thereby converting the number to the standard eight bit format
used by MIDI.  If filtering is turned off, or if the byte is not a
Clock or Active Sense message, the program calls print_midi(), which
processes the data with a call to interpret(), and then prints the
data byte, its index, and its interpretation.

interpret(), in its turn, simply determines whether the byte is a
status byte or a data byte, before calling a function to interpret the
byte.  The job of actually translating each byte to an appropriate
English description is handled by the functions num_to_status(),
num_to_note(), num_to_cc(), num_to_pb(), num_to_spp(), and
type_to_text(), each of which operates on a different type of data.
Unfortunately, the arrangement of status bytes, message lengths, and
meanings doesn't really exhibit any helpful patterns in MIDI, so there
is no simple way to index the various interpretations according the
the current status byte.

Rather than construct some elegant scheme to allow the use of
indexing, I used a brute force method based around the switch()
statement and arbitrarily assigned a type number to groups of
otherwise unrelated messages that have similar interpretations.  For
instance, the data bytes of a Program Change, Song Select, Channel
Pressure, or System Exclusive message are best represented as a number
between 0 and 127, so these messages are grouped together as type 4.
When a type 4 data byte is received, the function type_to_text is
called, which prints a generic label for data bytes of a number of
types that don't require much in the way of processing.  Many data
types are handled by a single function this way, simply to keep the
number of functions to a manageable level.

The program also maintains one other external variable that modifies
the interpretation of a data byte, called byte_num, which simply
counts the number of bytes in the current message, based on the
requirements of the most recent status byte.  byte_num is reset to 1
whenever a status byte is received, unless the status byte is a System
Real Time message.  The MIDI spec allows timing-related messages as
Start, Stop, and Clock to be inserted in the middle of a running
status message, without the need to send a new status byte, though few
instruments actually use this "loophole", and some machines seem to
have occasional problems with it.

Of the interpretation functions themselves, only num_to_pb() and
num_to_spp(), which handle Pitch Bend and Song Pointer messages, hold
any surprises.  The two data bytes for these message types are
combined by the program to produce a single interpretation.  Pitch
Bend data bytes are simply converted to a 14 bit signed integer, but
Song Pointer messages are translated to a measure:beat:step format.
The function assumes the standard MIDI clock resolution of 24 PPQN
(pulses per quarter note) is in use, and that a measure consists of
four quarter notes -- both of which are valid assumptions for 95% of
all music being made today, but which could lead to mild confusion in
other cases.




















