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 |
|
---|