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

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

Updates + fixes for DirectAudio interface

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