source: cmedia/trunk/Drv16/midistrm.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: 8.5 KB
Line 
1/* $Id: midistrm.cpp,v 1.2 2000/04/24 19:45:17 sandervl Exp $ */
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 * @notes
16 * MIDISTREAM class implementation. The Midi Stream class is derived
17 * from the Stream class.
18 * @version %I%
19 * @context Unless otherwise noted, all interfaces are Ring-0, 16-bit,
20 * kernel stack.
21 * @history
22 *
23 */
24#define INCL_NOPMAPI
25#define INCL_DOSERRORS // for ERROR_INVALID_FUNCTION
26#include <os2.h>
27#include <os2me.h>
28#include <audio.h> // for #define MIDI
29
30#include <include.h>
31#include <devhelp.h>
32
33#include "midistrm.hpp"
34#include "event.hpp"
35#include "maudio.hpp"
36
37#include <ossidc.h>
38#include <dbgos2.h>
39
40
41/**@internal CalcDelay
42 * @param None
43 * @return None
44 * @notes
45 * 600,000,000 microseconds/10 minutes
46 *---------------------------------------------------------- == X microseconds/clock
47 * usCPQNnum
48 * (ulTempo beats/10 min) * ( 24 * ------------- clocks/beat )
49 * usCPQNden
50 *
51 *
52 * 25,000,000 * usCPQNden
53 *== --------------------------
54 * ulTempo * usCPQNnum
55 *
56 * where
57 * usCPQNden = ((usCPQN & 0x3F) + 1) * 3
58 * usCPQNnum = 1 if bit 6 of usCPQN is set
59 *
60 * or
61 *
62 * usCPQNden = 1
63 * usCPQNnum = usCPQN + 1 if bit 6 is not set
64 */
65void MIDISTREAM::CalcDelay(void)
66{
67 ULONG ul;
68
69 if (usCPQN & 0x40) { // bit 6 is set if it's a denominator
70 ul = 25000000 * ((usCPQN & 0x3F) + 1);
71 ulPerClock = ul / ulTempo;
72// ulPerClock *= 3;
73 ulPerClock += ulPerClock << 1;
74 } else {
75 ul = ulTempo * (usCPQN+1);
76 ulPerClock = 25000000 / ul;
77 }
78}
79
80
81/**@internal MIDISTREAM::_allNotesOff
82 * Shut off all notes that are currently playing.
83 * @param None.
84 * @return void
85 * @notes This function walks the _notesOn array and shuts off any note
86 * that is flagged as being actively played.
87 */
88void MIDISTREAM::_allNotesOff( void )
89{
90 USHORT noteNum, mchan, chanmask;
91
92 for (noteNum = 0; noteNum < NUM_MidiNotes; ++noteNum)
93 if (_notesOn[noteNum])
94 // This note number is playing on one or more channels.
95 // Shut the note off on all channels on which it is playing.
96 for (mchan = 0; mchan < NUM_MidiChannels; ++mchan) {
97 chanmask = 1 << mchan;
98 if (_notesOn[noteNum] & chanmask) {
99 ((MIDIAUDIO*) pahw)->noteOff((BYTE)mchan, (BYTE)noteNum, 0 );
100 _notesOn[noteNum] &= ~chanmask;
101 }
102 }
103}
104
105
106/**@external MIDISTRM::Process
107 * Consume MIDI bytes from the MMPM/2 stream buffers and send
108 * them off to the MIDI parser to be interpreted.
109 * @param void
110 * @return void
111 * @notes Runs at Task time on a global context hook; does not run
112 * on an interrupt level. Interacts with the Timer object defined
113 * for this stream to obtain current time and to request next Stream
114 * time to be scheduled.
115 */
116void MIDISTREAM::Process( void )
117{
118 ULONG ulNewTime; // Time, in mSec, on entry.
119 ULONG ulElapsedTime; // Elapsed time, last tick to this one.
120 ULONG ulTimeNextRun;
121 PSTREAMBUFFER pStrmBuf;
122 PSTREAMBUF pData;
123
124 // Update time variables.
125 ulNewTime = GetCurrentTime();
126
127 ulElapsedTime =
128 ( ulNewTime > _ulLastProcess ) ? (ulNewTime - _ulLastProcess) : 0;
129
130 _ulLastProcess = ulCurrentTime = ulNewTime;
131
132// Rudi: check for !STREAM_STREAMING instead of STREAM_PAUSED
133 if (!qhInProcess.IsElements() || // no buffers to process?
134 usStreamState != STREAM_STREAMING) // is the stream paused?
135 return;
136
137 ProcessEvents();
138
139 lWait -= ulElapsedTime * 1000;
140
141 while (lWait <= 0 && (pStrmBuf = (PSTREAMBUFFER)qhInProcess.Head()) != NULL) {
142
143 pData = pStrmBuf->pBuffptr;
144
145 while (lWait <= 0) {
146 parse(pData[pStrmBuf->ulBuffpos++]);
147
148 if (pStrmBuf->ulBuffpos >= pStrmBuf->ulBuffsz) {
149 qhDone.PushOnTail(qhInProcess.PopHead());
150 break;
151 }
152 }
153 }
154
155 // Return finished buffer(s)
156 while (qhDone.IsElements())
157 ReturnBuffer();
158
159 // Determine next time to run. If we submit a time that has already
160 // passed, we'll get scheduled for the next tick.
161 ulTimeNextRun = ulCurrentTime + (lWait / 1000);
162 ((MIDIAUDIO*) pahw)->getTimer()->vSchedule( ulTimeNextRun );
163}
164
165
166// ### By getting Timer time, we're assuming that this stream owns the HW.
167ULONG MIDISTREAM::GetCurrentTime(void)
168{
169 return ((MIDIAUDIO*) pahw)->getTimer()->ulGetTime();
170}
171
172
173// ### By setting Timer time, we're assuming that this stream owns the HW.
174void MIDISTREAM::SetCurrentTime(ULONG time)
175{
176 ((MIDIAUDIO*) pahw)->getTimer()->vSetTime( time );
177 ulCurrentTime = time;
178
179dprintf(("SetCurrentTime: %ld", ulCurrentTime));
180}
181
182
183ULONG MIDISTREAM::Read(PSTREAMBUF, unsigned)
184{
185 return ERROR_INVALID_FUNCTION;
186}
187
188
189// Rudi: we need to do this multilpe times
190void MIDISTREAM::ResetParser(BOOL fFreshStart)
191{
192 if( fFreshStart ) {
193 usCPQN = 0; // default values
194 ulTempo = 1200;
195 CalcDelay(); // initialize ulPerClock
196 }
197
198 lWait = 0; // SvL, reset this too
199 _ulLastProcess = ulCurrentTime; // Rudi
200
201 state = S_Init; // Reset parser state.
202
203// message.clear(); // parse() does this ...
204}
205
206
207ULONG MIDISTREAM::StartStream(void)
208{
209 USHORT usOldState;
210
211dprintf(("MIDISTREAM::StartStream"));
212
213 if (usStreamState == STREAM_STREAMING) // already started
214 return ERROR_INVALID_SEQUENCE;
215
216 ResetParser(TRUE); // Rudi
217
218 usOldState = usStreamState;
219 usStreamState = STREAM_STREAMING;
220 if( !pahw->Start(this) ) {
221 usStreamState = usOldState;
222 return ERROR_START_STREAM;
223 }
224
225 OSS16_SetMidiOutVol(this, volume);
226 return NO_ERROR;
227}
228
229
230ULONG MIDISTREAM::StopStream(PCONTROL_PARM pControl)
231{
232 if (usStreamState == STREAM_STOPPED) // is the stream stopped ?
233 return ERROR_INVALID_SEQUENCE;
234
235 pahw->Stop(this);
236 _allNotesOff();
237 ReturnBuffers();
238
239 pControl->ulTime = GetCurrentTime();
240 usStreamState = STREAM_STOPPED;
241
242dprintf(("MIDISTREAM::StopStream %ld", pControl->ulTime));
243 return NO_ERROR;
244}
245
246
247ULONG MIDISTREAM::PauseStream(PCONTROL_PARM pControl)
248{
249// Rudi: allow Resume on suspend
250 if (usStreamState == STREAM_PAUSED) // is the stream paused ?
251 return ERROR_INVALID_SEQUENCE;
252
253 pahw->Pause(this);
254 _allNotesOff();
255
256 pControl->ulTime = GetCurrentTime();
257 usStreamState = STREAM_PAUSED;
258
259dprintf(("MIDISTREAM::PauseStream %ld", pControl->ulTime));
260 return NO_ERROR;
261}
262
263
264ULONG MIDISTREAM::ResumeStream(void)
265{
266 USHORT usOldState;
267
268dprintf(("MIDISTREAM::ResumeStream"));
269
270// Rudi: allow Resume on suspend
271 if (usStreamState == STREAM_STREAMING) // is the stream not running ?
272 return ERROR_INVALID_SEQUENCE;
273
274 ResetParser(FALSE); // Rudi
275
276 usOldState = usStreamState;
277 usStreamState = STREAM_STREAMING;
278 if( !pahw->Resume(this) ) {
279 usStreamState = usOldState;
280 return ERROR_START_STREAM;
281 }
282
283 OSS16_SetMidiOutVol(this, volume);
284 return NO_ERROR;
285}
286
287
288MIDISTREAM::MIDISTREAM(USHORT streamtype, USHORT filesysnum):
289 STREAM(streamtype, filesysnum)
290{
291 // Initialize tempo & scheduling information.
292 ulCurrentTime = 0;
293 ResetParser(TRUE);
294
295 // Reset our tracking of which notes are currently on.
296 memset(_notesOn, 0, sizeof(_notesOn));
297
298// Rudi: set stereo volume
299 volume = MAKE_VOLUME_LR(100, 100);
300}
301
302
303BOOL MIDISTREAM::SetProperty(int type, ULONG value, ULONG reserved)
304{
305 if( type == PROPERTY_VOLUME && volume != value ) {
306 volume = value; mixerchange = TRUE;
307 if( usStreamState == STREAM_STREAMING ) {
308 OSS16_SetMidiOutVol(this, volume);
309 }
310 return TRUE;
311 }
312
313 return STREAM::SetProperty(type, value, reserved);
314}
315
316
Note: See TracBrowser for help on using the repository browser.