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.