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

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

DirectAudio interface updates

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.7 KB
Line 
1/* $Id: wavestrm.cpp 178 2001-04-30 21:08:00Z 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 space -= byteswritten;
175 if(byteswritten == 0) break;
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 0;
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 // write the audio buffer
205 Buff_left = min(Buff_left, space);
206 byteswritten = OSS16_StreamAddBuffer(this, pdataBuf, Buff_left);
207 if(byteswritten == 0) {
208 return 0; //no more room
209 }
210
211 // update the buffer pos counter
212 pTemp->ulBuffpos += byteswritten;
213
214 if(pTemp != qhDone.Tail()) {
215 qhDone.PushOnTail(qhInProcess.PopHead());
216 }
217 dprintf4(("AddBuffer %lx size %d, bytes written %d", pdataBuf, (USHORT)Buff_left, (USHORT)byteswritten));
218 return byteswritten;
219}
220
221// Read data from the audio Buffer.
222// Called at interrupt time to get the good data from the audiobuffer object.
223//
224BOOL WAVESTREAM::_vReadAudioBuf(void)
225{
226 PSTREAMBUFFER pTemp = (PSTREAMBUFFER)qhInProcess.Head();
227 ULONG pdataBuf;
228 ULONG Buff_left, bytesread;
229
230 if(!pTemp) return FALSE;
231
232 // get the buffer pointer and amount of data remaining
233 pdataBuf = (ULONG)pTemp->pBuffptr + pTemp->ulBuffpos;
234 Buff_left = pTemp->ulBuffsz - pTemp->ulBuffpos;
235
236 // write the audio buffer
237 bytesread = OSS16_StreamAddBuffer(this, pdataBuf, Buff_left);
238 if(bytesread == 0) {
239 return FALSE; //no more data
240 }
241
242 dprintf4(("_vReadAudioBuf %lx size %d, bytes read %d", pdataBuf, Buff_left, bytesread));
243
244 // update the buffer pos counter
245 pTemp->ulBuffpos += bytesread;
246 _ulBytesProcessed += bytesread;
247
248 if(pTemp->ulBuffpos == pTemp->ulBuffsz) {
249 qhDone.PushOnTail(qhInProcess.PopHead());
250 ReturnBuffer();
251 dprintf4(("_vReadAudioBuf return buffer %lx size %ld, bytes read %ld", (ULONG)pTemp->pBuffptr, pTemp->ulBuffsz, bytesread));
252 }
253
254 return TRUE;
255}
256// called by the irq function in the hardware object when we get an interrupt
257// first call _vUpdateProcessed() to update the dma amd audio buffer related
258// stuff. Next if we have buffers on the primary queue try to read/write them
259// to the audiobuffer. Look at the buffers on the done queue and see if they
260// can be returned and finally process any events pending.
261void WAVESTREAM::Process(void)
262{
263 PSTREAMBUFFER ptemp;
264 ULONG ulCurBytesProcessed = 0;
265 ULONG bytesinc;
266
267 switch (ulStreamType & STREAM_WRITE) {
268 case STREAM_WRITE:
269 {
270 OSS16_StreamGetPos(this, &ulCurBytesProcessed);
271 if(ulCurBytesProcessed == 0) {
272 //shouldn't happen
273 DebugInt3();
274 return;
275 }
276 bytesinc = ulCurBytesProcessed - _ulBytesProcessed;
277 dprintf4(("Process: %lx %x", ulCurBytesProcessed, (USHORT)bytesinc));
278 if(ulCurBytesProcessed < _ulBytesProcessed) {
279 dprintf(("WARNING: Process: Current pos %ld incr %d", ulCurBytesProcessed, (USHORT)bytesinc));
280 }
281 _ulBytesProcessed = ulCurBytesProcessed;
282
283 while(bytesinc) {
284 if(qhDone.IsElements()) { // if there are buffers that have been
285 // completly written to the audio buffer
286 // check the first one on the done queue
287 // if it's data has been consumed by
288 // the hardware return it
289 ptemp = (PSTREAMBUFFER)qhDone.Head();
290 ptemp->ulDonepos += bytesinc;
291 bytesinc = 0;
292 if(ptemp->ulDonepos >= ptemp->ulBuffsz) {
293 //calc position in next buffer
294 bytesinc = ptemp->ulDonepos - ptemp->ulBuffsz;
295 dprintf3(("Process: Return buffer %lx size %d", ptemp->pBuffptr, ptemp->ulBuffsz));
296 ReturnBuffer();
297 }
298 }
299 else break; //shouldn't happen
300 }
301
302 //Get rid of empty buffers
303 ptemp = (PSTREAMBUFFER)qhInProcess.Head();
304 if(ptemp && (ptemp->ulBuffsz == 0 || ptemp->pBuffptr == NULL)) {
305 dprintf(("Returning 0 size buffer immediately"));
306 ReturnBuffer();
307 }
308 AddBuffers(FALSE);
309 break;
310 }
311 case STREAM_READ:
312 while(_vReadAudioBuf());
313 break;
314 default:
315 break;
316 } /* endswitch */
317
318 ProcessEvents();
319}
320
321#pragma off (unreferenced)
322ULONG WAVESTREAM::Write(PSTREAMBUF pbuf, ULONG uLength, BOOL fLooping)
323#pragma on (unreferenced)
324{
325 PSTREAMBUFFER pStreamBuf = new STREAMBUFFER(uLength, pbuf);
326
327 return Write(pStreamBuf);
328}
329
330ULONG WAVESTREAM::Write(PSTREAMBUFFER pStreamBuf)
331{
332 qhInProcess.PushOnTail((PQUEUEELEMENT)pStreamBuf);
333 dprintf2(("WAVESTREAM::Write: Push on tail %lx %ld", ((PSTREAMBUFFER)qhInProcess.Tail())->pBuffptr, ((PSTREAMBUFFER)qhInProcess.Tail())->ulBuffsz));
334 if(fUnderrun) {
335 fUnderrun = FALSE;
336 OSS16_StreamReset(this);
337 AddBuffers(TRUE);
338 if(ulStreamType == STREAM_WAVE_PLAY)
339 OSS16_SetWaveOutVol(this, volume);
340 }
341 return 0;
342}
343
344ULONG WAVESTREAM::Read(PSTREAMBUF pbuf, unsigned uLength)
345{
346 qhInProcess.PushOnTail((PQUEUEELEMENT)new STREAMBUFFER(uLength, pbuf));
347 dprintf2(("WAVESTREAM::Read: Push on tail %lx %d", ((PSTREAMBUFFER)qhInProcess.Head())->pBuffptr, ((PSTREAMBUFFER)qhInProcess.Head())->ulBuffsz));
348 return 0;
349}
350
351// WAVESTREAM::GetCurrentTime(void)
352// get current time will calculate the stream time in milliseconds based on
353// NOW.... the open mpeg folks this is the greatest thing since my last
354// pay raise!!
355// but then again you know what those ring 3 programmers are like....
356// the algorythum goes like this....
357// bytes consumed / consume rate = seconds
358// Note before calling BufferUpdate check to see if the stream is running.
359// if it is not then call with flags of 0. This will prevent the audio buffer
360// trying to get the latest consumption info from the hardware object. (dma or
361// or pci as the case may be) Asking a hardware object that is not running for
362// status information is just not a good idea.....
363//
364
365ULONG WAVESTREAM::GetCurrentTime()
366{
367 ULONG Seconds, MilliSeconds, Overflow, Processed;
368
369 if (ulStreamState == STREAM_STREAMING) // if the stream is active
370 {
371 if (ulStreamType & STREAM_WRITE) {
372 OSS16_StreamGetPos(this, &Processed);
373 }
374 else Processed = _ulBytesProcessed;
375 }
376 else Processed = _ulBytesProcessed;
377
378 // if we haven't processed anything then just return
379 // _ulTimeBase
380 if(Processed == 0)
381 return(_ulTimeBase);
382
383 Seconds = Processed / _configinfo.ulPCMConsumeRate;
384 Overflow = Processed - (Seconds * _configinfo.ulPCMConsumeRate);
385 MilliSeconds = (Overflow * 1000) / _configinfo.ulPCMConsumeRate;
386 MilliSeconds += (Seconds * 1000);
387 return(MilliSeconds + _ulTimeBase);
388}
389
390ULONG WAVESTREAM::GetCurrentPos(void)
391{
392 ULONG Processed;
393
394 if (ulStreamState == STREAM_STREAMING) // if the stream is active
395 {
396 if (ulStreamType & STREAM_WRITE) {
397 OSS16_StreamGetPos(this, &Processed);
398 }
399 else Processed = _ulBytesProcessed;
400 }
401 else Processed = _ulBytesProcessed;
402
403 return Processed;
404}
405
406ULONG WAVESTREAM::GetCurrentWritePos(void)
407{
408 ULONG writepos = 0;
409
410 cli();
411 PSTREAMBUFFER pTemp = (PSTREAMBUFFER)qhDone.Tail();
412
413 if(!pTemp) {
414 pTemp = (PSTREAMBUFFER)qhInProcess.Head();
415 }
416 if(pTemp) {
417 writepos = pTemp->ulBuffpos;
418 }
419 sti();
420 return writepos;
421}
422
423//
424// SetCurrentTime
425// MMPM will send in the "starting stream time" as they see it.
426// "our stream time" will always start at 0, so we save "their" time and
427// add it to the elapsed time we calculate when we need to return time.
428//
429void WAVESTREAM::SetCurrentTime(ULONG time)
430{
431 _ulTimeBase = time;
432}
433
434//
435//
436ULONG WAVESTREAM::StartStream(void)
437{
438 PSTREAMBUFFER pTemp = (PSTREAMBUFFER)qhInProcess.Head();
439
440 // configure the wave device
441 ((PWAVEAUDIO)pahw)->ConfigDev(this, &_configinfo);
442
443 if(ulStreamType == STREAM_WAVE_PLAY) {
444 fragsize = _configinfo.ulPCMConsumeRate/64; //start with 64 irqs/sec
445 }
446 else fragsize = _configinfo.ulPCMConsumeRate/32; //start with 32 irqs/sec (no need for more)
447
448 //if the buffer is smaller than our predefined fragmentsize (*2), then correct it
449 //I assume here that buffers sizes don't radically change (except the last one)
450 //while playing a stream. If they do get a lot smaller, then we'll run into problems.
451 //There's nothing we can do about it as the fragment size can't be changed
452 //while the stream is playing.
453 if(pTemp->ulBuffsz/2 < fragsize) {
454 fragsize = pTemp->ulBuffsz/2;
455 if(fragsize < _configinfo.ulPCMConsumeRate/256)
456 {//lower limit; don't accept extremely small buffers
457 fragsize = _configinfo.ulPCMConsumeRate/256;
458 }
459 }
460 OSS16_StreamSetFragment(this, fragsize);
461 dprintf(("WAVESTREAM::StartStream: Fragment size %d", (USHORT)fragsize));
462 _ulBytesProcessed = 0;
463 fUnderrun = FALSE;
464
465 ulStreamState = STREAM_STREAMING;
466 //Adding the first buffer also starts playback
467 if(ulStreamType == STREAM_WAVE_PLAY) {
468 AddBuffers(TRUE);
469 }
470 else {
471 if(!fRecSrcIOCTL90)
472 OSS16_SetVolume(this, MIX_SETINPUTSRC, inputsrc);
473 if(!fRecGainIOCTL90)
474 OSS16_SetVolume(this, MIX_SETINPUTGAIN, inputgain);
475 OSS16_StartStream(this);
476 }
477
478 //Must set volume after adding buffers (voices inside sblive driver might not
479 //be allocated otherwise (first start) )
480 if(ulStreamType == STREAM_WAVE_PLAY)
481 OSS16_SetWaveOutVol(this, volume);
482
483 dprintf(("WAVESTREAM::StartStream %lx", ulStreamId));
484 return NO_ERROR;
485
486}
487
488ULONG WAVESTREAM::StopStream(PCONTROL_PARM pControl)
489{
490 if(ulStreamState == STREAM_STOPPED) {
491 dprintf(("WAVESTREAM::StopStream %lx (already stopped)", ulStreamId));
492 fUnderrun = FALSE;
493 pControl->ulTime = GetCurrentTime();
494 return NO_ERROR;
495 }
496 pahw->Stop(this);
497 //Reset cleans up waveout instance
498 OSS16_StreamReset(this);
499
500 ulStreamState = STREAM_STOPPED;
501 fUnderrun = FALSE;
502 dprintf(("WAVESTREAM::StopStream %lx", ulStreamId));
503 ReturnBuffers();
504 pControl->ulTime = GetCurrentTime();
505 _ulTimeBase = GetCurrentTime();
506 return NO_ERROR;
507
508}
509
510ULONG WAVESTREAM::PauseStream(PCONTROL_PARM pControl)
511{
512 ULONG endpos;
513
514 OSS16_StreamGetPos(this, &endpos);
515
516 pahw->Stop(this);
517 //Reset cleans up waveout instance
518 OSS16_StreamReset(this);
519
520 ulStreamState = STREAM_PAUSED;
521 fUnderrun = FALSE;
522
523 dprintf(("WAVESTREAM::PauseStream %lx", ulStreamId));
524 _vRealignPausedBuffers(endpos);
525
526 _ulBytesProcessed = endpos;
527 pControl->ulTime = GetCurrentTime();
528 _ulTimeBase = GetCurrentTime();
529 return NO_ERROR;
530
531}
532ULONG WAVESTREAM::ResumeStream(void)
533{
534 // configure the wave device
535 ((PWAVEAUDIO)pahw)->ConfigDev(this, &_configinfo);
536 if(ulStreamType == STREAM_WAVE_PLAY)
537 OSS16_SetWaveOutVol(this, volume);
538
539 dprintf(("WAVESTREAM::ResumeStream %lx", ulStreamId));
540 _ulBytesProcessed = 0;
541 fUnderrun = FALSE;
542
543 ulStreamState = STREAM_STREAMING;
544 //Adding the first buffer also starts playback
545 AddBuffers(TRUE);
546
547 return NO_ERROR;
548
549}
550
551
552BOOL WAVESTREAM::SetProperty(int type, ULONG value, ULONG reserved)
553{
554 switch(type) {
555 case PROPERTY_VOLUME:
556 volume = value;
557 if(ulStreamState == STREAM_STREAMING && ulStreamType == STREAM_WAVE_PLAY) {
558 OSS16_SetWaveOutVol(this, volume);
559 }
560 break;
561
562 case PROPERTY_INPUTSRC:
563 inputsrc = value;
564 break;
565
566 case PROPERTY_INPUTGAIN:
567 inputgain = value;
568 break;
569
570 default:
571 return STREAM::SetProperty(type, value, reserved);
572
573 }
574 return TRUE;
575}
576
577ULONG WAVESTREAM::GetProperty(int type)
578{
579 switch(type) {
580 case PROPERTY_FREQUENCY:
581 return _configinfo.ulSampleRate;
582
583 case PROPERTY_INPUTSRC:
584 return inputsrc;
585
586 case PROPERTY_INPUTGAIN:
587 return inputgain;
588
589 default:
590 return STREAM::GetProperty(type);
591 }
592}
593
594WAVESTREAM::WAVESTREAM(ULONG streamtype, LPMCI_AUDIO_INIT pinit, USHORT filesysnum):
595 STREAM(streamtype, filesysnum)
596{
597 _configinfo.ulSampleRate = pinit->lSRate;
598 _configinfo.ulBitsPerSample = pinit->lBitsPerSRate;
599 _configinfo.ulNumChannels = pinit->sChannels;
600 _configinfo.ulDataType = pinit->sMode;
601 _ulBytesProcessed = 0;
602 _ulTimeBase = 0;
603
604 fUnderrun = FALSE;
605
606 pinit->ulFlags |= FIXED; // Fixed length data
607 pinit->ulFlags |= LEFT_ALIGNED; // Left align bits on byte bndry
608 if (pinit->lBitsPerSRate == 8)
609 pinit->ulFlags|= TWOS_COMPLEMENT; // 2's complement data
610
611 ulStreamId = OSS16_OpenStream(this);
612 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));
613}
614
615WAVESTREAM::~WAVESTREAM()
616{
617 dprintf(("WAVESTREAM dtor %lx", ulStreamId));
618 if(ulStreamId) {
619 OSS16_CloseStream(this);
620 }
621}
622
Note: See TracBrowser for help on using the repository browser.