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

Last change on this file since 147 was 147, checked in by sandervl, 25 years ago

Fixed wave volume, recording gain + wave recording

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