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

Last change on this file since 46 was 46, checked in by sandervl, 26 years ago

* empty log message *

File size: 18.9 KB
Line 
1/*
2 * Wave playback class
3 *
4 * Copyright 1998 Sander van Leeuwen (sandervl@xs4all.nl)
5 *
6 *
7 * Project Odin Software License can be found in LICENSE.TXT
8 *
9 */
10
11#define INCL_BASE
12#define INCL_OS2MM
13#include <os2.h>
14#include <os2me.h>
15#include <stdlib.h>
16#include <string.h>
17
18#define OS2_ONLY
19#include "win32type.h"
20
21#include "misc.h"
22#include "dwaveout.h"
23
24#ifndef min
25#define min(a, b) ((a > b) ? b : a)
26#endif
27
28LONG APIENTRY WaveOutHandler(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer, ULONG ulFlags);
29
30//TODO: mulaw, alaw & adpcm
31/******************************************************************************/
32/******************************************************************************/
33DartWaveOut::DartWaveOut(LPWAVEFORMATEX pwfx)
34{
35 Init(pwfx);
36}
37/******************************************************************************/
38/******************************************************************************/
39DartWaveOut::DartWaveOut(LPWAVEFORMATEX pwfx, ULONG nCallback, ULONG dwInstance)
40{
41 Init(pwfx);
42
43 callback = (LPDRVCALLBACK)nCallback;
44 this->dwInstance = dwInstance;
45
46 callback((ULONG)this, WOM_OPEN, dwInstance, 0, 0);
47}
48/******************************************************************************/
49/******************************************************************************/
50DartWaveOut::DartWaveOut(LPWAVEFORMATEX pwfx, HWND hwndCallback)
51{
52 Init(pwfx);
53
54 this->hwndCallback = hwndCallback;
55
56 WinPostMsg(hwndCallback, WOM_OPEN, 0, 0);
57}
58/******************************************************************************/
59/******************************************************************************/
60void DartWaveOut::Init(LPWAVEFORMATEX pwfx)
61{
62 MCI_GENERIC_PARMS GenericParms;
63 MCI_AMP_OPEN_PARMS AmpOpenParms;
64 APIRET rc;
65
66 curPlayBuf = curFillBuf = curFillPos = curPlayPos = 0;
67
68 fMixerSetup = FALSE;
69 next = NULL;
70 wavehdr = NULL;
71 curhdr = NULL;
72 callback = NULL;
73 hwndCallback = 0;
74 dwInstance = 0;
75 ulError = 0;
76 State = STATE_STOPPED;
77
78 MixBuffer = (MCI_MIX_BUFFER *)malloc(PREFILLBUF_DART*sizeof(MCI_MIX_BUFFER));
79 MixSetupParms = (MCI_MIXSETUP_PARMS *)malloc(sizeof(MCI_MIXSETUP_PARMS));
80 BufferParms = (MCI_BUFFER_PARMS *)malloc(sizeof(MCI_BUFFER_PARMS));
81
82 switch(pwfx->nBlockAlign) {
83 case 1://8 bits mono
84 BitsPerSample = 8;
85 break;
86 case 2://16 bits mono or 8 bits stereo
87 if(nChannels == 1)
88 BitsPerSample = 16;
89 else BitsPerSample = 8;
90 break;
91 case 4://16 bits stereo
92 BitsPerSample = 16;
93 break;
94 }
95 SampleRate = pwfx->nSamplesPerSec;
96 this->nChannels = pwfx->nChannels;
97 ulBufSize = DART_BUFSIZE;
98
99 // Setup the open structure, pass the playlist and tell MCI_OPEN to use it
100 memset(&AmpOpenParms,0,sizeof(AmpOpenParms));
101
102 AmpOpenParms.usDeviceID = ( USHORT ) 0;
103 AmpOpenParms.pszDeviceType = ( PSZ ) MCI_DEVTYPE_AUDIO_AMPMIX;
104
105 rc = mciSendCommand(0, MCI_OPEN,
106 MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE,
107 (PVOID) &AmpOpenParms,
108 0);
109 DeviceId = AmpOpenParms.usDeviceID;
110 if(rc) {
111#ifdef DEBUG
112 WriteLog("MCI_OPEN failed\n");
113#endif
114 mciError(rc);
115 ulError = MMSYSERR_NOTENABLED;
116 }
117 if(rc == 0) {
118 //Grab exclusive rights to device instance (NOT entire device)
119 GenericParms.hwndCallback = 0; //Not needed, so set to 0
120 rc = mciSendCommand(DeviceId, MCI_ACQUIREDEVICE, MCI_EXCLUSIVE_INSTANCE,
121 (PVOID)&GenericParms, 0);
122 if(rc) {
123#ifdef DEBUG
124 WriteLog("MCI_ACQUIREDEVICE failed\n");
125#endif
126 mciError(rc);
127 ulError = MMSYSERR_NOTENABLED;
128 }
129 }
130 State = STATE_STOPPED;
131
132 wmutex = new VMutex();
133 if(wmutex == NULL) {
134 ulError = MMSYSERR_NOTSUPPORTED;
135 }
136 if(wmutex)
137 wmutex->enter(VMUTEX_WAIT_FOREVER);
138
139 if(waveout == NULL) {
140 waveout = this;
141 }
142 else {
143 DartWaveOut *dwave = waveout;
144
145 while(dwave->next) {
146 dwave = dwave->next;
147 }
148 dwave->next = this;
149 }
150
151 if(wmutex)
152 wmutex->leave();
153}
154/******************************************************************************/
155/******************************************************************************/
156DartWaveOut::~DartWaveOut()
157{
158 MCI_GENERIC_PARMS GenericParms;
159
160 // Generic parameters
161 GenericParms.hwndCallback = 0; //hwndFrame
162
163 // Stop the playback.
164 mciSendCommand(DeviceId, MCI_STOP,MCI_WAIT, (PVOID)&GenericParms,0);
165
166 mciSendCommand(DeviceId,
167 MCI_BUFFER,
168 MCI_WAIT | MCI_DEALLOCATE_MEMORY,
169 (PVOID)&BufferParms,
170 0);
171
172 // Generic parameters
173 GenericParms.hwndCallback = 0; //hwndFrame
174
175 // Close the device
176 mciSendCommand(DeviceId, MCI_CLOSE, MCI_WAIT, (PVOID)&GenericParms, 0);
177
178 if(wmutex)
179 wmutex->enter(VMUTEX_WAIT_FOREVER);
180
181 State = STATE_STOPPED;
182
183 if(waveout == this) {
184 waveout = this->next;
185 }
186 else {
187 DartWaveOut *dwave = waveout;
188
189 while(dwave->next != this) {
190 dwave = dwave->next;
191 }
192 dwave->next = this->next;
193 }
194 if(wmutex)
195 wmutex->leave();
196
197 if(callback) {
198 callback((ULONG)this, WOM_CLOSE, dwInstance, 0, 0);
199 }
200 else
201 if(hwndCallback)
202 WinPostMsg(hwndCallback, WOM_CLOSE, 0, 0);
203
204 if(wmutex)
205 delete wmutex;
206
207 if(MixBuffer)
208 free(MixBuffer);
209 if(MixSetupParms)
210 free(MixSetupParms);
211 if(BufferParms)
212 free(BufferParms);
213}
214/******************************************************************************/
215/******************************************************************************/
216MMRESULT DartWaveOut::getError()
217{
218 return(ulError);
219}
220/******************************************************************************/
221/******************************************************************************/
222MMRESULT DartWaveOut::write(LPWAVEHDR pwh, UINT cbwh)
223{
224 MCI_GENERIC_PARMS GenericParms = {0};
225 APIRET rc;
226 int i, buflength;
227
228 if(fMixerSetup == FALSE) {
229#ifdef DEBUG
230 WriteLog("device acquired\n");
231#endif
232 /* Set the MixSetupParms data structure to match the loaded file.
233 * This is a global that is used to setup the mixer.
234 */
235 memset(MixSetupParms, 0, sizeof( MCI_MIXSETUP_PARMS ) );
236
237 MixSetupParms->ulBitsPerSample = BitsPerSample;
238 MixSetupParms->ulSamplesPerSec = SampleRate;
239 MixSetupParms->ulFormatTag = MCI_WAVE_FORMAT_PCM;
240 MixSetupParms->ulChannels = nChannels;
241
242#ifdef DEBUG
243 WriteLog("bps %d, sps %d chan %d\n", BitsPerSample, SampleRate, nChannels);
244#endif
245
246 /* Setup the mixer for playback of wave data
247 */
248 MixSetupParms->ulFormatMode = MCI_PLAY;
249 MixSetupParms->ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
250 MixSetupParms->pmixEvent = WaveOutHandler;
251
252 rc = mciSendCommand(DeviceId,
253 MCI_MIXSETUP,
254 MCI_WAIT | MCI_MIXSETUP_INIT,
255 (PVOID)MixSetupParms,
256 0);
257
258 if ( rc != MCIERR_SUCCESS ) {
259 mciError(rc);
260 mciSendCommand(DeviceId, MCI_RELEASEDEVICE, MCI_WAIT,
261 (PVOID)&GenericParms, 0);
262 return(MMSYSERR_NOTSUPPORTED);
263 }
264
265 /*
266 * Set up the BufferParms data structure and allocate
267 * device buffers from the Amp-Mixer
268 */
269#ifdef DEBUG
270 WriteLog("mix setup %d, %d\n", pwh->dwBufferLength, pwh->dwBufferLength);
271#endif
272#if 1
273 ulBufSize = pwh->dwBufferLength/2;
274#else
275 if(pwh->dwBufferLength >= 512 && pwh->dwBufferLength <= 1024)
276 ulBufSize = pwh->dwBufferLength;
277 else ulBufSize = 1024;
278#endif
279
280 MixSetupParms->ulBufferSize = ulBufSize;
281
282 BufferParms->ulNumBuffers = PREFILLBUF_DART;
283 BufferParms->ulBufferSize = MixSetupParms->ulBufferSize;
284 BufferParms->pBufList = MixBuffer;
285
286 for(i=0;i<PREFILLBUF_DART;i++) {
287 MixBuffer[i].ulUserParm = (ULONG)this;
288 }
289
290 rc = mciSendCommand(DeviceId,
291 MCI_BUFFER,
292 MCI_WAIT | MCI_ALLOCATE_MEMORY,
293 (PVOID)BufferParms,
294 0);
295
296 if(ULONG_LOWD(rc) != MCIERR_SUCCESS) {
297 mciError(rc);
298 mciSendCommand(DeviceId, MCI_RELEASEDEVICE, MCI_WAIT,
299 (PVOID)&GenericParms, 0);
300 return(MMSYSERR_NOTSUPPORTED);
301 }
302
303 wmutex->enter(VMUTEX_WAIT_FOREVER);
304 fMixerSetup = TRUE;
305
306 curPlayBuf = curFillBuf = curFillPos = curPlayPos = 0;
307
308 for(i=0;i<PREFILLBUF_DART;i++) {
309 memset(MixBuffer[i].pBuffer, 0, MixBuffer[i].ulBufferLength);
310 }
311#ifdef DEBUG
312 WriteLog("Dart opened, bufsize = %d\n", MixBuffer[i].ulBufferLength);
313#endif
314
315 wavehdr = pwh;
316 curhdr = pwh;
317 pwh->lpNext = NULL;
318
319 while(TRUE) {
320 buflength = min((ULONG)MixBuffer[curFillBuf].ulBufferLength - curPlayPos,
321 (ULONG)wavehdr->dwBufferLength - curFillPos);
322#ifdef DEBUG
323 WriteLog("Copying %d data; curPlayPos = %d curFillPos = %d\n", buflength, curPlayPos, curFillPos);
324#endif
325 memcpy((char *)MixBuffer[curFillBuf].pBuffer + curPlayPos,
326 wavehdr->lpData + curFillPos,
327 buflength);
328
329 curPlayPos += buflength;
330 curFillPos += buflength;
331 if(curFillPos == wavehdr->dwBufferLength) {
332#ifdef DEBUG
333 WriteLog("Processed first win32 buffer\n");
334#endif
335 curFillPos = 0;
336 wavehdr->dwFlags |= WHDR_DONE;
337 curhdr = NULL;
338 }
339 if(curPlayPos == MixBuffer[curPlayBuf].ulBufferLength) {
340 if(++curPlayBuf == PREFILLBUF_DART) {
341 curPlayBuf = 0;
342 break;
343 }
344 curPlayPos = 0;
345 }
346 if(curFillPos == 0)
347 break;
348 }
349#ifdef DEBUG
350 WriteLog("MixSetupParms = %X\n", MixSetupParms);
351#endif
352 State = STATE_PLAYING;
353 wmutex->leave();
354
355 MixSetupParms->pmixWrite(MixSetupParms->ulMixHandle,
356 MixBuffer,
357 PREFILLBUF_DART);
358#ifdef DEBUG
359 WriteLog("Dart playing\n");
360#endif
361 }
362 else {
363 wmutex->enter(VMUTEX_WAIT_FOREVER);
364 pwh->lpNext = NULL;
365 if(wavehdr) {
366 WAVEHDR *chdr = wavehdr;
367 while(chdr->lpNext) {
368 chdr = chdr->lpNext;
369 }
370 chdr->lpNext = pwh;
371 }
372 else wavehdr = pwh;
373 wmutex->leave();
374 if(State != STATE_PLAYING) {//continue playback
375 restart();
376 }
377 }
378
379 return(MMSYSERR_NOERROR);
380}
381/******************************************************************************/
382/******************************************************************************/
383MMRESULT DartWaveOut::pause()
384{
385 MCI_GENERIC_PARMS Params;
386
387 if(State != STATE_PLAYING)
388 return(MMSYSERR_HANDLEBUSY);
389
390 wmutex->enter(VMUTEX_WAIT_FOREVER);
391 State = STATE_PAUSED;
392 wmutex->leave();
393
394 memset(&Params, 0, sizeof(Params));
395
396 // Stop the playback.
397 mciSendCommand(DeviceId, MCI_PAUSE, MCI_WAIT, (PVOID)&Params, 0);
398
399 return(MMSYSERR_NOERROR);
400}
401/******************************************************************************/
402/******************************************************************************/
403MMRESULT DartWaveOut::reset()
404{
405 MCI_GENERIC_PARMS Params;
406
407 dprintf(("DartWaveOut::reset %s", (State == STATE_PLAYING) ? "playing" : "stopped"));
408 if(State != STATE_PLAYING)
409 return(MMSYSERR_HANDLEBUSY);
410
411 memset(&Params, 0, sizeof(Params));
412
413 // Stop the playback.
414 mciSendCommand(DeviceId, MCI_STOP, MCI_WAIT, (PVOID)&Params, 0);
415
416#ifdef DEBUG
417 WriteLog("Nr of threads blocked on mutex = %d\n", wmutex->getNrBlocked());
418#endif
419
420 wmutex->enter(VMUTEX_WAIT_FOREVER);
421 while(wavehdr) {
422 wavehdr->dwFlags |= WHDR_DONE;
423 wmutex->leave();
424 if(callback) {
425 callback((ULONG)this, WOM_DONE, dwInstance, wavehdr->dwUser, (ULONG)wavehdr);
426 }
427 else
428 if(hwndCallback)
429 WinPostMsg(hwndCallback, WOM_DONE, (MPARAM)wavehdr->dwUser, (MPARAM)wavehdr);
430
431 wmutex->enter(VMUTEX_WAIT_FOREVER);
432 wavehdr = wavehdr->lpNext;
433 }
434 wavehdr = NULL;
435 State = STATE_STOPPED;
436
437 wmutex->leave();
438 return(MMSYSERR_NOERROR);
439}
440/******************************************************************************/
441/******************************************************************************/
442MMRESULT DartWaveOut::restart()
443{
444 dprintf(("DartWaveOut::restart"));
445 wmutex->enter(VMUTEX_WAIT_FOREVER);
446 State = STATE_PLAYING;
447 wmutex->leave();
448 MixSetupParms->pmixWrite(MixSetupParms->ulMixHandle,
449 &MixBuffer[curPlayBuf],
450 PREFILLBUF_DART);
451 return(MMSYSERR_NOERROR);
452}
453/******************************************************************************/
454/******************************************************************************/
455BOOL DartWaveOut::queryFormat(ULONG formatTag, ULONG nChannels,
456 ULONG nSamplesPerSec, ULONG sampleSize)
457{
458 MCI_WAVE_GETDEVCAPS_PARMS mciAudioCaps;
459 MCI_GENERIC_PARMS GenericParms;
460 MCI_OPEN_PARMS mciOpenParms; /* open parms for MCI_OPEN */
461 int i, freqbits = 0;
462 ULONG rc, DeviceId;
463 BOOL winrc;
464
465 memset(&mciOpenParms, /* Object to fill with zeros. */
466 0, /* Value to place into the object. */
467 sizeof( mciOpenParms ) ); /* How many zero's to use. */
468
469 mciOpenParms.pszDeviceType = (PSZ)MCI_DEVTYPE_WAVEFORM_AUDIO;
470
471 rc = mciSendCommand( (USHORT) 0,
472 MCI_OPEN,
473 MCI_WAIT | MCI_OPEN_TYPE_ID,
474 (PVOID) &mciOpenParms,
475 0);
476 if (rc != 0) {
477 return(FALSE);
478 }
479 DeviceId = mciOpenParms.usDeviceID;
480
481 memset( &mciAudioCaps , 0, sizeof(MCI_WAVE_GETDEVCAPS_PARMS));
482
483 switch(sampleSize) {
484 case 1:
485 mciAudioCaps.ulBitsPerSample = 8;
486 break;
487 case 2:
488 if(nChannels == 1)
489 mciAudioCaps.ulBitsPerSample = 16;
490 else mciAudioCaps.ulBitsPerSample = 8;
491 break;
492 case 4:
493 mciAudioCaps.ulBitsPerSample = 16;
494 break;
495 }
496 mciAudioCaps.ulFormatTag = DATATYPE_WAVEFORM;
497 mciAudioCaps.ulSamplesPerSec = nSamplesPerSec;
498 mciAudioCaps.ulChannels = nChannels;
499 mciAudioCaps.ulFormatMode = MCI_PLAY;
500 mciAudioCaps.ulItem = MCI_GETDEVCAPS_WAVE_FORMAT;
501
502 rc = mciSendCommand(DeviceId, /* Device ID */
503 MCI_GETDEVCAPS,
504 MCI_WAIT | MCI_GETDEVCAPS_EXTENDED | MCI_GETDEVCAPS_ITEM,
505 (PVOID) &mciAudioCaps,
506 0);
507 if((rc & 0xFFFF) != MCIERR_SUCCESS) {
508 mciError(rc);
509 winrc = FALSE;
510 }
511 // Close the device
512 mciSendCommand(DeviceId,MCI_CLOSE,MCI_WAIT,(PVOID)&GenericParms,0);
513 return(winrc);
514}
515/******************************************************************************/
516/******************************************************************************/
517void DartWaveOut::mciError(ULONG ulError)
518{
519#ifdef DEBUG
520 char szError[256] = "";
521
522 mciGetErrorString(ulError, szError, sizeof(szError));
523 WriteLog("WINMM: DartWaveOut: %s\n", szError);
524#endif
525}
526//******************************************************************************
527//******************************************************************************
528BOOL DartWaveOut::find(DartWaveOut *dwave)
529{
530 DartWaveOut *curwave = waveout;
531
532 while(curwave) {
533 if(dwave == curwave) {
534 return(TRUE);
535 }
536 curwave = curwave->next;
537 }
538
539#ifdef DEBUG
540 WriteLog("WINMM:DartWaveOut not found!\n");
541#endif
542 return(FALSE);
543}
544/******************************************************************************/
545/******************************************************************************/
546void DartWaveOut::handler(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer, ULONG ulFlags)
547{
548 ULONG buflength;
549 WAVEHDR *whdr = wavehdr, *prevhdr = NULL;
550
551#ifdef DEBUG1
552 WriteLog("WINMM: handler %d\n", curPlayBuf);
553#endif
554 if(ulFlags == MIX_STREAM_ERROR) {
555 if(ulStatus == ERROR_DEVICE_UNDERRUN) {
556 dprintf(("WINMM: WaveOut handler UNDERRUN!\n"));
557 pause(); //out of buffers, so pause playback
558 return;
559 }
560 dprintf(("WINMM: WaveOut handler, Unknown error %X\n", ulStatus));
561 return;
562 }
563 wmutex->enter(VMUTEX_WAIT_FOREVER);
564
565 while(whdr) {
566 if(whdr->dwFlags & WHDR_DONE) {
567#ifdef DEBUG1
568 WriteLog("WINMM: handler buf %X done\n", whdr);
569#endif
570 whdr->dwFlags &= ~WHDR_INQUEUE;
571
572 if(prevhdr == NULL)
573 wavehdr = whdr->lpNext;
574 else prevhdr->lpNext = whdr->lpNext;
575
576 whdr->lpNext = NULL;
577 wmutex->leave();
578
579 if(callback) {
580 callback((ULONG)this, WOM_DONE, dwInstance, whdr->dwUser, (ULONG)whdr);
581 }
582 else
583 if(hwndCallback)
584 WinPostMsg(hwndCallback, WOM_DONE, (MPARAM)whdr->dwUser, (MPARAM)whdr);
585
586 wmutex->enter(VMUTEX_WAIT_FOREVER);
587 }
588 prevhdr = whdr;
589 whdr = whdr->lpNext;
590 }
591
592 if(curhdr == NULL)
593 curhdr = wavehdr;
594
595#ifdef DEBUG1
596 WriteLog("WINMM: handler cur (%d,%d), fill (%d,%d)\n", curPlayBuf, curPlayPos, curFillBuf, curFillPos);
597#endif
598
599 while(curhdr) {
600 buflength = min((ULONG)MixBuffer[curFillBuf].ulBufferLength - curPlayPos,
601 (ULONG)curhdr->dwBufferLength - curFillPos);
602 memcpy((char *)MixBuffer[curFillBuf].pBuffer + curPlayPos,
603 curhdr->lpData + curFillPos,
604 buflength);
605 curPlayPos += buflength;
606 curFillPos += buflength;
607#ifdef DEBUG1
608 WriteLog("WINMM: copied %d bytes, cufFillPos = %d, dwBufferLength = %d\n", buflength, curFillPos, curhdr->dwBufferLength);
609#endif
610 if(curFillPos == curhdr->dwBufferLength) {
611#ifdef DEBUG1
612 WriteLog("Buffer %d done\n", curFillBuf);
613#endif
614 curFillPos = 0;
615 curhdr->dwFlags |= WHDR_DONE;
616 //search for next unprocessed buffer
617 while(curhdr && curhdr->dwFlags & WHDR_DONE)
618 curhdr = curhdr->lpNext;
619 }
620 if(curPlayPos == MixBuffer[curFillBuf].ulBufferLength) {
621 curPlayPos = 0;
622 if(++curFillBuf == PREFILLBUF_DART) {
623 curFillBuf = 0;
624 }
625 if(curFillBuf == curPlayBuf)
626 break; //no more room left
627 }
628 }
629
630 if(curPlayBuf == PREFILLBUF_DART-1)
631 curPlayBuf = 0;
632 else curPlayBuf++;
633
634 wmutex->leave();
635 //Transfer buffer to DART
636 MixSetupParms->pmixWrite(MixSetupParms->ulMixHandle, &MixBuffer[curPlayBuf], 1);
637}
638/******************************************************************************/
639/******************************************************************************/
640LONG APIENTRY WaveOutHandler(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer,
641 ULONG ulFlags)
642{
643 DartWaveOut *dwave;
644 PTIB ptib;
645 PPIB ppib;
646
647 DosGetInfoBlocks(&ptib, &ppib);
648// dprintf(("WaveOutHandler: thread %d prio %X", ptib->tib_ptib2->tib2_ultid, ptib->tib_ptib2->tib2_ulpri));
649 if(pBuffer && pBuffer->ulUserParm) {
650 dwave = (DartWaveOut *)pBuffer->ulUserParm;
651 dwave->handler(ulStatus, pBuffer, ulFlags);
652 }
653 return(TRUE);
654}
655/******************************************************************************/
656/******************************************************************************/
657DartWaveOut *DartWaveOut::waveout = NULL;
Note: See TracBrowser for help on using the repository browser.