source: trunk/src/winmm/dwaveout.cpp@ 5348

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

underrun changes (pause & restart properly)

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