source: sbliveos2/trunk/drv16/midipars.cpp@ 188

Last change on this file since 188 was 147, checked in by sandervl, 25 years ago

Fixed wave volume, recording gain + wave recording

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