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

Last change on this file since 8706 was 8568, checked in by sandervl, 23 years ago

Updates for wave playback

File size: 13.5 KB
Line 
1/* $Id: waveoutdaud.cpp,v 1.8 2002-06-04 17:36:56 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();
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 || fUnderrun) {
163 wmutex.enter();
164 State = STATE_PLAYING;
165 fUnderrun = FALSE;
166 wmutex.leave();
167
168 if(sendIOCTL(DAUDIO_START, &cmd)) {
169 dprintf(("Unable to (re)start stream!!!!!"));
170 return MMSYSERR_ERROR;
171 }
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();
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();
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();
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/******************************************************************************/
267ULONG DAudioWaveOut::getPosition()
268{
269 DAUDIO_CMD cmd;
270 MMRESULT rc;
271
272 rc = sendIOCTL(DAUDIO_GETPOS, &cmd);
273 if(rc) {
274 return 0xFFFFFFFF;
275 }
276 return cmd.Pos.ulCurrentPos;
277}
278/******************************************************************************/
279/******************************************************************************/
280BOOL DAudioWaveOut::queryFormat(ULONG formatTag, ULONG nChannels, ULONG nSamplesPerSec, ULONG sampleSize)
281{
282 ULONG ParmLength = 0, DataLength;
283 MCI_AUDIO_INIT init = {0};
284 APIRET rc;
285 ULONG action;
286 HFILE hDriver;
287
288 rc = DosOpen("DAUDIO1$", &hDriver, &action, 0,
289 FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE |
290 OPEN_SHARE_DENYNONE | OPEN_FLAGS_WRITE_THROUGH,
291 NULL );
292 if(rc) {
293 dprintf(("DosOpen failed with error %d\n", rc));
294 return FALSE;
295 }
296
297 DataLength = sizeof(init);
298
299 init.lSRate = nSamplesPerSec;
300 init.lBitsPerSRate = sampleSize;
301 init.sChannels = nChannels;
302 init.sMode = PCM; //TODO: check formattag for ulaw/alaw/adpcm
303 rc = DosDevIOCtl(hDriver, DAUDIO_IOCTL_CAT, DAUDIO_QUERYFORMAT, NULL, 0,
304 &ParmLength, &init, DataLength, &DataLength);
305
306 if(rc) {
307 dprintf(("DosDevIOCtl failed with error %d\n", rc));
308 goto fail;
309 }
310 if(init.sReturnCode != 0) {
311 dprintf(("init.sReturnCode = %d\n", init.sReturnCode));
312 goto fail;
313 }
314
315 DosClose(hDriver);
316 return TRUE;
317
318fail:
319 DosClose(hDriver);
320 return FALSE;
321}
322/******************************************************************************/
323/******************************************************************************/
324BOOL DAudioWaveOut::isDirectAudioAvailable()
325{
326 static BOOL fAvailable = FALSE;
327 static BOOL fTested = FALSE;
328
329 APIRET rc;
330 ULONG action;
331 HFILE hDriver;
332
333 if(!fTested) {
334 if(PROFILE_GetOdinIniInt(SECTION_WINMM, KEY_DIRECTAUDIO, 1) == 0) {
335 fTested = TRUE;
336 return FALSE;
337 }
338 rc = DosOpen("DAUDIO1$", &hDriver, &action, 0,
339 FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE |
340 OPEN_SHARE_DENYNONE | OPEN_FLAGS_WRITE_THROUGH,
341 NULL );
342 fTested = TRUE;
343 if(rc) {
344 return FALSE;
345 }
346 DosClose(hDriver);
347 fAvailable = TRUE;
348 }
349 return fAvailable;
350
351}
352/******************************************************************************/
353/******************************************************************************/
354MMRESULT DAudioWaveOut::setVolume(ULONG ulVol)
355{
356 DAUDIO_CMD cmd;
357
358 //Scale down from 0-64k-1 -> 0-100
359 cmd.Vol.VolumeR = (((ulVol & 0xFFFF0000) >> 16)*100)/0xFFFF;
360 cmd.Vol.VolumeL = ((ulVol & 0x0000FFFF) *100)/0xFFFF;
361 dprintf(("DAudioWaveOut::setVolume %d %d", cmd.Vol.VolumeL, cmd.Vol.VolumeR));
362 return sendIOCTL(DAUDIO_SETVOLUME, &cmd);
363}
364/******************************************************************************/
365/******************************************************************************/
366MMRESULT DAudioWaveOut::sendIOCTL(ULONG cmd, DAUDIO_CMD *pDataPacket)
367{
368 ULONG DataLength, ParmLength = 0;
369 APIRET rc;
370
371 DataLength = sizeof(DAUDIO_CMD);
372
373 rc = DosDevIOCtl(hDAudioDrv, DAUDIO_IOCTL_CAT, cmd, NULL, 0,
374 &ParmLength, pDataPacket, DataLength, &DataLength);
375 if(rc) {
376 dprintf(("DosDevIOCtl failed with error %d (command %d)", rc, cmd));
377 return MMSYSERR_ERROR;
378 }
379 return(MMSYSERR_NOERROR);
380
381}
382/******************************************************************************/
383//TODO: Not entirely safe. (assumption that we get called for each buffer so we
384// always notify the win32 app for each buffer that was processed)
385/******************************************************************************/
386BOOL DAudioWaveOut::handler()
387{
388 LPWAVEHDR whdr = wavehdr;
389
390 dprintf2(("WINMM: handler buf %X done (play %d/%d, cop %d, ret %d)", whdr, bytesPlayed, getPosition(), bytesCopied, bytesReturned));
391
392 if(State != STATE_PLAYING || whdr == NULL)
393 return FALSE;
394
395 wmutex.enter();
396 queuedbuffers--;
397 whdr->dwFlags &= ~WHDR_INQUEUE;
398 whdr->dwFlags |= WHDR_DONE;
399 whdr->reserved = 0;
400 wavehdr = whdr->lpNext;
401 whdr->lpNext = NULL;
402
403 bytesReturned += whdr->dwBufferLength;
404 wmutex.leave();
405
406 callback(WOM_DONE, (ULONG)whdr, 0);
407 return TRUE;
408}
409/******************************************************************************/
410/******************************************************************************/
411DWORD WIN32API DAudioThreadHandler(LPVOID pUserData)
412{
413 APIRET rc;
414 ULONG postcnt;
415 HEV hSem;
416 BOOL fResult = TRUE;
417
418 DAudioWaveOut *dwave = (DAudioWaveOut *)pUserData;
419
420 if(WaveOut::find(dwave) == FALSE) {
421 dprintf(("DAudioThreadHandler: can't find waveout stream %x!!!", pUserData));
422 return 0;
423 }
424 hSem = dwave->hSem;
425
426 DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM, 0);
427
428 while(TRUE)
429 {
430 dprintf2(("Waiting for buffer notification\n"));
431 rc = DosWaitEventSem(hSem, SEM_INDEFINITE_WAIT);
432 if(rc) {
433 dprintf(("DosWaitEventSem failed with error %d\n", rc));
434 return 0;
435 }
436
437 rc = DosResetEventSem(hSem, &postcnt);
438 if(rc) {
439 dprintf(("DosWaitEventSem failed with error %d\n", rc));
440 return 0;
441 }
442 if(WaveOut::find(dwave) == FALSE) {
443 dprintf(("DAudioThreadHandler: can't find waveout stream %x", pUserData));
444 break;
445 }
446 for(int i=0;i<postcnt;i++) {
447 fResult = dwave->handler();
448 if(fResult == FALSE) {
449 break;
450 }
451 }
452 if(fResult == FALSE) {
453 dprintf(("DAudioThreadHandler: finished waveout stream %x", pUserData));
454 break;
455 }
456 }
457 return 0;
458}
459/******************************************************************************/
460/******************************************************************************/
461
Note: See TracBrowser for help on using the repository browser.