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

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

resume after underrun fix

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