source: cmedia/trunk/Drv16/midipars.cpp@ 354

Last change on this file since 354 was 354, checked in by stevenhl, 17 years ago

Import untested baseline cmedia sources, work products and binaries
Binaries and work products should be deleted from repository.
once new builds are verified to work.

File size: 12.8 KB
Line 
1/* SCCSID = %W% %E% */
2/****************************************************************************
3 * *
4 * Copyright (c) IBM Corporation 1994 - 1997. *
5 * *
6 * The following IBM OS/2 source code is provided to you solely for the *
7 * the purpose of assisting you in your development of OS/2 device drivers. *
8 * You may use this code in accordance with the IBM License Agreement *
9 * provided in the IBM Device Driver Source Kit for OS/2. *
10 * *
11 ****************************************************************************/
12/**@internal %W%
13 * Midi parser - parses and interprets incoming Midi stream. Handles timing
14 * commands by adjusting MIDISTREAM::lwait variable. Handles MIDI channel
15 * and system commands by calls to MIDI hardware object that this stream is
16 * associated with.
17 * @version %I%
18 * @context
19 * Unless otherwise noted, all interfaces are Ring-0, 16-bit, kernel stack.
20 * @notes
21 * See midimsg.cpp file for description of the IBM Sysex command.
22 * @history
23 */
24
25#ifndef OS2_INCLUDED // Doesn't like being included twice.
26extern "C" { // 16-bit header files are not C++ aware
27 #define INCL_NOPMAPI
28 #include <os2.h>
29}
30#endif // end OS2_INCLUDED
31
32#include <string.h> // _fmemcpy()
33#include "midistrm.hpp" // Object definition MIDISTREAM
34#include "midimsg.hpp" // Object definition
35
36//#include "log.hpp" // Object definition
37//#include "logmsg.hpp" // Logging messages, trace def's
38
39
40/**@internal parse
41 * Parse the next byte of the input stream. If we form a complete message,
42 * then dispatch the message for interpretation.
43 * @param MIDIBTE bInput - next byte in the input stream
44 * @return void
45 * @notes This function reads and updates many of the data members in the
46 * MIDISTREAM object. A few of the most pertinent ones are listed next.
47 * @notes reads/updates PARSER_STATE MIDISTREAM::state - current state of
48 * the parser.
49 * @notes reads/updates MIDIMSG MIDISTREAM::message - current message that
50 * we're assembling.
51 * @notes reads/updates MIDISTREAM::lwait variable, based on receipt of 0xF8
52 * timing pulses.
53 */
54void MIDISTREAM::parse( MIDIBYTE bInput )
55{
56 MIDIBYTETYPE eInputType; // 'bInput' status byte type.
57
58 //--- Algorithm starts here. First check for 0xF8 status byte
59 // (timing pulse). If present, then process & return immediately.
60
61 if (bInput == (MIDIBYTE) 0xF8) { // Timing Clock, return.
62 lWait += ulPerClock;
63 return;
64 }
65
66 //--- Not a timing pulse, so continue parse of input stream based
67 // on state and input.
68
69 eInputType = eMidiByteType( bInput );
70 // What class of bype did we get?
71 // Returns member of MBT_* enumerated type.
72 while (TRUE) {
73 switch ( state ) {
74 case S_Init:
75 message.clear();
76 message.addByte( bInput );
77 switch ( eInputType ) {
78 case MBT_ChannelStatus:
79 state = S_ChannelMsg;
80 break;
81 case MBT_Sysex:
82 state = S_SysexSignature;
83 break;
84 case MBT_SystemCommon:
85 if (bInput != MidiByte_EOX) { // We discard EOX's in Init state, such EOX's
86 // mark end of Sysex's that we don't do.
87 state = S_SystemCommon;
88 if ( message.isComplete() ) {
89 dispatch( message );
90 state = S_Init;
91 }
92 }
93 break;
94 case MBT_SystemRT: // Always 1 byte messgaes, handle immediately
95 dispatch( message ); // with no state change.
96 message.clear();
97 break;
98 case MBT_Data: // Discard input, no state change.
99 default:
100 break;
101 } // switch on midi byte type
102 return; // end S_Init state changes.
103
104 case S_ChannelMsg:
105 // Currently parsing a channel message. Anything other than a data
106 // byte will reset us back into Init state. If we have a complete
107 // message, note that we save the status byte and then re-initialize
108 // the message with the same status byte. This implements the
109 // "Running Status" feature of the MIDI definition.
110
111 if ( eInputType != MBT_Data ) {
112 state = S_Init; // Continue from top of loop
113 continue; // in S_Init state.
114 }
115 else { // Data byte, no state change.
116 message.addByte( bInput );
117 if ( message.isComplete() ) {
118 MIDIBYTE runningStatus = message[0];
119 dispatch( message );
120 message.clear();
121 message.addByte( runningStatus );
122 // Remain in "ChannelMsg" state.
123 }
124 return;
125 }
126
127 case S_SysexSignature:
128 if ( eInputType != MBT_Data ) {
129 // We got a new status byte before receiving enough bytes to
130 // perform a check for the IBM signature.
131
132 // Flush the message buffer out to the hardware object.
133 message.flush( (MIDIAUDIO*) pahw);
134
135 // Return to S_Init state. If an EOX, write it out and
136 // start fresh with next byte; otherwise let's loop back
137 // and interpret this new status byte.
138 state = S_Init;
139 if ( bInput == MidiByte_EOX ) {
140 ((MIDIAUDIO*) pahw)->writeByte( MidiByte_EOX );
141 return;
142 }
143 else
144 continue; // Continue from top of loop with same input.
145 }
146 else { // Data byte, no state change.
147 message.addByte( bInput ); // Add byte message.
148 // If we've got the signature length, figure out if it's ours.
149 if ( message.length() >= IBMSysexSigLen ) {
150 if ( message.isIBMSignature() )
151 state = S_SysexIBM;
152 else {
153 // Not IBM Sysex, flush buffered message and change state.
154 message.flush((MIDIAUDIO*) pahw);
155 state = S_SysexNotIBM;
156 }
157 }
158 return;
159 }
160
161 case S_SysexIBM:
162 // We enter this state after recognizing the IBM Sysex in the message.
163 if ( eInputType != MBT_Data ) { // Not a data byte?
164 state = S_Init; // Discard buffered msg, reset to Init state
165 continue; // and continue from top of loop.
166 }
167 message.addByte( bInput ); // Add byte to message.
168 if (message.isUnsupported()) {
169 // We haven't implemented this IBM Sysex...
170/* pSoftError->vLog( MIDISTREAM_ERR_BadIBMSysex,
171 (BYTE) message[IBMSysexSigLen + 0],
172 (BYTE) message[IBMSysexSigLen + 1] );
173*/ state = S_Init;
174 return;
175 }
176 else if (message.isComplete()) {
177 // If we've got all the bytes...
178 dispatch( message ); // Interpret the message
179 state = S_Init; // Back to Init state.
180 }
181 return;
182
183 case S_SysexNotIBM:
184 // Passthru bytes until we hit a status byte.
185 if ( eInputType == MBT_Data ) {
186 ((MIDIAUDIO*) pahw)->writeByte( bInput );
187 return; // No state change.
188 }
189 else if ( bInput == MidiByte_EOX ) {
190 ((MIDIAUDIO*) pahw)->writeByte( MidiByte_EOX );
191 state = S_Init;
192 return; // Sysex completed, start from S_Init when next byte arrives.
193 }
194 else { // Unexpected status byte.
195 state = S_Init; // Continue from top of loop
196 continue; // in S_Init state.
197 }
198
199 } // switch (state)
200 } // while TRUE
201}
202
203
204/**@internal dispatch
205 * Dispatch the MIDI message to the appropriate MIDI hw object.
206 * @param MIDIMESSAGE message - a complete, well formed MIDI message.
207 * @return void
208 * @notes reads/updates MIDISTREAM::lwait variable, based on time
209 * compression commands.
210 * @notes Non-IBM Sysex messages are not handled. The parser handles
211 * these as soon as the non-IBM Sysex is recognized, by passing thru
212 * the bytes directly to the hardware object.
213 */
214void MIDISTREAM::dispatch( MIDIMSG& message )
215{
216 MIDIBYTE statusByte = message[0];
217 MIDIBYTE cmdByte; // Used only for Sysex case.
218 MIDIBYTETYPE statusType = eMidiByteType( statusByte );
219 BYTE mchan; // MIDI channel for Channel cmds.
220
221 switch( statusType ) {
222 case MBT_ChannelStatus :
223 mchan = statusByte & (BYTE) 0x0F;
224 switch( statusByte & (BYTE) 0xF0 ) {
225
226 case 0x80: // Note Off
227// pTrace->vLog( MIDISTREAM_NoteOff, (BYTE) mchan, (BYTE) message[1] );
228 ((MIDIAUDIO*) pahw)->noteOff( mchan, message[1], message[2] );
229// _notesOn[ (USHORT) message[1] ] &= ~(_usBitNumber[ mchan ]);
230 _notesOn[ (USHORT) message[1] ] &= ~(1 << mchan);
231 break;
232
233 case 0x90: // Note On
234// pTrace->vLog( MIDISTREAM_NoteOn, (BYTE) mchan, (BYTE) message[1] );
235 ((MIDIAUDIO*) pahw)->noteOn( mchan, message[1], message[2] );
236// _notesOn[ (USHORT) message[1] ] |= _usBitNumber[ mchan ];
237 _notesOn[ (USHORT) message[1] ] |= 1 << mchan;
238 break;
239
240 case 0xA0: // Polyphonic Key Pressure (Aftertouch)
241// pTrace->vLog( MIDISTREAM_Polyphonic,
242// (BYTE) mchan, (BYTE) message[1], (BYTE) message[2] );
243 ((MIDIAUDIO*) pahw)->polyphonicPressure( mchan, message[1], message[2] );
244 break;
245
246 case 0xB0: // Control Change
247// pTrace->vLog( MIDISTREAM_Control,
248// (BYTE) mchan, (BYTE) message[1], (BYTE) message[2] );
249 ((MIDIAUDIO *) pahw)->controlChange( mchan, message[1], message[2] );
250 break;
251
252 case 0xC0: // Program Change
253// pTrace->vLog( MIDISTREAM_ProgramChange, (BYTE) message[1] );
254 ((MIDIAUDIO *) pahw)->programChange( mchan, message[1] );
255 break;
256
257 case 0xD0: // Channel Pressure (Aftertouch)
258// pTrace->vLog( MIDISTREAM_ChannelPressure, (BYTE) mchan, (BYTE) message[1] );
259 ((MIDIAUDIO*) pahw)->channelPressure( mchan, message[1] );
260 break;
261
262 case 0xE0: // Pitch Bend
263// pTrace->vLog( MIDISTREAM_PitchBend,
264// (BYTE) mchan, (BYTE) message[1], (BYTE) message[2] );
265 ((MIDIAUDIO *) pahw)->pitchBend( mchan, message[1], message[2] );
266 break;
267
268 default:
269 //### Should never get here, should log soft error.
270 break;
271
272 } // End switch() for Channel messages.
273 break;
274
275 case MBT_SystemRT: // System Real Time: all single byte messages.
276// pTrace->vLog( MIDISTREAM_SystemRT, (BYTE) statusByte );
277 ((MIDIAUDIO*) pahw)->writeByte( statusByte );
278 break;
279
280 case MBT_SystemCommon: // System Common
281// pTrace->vLog( MIDISTREAM_SysCommon,
282// (BYTE) message[0], (BYTE) message[1], (BYTE) message[2] );
283 message.flush((MIDIAUDIO*) pahw);
284 break;
285
286 case MBT_Sysex: // Must be the IBM sysex
287 //### Should check relative freq. of each cmd. Currently coded to
288 //### favor Timing Compression (short), then TC (long), then DD cmds
289
290 message.discardSignature(); // Remove the IBM signature prefix.
291 cmdByte = message[0]; // 1st cmd byte of the IBM sysex.
292
293 if (cmdByte >= 7) { // Timing Compression (Short) [ 7-127 ]
294 // SvL
295 lWait += cmdByte * ulPerClock;
296// pTrace->vLog( MIDISTREAM_TimeCompressionS, lWait );
297 }
298
299 else if (cmdByte == 1) { // Timing compression (Long) [ 1 ]
300 //SvL
301 lWait += (((ULONG) (message[2] << 7)) + ((ULONG) message[1])) * ulPerClock;
302// pTrace->vLog( MIDISTREAM_TimeCompressionL, lWait );
303 }
304
305 else if (cmdByte != 3) // Sysex (ignored) [0 | 1 | 2 | 4 | 5 | 6]
306;// pTrace->vLog( MIDISTREAM_SysXIgnored, (BYTE) message[1], (BYTE) message[2] );
307
308 else if (message[1] == 1) { // PPQN Control [ 3 ] [ 1 ] [time ctl] [PPQN value]
309 usCPQN = message[3];
310 CalcDelay();
311// pTrace->vLog( MIDISTREAM_PPQN, usCPQN, ulPerClock );
312 // ### Time control flags in message[2] are ignored
313 }
314
315 else if (message[1] == 2) { // Tempo Control [ 3 ] [ 2 ] [ lsb ] [ msb ]
316 ulTempo = (((WORD) message[3]) << 7) + message[2];
317 CalcDelay();
318// pTrace->vLog( MIDISTREAM_TempoValue, ulTempo, ulPerClock );
319 // ### Tempo fade in message[4] is ignored
320 }
321 else // Sysex (ignored) [ 3 ] ^[ 1 | 2 ]
322;// pTrace->vLog( MIDISTREAM_SysXIgnored, (BYTE) message[1], (BYTE) message[2] );
323
324 break;
325 // End IBM Sysex message processing.
326
327 default:
328 // ### Should never get here, should log soft error.
329 break;
330 }
331}
332
333
Note: See TracBrowser for help on using the repository browser.