[3ae31e9] | 1 | HOW IT WORKS
|
---|
| 2 |
|
---|
| 3 | MIDIVIEW is written in Megamax C, the language of choice among ST
|
---|
| 4 | developers, though it can be easily modified to work with any C that
|
---|
| 5 | has hooks to GEM and the XBIOS functions. The program was my first
|
---|
| 6 | attempt at programming in C, as well as my first serious ST MIDI
|
---|
| 7 | program (can any ST BASIC program be considered serious?), a double
|
---|
| 8 | challenge that was made much simpler by the convenience of the ST's
|
---|
| 9 | built in MIDI ports, and much tougher by the lack of good
|
---|
| 10 | documentation on using GEM and accessing the MIDI ports. The two
|
---|
| 11 | toughest parts of the program were modifying the ST's MIDI buffer and
|
---|
| 12 | figuring out which GEM calls to use to start and end the program, but
|
---|
| 13 | once these chores were done, the program practically wrote itself,
|
---|
| 14 | according to the rules set down in the MIDI Detailed Specification.
|
---|
| 15 |
|
---|
| 16 | The function main() calls a series of preparatory functions before
|
---|
| 17 | calling view_midi(), which is the heart of the program. init_gem()
|
---|
| 18 | does the minimal amount of work needed to get a simple program like
|
---|
| 19 | this started from the desktop, and is not much different than what's
|
---|
| 20 | used it other programs of this sort. midi_buffer(), which establishes
|
---|
| 21 | a new and larger buffer for the ST's MIDI input is a much more
|
---|
| 22 | interesting function, and is explained in detail below. The calls to
|
---|
| 23 | form_alert(), v_hide_c(), and v_clrwk() are standard GEM syntax, and
|
---|
| 24 | are explained fully in the Megamax documentation. The two Cconout()
|
---|
| 25 | calls send an "Esc H" sequence to the ST's keyboard processor, which
|
---|
| 26 | positions the text cursor in the upper left corner of the screen, and
|
---|
| 27 | flush_buffer() clears out the contents of the MIDI buffer before
|
---|
| 28 | view_midi() is called. When you're done examining MIDI data, the
|
---|
| 29 | program restores the original MIDI buffer, and ends with the usual GEM
|
---|
| 30 | calls.
|
---|
| 31 |
|
---|
| 32 | Since the program displays data much more slowly than it can be
|
---|
| 33 | received via MIDI, a relatively large input buffer is needed to avoid
|
---|
| 34 | an overflow situation. The ST's default MIDI buffer is a paltry 128
|
---|
| 35 | bytes, hardly up to the task, but it is possible to modify the size
|
---|
| 36 | and location of this buffer using the XBIOS function Iorec().
|
---|
| 37 | midi_buffer() handles the task of allocating memory for the new
|
---|
| 38 | buffer, saving the location and size of the old buffer, and telling
|
---|
| 39 | the op system to use the new buffer. Similarly, old_buffer() undoes
|
---|
| 40 | the work of midi_buffer(). MIDIVIEW uses the ST's maximum 32K MIDI
|
---|
| 41 | buffer size, though you can probably get by with less in most cases.
|
---|
| 42 |
|
---|
| 43 | view_midi(), which comprises the program's main loop, is made up of
|
---|
| 44 | two nested while() loops, one of which watches for the Control key to
|
---|
| 45 | be pressed, while the other looks for the Alternate key. The GEM
|
---|
| 46 | function graf_mkstate() modifies the external variable gr_mkkstate,
|
---|
| 47 | which holds the status of the Control, Alternate, and Shift keys, as
|
---|
| 48 | well as the left and right mouse buttons. As long as the Control key
|
---|
| 49 | is not pressed, the outer loop is executed, and if the Alternate key
|
---|
| 50 | is pressed, the program goes into a very tight loop and waits for the
|
---|
| 51 | key to be released. Next the status of the Shift keys are checked,
|
---|
| 52 | and if one was pressed, the flag for the Active Sense/Clock filter is
|
---|
| 53 | toggled. This part of the function also checks to make sure that the
|
---|
| 54 | current keyboard state is not the same as it was the last time through
|
---|
| 55 | the loop, which would result in the filter flag being toggled
|
---|
| 56 | repeatedly and unpredictably.
|
---|
| 57 |
|
---|
| 58 | With the keyboard thoroughly scanned, the program checks the ST's MIDI
|
---|
| 59 | input for unprocessed data. If a byte has been received, from_midi()
|
---|
| 60 | is called, which gets a word from the input buffer and masks the upper
|
---|
| 61 | byte, thereby converting the number to the standard eight bit format
|
---|
| 62 | used by MIDI. If filtering is turned off, or if the byte is not a
|
---|
| 63 | Clock or Active Sense message, the program calls print_midi(), which
|
---|
| 64 | processes the data with a call to interpret(), and then prints the
|
---|
| 65 | data byte, its index, and its interpretation.
|
---|
| 66 |
|
---|
| 67 | interpret(), in its turn, simply determines whether the byte is a
|
---|
| 68 | status byte or a data byte, before calling a function to interpret the
|
---|
| 69 | byte. The job of actually translating each byte to an appropriate
|
---|
| 70 | English description is handled by the functions num_to_status(),
|
---|
| 71 | num_to_note(), num_to_cc(), num_to_pb(), num_to_spp(), and
|
---|
| 72 | type_to_text(), each of which operates on a different type of data.
|
---|
| 73 | Unfortunately, the arrangement of status bytes, message lengths, and
|
---|
| 74 | meanings doesn't really exhibit any helpful patterns in MIDI, so there
|
---|
| 75 | is no simple way to index the various interpretations according the
|
---|
| 76 | the current status byte.
|
---|
| 77 |
|
---|
| 78 | Rather than construct some elegant scheme to allow the use of
|
---|
| 79 | indexing, I used a brute force method based around the switch()
|
---|
| 80 | statement and arbitrarily assigned a type number to groups of
|
---|
| 81 | otherwise unrelated messages that have similar interpretations. For
|
---|
| 82 | instance, the data bytes of a Program Change, Song Select, Channel
|
---|
| 83 | Pressure, or System Exclusive message are best represented as a number
|
---|
| 84 | between 0 and 127, so these messages are grouped together as type 4.
|
---|
| 85 | When a type 4 data byte is received, the function type_to_text is
|
---|
| 86 | called, which prints a generic label for data bytes of a number of
|
---|
| 87 | types that don't require much in the way of processing. Many data
|
---|
| 88 | types are handled by a single function this way, simply to keep the
|
---|
| 89 | number of functions to a manageable level.
|
---|
| 90 |
|
---|
| 91 | The program also maintains one other external variable that modifies
|
---|
| 92 | the interpretation of a data byte, called byte_num, which simply
|
---|
| 93 | counts the number of bytes in the current message, based on the
|
---|
| 94 | requirements of the most recent status byte. byte_num is reset to 1
|
---|
| 95 | whenever a status byte is received, unless the status byte is a System
|
---|
| 96 | Real Time message. The MIDI spec allows timing-related messages as
|
---|
| 97 | Start, Stop, and Clock to be inserted in the middle of a running
|
---|
| 98 | status message, without the need to send a new status byte, though few
|
---|
| 99 | instruments actually use this "loophole", and some machines seem to
|
---|
| 100 | have occasional problems with it.
|
---|
| 101 |
|
---|
| 102 | Of the interpretation functions themselves, only num_to_pb() and
|
---|
| 103 | num_to_spp(), which handle Pitch Bend and Song Pointer messages, hold
|
---|
| 104 | any surprises. The two data bytes for these message types are
|
---|
| 105 | combined by the program to produce a single interpretation. Pitch
|
---|
| 106 | Bend data bytes are simply converted to a 14 bit signed integer, but
|
---|
| 107 | Song Pointer messages are translated to a measure:beat:step format.
|
---|
| 108 | The function assumes the standard MIDI clock resolution of 24 PPQN
|
---|
| 109 | (pulses per quarter note) is in use, and that a measure consists of
|
---|
| 110 | four quarter notes -- both of which are valid assumptions for 95% of
|
---|
| 111 | all music being made today, but which could lead to mild confusion in
|
---|
| 112 | other cases.
|
---|
| 113 |
|
---|
| 114 |
|
---|
| 115 |
|
---|
| 116 |
|
---|
| 117 |
|
---|
| 118 |
|
---|
| 119 |
|
---|
| 120 |
|
---|
| 121 |
|
---|
| 122 |
|
---|
| 123 |
|
---|
| 124 |
|
---|
| 125 |
|
---|
| 126 |
|
---|
| 127 |
|
---|
| 128 |
|
---|
| 129 |
|
---|
| 130 |
|
---|
| 131 |
|
---|
| 132 |
|
---|