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

Last change on this file since 21358 was 21358, checked in by rlwalsh, 16 years ago

add FlashWaveOut class to winmm - see Ticket #2

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