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

Last change on this file since 9979 was 9979, checked in by sandervl, 22 years ago

Different method for detecting DirectAudio (uniaud, sblive, c-media) ; Added custom build function to disable DirectAudio (always use DART)

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