source: trunk/src/winmm/waveoutdart.cpp@ 10367

Last change on this file since 10367 was 10269, checked in by sandervl, 22 years ago

Make sure the timer object is not deleted inside the timer callback handler

File size: 26.0 KB
Line 
1/* $Id: waveoutdart.cpp,v 1.23 2003-10-13 09:18:38 sandervl Exp $ */
2
3/*
4 * Wave playback class (DART)
5 *
6 * Copyright 1998-2001 Sander van Leeuwen (sandervl@xs4all.nl)
7 *
8 *
9 * Project Odin Software License can be found in LICENSE.TXT
10 *
11 * Note:
12 * 2000/11/24 PH MCI_MIXSETUP_PARMS->pMixWrite does alter FS: selector!
13 * TODO: mulaw, alaw & adpcm
14 *
15 */
16
17
18/****************************************************************************
19 * Includes *
20 ****************************************************************************/
21
22
23
24#define INCL_BASE
25#define INCL_OS2MM
26#define INCL_DOSPROCESS
27#include <os2wrap.h> //Odin32 OS/2 api wrappers
28#include <os2mewrap.h> //Odin32 OS/2 MMPM/2 api wrappers
29#include <stdlib.h>
30#include <stddef.h>
31#include <string.h>
32#define OS2_ONLY
33#include <win32api.h>
34#include <wprocess.h>
35
36#include "misc.h"
37#include "waveoutdart.h"
38#include "initwinmm.h"
39
40#define DBG_LOCALLOG DBG_waveoutdart
41#include "dbglocal.h"
42
43#ifndef min
44#define min(a, b) ((a > b) ? b : a)
45#endif
46
47#ifndef max
48#define max(a, b) ((a > b) ? a : b)
49#endif
50
51LONG APIENTRY WaveOutHandler(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer, ULONG ulFlags);
52
53static BOOL fFixedWaveBufferSize = FALSE;
54
55//#define DEBUG_DUMP_PCM
56#ifdef DEBUG_DUMP_PCM
57#include <stdio.h>
58
59typedef struct {
60 int bits;
61 int rate;
62 int format;
63 int numchan;
64} REC_STRUCT;
65
66FILE *pcmfile = NULL;
67#endif
68
69//******************************************************************************
70// ODIN_waveOutSetFixedBuffers
71//
72// Tell WINMM to use DART buffers of the same size as the first buffer delivered
73// by waveOutWrite
74//
75// NOTE: This will only work in very specific cases; it is not a good general
76// purpose solution.
77//
78//******************************************************************************
79void WIN32API ODIN_waveOutSetFixedBuffers()
80{
81 fFixedWaveBufferSize = TRUE;
82}
83/******************************************************************************/
84/******************************************************************************/
85DartWaveOut::DartWaveOut(LPWAVEFORMATEX pwfx, ULONG fdwOpen, ULONG nCallback, ULONG dwInstance)
86 : WaveOut(pwfx, fdwOpen, nCallback, dwInstance)
87{
88 MCI_GENERIC_PARMS GenericParms;
89 MCI_AMP_OPEN_PARMS AmpOpenParms;
90 APIRET rc;
91
92 curPlayBuf = curFillBuf = curFillPos = curPlayPos = 0;
93 fMixerSetup = FALSE;
94 fUnderrun = FALSE;
95 ulUnderrunBase= 0;
96
97 ulBufSize = DART_BUFSIZE;
98
99 MixBuffer = (MCI_MIX_BUFFER *)malloc(PREFILLBUF_DART*sizeof(MCI_MIX_BUFFER));
100 MixSetupParms = (MCI_MIXSETUP_PARMS *)malloc(sizeof(MCI_MIXSETUP_PARMS));
101 BufferParms = (MCI_BUFFER_PARMS *)malloc(sizeof(MCI_BUFFER_PARMS));
102 if(!MixBuffer || !MixSetupParms || !BufferParms) {
103 dprintf(("ERROR: malloc failed!!"));
104 ulError = MMSYSERR_NOMEM;
105 return;
106 }
107
108 // Setup the open structure, pass the playlist and tell MCI_OPEN to use it
109 memset(&AmpOpenParms,0,sizeof(AmpOpenParms));
110
111 AmpOpenParms.usDeviceID = ( USHORT ) 0;
112 AmpOpenParms.pszDeviceType = ( PSZ ) MCI_DEVTYPE_AUDIO_AMPMIX;
113
114 rc = mymciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE,
115 (PVOID) &AmpOpenParms, 0);
116
117 DeviceId = AmpOpenParms.usDeviceID;
118 if(rc) {
119 dprintf(("MCI_OPEN failed\n"));
120 mciError(rc);
121 ulError = MMSYSERR_NODRIVER;
122 }
123 if(rc == 0) {
124 //Grab exclusive rights to device instance (NOT entire device)
125 GenericParms.hwndCallback = 0; //Not needed, so set to 0
126 rc = mymciSendCommand(DeviceId, MCI_ACQUIREDEVICE, MCI_EXCLUSIVE_INSTANCE,
127 (PVOID)&GenericParms, 0);
128 if(rc) {
129 dprintf(("MCI_ACQUIREDEVICE failed\n"));
130 mciError(rc);
131 ulError = MMSYSERR_NOTENABLED;
132 }
133 }
134 setVolume(volume);
135
136 if(!ulError)
137 callback(WOM_OPEN, 0, 0);
138}
139/******************************************************************************/
140/******************************************************************************/
141DartWaveOut::~DartWaveOut()
142{
143 MCI_GENERIC_PARMS GenericParms;
144
145 State = STATE_STOPPED;
146
147#ifdef DEBUG_DUMP_PCM
148 if(pcmfile) fclose(pcmfile);
149#endif
150
151 if(!ulError) {
152 // Generic parameters
153 GenericParms.hwndCallback = 0; //hwndFrame
154
155 // Stop the playback.
156 mymciSendCommand(DeviceId, MCI_STOP,MCI_WAIT, (PVOID)&GenericParms,0);
157
158 mymciSendCommand(DeviceId, MCI_BUFFER,
159 MCI_WAIT | MCI_DEALLOCATE_MEMORY,
160 (PVOID)&BufferParms, 0);
161
162 // Generic parameters
163 GenericParms.hwndCallback = 0; //hwndFrame
164
165 // Close the device
166 mymciSendCommand(DeviceId, MCI_CLOSE, MCI_WAIT, (PVOID)&GenericParms, 0);
167 }
168
169 if(!ulError)
170 {
171 callback(WOM_CLOSE, 0, 0);
172 }
173
174 if(MixBuffer)
175 free(MixBuffer);
176 if(MixSetupParms)
177 free(MixSetupParms);
178 if(BufferParms)
179 free(BufferParms);
180}
181/******************************************************************************/
182/******************************************************************************/
183MMRESULT DartWaveOut::write(LPWAVEHDR pwh, UINT cbwh)
184{
185 MCI_GENERIC_PARMS GenericParms = {0};
186 APIRET rc;
187 int i, buflength;
188
189 queuedbuffers++;
190 if(fMixerSetup == FALSE)
191 {
192 dprintf(("device acquired\n"));
193 /* Set the MixSetupParms data structure to match the loaded file.
194 * This is a global that is used to setup the mixer.
195 */
196 memset(MixSetupParms, 0, sizeof( MCI_MIXSETUP_PARMS ) );
197
198 MixSetupParms->ulBitsPerSample = BitsPerSample;
199 MixSetupParms->ulSamplesPerSec = SampleRate;
200 MixSetupParms->ulFormatTag = MCI_WAVE_FORMAT_PCM;
201 MixSetupParms->ulChannels = nChannels;
202
203 dprintf(("bps %d, sps %d chan %d\n", BitsPerSample, SampleRate, nChannels));
204
205#ifdef DEBUG_DUMP_PCM
206 REC_STRUCT recinfo;
207
208 pcmfile = fopen("dartpcm.dat", "wb");
209 recinfo.bits = BitsPerSample;
210 recinfo.rate = SampleRate;
211 recinfo.format = MCI_WAVE_FORMAT_PCM;
212 recinfo.numchan = nChannels;
213 fwrite(&recinfo, sizeof(recinfo), 1, pcmfile);
214#endif
215 /* Setup the mixer for playback of wave data
216 */
217 MixSetupParms->ulFormatMode = MCI_PLAY;
218 MixSetupParms->ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
219 MixSetupParms->pmixEvent = WaveOutHandler;
220
221 rc = mymciSendCommand(DeviceId,
222 MCI_MIXSETUP,
223 MCI_WAIT | MCI_MIXSETUP_INIT,
224 (PVOID)MixSetupParms,
225 0);
226
227 if ( rc != MCIERR_SUCCESS ) {
228 mciError(rc);
229 mymciSendCommand(DeviceId, MCI_RELEASEDEVICE, MCI_WAIT,
230 (PVOID)&GenericParms, 0);
231 return(MMSYSERR_NOTSUPPORTED);
232 }
233
234 /*
235 * Set up the BufferParms data structure and allocate
236 * device buffers from the Amp-Mixer
237 */
238 dprintf(("mix setup %d, %d\n", pwh->dwBufferLength, pwh->dwBufferLength));
239
240#if 1
241 if(fFixedWaveBufferSize == FALSE)
242 {//by default we need to select a small enough buffer to be able to
243 //signal buffer completion in time
244 int consumerate = getAvgBytesPerSecond();
245 int minbufsize = consumerate/32;
246
247 ulBufSize = pwh->dwBufferLength/2;
248 if(ulBufSize > minbufsize) {
249 dprintf(("set buffer size to %d bytes (org size = %d)", minbufsize, pwh->dwBufferLength));
250 ulBufSize = minbufsize;
251 }
252 }
253 else ulBufSize = pwh->dwBufferLength;
254#else
255 if(pwh->dwBufferLength >= 512 && pwh->dwBufferLength <= 1024)
256 ulBufSize = pwh->dwBufferLength;
257 else ulBufSize = 1024;
258#endif
259 MixSetupParms->ulBufferSize = ulBufSize;
260
261 BufferParms->ulNumBuffers = PREFILLBUF_DART;
262 BufferParms->ulBufferSize = MixSetupParms->ulBufferSize;
263 BufferParms->pBufList = MixBuffer;
264
265 for(i=0;i<PREFILLBUF_DART;i++) {
266 MixBuffer[i].ulUserParm = (ULONG)this;
267 }
268
269 rc = mymciSendCommand(DeviceId,
270 MCI_BUFFER,
271 MCI_WAIT | MCI_ALLOCATE_MEMORY,
272 (PVOID)BufferParms,
273 0);
274
275 if(ULONG_LOWD(rc) != MCIERR_SUCCESS) {
276 mciError(rc);
277 mymciSendCommand(DeviceId, MCI_RELEASEDEVICE, MCI_WAIT,
278 (PVOID)&GenericParms, 0);
279 return(MMSYSERR_NOTSUPPORTED);
280 }
281
282 wmutex.enter();
283 fMixerSetup = TRUE;
284
285 curPlayBuf = curFillBuf = curFillPos = curPlayPos = 0;
286 bytesPlayed = bytesCopied = bytesReturned = 0;
287
288 for(i=0;i<PREFILLBUF_DART;i++) {
289 memset(MixBuffer[i].pBuffer, 0, MixBuffer[i].ulBufferLength);
290 }
291 dprintf(("Dart opened, bufsize = %d\n", MixBuffer[0].ulBufferLength));
292
293 wavehdr = pwh;
294 curhdr = pwh;
295 pwh->lpNext = NULL;
296 pwh->reserved = 0;
297
298 if(State != STATE_STOPPED) {//don't start playback if paused
299 wmutex.leave();
300 return(MMSYSERR_NOERROR);
301 }
302
303 writeBuffer(); //must be called before (re)starting playback
304
305 dprintf(("MixSetupParms = %X\n", MixSetupParms));
306 State = STATE_PLAYING;
307 fUnderrun = FALSE;
308 wmutex.leave();
309
310 //write buffers to DART; starts playback
311 dprintf(("WINMM: transfer all buffers to DART"));
312 USHORT selTIB = RestoreOS2FS(); // save current FS selector
313
314 MixSetupParms->pmixWrite(MixSetupParms->ulMixHandle,
315 MixBuffer,
316 PREFILLBUF_DART);
317 SetFS(selTIB); // switch back to the saved FS selector
318 dprintf(("Dart playing\n"));
319 }
320 else
321 {
322 pwh->lpNext = NULL;
323 pwh->reserved = 0;
324 wmutex.enter();
325 if(wavehdr) {
326 WAVEHDR *chdr = wavehdr;
327 while(chdr->lpNext) {
328#ifdef DEBUG
329 if(chdr == pwh) dprintf(("adding already present buffer!!!!!"));
330#endif
331 chdr = chdr->lpNext;
332 }
333 chdr->lpNext = pwh;
334 }
335 else wavehdr = pwh;
336
337 if(!fUnderrun && State != STATE_STOPPED) {//don't start playback if paused
338 //write new data to the DART buffers (if there's any room left)
339 if(State == STATE_PLAYING) {
340 writeBuffer(); //must be called before (re)starting playback
341 }
342 wmutex.leave();
343 return(MMSYSERR_NOERROR);
344 }
345 writeBuffer(); //must be called before (re)starting playback
346 State = STATE_PLAYING;
347 fUnderrun = FALSE;
348 wmutex.leave();
349
350 //write buffers to DART; starts playback
351 dprintf(("WINMM: transfer all buffers to DART"));
352 USHORT selTIB = RestoreOS2FS(); // save current FS selector
353
354 MixSetupParms->pmixWrite(MixSetupParms->ulMixHandle,
355 MixBuffer,
356 PREFILLBUF_DART);
357 SetFS(selTIB); // switch back to the saved FS selector
358
359 dprintf(("Dart playing\n"));
360 }
361 return(MMSYSERR_NOERROR);
362}
363/******************************************************************************/
364/******************************************************************************/
365MMRESULT DartWaveOut::pause()
366{
367 MCI_GENERIC_PARMS Params;
368
369 dprintf(("WINMM: DartWaveOut::pause"));
370
371 wmutex.enter();
372 if(State != STATE_PLAYING) {
373 State = STATE_PAUSED;
374 wmutex.leave();
375 return(MMSYSERR_NOERROR);
376 }
377
378 State = STATE_PAUSED;
379 wmutex.leave();
380
381 memset(&Params, 0, sizeof(Params));
382
383 // Pause playback.
384 mymciSendCommand(DeviceId, MCI_PAUSE, MCI_WAIT, (PVOID)&Params, 0);
385
386 return(MMSYSERR_NOERROR);
387}
388/******************************************************************************/
389/******************************************************************************/
390MMRESULT DartWaveOut::resume()
391{
392 MCI_GENERIC_PARMS Params;
393 int i, curbuf;
394
395 dprintf(("DartWaveOut::resume"));
396
397 wmutex.enter();
398 if(State != STATE_PAUSED) {
399 wmutex.leave();
400 return(MMSYSERR_NOERROR);
401 }
402 State = STATE_PLAYING;
403 wmutex.leave();
404
405 //Only write buffers to dart if mixer has been initialized; if not, then
406 //the first buffer write will do this for us.
407 if(fMixerSetup == TRUE)
408 {
409 wmutex.enter();
410 State = STATE_PLAYING;
411 fUnderrun = FALSE;
412 curbuf = curPlayBuf;
413 writeBuffer(); //must be called before (re)starting playback
414 wmutex.leave();
415
416 // MCI_MIXSETUP_PARMS->pMixWrite does alter FS: selector!
417 USHORT selTIB = GetFS(); // save current FS selector
418
419 for(i=0;i<PREFILLBUF_DART;i++)
420 {
421 dprintf(("restart: write buffer at %x size %d", MixBuffer[curbuf].pBuffer, MixBuffer[curbuf].ulBufferLength));
422 MixSetupParms->pmixWrite(MixSetupParms->ulMixHandle, &MixBuffer[curbuf], 1);
423 if(++curbuf == PREFILLBUF_DART)
424 curbuf = 0;
425 }
426 SetFS(selTIB); // switch back to the saved FS selector
427 }
428 return(MMSYSERR_NOERROR);
429}
430/******************************************************************************/
431/******************************************************************************/
432MMRESULT DartWaveOut::stop()
433{
434 MCI_GENERIC_PARMS Params;
435
436 dprintf(("DartWaveOut::stop %s", (State == STATE_PLAYING) ? "playing" : "stopped"));
437 if(State != STATE_PLAYING)
438 return(MMSYSERR_HANDLEBUSY);
439
440 memset(&Params, 0, sizeof(Params));
441
442 // Stop the playback.
443 mymciSendCommand(DeviceId, MCI_STOP, MCI_WAIT, (PVOID)&Params, 0);
444
445 State = STATE_STOPPED;
446 fUnderrun = FALSE;
447
448 curPlayBuf = curFillBuf = curFillPos = curPlayPos = 0;
449 bytesPlayed = bytesCopied = bytesReturned = 0;
450
451 return(MMSYSERR_NOERROR);
452}
453/******************************************************************************/
454/******************************************************************************/
455MMRESULT DartWaveOut::reset()
456{
457 MCI_GENERIC_PARMS Params;
458 LPWAVEHDR tmpwavehdr;
459
460 dprintf(("DartWaveOut::reset %s", (State == STATE_PLAYING) ? "playing" : "stopped"));
461 if(State != STATE_PLAYING)
462 return(MMSYSERR_HANDLEBUSY);
463
464 memset(&Params, 0, sizeof(Params));
465
466 wmutex.enter();
467 State = STATE_STOPPED;
468 wmutex.leave();
469
470 // Stop the playback.
471 mymciSendCommand(DeviceId, MCI_STOP, MCI_WAIT, (PVOID)&Params, 0);
472
473 wmutex.enter();
474 while(wavehdr)
475 {
476 wavehdr->dwFlags |= WHDR_DONE;
477 wavehdr->dwFlags &= ~WHDR_INQUEUE;
478 wavehdr->reserved = 0;
479 tmpwavehdr = wavehdr;
480 wavehdr = wavehdr->lpNext;
481 tmpwavehdr->lpNext = NULL;
482 wmutex.leave();
483
484 callback(WOM_DONE, (ULONG)tmpwavehdr, 0);
485 wmutex.enter();
486 }
487 wavehdr = NULL;
488 fUnderrun = FALSE;
489 ulUnderrunBase = 0;
490
491 curPlayBuf = curFillBuf = curFillPos = curPlayPos = 0;
492 bytesPlayed = bytesCopied = bytesReturned = 0;
493 queuedbuffers = 0;
494
495 wmutex.leave();
496 return(MMSYSERR_NOERROR);
497}
498/******************************************************************************/
499/******************************************************************************/
500ULONG DartWaveOut::getPosition()
501{
502 MCI_STATUS_PARMS mciStatus = {0};
503 ULONG rc, nrbytes;
504
505 if(State == STATE_STOPPED) {
506 dprintf(("Not playing; return 0 position"));
507 return ulUnderrunBase;
508 }
509
510 mciStatus.ulItem = MCI_STATUS_POSITION;
511 rc = mymciSendCommand(DeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_WAIT, (PVOID)&mciStatus, 0);
512 if((rc & 0xFFFF) == MCIERR_SUCCESS) {
513 nrbytes = (ULONG)(((double)mciStatus.ulReturn * (double)getAvgBytesPerSecond())/1000.0);
514 return ulUnderrunBase+nrbytes;
515 }
516 mciError(rc);
517 return 0xFFFFFFFF;
518}
519/******************************************************************************/
520/******************************************************************************/
521BOOL DartWaveOut::queryFormat(ULONG formatTag, ULONG nChannels,
522 ULONG nSamplesPerSec, ULONG wBitsPerSample)
523{
524 MCI_WAVE_GETDEVCAPS_PARMS mciAudioCaps;
525 MCI_GENERIC_PARMS GenericParms;
526 MCI_OPEN_PARMS mciOpenParms; /* open parms for MCI_OPEN */
527 int i, freqbits = 0;
528 ULONG rc, DeviceId;
529 BOOL winrc;
530
531 dprintf(("DartWaveOut::queryFormat %x srate=%d, nchan=%d, bps=%d", formatTag, nSamplesPerSec, nChannels, wBitsPerSample));
532
533 memset(&mciOpenParms, /* Object to fill with zeros. */
534 0, /* Value to place into the object. */
535 sizeof( mciOpenParms ) ); /* How many zero's to use. */
536
537 mciOpenParms.pszDeviceType = (PSZ)MCI_DEVTYPE_WAVEFORM_AUDIO;
538
539 rc = mymciSendCommand( (USHORT) 0,
540 MCI_OPEN,
541 MCI_WAIT | MCI_OPEN_TYPE_ID,
542 (PVOID) &mciOpenParms,
543 0);
544 if((rc & 0xFFFF) != MCIERR_SUCCESS) {
545 mciError(rc);
546 return(FALSE);
547 }
548 DeviceId = mciOpenParms.usDeviceID;
549
550 memset( &mciAudioCaps , 0, sizeof(MCI_WAVE_GETDEVCAPS_PARMS));
551
552 mciAudioCaps.ulBitsPerSample = wBitsPerSample;
553 mciAudioCaps.ulFormatTag = DATATYPE_WAVEFORM;
554 mciAudioCaps.ulSamplesPerSec = nSamplesPerSec;
555 mciAudioCaps.ulChannels = nChannels;
556 mciAudioCaps.ulFormatMode = MCI_PLAY;
557 mciAudioCaps.ulItem = MCI_GETDEVCAPS_WAVE_FORMAT;
558
559 rc = mymciSendCommand(DeviceId, /* Device ID */
560 MCI_GETDEVCAPS,
561 MCI_WAIT | MCI_GETDEVCAPS_EXTENDED | MCI_GETDEVCAPS_ITEM,
562 (PVOID) &mciAudioCaps,
563 0);
564 if((rc & 0xFFFF) != MCIERR_SUCCESS) {
565 mciError(rc);
566 winrc = FALSE;
567 }
568 else winrc = TRUE;
569
570 // Close the device
571 mymciSendCommand(DeviceId,MCI_CLOSE,MCI_WAIT,(PVOID)&GenericParms,0);
572 return(winrc);
573}
574/******************************************************************************/
575/******************************************************************************/
576void DartWaveOut::mciError(ULONG ulError)
577{
578#ifdef DEBUG
579 char szError[256] = "";
580
581 mymciGetErrorString(ulError, szError, sizeof(szError));
582 dprintf(("WINMM: DartWaveOut: %s\n", szError));
583#endif
584}
585//******************************************************************************
586//******************************************************************************
587void DartWaveOut::handler(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer, ULONG ulFlags)
588{
589 ULONG buflength;
590 WAVEHDR *whdr, *prevhdr = NULL;
591
592 dprintf2(("WINMM: handler %d; buffers left %d", curPlayBuf, queuedbuffers));
593 if(ulFlags == MIX_STREAM_ERROR) {
594 if(ulStatus == ERROR_DEVICE_UNDERRUN) {
595 dprintf(("WINMM: WaveOut handler UNDERRUN! state %s", (State == STATE_PLAYING) ? "playing" : "stopped"));
596 if(State == STATE_PLAYING) {
597 fUnderrun = TRUE;
598 //save current position for when we continue later
599 ulUnderrunBase = bytesPlayed;
600 stop(); //out of buffers, so stop playback
601 }
602 return;
603 }
604 dprintf(("WINMM: WaveOut handler, Unknown error %X\n", ulStatus));
605 return;
606 }
607 if(State != STATE_PLAYING) {
608 return;
609 }
610
611 wmutex.enter();
612
613 bytesPlayed += MixBuffer[curPlayBuf].ulBufferLength;
614
615 //update our buffer index
616 if(curPlayBuf == PREFILLBUF_DART-1)
617 curPlayBuf = 0;
618 else curPlayBuf++;
619
620 fUnderrun = FALSE;
621
622 whdr = wavehdr;
623 while(whdr) {
624 if(whdr->reserved == WHDR_DONE)
625 {
626 if(bytesPlayed < bytesReturned + whdr->dwBufferLength) {
627 dprintf2(("Buffer marked done, but not yet played completely (play %d/%d, cop %d, ret %d)", bytesPlayed, getPosition(), bytesCopied, bytesReturned));
628 break; //not yet done
629 }
630
631 dprintf2(("WINMM: handler buf %X done (play %d/%d, cop %d, ret %d)", whdr, bytesPlayed, getPosition(), bytesCopied, bytesReturned));
632 queuedbuffers--;
633
634 whdr->dwFlags &= ~WHDR_INQUEUE;
635 whdr->dwFlags |= WHDR_DONE;
636 whdr->reserved = 0;
637
638 if(prevhdr == NULL)
639 wavehdr = whdr->lpNext;
640 else prevhdr->lpNext = whdr->lpNext;
641
642 whdr->lpNext = NULL;
643
644 bytesReturned += whdr->dwBufferLength;
645 wmutex.leave();
646
647 callback(WOM_DONE, (ULONG)whdr, 0);
648
649 wmutex.enter();
650 }
651 else break;
652
653 prevhdr = whdr;
654 whdr = whdr->lpNext;
655 }
656
657 if(wavehdr == NULL) {
658 //last buffer played -> no new ones -> return now
659 dprintf(("WINMM: WaveOut handler LAST BUFFER PLAYED! state %s (play %d (%d), cop %d, ret %d)", (State == STATE_PLAYING) ? "playing" : "stopped", bytesPlayed, getPosition(), bytesCopied, bytesReturned));
660 if(getPosition() > bytesPlayed) {
661 dprintf(("WINMM: WaveOut handler UNDERRUN! state %s", (State == STATE_PLAYING) ? "playing" : "stopped"));
662 //save current position for when we continue later
663 ulUnderrunBase = bytesPlayed;
664 fUnderrun = TRUE;
665 stop(); //out of buffers, so stop playback
666 }
667 wmutex.leave();
668 return;
669 }
670
671 writeBuffer();
672
673sendbuffer:
674 wmutex.leave();
675
676 //Transfer the buffer we just finished playing to DART
677 dprintf2(("WINMM: handler transfer buffer %d", pBuffer - MixBuffer));
678 USHORT selTIB = RestoreOS2FS(); // save current FS selector
679 MixSetupParms->pmixWrite(MixSetupParms->ulMixHandle, pBuffer, 1);
680 SetFS(selTIB); // switch back to the saved FS selector
681
682 dprintf2(("WINMM: handler DONE"));
683}
684/******************************************************************************/
685/******************************************************************************/
686void DartWaveOut::writeBuffer()
687{
688 ULONG buflength;
689
690 if(!fUnderrun && State == STATE_PLAYING && wavehdr == NULL && curFillBuf == curPlayBuf) {
691 dprintf2(("writeBuffer: no more room for more audio data"));
692 return; //no room left
693 }
694
695 if(curhdr == NULL)
696 curhdr = wavehdr;
697
698 while(curhdr && (curhdr->reserved == WHDR_DONE)) {
699 curhdr = curhdr->lpNext;
700 }
701
702 if(curhdr == NULL) {
703 return; //no unprocessed buffers left
704 }
705
706 dprintf2(("WINMM: handler cur (%d,%d), fill (%d,%d)\n", curPlayBuf, curPlayPos, curFillBuf, curFillPos));
707
708 while(curhdr) {
709 buflength = min((ULONG)MixBuffer[curFillBuf].ulBufferLength - curPlayPos,
710 (ULONG)curhdr->dwBufferLength - curFillPos);
711 dprintf2(("WINMM: copied %d bytes, cufFillPos = %d, curPlayPos = %d, dwBufferLength = %d\n", buflength, curFillPos, curPlayPos, curhdr->dwBufferLength));
712
713#ifdef DEBUG_DUMP_PCM
714 fwrite(curhdr->lpData + curFillPos, buflength, 1, pcmfile);
715#endif
716
717 memcpy((char *)MixBuffer[curFillBuf].pBuffer + curPlayPos,
718 curhdr->lpData + curFillPos, buflength);
719 curPlayPos += buflength;
720 curFillPos += buflength;
721 bytesCopied += buflength;
722
723 if(curFillPos == curhdr->dwBufferLength) {
724 dprintf2(("Buffer %d done ptr %x size %d %x %x %x %x %x %x", curFillBuf, curhdr->lpData, curhdr->dwBufferLength, curhdr->dwBytesRecorded, curhdr->dwUser, curhdr->dwFlags, curhdr->dwLoops, curhdr->lpNext, curhdr->reserved));
725
726 curFillPos = 0;
727 curhdr->reserved = WHDR_DONE;
728 //search for next unprocessed buffer
729 while(curhdr && (curhdr->reserved == WHDR_DONE))
730 curhdr = curhdr->lpNext;
731 }
732 if(curPlayPos == MixBuffer[curFillBuf].ulBufferLength) {
733 curPlayPos = 0;
734
735 if(++curFillBuf == PREFILLBUF_DART) {
736 curFillBuf = 0;
737 }
738 if(curFillBuf == curPlayBuf)
739 break; //no more room left
740 }
741 }
742}
743/******************************************************************************/
744/******************************************************************************/
745LONG APIENTRY WaveOutHandler(ULONG ulStatus,
746 PMCI_MIX_BUFFER pBuffer,
747 ULONG ulFlags)
748{
749 PTIB2 ptib2;
750 DartWaveOut *dwave;
751
752 dprintf2(("WaveOutHandler %x %x %x", ulStatus, pBuffer, ulFlags));
753
754 ptib2 = (PTIB2)_getTIBvalue(offsetof(TIB, tib_ptib2));
755 if(ptib2 && HIBYTE(ptib2->tib2_ulpri) != PRTYC_TIMECRITICAL &&
756 LOBYTE(ptib2->tib2_ulpri) != PRTYD_MAXIMUM)
757 {
758 dprintf(("Setting priority of DART thread to PRTYC_TIMECRITICAL/PRTYD_MAXIMUM"));
759 DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM, 0);
760 }
761 if(pBuffer && pBuffer->ulUserParm)
762 {
763 dwave = (DartWaveOut *)pBuffer->ulUserParm;
764 dwave->handler(ulStatus, pBuffer, ulFlags);
765 }
766 return(TRUE);
767}
768
769/******************************************************************************/
770/******************************************************************************/
771MMRESULT DartWaveOut::setVolume(ULONG ulVol)
772{
773 ULONG ulVolR = (((ulVol & 0xffff0000) >> 16 )*100)/0xFFFF; // Right Volume
774 ULONG ulVolL = ((ulVol& 0x0000ffff)*100)/0xFFFF; // Left Volume
775 MCI_SET_PARMS msp = {0};
776
777 dprintf(("DartWaveOut::setVolume %d %d", ulVolL, ulVolR));
778 volume = ulVol;
779
780// PD: My card (ESS 1868 PnP) driver can't change only
781// one channel Left or Right :-(
782//
783#ifdef GOOD_AUDIO_CARD_DRIVER
784
785 msp.ulAudio = MCI_SET_AUDIO_LEFT;
786 msp.ulLevel = ulVolL;
787
788 mymciSendCommand(DeviceId, MCI_SET,
789 MCI_WAIT | MCI_SET_AUDIO | MCI_SET_VOLUME,
790 &msp, 0);
791
792 msp.ulAudio = MCI_SET_AUDIO_RIGHT;
793 msp.ulLevel = ulVolR;
794
795#else
796 msp.ulAudio = MCI_SET_AUDIO_ALL;
797 msp.ulLevel = max(ulVolR,ulVolL);
798#endif
799
800 mymciSendCommand(DeviceId, MCI_SET,
801 MCI_WAIT | MCI_SET_AUDIO | MCI_SET_VOLUME,
802 &msp, 0);
803 return 0;
804}
805/******************************************************************************/
806/******************************************************************************/
807
Note: See TracBrowser for help on using the repository browser.