source: sbliveos2/trunk/drv16/wavestrm.cpp@ 191

Last change on this file since 191 was 188, checked in by sandervl, 24 years ago

joystick reactivated; version increase; Rudi's fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.6 KB
Line 
1/* $Id: wavestrm.cpp 188 2001-09-09 15:31:35Z 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 * @notes
16 * @version %I%
17 * @context Unless otherwise noted, all interfaces are Ring-0, 16-bit,
18 * <stack context>.
19 * @history
20 *
21 */
22#define INCL_NOPMAPI
23#define INCL_DOSERRORS // for ERROR_INVALID_FUNCTION
24#include <os2.h>
25#include <os2me.h>
26#include <audio.h> // for #define MIDI
27#include <include.h>
28
29#include "wavestrm.hpp"
30#include "audiohw.hpp"
31#include "waudio.hpp"
32#include "memutil.h"
33#include <ossidc.h>
34#include <dbgos2.h>
35#include "ioctl.h"
36
37#ifndef min
38#define min(a,b) (a>b) ? b : a
39#endif
40
41//
42// _vRealignBuffer
43// called just after a wave stream pause on a playback.
44// Gets the end position of the stream when paused and a pointer to a
45// STREAMBUFFER. Basicly this function looks at the streambuffer and if
46// there is any unplayed data in it it adjusts the bufpos counter.
47// the donepos counter is ALWAYS set to zero. It will return 0 if all
48// the data has been played and 1 if there is still some data left.
49//
50USHORT WAVESTREAM::_vRealignBuffer(ULONG FAR *bytesinc, PSTREAMBUFFER pbuffer)
51{
52 // if none of the data in this stream buffer has been consumed
53 if (!*bytesinc) {
54 pbuffer->ulDonepos = 0;
55 pbuffer->ulBuffpos = 0;
56 return 1;
57 }
58
59 pbuffer->ulDonepos += *bytesinc;
60 pbuffer->ulBuffpos = pbuffer->ulDonepos;
61 *bytesinc = 0;
62 if(pbuffer->ulDonepos >= pbuffer->ulBuffsz) {
63 //calc position in next buffer
64 *bytesinc = pbuffer->ulDonepos - pbuffer->ulBuffsz;
65 return 0; //all of the buffer has been consumed
66 }
67 return 1;
68}
69//
70// _vRealignPausedBuffers(void)
71// when a stream is paused we need to "realign" the data in the audio buffer
72// with reality. On playback, not all the data in the audio buffer has been
73// consumed. Likewise on a capture, not all the good data in the audio buffer
74// has been copied out. After receiving the DDCMDCONTROL Pause we will call
75// this function to line the MMPM buffers back up.
76// there are 2 cases here: first one is the case of a capture stream.
77// for a capture stream we simply read any data that is still in the audio
78// buffer into a MMPM buffer.
79// for a playback stream things are not so straight forward.
80// first check the STREAMBUFFER on pHead to see if any of it's data is in the
81// audio buffer and not consumed, if yes back up the ulBuffpos in the
82// STREAMBUFFER. Next check any STREAMBUFFERS on pdone starting with the last
83// one. (the one on the tail of the queue) If necessary back up the ulBuffpos
84// and put the STREAMBUFFER on the Head queue.
85//
86void WAVESTREAM::_vRealignPausedBuffers(ULONG endpos)
87{
88 PSTREAMBUFFER ptempbuff;
89
90 switch (ulStreamType & STREAM_WRITE) {
91 case STREAM_READ:
92 //SvL: Don't get the lastest recording data as a read command
93 // would now restart recording (as it's stopped)
94 // Just return what we've got or push the buffer on the inprocess queue
95 ptempbuff = (PSTREAMBUFFER)qhDone.Head();
96 if(ptempbuff) {
97 if(ptempbuff->ulBuffpos) {//if we recorded anything into this buffer, then return it now
98 ReturnBuffer();
99 return;
100 }
101 ptempbuff->ulBuffpos = 0;
102 ptempbuff->ulDonepos = 0;
103 qhInProcess.PushOnHead(qhDone.PopHead());
104 }
105 break;
106
107 case STREAM_WRITE:
108 {
109 PQUEUEHEAD pTempHead = new QUEUEHEAD;
110 ULONG bytesinc;
111 USHORT usRC;
112
113 bytesinc = endpos - _ulBytesProcessed;
114 bytesinc &= 0xFFFFFFFC; //keep it on a dword boundary
115
116 // if there are bufferes on the done queue, pop them off the head and
117 // push them on the head of qhTempHead. This will reorder them so
118 // that the more recently used ones will be in the front of the queue.
119 // Pass them all to _vRealignBuffer. If the rc from _vRealignBuffer is
120 // 0 then there is no unprocessed data in the buffer (it is ready to
121 // be returned) so put it on the Tail of the done queue.
122 // If the rc is 1 then put it on the head of the InProcess queue.
123
124 while (qhDone.IsElements()) {
125 pTempHead->PushOnTail(qhDone.PopHead());
126 } /* endwhile */
127
128 while (qhInProcess.IsElements()) {
129 pTempHead->PushOnTail(qhInProcess.PopHead());
130 } /* endwhile */
131
132 while(pTempHead->IsElements()) {
133 usRC = _vRealignBuffer(&bytesinc, (PSTREAMBUFFER)pTempHead->Head());
134 if (usRC) {
135 qhInProcess.PushOnTail(pTempHead->PopHead());
136 }
137 else {
138 qhDone.PushOnTail(pTempHead->PopHead());
139 }
140 } /* endwhile */
141 if(qhDone.IsElements())
142 ReturnBuffer();
143
144 delete pTempHead; // free the memory this ain't no Java here !!
145 break;
146 }
147 default:
148 break;
149 } /* endswitch */
150}
151//
152// get ready to start streaming
153// this requires the following:
154// call Initbuffer in the audiobuffer object
155// if this is a write stream call _vFillAudioBuf
156//
157#pragma off (unreferenced)
158void WAVESTREAM::AddBuffers(BOOL fFirst)
159#pragma on (unreferenced)
160{
161 ULONG space, byteswritten;
162
163 if (ulStreamType & STREAM_WRITE) {
164 if(!qhInProcess.Head() && !qhDone.Head()) {
165 //underrun: stop playback
166 dprintf(("underrun: stop playback"));
167 pahw->Stop(this);
168 fUnderrun = TRUE;
169 return;
170 }
171 space = OSS16_StreamGetSpace(this);
172 while(space) {
173 byteswritten = AddBuffer(space);
174 if(byteswritten == (ULONG)-1) break;
175 space -= byteswritten;
176 }
177 }
178}
179
180//
181// write one buffer to the audio buffer
182// the caller of this function MUST make sure it ok to write the audio buffer..
183// _AudioBufWrite will not check if there is room in the audio buffer of if
184// there are buffers on pHead... BEWARE
185//
186ULONG WAVESTREAM::AddBuffer(ULONG space)
187{
188 PSTREAMBUFFER pTemp = (PSTREAMBUFFER)qhDone.Tail();
189 ULONG pdataBuf;
190 ULONG Buff_left, byteswritten;
191
192 if(!pTemp || pTemp->ulBuffpos >= (pTemp->ulBuffsz & 0xFFFFFFFC)) {
193 pTemp = (PSTREAMBUFFER)qhInProcess.Head();
194 }
195 if(!pTemp) {
196 dprintf4(("AddBuffer: pTemp == NULL"));
197 return (ULONG)-1;
198 }
199
200 // get the buffer pointer and amount of data remaining
201 pdataBuf = (ULONG)pTemp->pBuffptr + pTemp->ulBuffpos;
202 Buff_left = pTemp->ulBuffsz - pTemp->ulBuffpos;
203
204 if(Buff_left) {
205 // write the audio buffer
206 Buff_left = min(Buff_left, space);
207 byteswritten = OSS16_StreamAddBuffer(this, pdataBuf, Buff_left);
208 if(byteswritten == 0) {
209 return (ULONG)-1; //no more room
210 }
211
212 // update the buffer pos counter
213 pTemp->ulBuffpos += byteswritten;
214 }
215 else byteswritten = 0;
216
217 if(pTemp == qhInProcess.Head()) {
218 qhDone.PushOnTail(qhInProcess.PopHead());
219 }
220 dprintf4(("AddBuffer %lx size %d, bytes written %d", pdataBuf, (USHORT)Buff_left, (USHORT)byteswritten));
221 return byteswritten;
222}
223
224// Read data from the audio Buffer.
225// Called at interrupt time to get the good data from the audiobuffer object.
226//
227BOOL WAVESTREAM::_vReadAudioBuf(void)
228{
229 PSTREAMBUFFER pTemp = (PSTREAMBUFFER)qhInProcess.Head();
230 ULONG pdataBuf;
231 ULONG Buff_left, bytesread;
232
233 if(!pTemp) return FALSE;
234
235 // get the buffer pointer and amount of data remaining
236 pdataBuf = (ULONG)pTemp->pBuffptr + pTemp->ulBuffpos;
237 Buff_left = pTemp->ulBuffsz - pTemp->ulBuffpos;
238
239 // write the audio buffer
240 bytesread = OSS16_StreamAddBuffer(this, pdataBuf, Buff_left);
241 if(bytesread == 0) {
242 return FALSE; //no more data
243 }
244
245 dprintf4(("_vReadAudioBuf %lx size %d, bytes read %d", pdataBuf, Buff_left, bytesread));
246
247 // update the buffer pos counter
248 pTemp->ulBuffpos += bytesread;
249 _ulBytesProcessed += bytesread;
250
251 if(pTemp->ulBuffpos == pTemp->ulBuffsz) {
252 qhDone.PushOnTail(qhInProcess.PopHead());
253 ReturnBuffer();
254 dprintf4(("_vReadAudioBuf return buffer %lx size %ld, bytes read %ld", (ULONG)pTemp->pBuffptr, pTemp->ulBuffsz, bytesread));
255 }
256
257 return TRUE;
258}
259// called by the irq function in the hardware object when we get an interrupt
260// first call _vUpdateProcessed() to update the dma amd audio buffer related
261// stuff. Next if we have buffers on the primary queue try to read/write them
262// to the audiobuffer. Look at the buffers on the done queue and see if they
263// can be returned and finally process any events pending.
264void WAVESTREAM::Process(void)
265{
266 PSTREAMBUFFER ptemp;
267 ULONG ulCurBytesProcessed = 0;
268 ULONG bytesinc;
269
270 switch (ulStreamType & STREAM_WRITE) {
271 case STREAM_WRITE:
272 {
273 OSS16_StreamGetPos(this, &ulCurBytesProcessed);
274 if(ulCurBytesProcessed == 0) {
275 //shouldn't happen
276 DebugInt3();
277 return;
278 }
279 bytesinc = ulCurBytesProcessed - _ulBytesProcessed;
280 dprintf4(("Process: %lx %x", ulCurBytesProcessed, (USHORT)bytesinc));
281 if(ulCurBytesProcessed < _ulBytesProcessed) {
282 dprintf(("WARNING: Process: Current pos %ld incr %d", ulCurBytesProcessed, (USHORT)bytesinc));
283 }
284 _ulBytesProcessed = ulCurBytesProcessed;
285
286 while(bytesinc) {
287 if(qhDone.IsElements()) { // if there are buffers that have been
288 // completly written to the audio buffer
289 // check the first one on the done queue
290 // if it's data has been consumed by
291 // the hardware return it
292 ptemp = (PSTREAMBUFFER)qhDone.Head();
293 ptemp->ulDonepos += bytesinc;
294 bytesinc = 0;
295 if(ptemp->ulDonepos >= ptemp->ulBuffsz) {
296 //calc position in next buffer
297 bytesinc = ptemp->ulDonepos - ptemp->ulBuffsz;
298 dprintf3(("Process: Return buffer %lx size %d", ptemp->pBuffptr, ptemp->ulBuffsz));
299 ReturnBuffer();
300 }
301 }
302 else break; //shouldn't happen
303 }
304 AddBuffers(FALSE);
305 break;
306 }
307 case STREAM_READ:
308 while(_vReadAudioBuf());
309 break;
310 default:
311 break;
312 } /* endswitch */
313
314 ProcessEvents();
315}
316
317#pragma off (unreferenced)
318ULONG WAVESTREAM::Write(PSTREAMBUF pbuf, ULONG uLength, BOOL fLooping)
319#pragma on (unreferenced)
320{
321 PSTREAMBUFFER pStreamBuf = new STREAMBUFFER(uLength, pbuf);
322
323 return Write(pStreamBuf);
324}
325
326ULONG WAVESTREAM::Write(PSTREAMBUFFER pStreamBuf)
327{
328 qhInProcess.PushOnTail((PQUEUEELEMENT)pStreamBuf);
329 dprintf2(("WAVESTREAM::Write: Push on tail %lx %ld", ((PSTREAMBUFFER)qhInProcess.Tail())->pBuffptr, ((PSTREAMBUFFER)qhInProcess.Tail())->ulBuffsz));
330 if(fUnderrun) {
331 fUnderrun = FALSE;
332 OSS16_StreamReset(this);
333 AddBuffers(TRUE);
334 if(ulStreamType == STREAM_WAVE_PLAY)
335 OSS16_SetWaveOutVol(this, volume);
336 }
337 return 0;
338}
339
340ULONG WAVESTREAM::Read(PSTREAMBUF pbuf, unsigned uLength)
341{
342 qhInProcess.PushOnTail((PQUEUEELEMENT)new STREAMBUFFER(uLength, pbuf));
343 dprintf2(("WAVESTREAM::Read: Push on tail %lx %d", ((PSTREAMBUFFER)qhInProcess.Head())->pBuffptr, ((PSTREAMBUFFER)qhInProcess.Head())->ulBuffsz));
344 return 0;
345}
346
347// WAVESTREAM::GetCurrentTime(void)
348// get current time will calculate the stream time in milliseconds based on
349// NOW.... the open mpeg folks this is the greatest thing since my last
350// pay raise!!
351// but then again you know what those ring 3 programmers are like....
352// the algorythum goes like this....
353// bytes consumed / consume rate = seconds
354// Note before calling BufferUpdate check to see if the stream is running.
355// if it is not then call with flags of 0. This will prevent the audio buffer
356// trying to get the latest consumption info from the hardware object. (dma or
357// or pci as the case may be) Asking a hardware object that is not running for
358// status information is just not a good idea.....
359//
360
361ULONG WAVESTREAM::GetCurrentTime()
362{
363 ULONG Seconds, MilliSeconds, Overflow, Processed;
364
365 if (ulStreamState == STREAM_STREAMING) // if the stream is active
366 {
367 if (ulStreamType & STREAM_WRITE) {
368 OSS16_StreamGetPos(this, &Processed);
369 }
370 else Processed = _ulBytesProcessed;
371 }
372 else Processed = _ulBytesProcessed;
373
374 // if we haven't processed anything then just return
375 // _ulTimeBase
376 if(Processed == 0)
377 return(_ulTimeBase);
378
379 Seconds = Processed / _configinfo.ulPCMConsumeRate;
380 Overflow = Processed - (Seconds * _configinfo.ulPCMConsumeRate);
381 MilliSeconds = (Overflow * 1000) / _configinfo.ulPCMConsumeRate;
382 MilliSeconds += (Seconds * 1000);
383 return(MilliSeconds + _ulTimeBase);
384}
385
386ULONG WAVESTREAM::GetCurrentPos(void)
387{
388 ULONG Processed;
389
390 if (ulStreamState == STREAM_STREAMING) // if the stream is active
391 {
392 if (ulStreamType & STREAM_WRITE) {
393 OSS16_StreamGetPos(this, &Processed);
394 }
395 else Processed = _ulBytesProcessed;
396 }
397 else Processed = _ulBytesProcessed;
398
399 return Processed;
400}
401
402ULONG WAVESTREAM::GetCurrentWritePos(void)
403{
404 ULONG writepos = 0;
405
406 cli();
407 PSTREAMBUFFER pTemp = (PSTREAMBUFFER)qhDone.Tail();
408
409 if(!pTemp) {
410 pTemp = (PSTREAMBUFFER)qhInProcess.Head();
411 }
412 if(pTemp) {
413 writepos = pTemp->ulBuffpos;
414 }
415 sti();
416 return writepos;
417}
418
419//
420// SetCurrentTime
421// MMPM will send in the "starting stream time" as they see it.
422// "our stream time" will always start at 0, so we save "their" time and
423// add it to the elapsed time we calculate when we need to return time.
424//
425void WAVESTREAM::SetCurrentTime(ULONG time)
426{
427 _ulTimeBase = time;
428}
429
430//
431//
432ULONG WAVESTREAM::StartStream(void)
433{
434 PSTREAMBUFFER pTemp = (PSTREAMBUFFER)qhInProcess.Head();
435
436 // configure the wave device
437 ((PWAVEAUDIO)pahw)->ConfigDev(this, &_configinfo);
438
439 if(ulStreamType == STREAM_WAVE_PLAY) {
440 fragsize = _configinfo.ulPCMConsumeRate/64; //start with 64 irqs/sec
441 }
442 else fragsize = _configinfo.ulPCMConsumeRate/32; //start with 32 irqs/sec (no need for more)
443
444 //if the buffer is smaller than our predefined fragmentsize (*2), then correct it
445 //I assume here that buffers sizes don't radically change (except the last one)
446 //while playing a stream. If they do get a lot smaller, then we'll run into problems.
447 //There's nothing we can do about it as the fragment size can't be changed
448 //while the stream is playing.
449 if(pTemp->ulBuffsz/2 < fragsize) {
450 fragsize = pTemp->ulBuffsz/2;
451 if(fragsize < _configinfo.ulPCMConsumeRate/256)
452 {//lower limit; don't accept extremely small buffers
453 fragsize = _configinfo.ulPCMConsumeRate/256;
454 }
455 }
456 OSS16_StreamSetFragment(this, fragsize);
457 dprintf(("WAVESTREAM::StartStream: Fragment size %d", (USHORT)fragsize));
458 _ulBytesProcessed = 0;
459 fUnderrun = FALSE;
460
461 ulStreamState = STREAM_STREAMING;
462 //Adding the first buffer also starts playback
463 if(ulStreamType == STREAM_WAVE_PLAY) {
464 AddBuffers(TRUE);
465 }
466 else {
467 if(!fRecSrcIOCTL90)
468 OSS16_SetVolume(this, MIX_SETINPUTSRC, inputsrc);
469 if(!fRecGainIOCTL90)
470 OSS16_SetVolume(this, MIX_SETINPUTGAIN, inputgain);
471 OSS16_StartStream(this);
472 }
473
474 //Must set volume after adding buffers (voices inside sblive driver might not
475 //be allocated otherwise (first start) )
476 if(ulStreamType == STREAM_WAVE_PLAY)
477 OSS16_SetWaveOutVol(this, volume);
478
479 dprintf(("WAVESTREAM::StartStream %lx", ulStreamId));
480 return NO_ERROR;
481
482}
483
484ULONG WAVESTREAM::StopStream(PCONTROL_PARM pControl)
485{
486 if(ulStreamState == STREAM_STOPPED) {
487 dprintf(("WAVESTREAM::StopStream %lx (already stopped)", ulStreamId));
488 fUnderrun = FALSE;
489 pControl->ulTime = GetCurrentTime();
490 return NO_ERROR;
491 }
492 pahw->Stop(this);
493 //Reset cleans up waveout instance
494 OSS16_StreamReset(this);
495
496 ulStreamState = STREAM_STOPPED;
497 fUnderrun = FALSE;
498 dprintf(("WAVESTREAM::StopStream %lx", ulStreamId));
499 ReturnBuffers();
500 pControl->ulTime = GetCurrentTime();
501 _ulTimeBase = GetCurrentTime();
502 return NO_ERROR;
503
504}
505
506ULONG WAVESTREAM::PauseStream(PCONTROL_PARM pControl)
507{
508 ULONG endpos;
509
510 OSS16_StreamGetPos(this, &endpos);
511
512 pahw->Stop(this);
513 //Reset cleans up waveout instance
514 OSS16_StreamReset(this);
515
516 ulStreamState = STREAM_PAUSED;
517 fUnderrun = FALSE;
518
519 dprintf(("WAVESTREAM::PauseStream %lx", ulStreamId));
520 _vRealignPausedBuffers(endpos);
521
522 _ulBytesProcessed = endpos;
523 pControl->ulTime = GetCurrentTime();
524 _ulTimeBase = GetCurrentTime();
525 return NO_ERROR;
526
527}
528ULONG WAVESTREAM::ResumeStream(void)
529{
530 // configure the wave device
531 ((PWAVEAUDIO)pahw)->ConfigDev(this, &_configinfo);
532 if(ulStreamType == STREAM_WAVE_PLAY)
533 OSS16_SetWaveOutVol(this, volume);
534
535 dprintf(("WAVESTREAM::ResumeStream %lx", ulStreamId));
536 _ulBytesProcessed = 0;
537 fUnderrun = FALSE;
538
539 ulStreamState = STREAM_STREAMING;
540 //Adding the first buffer also starts playback
541 AddBuffers(TRUE);
542
543 return NO_ERROR;
544
545}
546
547
548BOOL WAVESTREAM::SetProperty(int type, ULONG value, ULONG reserved)
549{
550 switch(type) {
551 case PROPERTY_VOLUME:
552 volume = value;
553 if(ulStreamState == STREAM_STREAMING && ulStreamType == STREAM_WAVE_PLAY) {
554 OSS16_SetWaveOutVol(this, volume);
555 }
556 break;
557
558 case PROPERTY_INPUTSRC:
559 inputsrc = value;
560 break;
561
562 case PROPERTY_INPUTGAIN:
563 inputgain = value;
564 break;
565
566 default:
567 return STREAM::SetProperty(type, value, reserved);
568
569 }
570 return TRUE;
571}
572
573ULONG WAVESTREAM::GetProperty(int type)
574{
575 switch(type) {
576 case PROPERTY_FREQUENCY:
577 return _configinfo.ulSampleRate;
578
579 case PROPERTY_INPUTSRC:
580 return inputsrc;
581
582 case PROPERTY_INPUTGAIN:
583 return inputgain;
584
585 default:
586 return STREAM::GetProperty(type);
587 }
588}
589
590WAVESTREAM::WAVESTREAM(ULONG streamtype, LPMCI_AUDIO_INIT pinit, USHORT filesysnum):
591 STREAM(streamtype, filesysnum)
592{
593 _configinfo.ulSampleRate = pinit->lSRate;
594 _configinfo.ulBitsPerSample = pinit->lBitsPerSRate;
595 _configinfo.ulNumChannels = pinit->sChannels;
596 _configinfo.ulDataType = pinit->sMode;
597 _ulBytesProcessed = 0;
598 _ulTimeBase = 0;
599
600 fUnderrun = FALSE;
601
602 pinit->ulFlags |= FIXED; // Fixed length data
603 pinit->ulFlags |= LEFT_ALIGNED; // Left align bits on byte bndry
604 if (pinit->lBitsPerSRate == 8)
605 pinit->ulFlags|= TWOS_COMPLEMENT; // 2's complement data
606
607 ulStreamId = OSS16_OpenStream(this);
608 dprintf(("WAVESTREAM ctor %lx: rate %d bps %d numchan %d type %x", ulStreamId, (USHORT)_configinfo.ulSampleRate, (USHORT)_configinfo.ulBitsPerSample, (USHORT)_configinfo.ulNumChannels, (USHORT)_configinfo.ulNumChannels, (USHORT)_configinfo.ulDataType));
609}
610
611WAVESTREAM::~WAVESTREAM()
612{
613 dprintf(("WAVESTREAM dtor %lx", ulStreamId));
614 if(ulStreamId) {
615 OSS16_CloseStream(this);
616 }
617}
618
Note: See TracBrowser for help on using the repository browser.