source: trunk/src/winmm/waveoutdaud.cpp@ 5487

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

cleanup daudio thread properly

File size: 13.8 KB
Line 
1/* $Id: waveoutdaud.cpp,v 1.4 2001-04-06 14:36:43 sandervl Exp $ */
2
3/*
4 * Wave playback class (DirectAudio)
5 *
6 * Copyright 2001 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 <os2mewrap.h> //Odin32 OS/2 MMPM/2 api wrappers
24#include <stdlib.h>
25#include <string.h>
26#define OS2_ONLY
27#include <win32api.h>
28#include <wprocess.h>
29#include <audio.h>
30#include <daudio.h>
31
32#include "misc.h"
33#include "waveoutdaud.h"
34
35#define DBG_LOCALLOG DBG_waveoutdaud
36#include "dbglocal.h"
37
38DWORD WIN32API DAudioThreadHandler(LPVOID pUserData);
39
40//TODO: mulaw, alaw & adpcm
41/******************************************************************************/
42/******************************************************************************/
43DAudioWaveOut::DAudioWaveOut(LPWAVEFORMATEX pwfx, ULONG fdwOpen, ULONG nCallback, ULONG dwInstance)
44 : WaveOut(pwfx, fdwOpen, nCallback, dwInstance)
45{
46 APIRET rc;
47 ULONG action;
48 HFILE hDriver;
49 MCI_AUDIO_INIT init = {0};
50 DAUDIO_CMD cmd;
51 ULONG ParmLength = 0, DataLength;
52
53 fUnderrun = FALSE;
54 hSem = 0;
55
56 rc = DosOpen("DAUDIO1$", &hDAudioDrv, &action, 0,
57 FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE |
58 OPEN_SHARE_DENYNONE | OPEN_FLAGS_WRITE_THROUGH,
59 NULL );
60 if(rc) {
61 dprintf(("DosOpen failed with error %d\n", rc));
62 ulError = MMSYSERR_NODRIVER;
63 goto fail;
64 }
65
66 DataLength = sizeof(init);
67
68 init.lSRate = pwfx->nSamplesPerSec;
69 init.lBitsPerSRate = pwfx->wBitsPerSample;
70 init.sChannels = pwfx->nChannels;
71 init.sMode = PCM; //todo!!
72
73 rc = DosDevIOCtl(hDAudioDrv, DAUDIO_IOCTL_CAT, DAUDIO_OPEN, NULL, 0,
74 &ParmLength, &init, DataLength, &DataLength);
75 if(rc) {
76 dprintf(("DosDevIOCtl failed with error %d\n", rc));
77 ulError = MMSYSERR_NODRIVER;
78 goto fail;
79 }
80 if(init.sReturnCode != 0) {
81 dprintf(("init.sReturnCode = %d\n", init.sReturnCode));
82 ulError = MMSYSERR_NODRIVER;
83 goto fail;
84 }
85
86 rc = DosCreateEventSem(NULL, &hSem, DC_SEM_SHARED, 0);
87 if(rc) {
88 dprintf(("DosCreateEventSem failed with error %d\n", rc));
89 ulError = MMSYSERR_NODRIVER;
90 goto fail;
91 }
92 cmd.Thread.hSemaphore = hSem;
93 rc = DosDevIOCtl(hDAudioDrv, DAUDIO_IOCTL_CAT, DAUDIO_REGISTER_THREAD, NULL, 0,
94 &ParmLength, &cmd, DataLength, &DataLength);
95 if(rc) {
96 dprintf(("DosDevIOCtl failed with error %d\n", rc));
97 ulError = MMSYSERR_NODRIVER;
98 goto fail;
99 }
100
101 hThread = CreateThread(NULL, 0x4000, (LPTHREAD_START_ROUTINE)DAudioThreadHandler,
102 (LPVOID)this, 0, &dwThreadID);
103
104 setVolume(volume);
105
106 if(!ulError)
107 callback(WOM_OPEN, 0, 0);
108
109fail:
110 return;
111}
112/******************************************************************************/
113/******************************************************************************/
114DAudioWaveOut::~DAudioWaveOut()
115{
116 DAUDIO_CMD cmd;
117
118 if(!ulError)
119 callback(WOM_CLOSE, 0, 0);
120
121 if(hDAudioDrv) {
122 cmd.Thread.hSemaphore = hSem;
123 sendIOCTL(DAUDIO_DEREGISTER_THREAD, &cmd);
124 sendIOCTL(DAUDIO_CLOSE, &cmd);
125 DosClose(hDAudioDrv);
126 hDAudioDrv = 0;
127 }
128 if(hSem) {
129 DosPostEventSem(hSem);
130 DosCloseEventSem(hSem);
131 }
132}
133/******************************************************************************/
134/******************************************************************************/
135MMRESULT DAudioWaveOut::write(LPWAVEHDR pwh, UINT cbwh)
136{
137 DAUDIO_CMD cmd;
138
139 queuedbuffers++;
140 pwh->lpNext = NULL;
141 pwh->reserved = 0;
142 wmutex.enter(VMUTEX_WAIT_FOREVER);
143 if(wavehdr) {
144 WAVEHDR *chdr = wavehdr;
145 while(chdr->lpNext) {
146 chdr = chdr->lpNext;
147 }
148 chdr->lpNext = pwh;
149 }
150 else wavehdr = pwh;
151
152 wmutex.leave();
153
154 cmd.Buffer.lpBuffer = (ULONG)pwh->lpData;
155 cmd.Buffer.ulBufferLength = pwh->dwBufferLength;
156 if(sendIOCTL(DAUDIO_ADDBUFFER, &cmd)) {
157 dprintf(("Unable to add buffer!!!!!"));
158 return MMSYSERR_ERROR;
159 }
160
161 if(State == STATE_STOPPED) {//continue playback
162 restart();
163 }
164 else
165 if(fUnderrun) {
166 dprintf(("Resume playback after underrun"));
167 fUnderrun = FALSE;
168 State = STATE_PLAYING;
169
170 // Resume the playback.
171 resume();
172 }
173 return(MMSYSERR_NOERROR);
174}
175/******************************************************************************/
176/******************************************************************************/
177MMRESULT DAudioWaveOut::pause()
178{
179 DAUDIO_CMD cmd;
180
181 dprintf(("WINMM: DAudioWaveOut::pause"));
182
183 // Pause the playback.
184 sendIOCTL(DAUDIO_PAUSE, &cmd);
185
186 wmutex.enter(VMUTEX_WAIT_FOREVER);
187 if(State != STATE_PLAYING) {
188 State = STATE_PAUSED;
189 wmutex.leave();
190 return(MMSYSERR_NOERROR);
191 }
192
193 State = STATE_PAUSED;
194 wmutex.leave();
195
196 return(MMSYSERR_NOERROR);
197}
198/******************************************************************************/
199/******************************************************************************/
200MMRESULT DAudioWaveOut::resume()
201{
202 DAUDIO_CMD cmd;
203
204 return sendIOCTL(DAUDIO_RESUME, &cmd);
205}
206/******************************************************************************/
207/******************************************************************************/
208MMRESULT DAudioWaveOut::stop()
209{
210 DAUDIO_CMD cmd;
211 MMRESULT rc;
212
213 dprintf(("DAudioWaveOut::stop %s", (State == STATE_PLAYING) ? "playing" : "stopped"));
214 if(State != STATE_PLAYING)
215 return(MMSYSERR_HANDLEBUSY);
216
217 // Stop the playback.
218 rc = sendIOCTL(DAUDIO_STOP, &cmd);
219
220 State = STATE_STOPPED;
221 fUnderrun = FALSE;
222
223 bytesPlayed = bytesCopied = bytesReturned = 0;
224
225 return rc;
226}
227/******************************************************************************/
228/******************************************************************************/
229MMRESULT DAudioWaveOut::reset()
230{
231 DAUDIO_CMD cmd;
232 LPWAVEHDR tmpwavehdr;
233
234 dprintf(("DAudioWaveOut::reset %s", (State == STATE_PLAYING) ? "playing" : "stopped"));
235 if(State != STATE_PLAYING)
236 return(MMSYSERR_HANDLEBUSY);
237
238 // Stop the playback.
239 sendIOCTL(DAUDIO_STOP, &cmd);
240
241 wmutex.enter(VMUTEX_WAIT_FOREVER);
242 while(wavehdr)
243 {
244 wavehdr->dwFlags |= WHDR_DONE;
245 wavehdr->dwFlags &= ~WHDR_INQUEUE;
246 wavehdr->reserved = 0;
247 tmpwavehdr = wavehdr;
248 wavehdr = wavehdr->lpNext;
249 tmpwavehdr->lpNext = NULL;
250 wmutex.leave();
251
252 callback(WOM_DONE, (ULONG)tmpwavehdr, 0);
253 wmutex.enter(VMUTEX_WAIT_FOREVER);
254 }
255 wavehdr = NULL;
256 State = STATE_STOPPED;
257 fUnderrun = FALSE;
258
259 bytesPlayed = bytesCopied = bytesReturned = 0;
260 queuedbuffers = 0;
261
262 wmutex.leave();
263 return(MMSYSERR_NOERROR);
264}
265/******************************************************************************/
266/******************************************************************************/
267MMRESULT DAudioWaveOut::restart()
268{
269 DAUDIO_CMD cmd;
270
271 dprintf(("DAudioWaveOut::restart"));
272 if(State == STATE_PLAYING)
273 return(MMSYSERR_NOERROR);
274
275 wmutex.enter(VMUTEX_WAIT_FOREVER);
276 State = STATE_PLAYING;
277 fUnderrun = FALSE;
278 wmutex.leave();
279
280 return sendIOCTL(DAUDIO_START, &cmd);
281}
282/******************************************************************************/
283/******************************************************************************/
284ULONG DAudioWaveOut::getPosition()
285{
286 DAUDIO_CMD cmd;
287 MMRESULT rc;
288
289 rc = sendIOCTL(DAUDIO_GETPOS, &cmd);
290 if(rc) {
291 return 0xFFFFFFFF;
292 }
293 return cmd.Pos.ulCurrentPos;
294}
295/******************************************************************************/
296/******************************************************************************/
297BOOL DAudioWaveOut::queryFormat(ULONG formatTag, ULONG nChannels, ULONG nSamplesPerSec, ULONG sampleSize)
298{
299 ULONG ParmLength = 0, DataLength;
300 MCI_AUDIO_INIT init = {0};
301 APIRET rc;
302 ULONG action;
303 HFILE hDriver;
304
305 rc = DosOpen("DAUDIO1$", &hDriver, &action, 0,
306 FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE |
307 OPEN_SHARE_DENYNONE | OPEN_FLAGS_WRITE_THROUGH,
308 NULL );
309 if(rc) {
310 dprintf(("DosOpen failed with error %d\n", rc));
311 return FALSE;
312 }
313
314 DataLength = sizeof(init);
315
316 init.lSRate = nSamplesPerSec;
317 init.lBitsPerSRate = sampleSize;
318 init.sChannels = nChannels;
319 init.sMode = PCM; //TODO: check formattag for ulaw/alaw/adpcm
320 rc = DosDevIOCtl(hDriver, DAUDIO_IOCTL_CAT, DAUDIO_QUERYFORMAT, NULL, 0,
321 &ParmLength, &init, DataLength, &DataLength);
322
323 if(rc) {
324 dprintf(("DosDevIOCtl failed with error %d\n", rc));
325 goto fail;
326 }
327 if(init.sReturnCode != 0) {
328 dprintf(("init.sReturnCode = %d\n", init.sReturnCode));
329 goto fail;
330 }
331
332 DosClose(hDriver);
333 return TRUE;
334
335fail:
336 DosClose(hDriver);
337 return FALSE;
338}
339/******************************************************************************/
340/******************************************************************************/
341BOOL DAudioWaveOut::isDirectAudioAvailable()
342{
343 static BOOL fAvailable = FALSE;
344 static BOOL fTested = FALSE;
345
346 APIRET rc;
347 ULONG action;
348 HFILE hDriver;
349
350 if(!fTested) {
351 rc = DosOpen("DAUDIO1$", &hDriver, &action, 0,
352 FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE |
353 OPEN_SHARE_DENYNONE | OPEN_FLAGS_WRITE_THROUGH,
354 NULL );
355 fTested = TRUE;
356 if(rc) {
357 return FALSE;
358 }
359 DosClose(hDriver);
360 fAvailable = TRUE;
361 }
362 return fAvailable;
363
364}
365/******************************************************************************/
366/******************************************************************************/
367MMRESULT DAudioWaveOut::setVolume(ULONG ulVol)
368{
369 DAUDIO_CMD cmd;
370
371 //Scale down from 0-64k-1 -> 0-100
372 cmd.Vol.VolumeR = (((ulVol & 0xFFFF0000) >> 16)*100)/0xFFFF;
373 cmd.Vol.VolumeL = ((ulVol & 0x0000FFFF) *100)/0xFFFF;
374 return sendIOCTL(DAUDIO_SETVOLUME, &cmd);
375}
376/******************************************************************************/
377/******************************************************************************/
378MMRESULT DAudioWaveOut::sendIOCTL(ULONG cmd, DAUDIO_CMD *pDataPacket)
379{
380 ULONG DataLength, ParmLength = 0;
381 APIRET rc;
382
383 DataLength = sizeof(DAUDIO_CMD);
384
385 rc = DosDevIOCtl(hDAudioDrv, DAUDIO_IOCTL_CAT, cmd, NULL, 0,
386 &ParmLength, pDataPacket, DataLength, &DataLength);
387 if(rc) {
388 dprintf(("DosDevIOCtl failed with error %d (command %d)", rc, cmd));
389 return MMSYSERR_ERROR;
390 }
391 return(MMSYSERR_NOERROR);
392
393}
394/******************************************************************************/
395//TODO: Not entirely safe. (assumption that we get called for each buffer so we
396// always notify the win32 app for each buffer that was processed)
397/******************************************************************************/
398BOOL DAudioWaveOut::handler()
399{
400 LPWAVEHDR whdr = wavehdr;
401
402 dprintf2(("WINMM: handler buf %X done (play %d/%d, cop %d, ret %d)", whdr, bytesPlayed, getPosition(), bytesCopied, bytesReturned));
403
404 if(State != STATE_PLAYING || whdr == NULL)
405 return FALSE;
406
407 wmutex.enter();
408 queuedbuffers--;
409 whdr->dwFlags &= ~WHDR_INQUEUE;
410 whdr->dwFlags |= WHDR_DONE;
411 whdr->reserved = 0;
412 wavehdr = whdr->lpNext;
413 whdr->lpNext = NULL;
414
415 bytesReturned += whdr->dwBufferLength;
416 wmutex.leave();
417
418 callback(WOM_DONE, (ULONG)whdr, 0);
419 return TRUE;
420}
421/******************************************************************************/
422/******************************************************************************/
423DWORD WIN32API DAudioThreadHandler(LPVOID pUserData)
424{
425 APIRET rc;
426 ULONG postcnt;
427 HEV hSem;
428 BOOL fResult = TRUE;
429
430 DAudioWaveOut *dwave = (DAudioWaveOut *)pUserData;
431
432 if(WaveOut::find(dwave) == FALSE) {
433 dprintf(("DAudioThreadHandler: can't find waveout stream %x!!!", pUserData));
434 return 0;
435 }
436 hSem = dwave->hSem;
437
438 DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM, 0);
439
440 while(TRUE)
441 {
442 dprintf2(("Waiting for buffer notification\n"));
443 rc = DosWaitEventSem(hSem, SEM_INDEFINITE_WAIT);
444 if(rc) {
445 dprintf(("DosWaitEventSem failed with error %d\n", rc));
446 return 0;
447 }
448
449 rc = DosResetEventSem(hSem, &postcnt);
450 if(rc) {
451 dprintf(("DosWaitEventSem failed with error %d\n", rc));
452 return 0;
453 }
454 if(WaveOut::find(dwave) == FALSE) {
455 dprintf(("DAudioThreadHandler: can't find waveout stream %x", pUserData));
456 break;
457 }
458 for(int i=0;i<postcnt;i++) {
459 fResult = dwave->handler();
460 if(fResult == FALSE) {
461 break;
462 }
463 }
464 if(fResult == FALSE) {
465 dprintf(("DAudioThreadHandler: finished waveout stream %x", pUserData));
466 break;
467 }
468 }
469 return 0;
470}
471/******************************************************************************/
472/******************************************************************************/
473
Note: See TracBrowser for help on using the repository browser.