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

Last change on this file since 668 was 668, checked in by phaller, 26 years ago

Fix: added SetWin32TIB wrappers to callback functions

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