1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
5 * Copyright 1994 Martin Ayotte
6 * 1999 Eric Pouech (async playing in waveOut)
10 * pause in waveOut does not work correctly
11 * implement async handling in waveIn
14 /*#define EMULATE_SB16*/
21 #include <sys/ioctl.h>
22 #include "wine/winuser16.h"
24 #include "multimedia.h"
31 #define SOUND_DEV "/dev/dsp"
32 #define MIXER_DEV "/dev/mixer"
34 #define MAX_WAVEOUTDRV (1)
35 #define MAX_WAVEINDRV (1)
37 /* state diagram for waveOut writing:
38 * FIXME: I'm not 100% pleased with the handling of RESETTING and CLOSING which are
40 * Basically, they are events handled in waveOutXXXX functions (open, write, pause, restart),
41 * and the others (resetn close) which are handled by the player. We (I ?) use transient
42 * states (RESETTING and CLOSING) to send the event from the waveOutXXX functions to
43 * the player. Not very good.
46 * PAUSED write() PAUSED
47 * STOPPED write() PLAYING
48 * (other) write() <error>
49 * (any) pause() PAUSED
50 * PAUSED restart() PLAYING if hdr, STOPPED otherwise
51 * reset() RESETTING => STOPPED
52 * close() CLOSING => CLOSED
55 #define WINE_WS_PLAYING 0
56 #define WINE_WS_PAUSED 1
57 #define WINE_WS_STOPPED 2
58 #define WINE_WS_RESETTING 3
59 #define WINE_WS_CLOSING 4
60 #define WINE_WS_CLOSED 5
64 volatile int state
; /* one of the WINE_WS_ manifest constants */
66 WAVEOPENDESC waveDesc
;
72 CRITICAL_SECTION critSect
;
73 /* used only by wodPlayer */
74 DWORD dwOffCurrHdr
; /* offset in lpQueueHdr->lpWaveHdr->lpData for fragments */
75 DWORD dwRemain
; /* number of bytes to write to end the current fragment */
76 DWORD dwNotifiedBytes
; /* number of bytes for which wavehdr notification has been done */
77 WAVEHDR
* lpNotifyHdr
; /* list of wavehdr for which write() has been called, pending for notification */
83 DWORD dwFragmentSize
; /* OpenSound '/dev/dsp' give us that size */
84 WAVEOPENDESC waveDesc
;
88 DWORD dwTotalRecorded
;
91 static WINE_WAVEOUT WOutDev
[MAX_WAVEOUTDRV
];
92 static WINE_WAVEIN WInDev
[MAX_WAVEOUTDRV
];
94 /*======================================================================*
95 * Low level WAVE implemantation *
96 *======================================================================*/
98 /**************************************************************************
99 * WAVE_NotifyClient [internal]
101 static DWORD
WAVE_NotifyClient(UINT16 wDevID
, WORD wMsg
,
102 DWORD dwParam1
, DWORD dwParam2
)
104 TRACE(wave
, "wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID
, wMsg
, dwParam1
, dwParam2
);
110 if (wDevID
> MAX_WAVEOUTDRV
) return MCIERR_INTERNAL
;
112 if (WOutDev
[wDevID
].wFlags
!= DCB_NULL
&&
113 !DriverCallback16(WOutDev
[wDevID
].waveDesc
.dwCallBack
,
114 WOutDev
[wDevID
].wFlags
,
115 WOutDev
[wDevID
].waveDesc
.hWave
,
117 WOutDev
[wDevID
].waveDesc
.dwInstance
,
120 WARN(wave
, "can't notify client !\n");
121 return MMSYSERR_NOERROR
;
128 if (wDevID
> MAX_WAVEINDRV
) return MCIERR_INTERNAL
;
130 if (WInDev
[wDevID
].wFlags
!= DCB_NULL
&&
131 !DriverCallback16(WInDev
[wDevID
].waveDesc
.dwCallBack
,
132 WInDev
[wDevID
].wFlags
,
133 WInDev
[wDevID
].waveDesc
.hWave
,
135 WInDev
[wDevID
].waveDesc
.dwInstance
,
138 WARN(wave
, "can't notify client !\n");
139 return MMSYSERR_NOERROR
;
146 /*======================================================================*
147 * Low level WAVE OUT implemantation *
148 *======================================================================*/
150 /**************************************************************************
151 * wodPlayer_WriteFragments [internal]
153 * wodPlayer helper. Writes as many fragments it can to unixdev.
154 * Returns TRUE in case of buffer underrun.
156 static BOOL
wodPlayer_WriteFragments(WINE_WAVEOUT
* wwo
)
162 audio_buf_info abinfo
;
165 /* get number of writable fragments */
166 if (ioctl(wwo
->unixdev
, SNDCTL_DSP_GETOSPACE
, &abinfo
) == -1) {
167 perror("ioctl SNDCTL_DSP_GETOSPACE");
169 LeaveCriticalSection(&wwo
->critSect
);
172 TRACE(wave
, "Can write %d fragments on fd %d\n", abinfo
.fragments
, wwo
->unixdev
);
174 if (abinfo
.fragments
== 0) /* output queue is full, wait a bit */
177 lpWaveHdr
= wwo
->lpQueueHdr
;
179 if (wwo
->dwRemain
> 0 && /* still data to send to complete current fragment */
180 wwo
->dwNotifiedBytes
>= wwo
->dwFragmentSize
&& /* first fragment has been played */
181 abinfo
.fragstotal
- abinfo
.fragments
< 2) { /* done with all waveOutWrite()' fragments */
183 /* FIXME: should do better handling here */
184 TRACE(wave
, "Oooch, buffer underrun !\n");
185 return TRUE
; /* force resetting of waveOut device */
187 return FALSE
; /* wait a bit */
190 if (wwo
->dwOffCurrHdr
== 0) {
191 TRACE(wave
, "Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr
, lpWaveHdr
->dwBufferLength
);
192 if (lpWaveHdr
->dwFlags
& WHDR_BEGINLOOP
)
193 FIXME(wave
, "NIY: loops (%lu) in wavehdr\n", lpWaveHdr
->dwLoops
);
196 lpData
= ((DWORD
)lpWaveHdr
== lpWaveHdr
->reserved
) ?
197 (LPBYTE
)lpWaveHdr
->lpData
: (LPBYTE
)PTR_SEG_TO_LIN(lpWaveHdr
->lpData
);
199 /* finish current wave hdr ? */
200 if (wwo
->dwOffCurrHdr
+ wwo
->dwRemain
>= lpWaveHdr
->dwBufferLength
) {
201 DWORD toWrite
= lpWaveHdr
->dwBufferLength
- wwo
->dwOffCurrHdr
;
203 /* write end of current wave hdr */
204 count
= write(wwo
->unixdev
, lpData
+ wwo
->dwOffCurrHdr
, toWrite
);
205 TRACE(wave
, "write(%p[%5lu], %5lu) => %d\n", lpData
, wwo
->dwOffCurrHdr
, toWrite
, count
);
207 /* move lpWaveHdr to notify list */
208 if (wwo
->lpNotifyHdr
) {
209 for (wh
= wwo
->lpNotifyHdr
; wh
->lpNext
!= NULL
; wh
= wh
->lpNext
);
210 wh
->lpNext
= lpWaveHdr
;
212 wwo
->lpNotifyHdr
= lpWaveHdr
;
214 wwo
->lpQueueHdr
= lpWaveHdr
->lpNext
;
215 lpWaveHdr
->lpNext
= 0;
217 wwo
->dwOffCurrHdr
= 0;
218 if ((wwo
->dwRemain
-= toWrite
) == 0)
219 wwo
->dwRemain
= wwo
->dwFragmentSize
;
220 continue; /* try to go to use next wavehdr */
222 count
= write(wwo
->unixdev
, lpData
+ wwo
->dwOffCurrHdr
, wwo
->dwRemain
);
223 TRACE(wave
, "write(%p[%5lu], %5lu) => %d\n", lpData
, wwo
->dwOffCurrHdr
, wwo
->dwRemain
, count
);
224 wwo
->dwOffCurrHdr
+= count
;
225 wwo
->dwRemain
= wwo
->dwFragmentSize
;
230 /**************************************************************************
231 * wodPlayer_WriteFragments [internal]
233 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
234 * have been played (actually to speaker, not to unixdev fd).
236 static void wodPlayer_Notify(WINE_WAVEOUT
* wwo
, WORD uDevID
, BOOL force
)
241 /* get effective number of written bytes */
243 if (ioctl(wwo
->unixdev
, SNDCTL_DSP_GETOPTR
, &cinfo
) == -1) {
244 perror("ioctl SNDCTL_DSP_GETOPTR");
246 LeaveCriticalSection(&wwo
->critSect
);
249 TRACE(wave
, "Played %d bytes on fd %d\n", cinfo
.bytes
, wwo
->unixdev
);
250 /* FIXME: this will not work if a RESET or SYNC occurs somewhere */
251 wwo
->dwTotalPlayed
= cinfo
.bytes
;
253 if (force
|| cinfo
.bytes
> wwo
->dwNotifiedBytes
) {
254 /* remove all wavehdr which can be notified */
255 while (wwo
->lpNotifyHdr
&&
256 (force
|| (cinfo
.bytes
>= wwo
->dwNotifiedBytes
+ wwo
->lpNotifyHdr
->dwBufferLength
))) {
257 lpWaveHdr
= wwo
->lpNotifyHdr
;
259 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
260 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
262 wwo
->dwNotifiedBytes
+= lpWaveHdr
->dwBufferLength
;
263 wwo
->lpNotifyHdr
= lpWaveHdr
->lpNext
;
265 /* FIXME: should we keep the wwo->critsect locked during callback ? */
266 /* normally yes, only a limited set of functions can be called...
267 * the ones which can be called inside an i386 interruption
268 * so this should be fine
270 LeaveCriticalSection(&wwo
->critSect
);
271 TRACE(wave
, "Notifying client with %p\n", lpWaveHdr
);
272 if (WAVE_NotifyClient(uDevID
, WOM_DONE
, lpWaveHdr
->reserved
, 0) != MMSYSERR_NOERROR
) {
273 WARN(wave
, "can't notify client !\n");
275 EnterCriticalSection(&wwo
->critSect
);
280 /**************************************************************************
281 * wodPlayer [internal]
283 static DWORD WINAPI
wodPlayer(LPVOID pmt
)
285 WORD uDevID
= (DWORD
)pmt
;
286 WINE_WAVEOUT
* wwo
= (WINE_WAVEOUT
*)&WOutDev
[uDevID
];
291 wwo
->dwNotifiedBytes
= 0;
292 wwo
->dwOffCurrHdr
= 0;
293 wwo
->dwRemain
= wwo
->dwFragmentSize
;
294 wwo
->lpNotifyHdr
= NULL
;
296 /* make sleep time to be # of ms to output a fragment */
297 dwSleepTime
= (wwo
->dwFragmentSize
* 1000) / wwo
->format
.wf
.nAvgBytesPerSec
;
301 EnterCriticalSection(&wwo
->critSect
);
303 switch (wwo
->state
) {
305 wodPlayer_Notify(wwo
, uDevID
, FALSE
);
306 if (laststate
!= WINE_WS_PAUSED
) {
307 if (ioctl(wwo
->unixdev
, SNDCTL_DSP_POST
, 0) == -1) {
308 perror("ioctl SNDCTL_DSP_POST");
310 LeaveCriticalSection(&wwo
->critSect
);
315 case WINE_WS_STOPPED
:
316 /* should we kill the Thread here and to restart it later when needed ? */
318 case WINE_WS_PLAYING
:
319 if (!wodPlayer_WriteFragments(wwo
)) {
320 wodPlayer_Notify(wwo
, uDevID
, FALSE
);
324 case WINE_WS_RESETTING
:
325 /* updates current notify list */
326 wodPlayer_Notify(wwo
, uDevID
, FALSE
);
327 /* flush all possible output */
328 if (ioctl(wwo
->unixdev
, SNDCTL_DSP_SYNC
, 0) == -1) {
329 perror("ioctl SNDCTL_DSP_SYNC");
331 LeaveCriticalSection(&wwo
->critSect
);
334 /* DSP_SYNC resets the number of send bytes */
335 wwo
->dwNotifiedBytes
= 0;
336 /* empty notify list */
337 wodPlayer_Notify(wwo
, uDevID
, TRUE
);
338 if (wwo
->lpNotifyHdr
) {
339 ERR(wave
, "out of sync\n");
341 wwo
->dwOffCurrHdr
= 0;
342 /* get rid also of all the current queue */
343 for (lpWaveHdr
= wwo
->lpQueueHdr
; lpWaveHdr
; lpWaveHdr
= lpWaveHdr
->lpNext
) {
344 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
345 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
347 if (WAVE_NotifyClient((DWORD
)pmt
, WOM_DONE
, lpWaveHdr
->reserved
, 0) != MMSYSERR_NOERROR
) {
348 WARN(wave
, "can't notify client !\n");
352 wwo
->state
= WINE_WS_STOPPED
;
354 case WINE_WS_CLOSING
:
355 /* this should not happen since the device must have been reset before */
356 if (wwo
->lpNotifyHdr
|| wwo
->lpQueueHdr
) {
357 ERR(wave
, "out of sync\n");
360 wwo
->state
= WINE_WS_CLOSED
;
361 LeaveCriticalSection(&wwo
->critSect
);
362 DeleteCriticalSection(&wwo
->critSect
);
364 /* shouldn't go here */
366 FIXME(wave
, "unknown state %d\n", wwo
->state
);
369 laststate
= wwo
->state
;
370 LeaveCriticalSection(&wwo
->critSect
);
371 /* sleep for one fragment to be sent */
377 /**************************************************************************
378 * wodGetDevCaps [internal]
380 static DWORD
wodGetDevCaps(WORD wDevID
, LPWAVEOUTCAPS16 lpCaps
, DWORD dwSize
)
389 TRACE(wave
, "(%u, %p, %lu);\n", wDevID
, lpCaps
, dwSize
);
391 if (lpCaps
== NULL
) return MMSYSERR_NOTENABLED
;
392 if (access(SOUND_DEV
,0) != 0) return MMSYSERR_NOTENABLED
;
394 if (wDevID
>= MAX_WAVEOUTDRV
) {
395 TRACE(wave
, "MAX_WAVOUTDRV reached !\n");
396 return MMSYSERR_BADDEVICEID
;
399 if (WOutDev
[wDevID
].unixdev
== 0) {
400 audio
= open(SOUND_DEV
, O_WRONLY
, 0);
401 if (audio
== -1) return MMSYSERR_ALLOCATED
;
403 audio
= WOutDev
[wDevID
].unixdev
;
406 /* FIXME: some programs compare this string against the content of the registry
407 * for MM drivers. The name have to match in order the program to work
408 * (e.g. MS win9x mplayer.exe)
411 lpCaps
->wMid
= 0x0002;
412 lpCaps
->wPid
= 0x0104;
413 strcpy(lpCaps
->szPname
, "SB16 Wave Out");
415 lpCaps
->wMid
= 0x00FF; /* Manufac ID */
416 lpCaps
->wPid
= 0x0001; /* Product ID */
417 /* strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");*/
418 strcpy(lpCaps
->szPname
, "CS4236/37/38");
420 lpCaps
->vDriverVersion
= 0x0100;
421 lpCaps
->dwFormats
= 0x00000000;
422 lpCaps
->dwSupport
= WAVECAPS_VOLUME
;
424 /* First bytespersampl, then stereo */
425 bytespersmpl
= (IOCTL(audio
, SNDCTL_DSP_SAMPLESIZE
, samplesize
) != 0) ? 1 : 2;
427 lpCaps
->wChannels
= (IOCTL(audio
, SNDCTL_DSP_STEREO
, dsp_stereo
) != 0) ? 1 : 2;
428 if (lpCaps
->wChannels
> 1) lpCaps
->dwSupport
|= WAVECAPS_LRVOLUME
;
431 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
432 lpCaps
->dwFormats
|= WAVE_FORMAT_4M08
;
433 if (lpCaps
->wChannels
> 1)
434 lpCaps
->dwFormats
|= WAVE_FORMAT_4S08
;
435 if (bytespersmpl
> 1) {
436 lpCaps
->dwFormats
|= WAVE_FORMAT_4M16
;
437 if (lpCaps
->wChannels
> 1)
438 lpCaps
->dwFormats
|= WAVE_FORMAT_4S16
;
442 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
443 lpCaps
->dwFormats
|= WAVE_FORMAT_2M08
;
444 if (lpCaps
->wChannels
> 1)
445 lpCaps
->dwFormats
|= WAVE_FORMAT_2S08
;
446 if (bytespersmpl
> 1) {
447 lpCaps
->dwFormats
|= WAVE_FORMAT_2M16
;
448 if (lpCaps
->wChannels
> 1)
449 lpCaps
->dwFormats
|= WAVE_FORMAT_2S16
;
453 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
454 lpCaps
->dwFormats
|= WAVE_FORMAT_1M08
;
455 if (lpCaps
->wChannels
> 1)
456 lpCaps
->dwFormats
|= WAVE_FORMAT_1S08
;
457 if (bytespersmpl
> 1) {
458 lpCaps
->dwFormats
|= WAVE_FORMAT_1M16
;
459 if (lpCaps
->wChannels
> 1)
460 lpCaps
->dwFormats
|= WAVE_FORMAT_1S16
;
463 if (IOCTL(audio
, SNDCTL_DSP_GETCAPS
, caps
) == 0) {
464 if ((caps
& DSP_CAP_REALTIME
) && !(caps
&& DSP_CAP_BATCH
))
465 lpCaps
->dwFormats
|= WAVECAPS_SAMPLEACCURATE
;
467 if (WOutDev
[wDevID
].unixdev
== 0) {
470 TRACE(wave
, "dwFormats = %08lX\n", lpCaps
->dwFormats
);
471 return MMSYSERR_NOERROR
;
475 /**************************************************************************
478 static DWORD
wodOpen(WORD wDevID
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
487 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
488 if (lpDesc
== NULL
) {
489 WARN(wave
, "Invalid Parameter !\n");
490 return MMSYSERR_INVALPARAM
;
492 if (wDevID
>= MAX_WAVEOUTDRV
) {
493 TRACE(wave
, "MAX_WAVOUTDRV reached !\n");
494 return MMSYSERR_BADDEVICEID
;
496 /* only PCM format is supported so far... */
497 if (lpDesc
->lpFormat
->wFormatTag
!= WAVE_FORMAT_PCM
||
498 lpDesc
->lpFormat
->nChannels
== 0 ||
499 lpDesc
->lpFormat
->nSamplesPerSec
== 0) {
500 WARN(wave
, "Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
501 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
502 lpDesc
->lpFormat
->nSamplesPerSec
);
503 return WAVERR_BADFORMAT
;
506 if (dwFlags
& WAVE_FORMAT_QUERY
)
507 return MMSYSERR_NOERROR
;
509 WOutDev
[wDevID
].unixdev
= 0;
510 if (access(SOUND_DEV
, 0) != 0)
511 return MMSYSERR_NOTENABLED
;
512 audio
= open(SOUND_DEV
, O_WRONLY
, 0);
514 WARN(wave
, "can't open !\n");
515 return MMSYSERR_ALLOCATED
;
518 WOutDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
520 memcpy(&WOutDev
[wDevID
].waveDesc
, lpDesc
, sizeof(WAVEOPENDESC
));
521 memcpy(&WOutDev
[wDevID
].format
, lpDesc
->lpFormat
, sizeof(PCMWAVEFORMAT
));
523 if (WOutDev
[wDevID
].format
.wBitsPerSample
== 0) {
524 WOutDev
[wDevID
].format
.wBitsPerSample
= 8 *
525 (WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/
526 WOutDev
[wDevID
].format
.wf
.nSamplesPerSec
) /
527 WOutDev
[wDevID
].format
.wf
.nChannels
;
530 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
531 * thus leading to 46ms per fragment, and a turnaround time of 185ms
533 /* 2^10=1024 bytes per fragment, 16 fragments max */
534 audio_fragment
= 0x000F000A;
535 sample_size
= WOutDev
[wDevID
].format
.wBitsPerSample
;
536 sample_rate
= WOutDev
[wDevID
].format
.wf
.nSamplesPerSec
;
537 dsp_stereo
= (WOutDev
[wDevID
].format
.wf
.nChannels
> 1) ? TRUE
: FALSE
;
539 IOCTL(audio
, SNDCTL_DSP_SETFRAGMENT
, audio_fragment
);
540 /* First size and stereo then samplerate */
541 IOCTL(audio
, SNDCTL_DSP_SAMPLESIZE
, sample_size
);
542 IOCTL(audio
, SNDCTL_DSP_STEREO
, dsp_stereo
);
543 IOCTL(audio
, SNDCTL_DSP_SPEED
, sample_rate
);
545 /* even if we set fragment size above, read it again, just in case */
546 IOCTL(audio
, SNDCTL_DSP_GETBLKSIZE
, fragment_size
);
548 WOutDev
[wDevID
].lpQueueHdr
= NULL
;
549 WOutDev
[wDevID
].unixdev
= audio
;
550 WOutDev
[wDevID
].dwTotalPlayed
= 0;
551 WOutDev
[wDevID
].dwFragmentSize
= fragment_size
;
552 WOutDev
[wDevID
].state
= WINE_WS_STOPPED
;
553 WOutDev
[wDevID
].hThread
= 0;
555 /* FIXME: do we need to make critsect global ? */
556 InitializeCriticalSection(&WOutDev
[wDevID
].critSect
);
557 MakeCriticalSectionGlobal(&WOutDev
[wDevID
].critSect
);
559 TRACE(wave
, "fd=%d fragmentSize=%ld\n",
560 WOutDev
[wDevID
].unixdev
, WOutDev
[wDevID
].dwFragmentSize
);
562 TRACE(wave
, "wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u !\n",
563 WOutDev
[wDevID
].format
.wBitsPerSample
, WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
,
564 WOutDev
[wDevID
].format
.wf
.nSamplesPerSec
, WOutDev
[wDevID
].format
.wf
.nChannels
);
566 if (WAVE_NotifyClient(wDevID
, WOM_OPEN
, 0L, 0L) != MMSYSERR_NOERROR
) {
567 WARN(wave
, "can't notify client !\n");
568 return MMSYSERR_INVALPARAM
;
570 return MMSYSERR_NOERROR
;
573 /**************************************************************************
574 * wodClose [internal]
576 static DWORD
wodClose(WORD wDevID
)
578 DWORD ret
= MMSYSERR_NOERROR
;
580 TRACE(wave
, "(%u);\n", wDevID
);
582 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
583 WARN(wave
, "bad device ID !\n");
584 return MMSYSERR_BADDEVICEID
;
587 if (WOutDev
[wDevID
].lpQueueHdr
!= NULL
|| WOutDev
[wDevID
].lpNotifyHdr
!= NULL
) {
588 WARN(wave
, "buffers still playing !\n");
589 ret
= WAVERR_STILLPLAYING
;
591 EnterCriticalSection(&WOutDev
[wDevID
].critSect
);
592 WOutDev
[wDevID
].state
= WINE_WS_CLOSING
;
593 LeaveCriticalSection(&WOutDev
[wDevID
].critSect
);
595 while (WOutDev
[wDevID
].state
!= WINE_WS_CLOSED
)
598 close(WOutDev
[wDevID
].unixdev
);
599 WOutDev
[wDevID
].unixdev
= 0;
600 WOutDev
[wDevID
].dwFragmentSize
= 0;
601 if (WAVE_NotifyClient(wDevID
, WOM_CLOSE
, 0L, 0L) != MMSYSERR_NOERROR
) {
602 WARN(wave
, "can't notify client !\n");
603 ret
= MMSYSERR_INVALPARAM
;
609 /**************************************************************************
610 * wodWrite [internal]
613 static DWORD
wodWrite(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
615 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
617 /* first, do the sanity checks... */
618 if (wDevID
>= MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
619 WARN(wave
, "bad dev ID !\n");
620 return MMSYSERR_BADDEVICEID
;
623 if (lpWaveHdr
->lpData
== NULL
|| !(lpWaveHdr
->dwFlags
& WHDR_PREPARED
))
624 return WAVERR_UNPREPARED
;
626 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
627 return WAVERR_STILLPLAYING
;
629 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
630 lpWaveHdr
->dwFlags
|= WHDR_INQUEUE
;
631 lpWaveHdr
->lpNext
= 0;
633 EnterCriticalSection(&WOutDev
[wDevID
].critSect
);
634 /* insert buffer in queue */
635 if (WOutDev
[wDevID
].lpQueueHdr
) {
637 for (wh
= WOutDev
[wDevID
].lpQueueHdr
; wh
->lpNext
; wh
= wh
->lpNext
);
638 wh
->lpNext
= lpWaveHdr
;
640 WOutDev
[wDevID
].lpQueueHdr
= lpWaveHdr
;
642 /* starts thread if wod is not paused */
643 switch (WOutDev
[wDevID
].state
) {
645 case WINE_WS_PLAYING
:
648 case WINE_WS_STOPPED
:
649 WOutDev
[wDevID
].state
= WINE_WS_PLAYING
;
650 if (WOutDev
[wDevID
].hThread
== 0) {
651 WOutDev
[wDevID
].hThread
= CreateThread(NULL
, 0, wodPlayer
, (LPVOID
)(DWORD
)wDevID
, 0, NULL
);
655 FIXME(wave
, "Unsupported state %d\n", WOutDev
[wDevID
].state
);
657 LeaveCriticalSection(&WOutDev
[wDevID
].critSect
);
659 return MMSYSERR_NOERROR
;
662 /**************************************************************************
663 * wodPrepare [internal]
665 static DWORD
wodPrepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
667 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
669 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
670 WARN(wave
, "bad device ID !\n");
671 return MMSYSERR_BADDEVICEID
;
674 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
675 return WAVERR_STILLPLAYING
;
677 lpWaveHdr
->dwFlags
|= WHDR_PREPARED
;
678 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
679 return MMSYSERR_NOERROR
;
682 /**************************************************************************
683 * wodUnprepare [internal]
685 static DWORD
wodUnprepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
687 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
689 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
690 WARN(wave
, "bad device ID !\n");
691 return MMSYSERR_BADDEVICEID
;
694 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
695 return WAVERR_STILLPLAYING
;
697 lpWaveHdr
->dwFlags
&= ~WHDR_PREPARED
;
698 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
700 return MMSYSERR_NOERROR
;
703 /**************************************************************************
704 * wodPause [internal]
706 static DWORD
wodPause(WORD wDevID
)
708 TRACE(wave
, "(%u);!\n", wDevID
);
710 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
711 WARN(wave
, "bad device ID !\n");
712 return MMSYSERR_BADDEVICEID
;
715 EnterCriticalSection(&WOutDev
[wDevID
].critSect
);
716 WOutDev
[wDevID
].state
= WINE_WS_PAUSED
;
717 LeaveCriticalSection(&WOutDev
[wDevID
].critSect
);
719 return MMSYSERR_NOERROR
;
722 /**************************************************************************
725 static DWORD
wodStop(WORD wDevID
)
727 FIXME(wave
, "(%u); NIY!\n", wDevID
);
729 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
730 WARN(wave
, "bad device ID !\n");
731 return MMSYSERR_BADDEVICEID
;
734 return MMSYSERR_NOERROR
;
737 /**************************************************************************
738 * wodRestart [internal]
740 static DWORD
wodRestart(WORD wDevID
)
742 TRACE(wave
, "(%u);\n", wDevID
);
744 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
745 WARN(wave
, "bad device ID !\n");
746 return MMSYSERR_BADDEVICEID
;
749 EnterCriticalSection(&WOutDev
[wDevID
].critSect
);
750 if (WOutDev
[wDevID
].state
== WINE_WS_PAUSED
) {
751 if (WOutDev
[wDevID
].lpQueueHdr
!= NULL
) {
752 WOutDev
[wDevID
].state
= WINE_WS_PLAYING
;
753 if (WOutDev
[wDevID
].hThread
== 0)
754 WOutDev
[wDevID
].hThread
= CreateThread(NULL
, 0, wodPlayer
, (LPVOID
)(DWORD
)wDevID
, 0, NULL
);
756 WOutDev
[wDevID
].state
= WINE_WS_STOPPED
;
759 LeaveCriticalSection(&WOutDev
[wDevID
].critSect
);
761 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
762 /* FIXME: Myst crashes with this ... hmm -MM
763 if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
764 WARN(wave, "can't notify client !\n");
765 return MMSYSERR_INVALPARAM;
769 return MMSYSERR_NOERROR
;
772 /**************************************************************************
773 * wodReset [internal]
775 static DWORD
wodReset(WORD wDevID
)
777 TRACE(wave
, "(%u);\n", wDevID
);
779 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
780 WARN(wave
, "bad device ID !\n");
781 return MMSYSERR_BADDEVICEID
;
784 EnterCriticalSection(&WOutDev
[wDevID
].critSect
);
785 if (WOutDev
[wDevID
].hThread
== 0) {
786 WOutDev
[wDevID
].state
= WINE_WS_STOPPED
;
788 WOutDev
[wDevID
].state
= WINE_WS_RESETTING
;
790 LeaveCriticalSection(&WOutDev
[wDevID
].critSect
);
792 while (WOutDev
[wDevID
].state
!= WINE_WS_STOPPED
)
795 return MMSYSERR_NOERROR
;
799 /**************************************************************************
800 * wodGetPosition [internal]
802 static DWORD
wodGetPosition(WORD wDevID
, LPMMTIME16 lpTime
, DWORD uSize
)
806 TRACE(wave
, "(%u, %p, %lu);\n", wDevID
, lpTime
, uSize
);
808 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
809 WARN(wave
, "bad device ID !\n");
810 return MMSYSERR_BADDEVICEID
;
813 if (lpTime
== NULL
) return MMSYSERR_INVALPARAM
;
814 TRACE(wave
, "wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
815 lpTime
->wType
, WOutDev
[wDevID
].format
.wBitsPerSample
,
816 WOutDev
[wDevID
].format
.wf
.nSamplesPerSec
, WOutDev
[wDevID
].format
.wf
.nChannels
,
817 WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
);
818 TRACE(wave
, "dwTotalPlayed=%lu\n", WOutDev
[wDevID
].dwTotalPlayed
);
820 switch (lpTime
->wType
) {
822 lpTime
->u
.cb
= WOutDev
[wDevID
].dwTotalPlayed
;
823 TRACE(wave
, "TIME_BYTES=%lu\n", lpTime
->u
.cb
);
826 lpTime
->u
.sample
= WOutDev
[wDevID
].dwTotalPlayed
* 8 /
827 WOutDev
[wDevID
].format
.wBitsPerSample
;
828 TRACE(wave
, "TIME_SAMPLES=%lu\n", lpTime
->u
.sample
);
831 time
= WOutDev
[wDevID
].dwTotalPlayed
/
832 (WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/ 1000);
833 lpTime
->u
.smpte
.hour
= time
/ 108000;
834 time
-= lpTime
->u
.smpte
.hour
* 108000;
835 lpTime
->u
.smpte
.min
= time
/ 1800;
836 time
-= lpTime
->u
.smpte
.min
* 1800;
837 lpTime
->u
.smpte
.sec
= time
/ 30;
838 time
-= lpTime
->u
.smpte
.sec
* 30;
839 lpTime
->u
.smpte
.frame
= time
;
840 lpTime
->u
.smpte
.fps
= 30;
841 TRACE(wave
, "TIME_SMPTE=%02u:%02u:%02u:%02u\n",
842 lpTime
->u
.smpte
.hour
, lpTime
->u
.smpte
.min
,
843 lpTime
->u
.smpte
.sec
, lpTime
->u
.smpte
.frame
);
846 FIXME(wave
, "Format %d not supported ! use TIME_MS !\n", lpTime
->wType
);
847 lpTime
->wType
= TIME_MS
;
849 lpTime
->u
.ms
= WOutDev
[wDevID
].dwTotalPlayed
/
850 (WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/ 1000);
851 TRACE(wave
, "TIME_MS=%lu\n", lpTime
->u
.ms
);
854 return MMSYSERR_NOERROR
;
857 /**************************************************************************
858 * wodGetVolume [internal]
860 static DWORD
wodGetVolume(WORD wDevID
, LPDWORD lpdwVol
)
863 int volume
, left
, right
;
865 TRACE(wave
, "(%u, %p);\n", wDevID
, lpdwVol
);
868 return MMSYSERR_NOTENABLED
;
869 if ((mixer
= open(MIXER_DEV
, O_RDONLY
)) < 0) {
870 WARN(wave
, "mixer device not available !\n");
871 return MMSYSERR_NOTENABLED
;
873 if (ioctl(mixer
, SOUND_MIXER_READ_PCM
, &volume
) == -1) {
874 WARN(wave
, "unable read mixer !\n");
875 return MMSYSERR_NOTENABLED
;
878 left
= volume
& 0x7F;
879 right
= (volume
>> 8) & 0x7F;
880 TRACE(wave
, "left=%d right=%d !\n", left
, right
);
881 *lpdwVol
= MAKELONG(left
<< 9, right
<< 9);
882 return MMSYSERR_NOERROR
;
886 /**************************************************************************
887 * wodSetVolume [internal]
889 static DWORD
wodSetVolume(WORD wDevID
, DWORD dwParam
)
893 TRACE(wave
, "(%u, %08lX);\n", wDevID
, dwParam
);
894 volume
= (LOWORD(dwParam
) >> 9 & 0x7F) +
895 ((HIWORD(dwParam
) >> 9 & 0x7F) << 8);
896 if ((mixer
= open(MIXER_DEV
, O_WRONLY
)) < 0) {
897 WARN(wave
, "mixer device not available !\n");
898 return MMSYSERR_NOTENABLED
;
900 if (ioctl(mixer
, SOUND_MIXER_WRITE_PCM
, &volume
) == -1) {
901 WARN(wave
, "unable set mixer !\n");
902 return MMSYSERR_NOTENABLED
;
905 return MMSYSERR_NOERROR
;
908 /**************************************************************************
909 * wodGetNumDevs [internal]
911 static DWORD
wodGetNumDevs(void)
915 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
916 int audio
= open(SOUND_DEV
, O_WRONLY
, 0);
927 /**************************************************************************
928 * wodMessage [sample driver]
930 DWORD WINAPI
wodMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
931 DWORD dwParam1
, DWORD dwParam2
)
933 TRACE(wave
, "(%u, %04X, %08lX, %08lX, %08lX);\n",
934 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
937 case WODM_OPEN
: return wodOpen (wDevID
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
938 case WODM_CLOSE
: return wodClose (wDevID
);
939 case WODM_WRITE
: return wodWrite (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
940 case WODM_PAUSE
: return wodPause (wDevID
);
941 case WODM_STOP
: return wodStop (wDevID
);
942 case WODM_GETPOS
: return wodGetPosition (wDevID
, (LPMMTIME16
)dwParam1
, dwParam2
);
943 case WODM_BREAKLOOP
: return MMSYSERR_NOTSUPPORTED
;
944 case WODM_PREPARE
: return wodPrepare (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
945 case WODM_UNPREPARE
: return wodUnprepare (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
946 case WODM_GETDEVCAPS
: return wodGetDevCaps (wDevID
, (LPWAVEOUTCAPS16
)dwParam1
, dwParam2
);
947 case WODM_GETNUMDEVS
: return wodGetNumDevs ();
948 case WODM_GETPITCH
: return MMSYSERR_NOTSUPPORTED
;
949 case WODM_SETPITCH
: return MMSYSERR_NOTSUPPORTED
;
950 case WODM_GETPLAYBACKRATE
: return MMSYSERR_NOTSUPPORTED
;
951 case WODM_SETPLAYBACKRATE
: return MMSYSERR_NOTSUPPORTED
;
952 case WODM_GETVOLUME
: return wodGetVolume (wDevID
, (LPDWORD
)dwParam1
);
953 case WODM_SETVOLUME
: return wodSetVolume (wDevID
, dwParam1
);
954 case WODM_RESTART
: return wodRestart (wDevID
);
955 case WODM_RESET
: return wodReset (wDevID
);
957 FIXME(wave
, "unknown message %d!\n", wMsg
);
959 return MMSYSERR_NOTSUPPORTED
;
962 /*======================================================================*
963 * Low level WAVE IN implemantation *
964 *======================================================================*/
966 /**************************************************************************
967 * widGetDevCaps [internal]
969 static DWORD
widGetDevCaps(WORD wDevID
, LPWAVEINCAPS16 lpCaps
, DWORD dwSize
)
971 int audio
,smplrate
,samplesize
=16,dsp_stereo
=1,bytespersmpl
;
973 TRACE(wave
, "(%u, %p, %lu);\n", wDevID
, lpCaps
, dwSize
);
974 if (lpCaps
== NULL
) return MMSYSERR_NOTENABLED
;
975 if (access(SOUND_DEV
,0) != 0) return MMSYSERR_NOTENABLED
;
976 audio
= open(SOUND_DEV
, O_RDONLY
, 0);
977 if (audio
== -1) return MMSYSERR_ALLOCATED
;
979 lpCaps
->wMid
= 0x0002;
980 lpCaps
->wPid
= 0x0004;
981 strcpy(lpCaps
->szPname
, "SB16 Wave In");
983 lpCaps
->wMid
= 0x00FF; /* Manufac ID */
984 lpCaps
->wPid
= 0x0001; /* Product ID */
985 strcpy(lpCaps
->szPname
, "OpenSoundSystem WAVIN Driver");
987 lpCaps
->dwFormats
= 0x00000000;
988 lpCaps
->wChannels
= (IOCTL(audio
, SNDCTL_DSP_STEREO
, dsp_stereo
) != 0) ? 1 : 2;
989 bytespersmpl
= (IOCTL(audio
, SNDCTL_DSP_SAMPLESIZE
, samplesize
) != 0) ? 1 : 2;
991 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
992 lpCaps
->dwFormats
|= WAVE_FORMAT_4M08
;
993 if (lpCaps
->wChannels
> 1)
994 lpCaps
->dwFormats
|= WAVE_FORMAT_4S08
;
995 if (bytespersmpl
> 1) {
996 lpCaps
->dwFormats
|= WAVE_FORMAT_4M16
;
997 if (lpCaps
->wChannels
> 1)
998 lpCaps
->dwFormats
|= WAVE_FORMAT_4S16
;
1002 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
1003 lpCaps
->dwFormats
|= WAVE_FORMAT_2M08
;
1004 if (lpCaps
->wChannels
> 1)
1005 lpCaps
->dwFormats
|= WAVE_FORMAT_2S08
;
1006 if (bytespersmpl
> 1) {
1007 lpCaps
->dwFormats
|= WAVE_FORMAT_2M16
;
1008 if (lpCaps
->wChannels
> 1)
1009 lpCaps
->dwFormats
|= WAVE_FORMAT_2S16
;
1013 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
1014 lpCaps
->dwFormats
|= WAVE_FORMAT_1M08
;
1015 if (lpCaps
->wChannels
> 1)
1016 lpCaps
->dwFormats
|= WAVE_FORMAT_1S08
;
1017 if (bytespersmpl
> 1) {
1018 lpCaps
->dwFormats
|= WAVE_FORMAT_1M16
;
1019 if (lpCaps
->wChannels
> 1)
1020 lpCaps
->dwFormats
|= WAVE_FORMAT_1S16
;
1024 TRACE(wave
, "dwFormats = %08lX\n", lpCaps
->dwFormats
);
1025 return MMSYSERR_NOERROR
;
1028 /**************************************************************************
1029 * widOpen [internal]
1031 static DWORD
widOpen(WORD wDevID
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
1033 int audio
,abuf_size
,smplrate
,samplesize
,dsp_stereo
;
1034 LPWAVEFORMAT lpFormat
;
1036 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
1037 if (lpDesc
== NULL
) {
1038 WARN(wave
, "Invalid Parameter !\n");
1039 return MMSYSERR_INVALPARAM
;
1041 if (wDevID
>= MAX_WAVEINDRV
) {
1042 TRACE(wave
, "MAX_WAVINDRV reached !\n");
1043 return MMSYSERR_ALLOCATED
;
1045 WInDev
[wDevID
].unixdev
= 0;
1046 if (access(SOUND_DEV
,0) != 0) return MMSYSERR_NOTENABLED
;
1047 audio
= open(SOUND_DEV
, O_RDONLY
, 0);
1049 WARN(wave
, "can't open !\n");
1050 return MMSYSERR_ALLOCATED
;
1052 IOCTL(audio
, SNDCTL_DSP_GETBLKSIZE
, abuf_size
);
1053 if (abuf_size
< 1024 || abuf_size
> 65536) {
1054 if (abuf_size
== -1)
1055 WARN(wave
, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1057 WARN(wave
, "SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize !\n");
1058 return MMSYSERR_NOTENABLED
;
1060 WInDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
1062 if (WInDev
[wDevID
].lpQueueHdr
) {
1063 HeapFree(GetProcessHeap(), 0, WInDev
[wDevID
].lpQueueHdr
);
1064 WInDev
[wDevID
].lpQueueHdr
= NULL
;
1066 WInDev
[wDevID
].unixdev
= audio
;
1067 WInDev
[wDevID
].dwFragmentSize
= abuf_size
;
1068 WInDev
[wDevID
].dwTotalRecorded
= 0;
1069 memcpy(&WInDev
[wDevID
].waveDesc
, lpDesc
, sizeof(WAVEOPENDESC
));
1070 lpFormat
= (LPWAVEFORMAT
) lpDesc
->lpFormat
;
1071 if (lpFormat
->wFormatTag
!= WAVE_FORMAT_PCM
) {
1072 WARN(wave
, "Bad format %04X !\n",
1073 lpFormat
->wFormatTag
);
1074 return WAVERR_BADFORMAT
;
1076 memcpy(&WInDev
[wDevID
].format
, lpFormat
, sizeof(PCMWAVEFORMAT
));
1077 WInDev
[wDevID
].format
.wBitsPerSample
= 8; /* <-------------- */
1078 if (WInDev
[wDevID
].format
.wf
.nChannels
== 0) return WAVERR_BADFORMAT
;
1079 if (WInDev
[wDevID
].format
.wf
.nSamplesPerSec
== 0) return WAVERR_BADFORMAT
;
1080 if (WInDev
[wDevID
].format
.wBitsPerSample
== 0) {
1081 WInDev
[wDevID
].format
.wBitsPerSample
= 8 *
1082 (WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/
1083 WInDev
[wDevID
].format
.wf
.nSamplesPerSec
) /
1084 WInDev
[wDevID
].format
.wf
.nChannels
;
1086 samplesize
= WInDev
[wDevID
].format
.wBitsPerSample
;
1087 smplrate
= WInDev
[wDevID
].format
.wf
.nSamplesPerSec
;
1088 dsp_stereo
= (WInDev
[wDevID
].format
.wf
.nChannels
> 1) ? TRUE
: FALSE
;
1089 IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
);
1090 IOCTL(audio
, SNDCTL_DSP_SAMPLESIZE
, samplesize
);
1091 IOCTL(audio
, SNDCTL_DSP_STEREO
, dsp_stereo
);
1092 TRACE(wave
, "wBitsPerSample=%u !\n", WInDev
[wDevID
].format
.wBitsPerSample
);
1093 TRACE(wave
, "nSamplesPerSec=%lu !\n", WInDev
[wDevID
].format
.wf
.nSamplesPerSec
);
1094 TRACE(wave
, "nChannels=%u !\n", WInDev
[wDevID
].format
.wf
.nChannels
);
1095 TRACE(wave
, "nAvgBytesPerSec=%lu\n", WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
);
1096 if (WAVE_NotifyClient(wDevID
, WIM_OPEN
, 0L, 0L) != MMSYSERR_NOERROR
) {
1097 WARN(wave
, "can't notify client !\n");
1098 return MMSYSERR_INVALPARAM
;
1100 return MMSYSERR_NOERROR
;
1103 /**************************************************************************
1104 * widClose [internal]
1106 static DWORD
widClose(WORD wDevID
)
1108 TRACE(wave
, "(%u);\n", wDevID
);
1109 if (wDevID
> MAX_WAVEINDRV
) return MMSYSERR_INVALPARAM
;
1110 if (WInDev
[wDevID
].unixdev
== 0) {
1111 WARN(wave
, "can't close !\n");
1112 return MMSYSERR_NOTENABLED
;
1114 if (WInDev
[wDevID
].lpQueueHdr
!= NULL
) {
1115 WARN(wave
, "still buffers open !\n");
1116 return WAVERR_STILLPLAYING
;
1118 close(WInDev
[wDevID
].unixdev
);
1119 WInDev
[wDevID
].unixdev
= 0;
1120 WInDev
[wDevID
].dwFragmentSize
= 0;
1121 if (WAVE_NotifyClient(wDevID
, WIM_CLOSE
, 0L, 0L) != MMSYSERR_NOERROR
) {
1122 WARN(wave
, "can't notify client !\n");
1123 return MMSYSERR_INVALPARAM
;
1125 return MMSYSERR_NOERROR
;
1128 /**************************************************************************
1129 * widAddBuffer [internal]
1131 static DWORD
widAddBuffer(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
1136 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
1137 if (WInDev
[wDevID
].unixdev
== 0) {
1138 WARN(wave
, "can't do it !\n");
1139 return MMSYSERR_NOTENABLED
;
1141 if (!(lpWaveHdr
->dwFlags
& WHDR_PREPARED
)) {
1142 TRACE(wave
, "never been prepared !\n");
1143 return WAVERR_UNPREPARED
;
1145 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
) {
1146 TRACE(wave
, "header already in use !\n");
1147 return WAVERR_STILLPLAYING
;
1149 lpWaveHdr
->dwFlags
|= WHDR_PREPARED
;
1150 lpWaveHdr
->dwFlags
|= WHDR_INQUEUE
;
1151 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
1152 lpWaveHdr
->dwBytesRecorded
= 0;
1153 if (WInDev
[wDevID
].lpQueueHdr
== NULL
) {
1154 WInDev
[wDevID
].lpQueueHdr
= lpWaveHdr
;
1156 lpWIHdr
= WInDev
[wDevID
].lpQueueHdr
;
1157 while (lpWIHdr
->lpNext
!= NULL
) {
1158 lpWIHdr
= lpWIHdr
->lpNext
;
1161 lpWIHdr
->lpNext
= lpWaveHdr
;
1162 lpWaveHdr
->lpNext
= NULL
;
1165 TRACE(wave
, "buffer added ! (now %u in queue)\n", count
);
1166 return MMSYSERR_NOERROR
;
1169 /**************************************************************************
1170 * widPrepare [internal]
1172 static DWORD
widPrepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
1174 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
1175 if (WInDev
[wDevID
].unixdev
== 0) {
1176 WARN(wave
, "can't prepare !\n");
1177 return MMSYSERR_NOTENABLED
;
1179 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
1180 return WAVERR_STILLPLAYING
;
1181 lpWaveHdr
->dwFlags
|= WHDR_PREPARED
;
1182 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
1183 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
1184 lpWaveHdr
->dwBytesRecorded
= 0;
1185 TRACE(wave
, "header prepared !\n");
1186 return MMSYSERR_NOERROR
;
1189 /**************************************************************************
1190 * widUnprepare [internal]
1192 static DWORD
widUnprepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
1194 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
1195 if (WInDev
[wDevID
].unixdev
== 0) {
1196 WARN(wave
, "can't unprepare !\n");
1197 return MMSYSERR_NOTENABLED
;
1199 lpWaveHdr
->dwFlags
&= ~WHDR_PREPARED
;
1200 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
1201 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
1203 return MMSYSERR_NOERROR
;
1206 /**************************************************************************
1207 * widStart [internal]
1209 static DWORD
widStart(WORD wDevID
)
1213 LPWAVEHDR
*lpWaveHdr
;
1216 TRACE(wave
, "(%u);\n", wDevID
);
1217 if (WInDev
[wDevID
].unixdev
== 0) {
1218 WARN(wave
, "can't start recording !\n");
1219 return MMSYSERR_NOTENABLED
;
1222 lpWaveHdr
= &(WInDev
[wDevID
].lpQueueHdr
);
1223 TRACE(wave
, "lpWaveHdr = %08lx\n",(DWORD
)lpWaveHdr
);
1225 if (!*lpWaveHdr
|| !(*lpWaveHdr
)->lpData
) {
1226 TRACE(wave
, "never been prepared !\n");
1227 return WAVERR_UNPREPARED
;
1230 while (*lpWaveHdr
!= NULL
) {
1232 lpData
= ((DWORD
)*lpWaveHdr
== (*lpWaveHdr
)->reserved
) ?
1233 (LPBYTE
)(*lpWaveHdr
)->lpData
: (LPBYTE
)PTR_SEG_TO_LIN((*lpWaveHdr
)->lpData
);
1235 TRACE(wave
, "recording buf#%u=%p size=%lu \n",
1236 count
, lpData
, (*lpWaveHdr
)->dwBufferLength
);
1239 bytesRead
= read(WInDev
[wDevID
].unixdev
, lpData
, (*lpWaveHdr
)->dwBufferLength
);
1241 if (bytesRead
== -1)
1242 perror("read from audio device");
1244 TRACE(wave
, "bytesread=%d (%ld)\n", bytesRead
, (*lpWaveHdr
)->dwBufferLength
);
1245 (*lpWaveHdr
)->dwBytesRecorded
= bytesRead
;
1246 WInDev
[wDevID
].dwTotalRecorded
+= (*lpWaveHdr
)->dwBytesRecorded
;
1247 (*lpWaveHdr
)->dwFlags
&= ~WHDR_INQUEUE
;
1248 (*lpWaveHdr
)->dwFlags
|= WHDR_DONE
;
1250 if (WAVE_NotifyClient(wDevID
, WIM_DATA
, (*lpWaveHdr
)->reserved
, (*lpWaveHdr
)->dwBytesRecorded
) != MMSYSERR_NOERROR
) {
1251 WARN(wave
, "can't notify client !\n");
1252 return MMSYSERR_INVALPARAM
;
1254 /* removes the current block from the queue */
1255 *lpWaveHdr
= (*lpWaveHdr
)->lpNext
;
1258 TRACE(wave
, "end of recording !\n");
1260 return MMSYSERR_NOERROR
;
1263 /**************************************************************************
1264 * widStop [internal]
1266 static DWORD
widStop(WORD wDevID
)
1268 TRACE(wave
, "(%u);\n", wDevID
);
1269 if (WInDev
[wDevID
].unixdev
== 0) {
1270 WARN(wave
, "can't stop !\n");
1271 return MMSYSERR_NOTENABLED
;
1273 return MMSYSERR_NOERROR
;
1276 /**************************************************************************
1277 * widReset [internal]
1279 static DWORD
widReset(WORD wDevID
)
1281 TRACE(wave
, "(%u);\n", wDevID
);
1282 if (WInDev
[wDevID
].unixdev
== 0) {
1283 WARN(wave
, "can't reset !\n");
1284 return MMSYSERR_NOTENABLED
;
1286 return MMSYSERR_NOERROR
;
1289 /**************************************************************************
1290 * widGetPosition [internal]
1292 static DWORD
widGetPosition(WORD wDevID
, LPMMTIME16 lpTime
, DWORD uSize
)
1296 TRACE(wave
, "(%u, %p, %lu);\n", wDevID
, lpTime
, uSize
);
1297 if (WInDev
[wDevID
].unixdev
== 0) {
1298 WARN(wave
, "can't get pos !\n");
1299 return MMSYSERR_NOTENABLED
;
1301 if (lpTime
== NULL
) return MMSYSERR_INVALPARAM
;
1302 TRACE(wave
, "wType=%04X !\n", lpTime
->wType
);
1303 TRACE(wave
, "wBitsPerSample=%u\n", WInDev
[wDevID
].format
.wBitsPerSample
);
1304 TRACE(wave
, "nSamplesPerSec=%lu\n", WInDev
[wDevID
].format
.wf
.nSamplesPerSec
);
1305 TRACE(wave
, "nChannels=%u\n", WInDev
[wDevID
].format
.wf
.nChannels
);
1306 TRACE(wave
, "nAvgBytesPerSec=%lu\n", WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
);
1308 switch (lpTime
->wType
) {
1310 lpTime
->u
.cb
= WInDev
[wDevID
].dwTotalRecorded
;
1311 TRACE(wave
, "TIME_BYTES=%lu\n", lpTime
->u
.cb
);
1314 lpTime
->u
.sample
= WInDev
[wDevID
].dwTotalRecorded
* 8 /
1315 WInDev
[wDevID
].format
.wBitsPerSample
;
1316 TRACE(wave
, "TIME_SAMPLES=%lu\n", lpTime
->u
.sample
);
1319 time
= WInDev
[wDevID
].dwTotalRecorded
/
1320 (WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/ 1000);
1321 lpTime
->u
.smpte
.hour
= time
/ 108000;
1322 time
-= lpTime
->u
.smpte
.hour
* 108000;
1323 lpTime
->u
.smpte
.min
= time
/ 1800;
1324 time
-= lpTime
->u
.smpte
.min
* 1800;
1325 lpTime
->u
.smpte
.sec
= time
/ 30;
1326 time
-= lpTime
->u
.smpte
.sec
* 30;
1327 lpTime
->u
.smpte
.frame
= time
;
1328 lpTime
->u
.smpte
.fps
= 30;
1329 TRACE(wave
, "TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1330 lpTime
->u
.smpte
.hour
, lpTime
->u
.smpte
.min
,
1331 lpTime
->u
.smpte
.sec
, lpTime
->u
.smpte
.frame
);
1334 lpTime
->u
.ms
= WInDev
[wDevID
].dwTotalRecorded
/
1335 (WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/ 1000);
1336 TRACE(wave
, "TIME_MS=%lu\n", lpTime
->u
.ms
);
1339 FIXME(wave
, "format not supported (%u) ! use TIME_MS !\n", lpTime
->wType
);
1340 lpTime
->wType
= TIME_MS
;
1342 return MMSYSERR_NOERROR
;
1345 /**************************************************************************
1346 * widMessage [sample driver]
1348 DWORD WINAPI
widMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
1349 DWORD dwParam1
, DWORD dwParam2
)
1351 TRACE(wave
, "(%u, %04X, %08lX, %08lX, %08lX);\n",
1352 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1355 case WIDM_OPEN
: return widOpen(wDevID
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
1356 case WIDM_CLOSE
: return widClose(wDevID
);
1357 case WIDM_ADDBUFFER
: return widAddBuffer(wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
1358 case WIDM_PREPARE
: return widPrepare(wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
1359 case WIDM_UNPREPARE
: return widUnprepare(wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
1360 case WIDM_GETDEVCAPS
: return widGetDevCaps(wDevID
, (LPWAVEINCAPS16
)dwParam1
,dwParam2
);
1361 case WIDM_GETNUMDEVS
: return wodGetNumDevs(); /* same number of devices in output as in input */
1362 case WIDM_GETPOS
: return widGetPosition(wDevID
, (LPMMTIME16
)dwParam1
, dwParam2
);
1363 case WIDM_RESET
: return widReset(wDevID
);
1364 case WIDM_START
: return widStart(wDevID
);
1365 case WIDM_PAUSE
: return widStop(wDevID
);
1366 case WIDM_STOP
: return widStop(wDevID
);
1368 FIXME(wave
, "unknown message %u!\n", wMsg
);
1370 return MMSYSERR_NOTSUPPORTED
;
1373 /*======================================================================*
1374 * Low level WAVE implemantation - DriverProc *
1375 *======================================================================*/
1377 /**************************************************************************
1378 * WAVE_DriverProc [sample driver]
1380 LONG
WAVE_DriverProc(DWORD dwDevID
, HDRVR16 hDriv
, DWORD wMsg
,
1381 DWORD dwParam1
, DWORD dwParam2
)
1383 TRACE(wave
, "(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1385 case DRV_LOAD
: return 1;
1386 case DRV_FREE
: return 1;
1387 case DRV_OPEN
: return 1;
1388 case DRV_CLOSE
: return 1;
1389 case DRV_ENABLE
: return 1;
1390 case DRV_DISABLE
: return 1;
1391 case DRV_QUERYCONFIGURE
: return 1;
1392 case DRV_CONFIGURE
: MessageBoxA(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK
); return 1;
1393 case DRV_INSTALL
: return DRVCNF_RESTART
;
1394 case DRV_REMOVE
: return DRVCNF_RESTART
;
1396 FIXME(wave
, "is probably wrong msg=0x%04lx\n", wMsg
);
1397 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1399 return MMSYSERR_NOTENABLED
;
1402 #else /* !HAVE_OSS */
1404 /**************************************************************************
1405 * wodMessage [sample driver]
1407 DWORD WINAPI
wodMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
1408 DWORD dwParam1
, DWORD dwParam2
)
1410 FIXME(wave
, "(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1411 return MMSYSERR_NOTENABLED
;
1414 /**************************************************************************
1415 * widMessage [sample driver]
1417 DWORD WINAPI
widMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
1418 DWORD dwParam1
, DWORD dwParam2
)
1420 FIXME(wave
, "(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1421 return MMSYSERR_NOTENABLED
;
1424 /**************************************************************************
1425 * WAVE_DriverProc [sample driver]
1427 LONG
WAVE_DriverProc(DWORD dwDevID
, HDRVR16 hDriv
, DWORD wMsg
,
1428 DWORD dwParam1
, DWORD dwParam2
)
1430 return MMSYSERR_NOTENABLED
;
1432 #endif /* HAVE_OSS */