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

Last change on this file since 4 was 4, checked in by ktk, 26 years ago

Import

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