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