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"
29 DEFAULT_DEBUG_CHANNEL(wave
)
33 #define SOUND_DEV "/dev/dsp"
34 #define MIXER_DEV "/dev/mixer"
36 #define MAX_WAVEOUTDRV (1)
37 #define MAX_WAVEINDRV (1)
39 /* state diagram for waveOut writing:
40 * FIXME: I'm not 100% pleased with the handling of RESETTING and CLOSING which are
42 * Basically, they are events handled in waveOutXXXX functions (open, write, pause, restart),
43 * and the others (resetn close) which are handled by the player. We (I ?) use transient
44 * states (RESETTING and CLOSING) to send the event from the waveOutXXX functions to
45 * the player. Not very good.
48 * PAUSED write() PAUSED
49 * STOPPED write() PLAYING
50 * (other) write() <error>
51 * (any) pause() PAUSED
52 * PAUSED restart() PLAYING if hdr, STOPPED otherwise
53 * reset() RESETTING => STOPPED
54 * close() CLOSING => CLOSED
57 #define WINE_WS_PLAYING 0
58 #define WINE_WS_PAUSED 1
59 #define WINE_WS_STOPPED 2
60 #define WINE_WS_RESETTING 3
61 #define WINE_WS_CLOSING 4
62 #define WINE_WS_CLOSED 5
66 volatile int state
; /* one of the WINE_WS_ manifest constants */
68 WAVEOPENDESC waveDesc
;
74 CRITICAL_SECTION critSect
;
75 /* used only by wodPlayer */
76 DWORD dwOffCurrHdr
; /* offset in lpQueueHdr->lpWaveHdr->lpData for fragments */
77 DWORD dwRemain
; /* number of bytes to write to end the current fragment */
78 DWORD dwNotifiedBytes
; /* number of bytes for which wavehdr notification has been done */
79 WAVEHDR
* lpNotifyHdr
; /* list of wavehdr for which write() has been called, pending for notification */
85 DWORD dwFragmentSize
; /* OpenSound '/dev/dsp' give us that size */
86 WAVEOPENDESC waveDesc
;
90 DWORD dwTotalRecorded
;
93 static WINE_WAVEOUT WOutDev
[MAX_WAVEOUTDRV
];
94 static WINE_WAVEIN WInDev
[MAX_WAVEOUTDRV
];
96 /*======================================================================*
97 * Low level WAVE implemantation *
98 *======================================================================*/
100 /**************************************************************************
101 * WAVE_NotifyClient [internal]
103 static DWORD
WAVE_NotifyClient(UINT16 wDevID
, WORD wMsg
,
104 DWORD dwParam1
, DWORD dwParam2
)
106 TRACE(wave
, "wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID
, wMsg
, dwParam1
, dwParam2
);
112 if (wDevID
> MAX_WAVEOUTDRV
) return MCIERR_INTERNAL
;
114 if (WOutDev
[wDevID
].wFlags
!= DCB_NULL
&&
115 !DriverCallback16(WOutDev
[wDevID
].waveDesc
.dwCallBack
,
116 WOutDev
[wDevID
].wFlags
,
117 WOutDev
[wDevID
].waveDesc
.hWave
,
119 WOutDev
[wDevID
].waveDesc
.dwInstance
,
122 WARN(wave
, "can't notify client !\n");
123 return MMSYSERR_NOERROR
;
130 if (wDevID
> MAX_WAVEINDRV
) return MCIERR_INTERNAL
;
132 if (WInDev
[wDevID
].wFlags
!= DCB_NULL
&&
133 !DriverCallback16(WInDev
[wDevID
].waveDesc
.dwCallBack
,
134 WInDev
[wDevID
].wFlags
,
135 WInDev
[wDevID
].waveDesc
.hWave
,
137 WInDev
[wDevID
].waveDesc
.dwInstance
,
140 WARN(wave
, "can't notify client !\n");
141 return MMSYSERR_NOERROR
;
145 FIXME(wave
, "Unknown CB message %u\n", wMsg
);
151 /*======================================================================*
152 * Low level WAVE OUT implemantation *
153 *======================================================================*/
155 /**************************************************************************
156 * wodPlayer_WriteFragments [internal]
158 * wodPlayer helper. Writes as many fragments it can to unixdev.
159 * Returns TRUE in case of buffer underrun.
161 static BOOL
wodPlayer_WriteFragments(WINE_WAVEOUT
* wwo
)
167 audio_buf_info abinfo
;
170 /* get number of writable fragments */
171 if (ioctl(wwo
->unixdev
, SNDCTL_DSP_GETOSPACE
, &abinfo
) == -1) {
172 perror("ioctl SNDCTL_DSP_GETOSPACE");
174 LeaveCriticalSection(&wwo
->critSect
);
177 TRACE(wave
, "Can write %d fragments on fd %d\n", abinfo
.fragments
, wwo
->unixdev
);
179 if (abinfo
.fragments
== 0) /* output queue is full, wait a bit */
182 lpWaveHdr
= wwo
->lpQueueHdr
;
184 if (wwo
->dwRemain
> 0 && /* still data to send to complete current fragment */
185 wwo
->dwNotifiedBytes
>= wwo
->dwFragmentSize
&& /* first fragment has been played */
186 abinfo
.fragstotal
- abinfo
.fragments
< 2) { /* done with all waveOutWrite()' fragments */
188 /* FIXME: should do better handling here */
189 TRACE(wave
, "Oooch, buffer underrun !\n");
190 return TRUE
; /* force resetting of waveOut device */
192 return FALSE
; /* wait a bit */
195 if (wwo
->dwOffCurrHdr
== 0) {
196 TRACE(wave
, "Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr
, lpWaveHdr
->dwBufferLength
);
197 if (lpWaveHdr
->dwFlags
& WHDR_BEGINLOOP
)
198 FIXME(wave
, "NIY: loops (%lu) in wavehdr\n", lpWaveHdr
->dwLoops
);
201 lpData
= ((DWORD
)lpWaveHdr
== lpWaveHdr
->reserved
) ?
202 (LPBYTE
)lpWaveHdr
->lpData
: (LPBYTE
)PTR_SEG_TO_LIN(lpWaveHdr
->lpData
);
204 /* finish current wave hdr ? */
205 if (wwo
->dwOffCurrHdr
+ wwo
->dwRemain
>= lpWaveHdr
->dwBufferLength
) {
206 DWORD toWrite
= lpWaveHdr
->dwBufferLength
- wwo
->dwOffCurrHdr
;
208 /* write end of current wave hdr */
209 count
= write(wwo
->unixdev
, lpData
+ wwo
->dwOffCurrHdr
, toWrite
);
210 TRACE(wave
, "write(%p[%5lu], %5lu) => %d\n", lpData
, wwo
->dwOffCurrHdr
, toWrite
, count
);
212 /* move lpWaveHdr to notify list */
213 if (wwo
->lpNotifyHdr
) {
214 for (wh
= wwo
->lpNotifyHdr
; wh
->lpNext
!= NULL
; wh
= wh
->lpNext
);
215 wh
->lpNext
= lpWaveHdr
;
217 wwo
->lpNotifyHdr
= lpWaveHdr
;
219 wwo
->lpQueueHdr
= lpWaveHdr
->lpNext
;
220 lpWaveHdr
->lpNext
= 0;
222 wwo
->dwOffCurrHdr
= 0;
223 if ((wwo
->dwRemain
-= toWrite
) == 0)
224 wwo
->dwRemain
= wwo
->dwFragmentSize
;
225 continue; /* try to go to use next wavehdr */
227 count
= write(wwo
->unixdev
, lpData
+ wwo
->dwOffCurrHdr
, wwo
->dwRemain
);
228 TRACE(wave
, "write(%p[%5lu], %5lu) => %d\n", lpData
, wwo
->dwOffCurrHdr
, wwo
->dwRemain
, count
);
230 wwo
->dwOffCurrHdr
+= wwo
->dwRemain
;
231 wwo
->dwRemain
= wwo
->dwFragmentSize
;
237 /**************************************************************************
238 * wodPlayer_WriteFragments [internal]
240 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
241 * have been played (actually to speaker, not to unixdev fd).
243 static void wodPlayer_Notify(WINE_WAVEOUT
* wwo
, WORD uDevID
, BOOL force
)
248 /* get effective number of written bytes */
250 if (ioctl(wwo
->unixdev
, SNDCTL_DSP_GETOPTR
, &cinfo
) == -1) {
251 perror("ioctl SNDCTL_DSP_GETOPTR");
253 LeaveCriticalSection(&wwo
->critSect
);
256 TRACE(wave
, "Played %d bytes on fd %d\n", cinfo
.bytes
, wwo
->unixdev
);
257 /* FIXME: this will not work if a RESET or SYNC occurs somewhere */
258 wwo
->dwTotalPlayed
= cinfo
.bytes
;
260 if (force
|| cinfo
.bytes
> wwo
->dwNotifiedBytes
) {
261 /* remove all wavehdr which can be notified */
262 while (wwo
->lpNotifyHdr
&&
263 (force
|| (cinfo
.bytes
>= wwo
->dwNotifiedBytes
+ wwo
->lpNotifyHdr
->dwBufferLength
))) {
264 lpWaveHdr
= wwo
->lpNotifyHdr
;
266 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
267 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
269 wwo
->dwNotifiedBytes
+= lpWaveHdr
->dwBufferLength
;
270 wwo
->lpNotifyHdr
= lpWaveHdr
->lpNext
;
272 /* FIXME: should we keep the wwo->critsect locked during callback ? */
273 /* normally yes, only a limited set of functions can be called...
274 * the ones which can be called inside an i386 interruption
275 * so this should be fine
277 LeaveCriticalSection(&wwo
->critSect
);
278 TRACE(wave
, "Notifying client with %p\n", lpWaveHdr
);
279 if (WAVE_NotifyClient(uDevID
, WOM_DONE
, lpWaveHdr
->reserved
, 0) != MMSYSERR_NOERROR
) {
280 WARN(wave
, "can't notify client !\n");
282 EnterCriticalSection(&wwo
->critSect
);
287 /**************************************************************************
288 * wodPlayer [internal]
290 static DWORD WINAPI
wodPlayer(LPVOID pmt
)
292 WORD uDevID
= (DWORD
)pmt
;
293 WINE_WAVEOUT
* wwo
= (WINE_WAVEOUT
*)&WOutDev
[uDevID
];
298 wwo
->dwNotifiedBytes
= 0;
299 wwo
->dwOffCurrHdr
= 0;
300 wwo
->dwRemain
= wwo
->dwFragmentSize
;
301 wwo
->lpNotifyHdr
= NULL
;
303 /* make sleep time to be # of ms to output a fragment */
304 dwSleepTime
= (wwo
->dwFragmentSize
* 1000) / wwo
->format
.wf
.nAvgBytesPerSec
;
308 EnterCriticalSection(&wwo
->critSect
);
310 switch (wwo
->state
) {
312 wodPlayer_Notify(wwo
, uDevID
, FALSE
);
313 if (laststate
!= WINE_WS_PAUSED
) {
314 if (ioctl(wwo
->unixdev
, SNDCTL_DSP_POST
, 0) == -1) {
315 perror("ioctl SNDCTL_DSP_POST");
317 LeaveCriticalSection(&wwo
->critSect
);
322 case WINE_WS_STOPPED
:
323 /* should we kill the Thread here and to restart it later when needed ? */
325 case WINE_WS_PLAYING
:
326 if (!wodPlayer_WriteFragments(wwo
)) {
327 wodPlayer_Notify(wwo
, uDevID
, FALSE
);
331 case WINE_WS_RESETTING
:
332 /* updates current notify list */
333 wodPlayer_Notify(wwo
, uDevID
, FALSE
);
334 /* flush all possible output */
335 if (ioctl(wwo
->unixdev
, SNDCTL_DSP_SYNC
, 0) == -1) {
336 perror("ioctl SNDCTL_DSP_SYNC");
338 LeaveCriticalSection(&wwo
->critSect
);
341 /* DSP_SYNC resets the number of send bytes */
342 wwo
->dwNotifiedBytes
= 0;
343 /* empty notify list */
344 wodPlayer_Notify(wwo
, uDevID
, TRUE
);
345 if (wwo
->lpNotifyHdr
) {
346 ERR(wave
, "out of sync\n");
348 wwo
->dwOffCurrHdr
= 0;
349 /* get rid also of all the current queue */
350 for (lpWaveHdr
= wwo
->lpQueueHdr
; lpWaveHdr
; lpWaveHdr
= lpWaveHdr
->lpNext
) {
351 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
352 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
354 if (WAVE_NotifyClient((DWORD
)pmt
, WOM_DONE
, lpWaveHdr
->reserved
, 0) != MMSYSERR_NOERROR
) {
355 WARN(wave
, "can't notify client !\n");
359 wwo
->state
= WINE_WS_STOPPED
;
361 case WINE_WS_CLOSING
:
362 /* this should not happen since the device must have been reset before */
363 if (wwo
->lpNotifyHdr
|| wwo
->lpQueueHdr
) {
364 ERR(wave
, "out of sync\n");
367 wwo
->state
= WINE_WS_CLOSED
;
368 LeaveCriticalSection(&wwo
->critSect
);
369 DeleteCriticalSection(&wwo
->critSect
);
371 /* shouldn't go here */
373 FIXME(wave
, "unknown state %d\n", wwo
->state
);
376 laststate
= wwo
->state
;
377 LeaveCriticalSection(&wwo
->critSect
);
378 /* sleep for one fragment to be sent */
384 /**************************************************************************
385 * wodGetDevCaps [internal]
387 static DWORD
wodGetDevCaps(WORD wDevID
, LPWAVEOUTCAPS16 lpCaps
, DWORD dwSize
)
396 TRACE(wave
, "(%u, %p, %lu);\n", wDevID
, lpCaps
, dwSize
);
398 if (lpCaps
== NULL
) return MMSYSERR_NOTENABLED
;
399 if (access(SOUND_DEV
,0) != 0) return MMSYSERR_NOTENABLED
;
401 if (wDevID
>= MAX_WAVEOUTDRV
) {
402 TRACE(wave
, "MAX_WAVOUTDRV reached !\n");
403 return MMSYSERR_BADDEVICEID
;
406 if (WOutDev
[wDevID
].unixdev
== 0) {
407 audio
= open(SOUND_DEV
, O_WRONLY
, 0);
408 if (audio
== -1) return MMSYSERR_ALLOCATED
;
410 audio
= WOutDev
[wDevID
].unixdev
;
413 /* FIXME: some programs compare this string against the content of the registry
414 * for MM drivers. The name have to match in order the program to work
415 * (e.g. MS win9x mplayer.exe)
418 lpCaps
->wMid
= 0x0002;
419 lpCaps
->wPid
= 0x0104;
420 strcpy(lpCaps
->szPname
, "SB16 Wave Out");
422 lpCaps
->wMid
= 0x00FF; /* Manufac ID */
423 lpCaps
->wPid
= 0x0001; /* Product ID */
424 /* strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");*/
425 strcpy(lpCaps
->szPname
, "CS4236/37/38");
427 lpCaps
->vDriverVersion
= 0x0100;
428 lpCaps
->dwFormats
= 0x00000000;
429 lpCaps
->dwSupport
= WAVECAPS_VOLUME
;
431 /* First bytespersampl, then stereo */
432 bytespersmpl
= (IOCTL(audio
, SNDCTL_DSP_SAMPLESIZE
, samplesize
) != 0) ? 1 : 2;
434 lpCaps
->wChannels
= (IOCTL(audio
, SNDCTL_DSP_STEREO
, dsp_stereo
) != 0) ? 1 : 2;
435 if (lpCaps
->wChannels
> 1) lpCaps
->dwSupport
|= WAVECAPS_LRVOLUME
;
438 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
439 lpCaps
->dwFormats
|= WAVE_FORMAT_4M08
;
440 if (lpCaps
->wChannels
> 1)
441 lpCaps
->dwFormats
|= WAVE_FORMAT_4S08
;
442 if (bytespersmpl
> 1) {
443 lpCaps
->dwFormats
|= WAVE_FORMAT_4M16
;
444 if (lpCaps
->wChannels
> 1)
445 lpCaps
->dwFormats
|= WAVE_FORMAT_4S16
;
449 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
450 lpCaps
->dwFormats
|= WAVE_FORMAT_2M08
;
451 if (lpCaps
->wChannels
> 1)
452 lpCaps
->dwFormats
|= WAVE_FORMAT_2S08
;
453 if (bytespersmpl
> 1) {
454 lpCaps
->dwFormats
|= WAVE_FORMAT_2M16
;
455 if (lpCaps
->wChannels
> 1)
456 lpCaps
->dwFormats
|= WAVE_FORMAT_2S16
;
460 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
461 lpCaps
->dwFormats
|= WAVE_FORMAT_1M08
;
462 if (lpCaps
->wChannels
> 1)
463 lpCaps
->dwFormats
|= WAVE_FORMAT_1S08
;
464 if (bytespersmpl
> 1) {
465 lpCaps
->dwFormats
|= WAVE_FORMAT_1M16
;
466 if (lpCaps
->wChannels
> 1)
467 lpCaps
->dwFormats
|= WAVE_FORMAT_1S16
;
470 if (IOCTL(audio
, SNDCTL_DSP_GETCAPS
, caps
) == 0) {
471 if ((caps
& DSP_CAP_REALTIME
) && !(caps
&& DSP_CAP_BATCH
))
472 lpCaps
->dwFormats
|= WAVECAPS_SAMPLEACCURATE
;
474 if (WOutDev
[wDevID
].unixdev
== 0) {
477 TRACE(wave
, "dwFormats = %08lX\n", lpCaps
->dwFormats
);
478 return MMSYSERR_NOERROR
;
482 /**************************************************************************
485 static DWORD
wodOpen(WORD wDevID
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
494 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
495 if (lpDesc
== NULL
) {
496 WARN(wave
, "Invalid Parameter !\n");
497 return MMSYSERR_INVALPARAM
;
499 if (wDevID
>= MAX_WAVEOUTDRV
) {
500 TRACE(wave
, "MAX_WAVOUTDRV reached !\n");
501 return MMSYSERR_BADDEVICEID
;
503 /* only PCM format is supported so far... */
504 if (lpDesc
->lpFormat
->wFormatTag
!= WAVE_FORMAT_PCM
||
505 lpDesc
->lpFormat
->nChannels
== 0 ||
506 lpDesc
->lpFormat
->nSamplesPerSec
== 0) {
507 WARN(wave
, "Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
508 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
509 lpDesc
->lpFormat
->nSamplesPerSec
);
510 return WAVERR_BADFORMAT
;
513 if (dwFlags
& WAVE_FORMAT_QUERY
)
514 return MMSYSERR_NOERROR
;
516 WOutDev
[wDevID
].unixdev
= 0;
517 if (access(SOUND_DEV
, 0) != 0)
518 return MMSYSERR_NOTENABLED
;
519 audio
= open(SOUND_DEV
, O_WRONLY
, 0);
521 WARN(wave
, "can't open !\n");
522 return MMSYSERR_ALLOCATED
;
525 WOutDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
527 memcpy(&WOutDev
[wDevID
].waveDesc
, lpDesc
, sizeof(WAVEOPENDESC
));
528 memcpy(&WOutDev
[wDevID
].format
, lpDesc
->lpFormat
, sizeof(PCMWAVEFORMAT
));
530 if (WOutDev
[wDevID
].format
.wBitsPerSample
== 0) {
531 WOutDev
[wDevID
].format
.wBitsPerSample
= 8 *
532 (WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/
533 WOutDev
[wDevID
].format
.wf
.nSamplesPerSec
) /
534 WOutDev
[wDevID
].format
.wf
.nChannels
;
537 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
538 * thus leading to 46ms per fragment, and a turnaround time of 185ms
540 /* 2^10=1024 bytes per fragment, 16 fragments max */
541 audio_fragment
= 0x000F000A;
542 sample_size
= WOutDev
[wDevID
].format
.wBitsPerSample
;
543 sample_rate
= WOutDev
[wDevID
].format
.wf
.nSamplesPerSec
;
544 dsp_stereo
= (WOutDev
[wDevID
].format
.wf
.nChannels
> 1) ? TRUE
: FALSE
;
546 IOCTL(audio
, SNDCTL_DSP_SETFRAGMENT
, audio_fragment
);
547 /* First size and stereo then samplerate */
548 IOCTL(audio
, SNDCTL_DSP_SAMPLESIZE
, sample_size
);
549 IOCTL(audio
, SNDCTL_DSP_STEREO
, dsp_stereo
);
550 IOCTL(audio
, SNDCTL_DSP_SPEED
, sample_rate
);
552 /* even if we set fragment size above, read it again, just in case */
553 IOCTL(audio
, SNDCTL_DSP_GETBLKSIZE
, fragment_size
);
555 WOutDev
[wDevID
].lpQueueHdr
= NULL
;
556 WOutDev
[wDevID
].unixdev
= audio
;
557 WOutDev
[wDevID
].dwTotalPlayed
= 0;
558 WOutDev
[wDevID
].dwFragmentSize
= fragment_size
;
559 WOutDev
[wDevID
].state
= WINE_WS_STOPPED
;
560 WOutDev
[wDevID
].hThread
= 0;
562 /* FIXME: do we need to make critsect global ? */
563 InitializeCriticalSection(&WOutDev
[wDevID
].critSect
);
564 MakeCriticalSectionGlobal(&WOutDev
[wDevID
].critSect
);
566 TRACE(wave
, "fd=%d fragmentSize=%ld\n",
567 WOutDev
[wDevID
].unixdev
, WOutDev
[wDevID
].dwFragmentSize
);
569 TRACE(wave
, "wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u !\n",
570 WOutDev
[wDevID
].format
.wBitsPerSample
, WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
,
571 WOutDev
[wDevID
].format
.wf
.nSamplesPerSec
, WOutDev
[wDevID
].format
.wf
.nChannels
);
573 if (WAVE_NotifyClient(wDevID
, WOM_OPEN
, 0L, 0L) != MMSYSERR_NOERROR
) {
574 WARN(wave
, "can't notify client !\n");
575 return MMSYSERR_INVALPARAM
;
577 return MMSYSERR_NOERROR
;
580 /**************************************************************************
581 * wodClose [internal]
583 static DWORD
wodClose(WORD wDevID
)
585 DWORD ret
= MMSYSERR_NOERROR
;
587 TRACE(wave
, "(%u);\n", wDevID
);
589 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
590 WARN(wave
, "bad device ID !\n");
591 return MMSYSERR_BADDEVICEID
;
594 if (WOutDev
[wDevID
].lpQueueHdr
!= NULL
|| WOutDev
[wDevID
].lpNotifyHdr
!= NULL
) {
595 WARN(wave
, "buffers still playing !\n");
596 ret
= WAVERR_STILLPLAYING
;
598 EnterCriticalSection(&WOutDev
[wDevID
].critSect
);
599 WOutDev
[wDevID
].state
= WINE_WS_CLOSING
;
600 LeaveCriticalSection(&WOutDev
[wDevID
].critSect
);
602 while (WOutDev
[wDevID
].state
!= WINE_WS_CLOSED
)
605 close(WOutDev
[wDevID
].unixdev
);
606 WOutDev
[wDevID
].unixdev
= 0;
607 WOutDev
[wDevID
].dwFragmentSize
= 0;
608 if (WAVE_NotifyClient(wDevID
, WOM_CLOSE
, 0L, 0L) != MMSYSERR_NOERROR
) {
609 WARN(wave
, "can't notify client !\n");
610 ret
= MMSYSERR_INVALPARAM
;
616 /**************************************************************************
617 * wodWrite [internal]
620 static DWORD
wodWrite(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
622 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
624 /* first, do the sanity checks... */
625 if (wDevID
>= MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
626 WARN(wave
, "bad dev ID !\n");
627 return MMSYSERR_BADDEVICEID
;
630 if (lpWaveHdr
->lpData
== NULL
|| !(lpWaveHdr
->dwFlags
& WHDR_PREPARED
))
631 return WAVERR_UNPREPARED
;
633 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
634 return WAVERR_STILLPLAYING
;
636 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
637 lpWaveHdr
->dwFlags
|= WHDR_INQUEUE
;
638 lpWaveHdr
->lpNext
= 0;
640 EnterCriticalSection(&WOutDev
[wDevID
].critSect
);
641 /* insert buffer in queue */
642 if (WOutDev
[wDevID
].lpQueueHdr
) {
644 for (wh
= WOutDev
[wDevID
].lpQueueHdr
; wh
->lpNext
; wh
= wh
->lpNext
);
645 wh
->lpNext
= lpWaveHdr
;
647 WOutDev
[wDevID
].lpQueueHdr
= lpWaveHdr
;
649 /* starts thread if wod is not paused */
650 switch (WOutDev
[wDevID
].state
) {
652 case WINE_WS_PLAYING
:
655 case WINE_WS_STOPPED
:
656 WOutDev
[wDevID
].state
= WINE_WS_PLAYING
;
657 if (WOutDev
[wDevID
].hThread
== 0) {
658 WOutDev
[wDevID
].hThread
= CreateThread(NULL
, 0, wodPlayer
, (LPVOID
)(DWORD
)wDevID
, 0, NULL
);
662 FIXME(wave
, "Unsupported state %d\n", WOutDev
[wDevID
].state
);
664 LeaveCriticalSection(&WOutDev
[wDevID
].critSect
);
666 return MMSYSERR_NOERROR
;
669 /**************************************************************************
670 * wodPrepare [internal]
672 static DWORD
wodPrepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
674 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
676 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
677 WARN(wave
, "bad device ID !\n");
678 return MMSYSERR_BADDEVICEID
;
681 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
682 return WAVERR_STILLPLAYING
;
684 lpWaveHdr
->dwFlags
|= WHDR_PREPARED
;
685 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
686 return MMSYSERR_NOERROR
;
689 /**************************************************************************
690 * wodUnprepare [internal]
692 static DWORD
wodUnprepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
694 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
696 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
697 WARN(wave
, "bad device ID !\n");
698 return MMSYSERR_BADDEVICEID
;
701 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
702 return WAVERR_STILLPLAYING
;
704 lpWaveHdr
->dwFlags
&= ~WHDR_PREPARED
;
705 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
707 return MMSYSERR_NOERROR
;
710 /**************************************************************************
711 * wodPause [internal]
713 static DWORD
wodPause(WORD wDevID
)
715 TRACE(wave
, "(%u);!\n", wDevID
);
717 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
718 WARN(wave
, "bad device ID !\n");
719 return MMSYSERR_BADDEVICEID
;
722 EnterCriticalSection(&WOutDev
[wDevID
].critSect
);
723 WOutDev
[wDevID
].state
= WINE_WS_PAUSED
;
724 LeaveCriticalSection(&WOutDev
[wDevID
].critSect
);
726 return MMSYSERR_NOERROR
;
729 /**************************************************************************
732 static DWORD
wodStop(WORD wDevID
)
734 FIXME(wave
, "(%u); NIY!\n", wDevID
);
736 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
737 WARN(wave
, "bad device ID !\n");
738 return MMSYSERR_BADDEVICEID
;
741 return MMSYSERR_NOERROR
;
744 /**************************************************************************
745 * wodRestart [internal]
747 static DWORD
wodRestart(WORD wDevID
)
749 TRACE(wave
, "(%u);\n", wDevID
);
751 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
752 WARN(wave
, "bad device ID !\n");
753 return MMSYSERR_BADDEVICEID
;
756 EnterCriticalSection(&WOutDev
[wDevID
].critSect
);
757 if (WOutDev
[wDevID
].state
== WINE_WS_PAUSED
) {
758 if (WOutDev
[wDevID
].lpQueueHdr
!= NULL
) {
759 WOutDev
[wDevID
].state
= WINE_WS_PLAYING
;
760 if (WOutDev
[wDevID
].hThread
== 0)
761 WOutDev
[wDevID
].hThread
= CreateThread(NULL
, 0, wodPlayer
, (LPVOID
)(DWORD
)wDevID
, 0, NULL
);
763 WOutDev
[wDevID
].state
= WINE_WS_STOPPED
;
766 LeaveCriticalSection(&WOutDev
[wDevID
].critSect
);
768 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
769 /* FIXME: Myst crashes with this ... hmm -MM
770 if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
771 WARN(wave, "can't notify client !\n");
772 return MMSYSERR_INVALPARAM;
776 return MMSYSERR_NOERROR
;
779 /**************************************************************************
780 * wodReset [internal]
782 static DWORD
wodReset(WORD wDevID
)
784 TRACE(wave
, "(%u);\n", wDevID
);
786 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
787 WARN(wave
, "bad device ID !\n");
788 return MMSYSERR_BADDEVICEID
;
791 EnterCriticalSection(&WOutDev
[wDevID
].critSect
);
792 if (WOutDev
[wDevID
].hThread
== 0) {
793 WOutDev
[wDevID
].state
= WINE_WS_STOPPED
;
795 WOutDev
[wDevID
].state
= WINE_WS_RESETTING
;
797 LeaveCriticalSection(&WOutDev
[wDevID
].critSect
);
799 while (WOutDev
[wDevID
].state
!= WINE_WS_STOPPED
)
802 return MMSYSERR_NOERROR
;
806 /**************************************************************************
807 * wodGetPosition [internal]
809 static DWORD
wodGetPosition(WORD wDevID
, LPMMTIME16 lpTime
, DWORD uSize
)
813 TRACE(wave
, "(%u, %p, %lu);\n", wDevID
, lpTime
, uSize
);
815 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
816 WARN(wave
, "bad device ID !\n");
817 return MMSYSERR_BADDEVICEID
;
820 if (lpTime
== NULL
) return MMSYSERR_INVALPARAM
;
821 TRACE(wave
, "wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
822 lpTime
->wType
, WOutDev
[wDevID
].format
.wBitsPerSample
,
823 WOutDev
[wDevID
].format
.wf
.nSamplesPerSec
, WOutDev
[wDevID
].format
.wf
.nChannels
,
824 WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
);
825 TRACE(wave
, "dwTotalPlayed=%lu\n", WOutDev
[wDevID
].dwTotalPlayed
);
827 switch (lpTime
->wType
) {
829 lpTime
->u
.cb
= WOutDev
[wDevID
].dwTotalPlayed
;
830 TRACE(wave
, "TIME_BYTES=%lu\n", lpTime
->u
.cb
);
833 lpTime
->u
.sample
= WOutDev
[wDevID
].dwTotalPlayed
* 8 /
834 WOutDev
[wDevID
].format
.wBitsPerSample
;
835 TRACE(wave
, "TIME_SAMPLES=%lu\n", lpTime
->u
.sample
);
838 time
= WOutDev
[wDevID
].dwTotalPlayed
/
839 (WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/ 1000);
840 lpTime
->u
.smpte
.hour
= time
/ 108000;
841 time
-= lpTime
->u
.smpte
.hour
* 108000;
842 lpTime
->u
.smpte
.min
= time
/ 1800;
843 time
-= lpTime
->u
.smpte
.min
* 1800;
844 lpTime
->u
.smpte
.sec
= time
/ 30;
845 time
-= lpTime
->u
.smpte
.sec
* 30;
846 lpTime
->u
.smpte
.frame
= time
;
847 lpTime
->u
.smpte
.fps
= 30;
848 TRACE(wave
, "TIME_SMPTE=%02u:%02u:%02u:%02u\n",
849 lpTime
->u
.smpte
.hour
, lpTime
->u
.smpte
.min
,
850 lpTime
->u
.smpte
.sec
, lpTime
->u
.smpte
.frame
);
853 FIXME(wave
, "Format %d not supported ! use TIME_MS !\n", lpTime
->wType
);
854 lpTime
->wType
= TIME_MS
;
856 lpTime
->u
.ms
= WOutDev
[wDevID
].dwTotalPlayed
/
857 (WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/ 1000);
858 TRACE(wave
, "TIME_MS=%lu\n", lpTime
->u
.ms
);
861 return MMSYSERR_NOERROR
;
864 /**************************************************************************
865 * wodGetVolume [internal]
867 static DWORD
wodGetVolume(WORD wDevID
, LPDWORD lpdwVol
)
870 int volume
, left
, right
;
872 TRACE(wave
, "(%u, %p);\n", wDevID
, lpdwVol
);
875 return MMSYSERR_NOTENABLED
;
876 if ((mixer
= open(MIXER_DEV
, O_RDONLY
)) < 0) {
877 WARN(wave
, "mixer device not available !\n");
878 return MMSYSERR_NOTENABLED
;
880 if (ioctl(mixer
, SOUND_MIXER_READ_PCM
, &volume
) == -1) {
881 WARN(wave
, "unable read mixer !\n");
882 return MMSYSERR_NOTENABLED
;
885 left
= volume
& 0x7F;
886 right
= (volume
>> 8) & 0x7F;
887 TRACE(wave
, "left=%d right=%d !\n", left
, right
);
888 *lpdwVol
= MAKELONG(left
<< 9, right
<< 9);
889 return MMSYSERR_NOERROR
;
893 /**************************************************************************
894 * wodSetVolume [internal]
896 static DWORD
wodSetVolume(WORD wDevID
, DWORD dwParam
)
900 TRACE(wave
, "(%u, %08lX);\n", wDevID
, dwParam
);
901 volume
= (LOWORD(dwParam
) >> 9 & 0x7F) +
902 ((HIWORD(dwParam
) >> 9 & 0x7F) << 8);
903 if ((mixer
= open(MIXER_DEV
, O_WRONLY
)) < 0) {
904 WARN(wave
, "mixer device not available !\n");
905 return MMSYSERR_NOTENABLED
;
907 if (ioctl(mixer
, SOUND_MIXER_WRITE_PCM
, &volume
) == -1) {
908 WARN(wave
, "unable set mixer !\n");
909 return MMSYSERR_NOTENABLED
;
912 return MMSYSERR_NOERROR
;
915 /**************************************************************************
916 * wodGetNumDevs [internal]
918 static DWORD
wodGetNumDevs(void)
922 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
923 int audio
= open(SOUND_DEV
, O_WRONLY
, 0);
934 /**************************************************************************
935 * wodMessage [sample driver]
937 DWORD WINAPI
wodMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
938 DWORD dwParam1
, DWORD dwParam2
)
940 TRACE(wave
, "(%u, %04X, %08lX, %08lX, %08lX);\n",
941 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
944 case WODM_OPEN
: return wodOpen (wDevID
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
945 case WODM_CLOSE
: return wodClose (wDevID
);
946 case WODM_WRITE
: return wodWrite (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
947 case WODM_PAUSE
: return wodPause (wDevID
);
948 case WODM_STOP
: return wodStop (wDevID
);
949 case WODM_GETPOS
: return wodGetPosition (wDevID
, (LPMMTIME16
)dwParam1
, dwParam2
);
950 case WODM_BREAKLOOP
: return MMSYSERR_NOTSUPPORTED
;
951 case WODM_PREPARE
: return wodPrepare (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
952 case WODM_UNPREPARE
: return wodUnprepare (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
953 case WODM_GETDEVCAPS
: return wodGetDevCaps (wDevID
, (LPWAVEOUTCAPS16
)dwParam1
, dwParam2
);
954 case WODM_GETNUMDEVS
: return wodGetNumDevs ();
955 case WODM_GETPITCH
: return MMSYSERR_NOTSUPPORTED
;
956 case WODM_SETPITCH
: return MMSYSERR_NOTSUPPORTED
;
957 case WODM_GETPLAYBACKRATE
: return MMSYSERR_NOTSUPPORTED
;
958 case WODM_SETPLAYBACKRATE
: return MMSYSERR_NOTSUPPORTED
;
959 case WODM_GETVOLUME
: return wodGetVolume (wDevID
, (LPDWORD
)dwParam1
);
960 case WODM_SETVOLUME
: return wodSetVolume (wDevID
, dwParam1
);
961 case WODM_RESTART
: return wodRestart (wDevID
);
962 case WODM_RESET
: return wodReset (wDevID
);
964 FIXME(wave
, "unknown message %d!\n", wMsg
);
966 return MMSYSERR_NOTSUPPORTED
;
969 /*======================================================================*
970 * Low level WAVE IN implemantation *
971 *======================================================================*/
973 /**************************************************************************
974 * widGetDevCaps [internal]
976 static DWORD
widGetDevCaps(WORD wDevID
, LPWAVEINCAPS16 lpCaps
, DWORD dwSize
)
978 int audio
,smplrate
,samplesize
=16,dsp_stereo
=1,bytespersmpl
;
980 TRACE(wave
, "(%u, %p, %lu);\n", wDevID
, lpCaps
, dwSize
);
981 if (lpCaps
== NULL
) return MMSYSERR_NOTENABLED
;
982 if (access(SOUND_DEV
,0) != 0) return MMSYSERR_NOTENABLED
;
983 audio
= open(SOUND_DEV
, O_RDONLY
, 0);
984 if (audio
== -1) return MMSYSERR_ALLOCATED
;
986 lpCaps
->wMid
= 0x0002;
987 lpCaps
->wPid
= 0x0004;
988 strcpy(lpCaps
->szPname
, "SB16 Wave In");
990 lpCaps
->wMid
= 0x00FF; /* Manufac ID */
991 lpCaps
->wPid
= 0x0001; /* Product ID */
992 strcpy(lpCaps
->szPname
, "OpenSoundSystem WAVIN Driver");
994 lpCaps
->dwFormats
= 0x00000000;
995 lpCaps
->wChannels
= (IOCTL(audio
, SNDCTL_DSP_STEREO
, dsp_stereo
) != 0) ? 1 : 2;
996 bytespersmpl
= (IOCTL(audio
, SNDCTL_DSP_SAMPLESIZE
, samplesize
) != 0) ? 1 : 2;
998 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
999 lpCaps
->dwFormats
|= WAVE_FORMAT_4M08
;
1000 if (lpCaps
->wChannels
> 1)
1001 lpCaps
->dwFormats
|= WAVE_FORMAT_4S08
;
1002 if (bytespersmpl
> 1) {
1003 lpCaps
->dwFormats
|= WAVE_FORMAT_4M16
;
1004 if (lpCaps
->wChannels
> 1)
1005 lpCaps
->dwFormats
|= WAVE_FORMAT_4S16
;
1009 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
1010 lpCaps
->dwFormats
|= WAVE_FORMAT_2M08
;
1011 if (lpCaps
->wChannels
> 1)
1012 lpCaps
->dwFormats
|= WAVE_FORMAT_2S08
;
1013 if (bytespersmpl
> 1) {
1014 lpCaps
->dwFormats
|= WAVE_FORMAT_2M16
;
1015 if (lpCaps
->wChannels
> 1)
1016 lpCaps
->dwFormats
|= WAVE_FORMAT_2S16
;
1020 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
1021 lpCaps
->dwFormats
|= WAVE_FORMAT_1M08
;
1022 if (lpCaps
->wChannels
> 1)
1023 lpCaps
->dwFormats
|= WAVE_FORMAT_1S08
;
1024 if (bytespersmpl
> 1) {
1025 lpCaps
->dwFormats
|= WAVE_FORMAT_1M16
;
1026 if (lpCaps
->wChannels
> 1)
1027 lpCaps
->dwFormats
|= WAVE_FORMAT_1S16
;
1031 TRACE(wave
, "dwFormats = %08lX\n", lpCaps
->dwFormats
);
1032 return MMSYSERR_NOERROR
;
1035 /**************************************************************************
1036 * widOpen [internal]
1038 static DWORD
widOpen(WORD wDevID
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
1040 int audio
,abuf_size
,smplrate
,samplesize
,dsp_stereo
;
1041 LPWAVEFORMAT lpFormat
;
1043 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
1044 if (lpDesc
== NULL
) {
1045 WARN(wave
, "Invalid Parameter !\n");
1046 return MMSYSERR_INVALPARAM
;
1048 if (wDevID
>= MAX_WAVEINDRV
) {
1049 TRACE(wave
, "MAX_WAVINDRV reached !\n");
1050 return MMSYSERR_ALLOCATED
;
1052 WInDev
[wDevID
].unixdev
= 0;
1053 if (access(SOUND_DEV
,0) != 0) return MMSYSERR_NOTENABLED
;
1054 audio
= open(SOUND_DEV
, O_RDONLY
, 0);
1056 WARN(wave
, "can't open !\n");
1057 return MMSYSERR_ALLOCATED
;
1059 IOCTL(audio
, SNDCTL_DSP_GETBLKSIZE
, abuf_size
);
1060 if (abuf_size
< 1024 || abuf_size
> 65536) {
1061 if (abuf_size
== -1)
1062 WARN(wave
, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1064 WARN(wave
, "SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize !\n");
1065 return MMSYSERR_NOTENABLED
;
1067 WInDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
1069 if (WInDev
[wDevID
].lpQueueHdr
) {
1070 HeapFree(GetProcessHeap(), 0, WInDev
[wDevID
].lpQueueHdr
);
1071 WInDev
[wDevID
].lpQueueHdr
= NULL
;
1073 WInDev
[wDevID
].unixdev
= audio
;
1074 WInDev
[wDevID
].dwFragmentSize
= abuf_size
;
1075 WInDev
[wDevID
].dwTotalRecorded
= 0;
1076 memcpy(&WInDev
[wDevID
].waveDesc
, lpDesc
, sizeof(WAVEOPENDESC
));
1077 lpFormat
= (LPWAVEFORMAT
) lpDesc
->lpFormat
;
1078 if (lpFormat
->wFormatTag
!= WAVE_FORMAT_PCM
) {
1079 WARN(wave
, "Bad format %04X !\n",
1080 lpFormat
->wFormatTag
);
1081 return WAVERR_BADFORMAT
;
1083 memcpy(&WInDev
[wDevID
].format
, lpFormat
, sizeof(PCMWAVEFORMAT
));
1084 WInDev
[wDevID
].format
.wBitsPerSample
= 8; /* <-------------- */
1085 if (WInDev
[wDevID
].format
.wf
.nChannels
== 0) return WAVERR_BADFORMAT
;
1086 if (WInDev
[wDevID
].format
.wf
.nSamplesPerSec
== 0) return WAVERR_BADFORMAT
;
1087 if (WInDev
[wDevID
].format
.wBitsPerSample
== 0) {
1088 WInDev
[wDevID
].format
.wBitsPerSample
= 8 *
1089 (WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/
1090 WInDev
[wDevID
].format
.wf
.nSamplesPerSec
) /
1091 WInDev
[wDevID
].format
.wf
.nChannels
;
1093 samplesize
= WInDev
[wDevID
].format
.wBitsPerSample
;
1094 smplrate
= WInDev
[wDevID
].format
.wf
.nSamplesPerSec
;
1095 dsp_stereo
= (WInDev
[wDevID
].format
.wf
.nChannels
> 1) ? TRUE
: FALSE
;
1096 IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
);
1097 IOCTL(audio
, SNDCTL_DSP_SAMPLESIZE
, samplesize
);
1098 IOCTL(audio
, SNDCTL_DSP_STEREO
, dsp_stereo
);
1099 TRACE(wave
, "wBitsPerSample=%u !\n", WInDev
[wDevID
].format
.wBitsPerSample
);
1100 TRACE(wave
, "nSamplesPerSec=%lu !\n", WInDev
[wDevID
].format
.wf
.nSamplesPerSec
);
1101 TRACE(wave
, "nChannels=%u !\n", WInDev
[wDevID
].format
.wf
.nChannels
);
1102 TRACE(wave
, "nAvgBytesPerSec=%lu\n", WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
);
1103 if (WAVE_NotifyClient(wDevID
, WIM_OPEN
, 0L, 0L) != MMSYSERR_NOERROR
) {
1104 WARN(wave
, "can't notify client !\n");
1105 return MMSYSERR_INVALPARAM
;
1107 return MMSYSERR_NOERROR
;
1110 /**************************************************************************
1111 * widClose [internal]
1113 static DWORD
widClose(WORD wDevID
)
1115 TRACE(wave
, "(%u);\n", wDevID
);
1116 if (wDevID
> MAX_WAVEINDRV
) return MMSYSERR_INVALPARAM
;
1117 if (WInDev
[wDevID
].unixdev
== 0) {
1118 WARN(wave
, "can't close !\n");
1119 return MMSYSERR_NOTENABLED
;
1121 if (WInDev
[wDevID
].lpQueueHdr
!= NULL
) {
1122 WARN(wave
, "still buffers open !\n");
1123 return WAVERR_STILLPLAYING
;
1125 close(WInDev
[wDevID
].unixdev
);
1126 WInDev
[wDevID
].unixdev
= 0;
1127 WInDev
[wDevID
].dwFragmentSize
= 0;
1128 if (WAVE_NotifyClient(wDevID
, WIM_CLOSE
, 0L, 0L) != MMSYSERR_NOERROR
) {
1129 WARN(wave
, "can't notify client !\n");
1130 return MMSYSERR_INVALPARAM
;
1132 return MMSYSERR_NOERROR
;
1135 /**************************************************************************
1136 * widAddBuffer [internal]
1138 static DWORD
widAddBuffer(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
1143 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
1144 if (WInDev
[wDevID
].unixdev
== 0) {
1145 WARN(wave
, "can't do it !\n");
1146 return MMSYSERR_NOTENABLED
;
1148 if (!(lpWaveHdr
->dwFlags
& WHDR_PREPARED
)) {
1149 TRACE(wave
, "never been prepared !\n");
1150 return WAVERR_UNPREPARED
;
1152 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
) {
1153 TRACE(wave
, "header already in use !\n");
1154 return WAVERR_STILLPLAYING
;
1156 lpWaveHdr
->dwFlags
|= WHDR_PREPARED
;
1157 lpWaveHdr
->dwFlags
|= WHDR_INQUEUE
;
1158 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
1159 lpWaveHdr
->dwBytesRecorded
= 0;
1160 if (WInDev
[wDevID
].lpQueueHdr
== NULL
) {
1161 WInDev
[wDevID
].lpQueueHdr
= lpWaveHdr
;
1163 lpWIHdr
= WInDev
[wDevID
].lpQueueHdr
;
1164 while (lpWIHdr
->lpNext
!= NULL
) {
1165 lpWIHdr
= lpWIHdr
->lpNext
;
1168 lpWIHdr
->lpNext
= lpWaveHdr
;
1169 lpWaveHdr
->lpNext
= NULL
;
1172 TRACE(wave
, "buffer added ! (now %u in queue)\n", count
);
1173 return MMSYSERR_NOERROR
;
1176 /**************************************************************************
1177 * widPrepare [internal]
1179 static DWORD
widPrepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
1181 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
1182 if (WInDev
[wDevID
].unixdev
== 0) {
1183 WARN(wave
, "can't prepare !\n");
1184 return MMSYSERR_NOTENABLED
;
1186 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
1187 return WAVERR_STILLPLAYING
;
1188 lpWaveHdr
->dwFlags
|= WHDR_PREPARED
;
1189 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
1190 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
1191 lpWaveHdr
->dwBytesRecorded
= 0;
1192 TRACE(wave
, "header prepared !\n");
1193 return MMSYSERR_NOERROR
;
1196 /**************************************************************************
1197 * widUnprepare [internal]
1199 static DWORD
widUnprepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
1201 TRACE(wave
, "(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
1202 if (WInDev
[wDevID
].unixdev
== 0) {
1203 WARN(wave
, "can't unprepare !\n");
1204 return MMSYSERR_NOTENABLED
;
1206 lpWaveHdr
->dwFlags
&= ~WHDR_PREPARED
;
1207 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
1208 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
1210 return MMSYSERR_NOERROR
;
1213 /**************************************************************************
1214 * widStart [internal]
1216 static DWORD
widStart(WORD wDevID
)
1220 LPWAVEHDR
*lpWaveHdr
;
1223 TRACE(wave
, "(%u);\n", wDevID
);
1224 if (WInDev
[wDevID
].unixdev
== 0) {
1225 WARN(wave
, "can't start recording !\n");
1226 return MMSYSERR_NOTENABLED
;
1229 lpWaveHdr
= &(WInDev
[wDevID
].lpQueueHdr
);
1230 TRACE(wave
, "lpWaveHdr = %08lx\n",(DWORD
)lpWaveHdr
);
1232 if (!*lpWaveHdr
|| !(*lpWaveHdr
)->lpData
) {
1233 TRACE(wave
, "never been prepared !\n");
1234 return WAVERR_UNPREPARED
;
1237 while (*lpWaveHdr
!= NULL
) {
1239 lpData
= ((DWORD
)*lpWaveHdr
== (*lpWaveHdr
)->reserved
) ?
1240 (LPBYTE
)(*lpWaveHdr
)->lpData
: (LPBYTE
)PTR_SEG_TO_LIN((*lpWaveHdr
)->lpData
);
1242 TRACE(wave
, "recording buf#%u=%p size=%lu \n",
1243 count
, lpData
, (*lpWaveHdr
)->dwBufferLength
);
1245 bytesRead
= read(WInDev
[wDevID
].unixdev
, lpData
, (*lpWaveHdr
)->dwBufferLength
);
1247 if (bytesRead
== -1)
1248 perror("read from audio device");
1250 TRACE(wave
, "bytesread=%d (%ld)\n", bytesRead
, (*lpWaveHdr
)->dwBufferLength
);
1251 (*lpWaveHdr
)->dwBytesRecorded
= bytesRead
;
1252 WInDev
[wDevID
].dwTotalRecorded
+= (*lpWaveHdr
)->dwBytesRecorded
;
1253 (*lpWaveHdr
)->dwFlags
&= ~WHDR_INQUEUE
;
1254 (*lpWaveHdr
)->dwFlags
|= WHDR_DONE
;
1256 if (WAVE_NotifyClient(wDevID
, WIM_DATA
, (*lpWaveHdr
)->reserved
, (*lpWaveHdr
)->dwBytesRecorded
) != MMSYSERR_NOERROR
) {
1257 WARN(wave
, "can't notify client !\n");
1258 return MMSYSERR_INVALPARAM
;
1260 /* removes the current block from the queue */
1261 *lpWaveHdr
= (*lpWaveHdr
)->lpNext
;
1264 TRACE(wave
, "end of recording !\n");
1265 return MMSYSERR_NOERROR
;
1268 /**************************************************************************
1269 * widStop [internal]
1271 static DWORD
widStop(WORD wDevID
)
1273 TRACE(wave
, "(%u);\n", wDevID
);
1274 if (WInDev
[wDevID
].unixdev
== 0) {
1275 WARN(wave
, "can't stop !\n");
1276 return MMSYSERR_NOTENABLED
;
1278 return MMSYSERR_NOERROR
;
1281 /**************************************************************************
1282 * widReset [internal]
1284 static DWORD
widReset(WORD wDevID
)
1286 TRACE(wave
, "(%u);\n", wDevID
);
1287 if (WInDev
[wDevID
].unixdev
== 0) {
1288 WARN(wave
, "can't reset !\n");
1289 return MMSYSERR_NOTENABLED
;
1291 return MMSYSERR_NOERROR
;
1294 /**************************************************************************
1295 * widGetPosition [internal]
1297 static DWORD
widGetPosition(WORD wDevID
, LPMMTIME16 lpTime
, DWORD uSize
)
1301 TRACE(wave
, "(%u, %p, %lu);\n", wDevID
, lpTime
, uSize
);
1302 if (WInDev
[wDevID
].unixdev
== 0) {
1303 WARN(wave
, "can't get pos !\n");
1304 return MMSYSERR_NOTENABLED
;
1306 if (lpTime
== NULL
) return MMSYSERR_INVALPARAM
;
1307 TRACE(wave
, "wType=%04X !\n", lpTime
->wType
);
1308 TRACE(wave
, "wBitsPerSample=%u\n", WInDev
[wDevID
].format
.wBitsPerSample
);
1309 TRACE(wave
, "nSamplesPerSec=%lu\n", WInDev
[wDevID
].format
.wf
.nSamplesPerSec
);
1310 TRACE(wave
, "nChannels=%u\n", WInDev
[wDevID
].format
.wf
.nChannels
);
1311 TRACE(wave
, "nAvgBytesPerSec=%lu\n", WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
);
1312 switch (lpTime
->wType
) {
1314 lpTime
->u
.cb
= WInDev
[wDevID
].dwTotalRecorded
;
1315 TRACE(wave
, "TIME_BYTES=%lu\n", lpTime
->u
.cb
);
1318 lpTime
->u
.sample
= WInDev
[wDevID
].dwTotalRecorded
* 8 /
1319 WInDev
[wDevID
].format
.wBitsPerSample
;
1320 TRACE(wave
, "TIME_SAMPLES=%lu\n", lpTime
->u
.sample
);
1323 time
= WInDev
[wDevID
].dwTotalRecorded
/
1324 (WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/ 1000);
1325 lpTime
->u
.smpte
.hour
= time
/ 108000;
1326 time
-= lpTime
->u
.smpte
.hour
* 108000;
1327 lpTime
->u
.smpte
.min
= time
/ 1800;
1328 time
-= lpTime
->u
.smpte
.min
* 1800;
1329 lpTime
->u
.smpte
.sec
= time
/ 30;
1330 time
-= lpTime
->u
.smpte
.sec
* 30;
1331 lpTime
->u
.smpte
.frame
= time
;
1332 lpTime
->u
.smpte
.fps
= 30;
1333 TRACE(wave
, "TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1334 lpTime
->u
.smpte
.hour
, lpTime
->u
.smpte
.min
,
1335 lpTime
->u
.smpte
.sec
, lpTime
->u
.smpte
.frame
);
1338 lpTime
->u
.ms
= WInDev
[wDevID
].dwTotalRecorded
/
1339 (WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/ 1000);
1340 TRACE(wave
, "TIME_MS=%lu\n", lpTime
->u
.ms
);
1343 FIXME(wave
, "format not supported (%u) ! use TIME_MS !\n", lpTime
->wType
);
1344 lpTime
->wType
= TIME_MS
;
1346 return MMSYSERR_NOERROR
;
1349 /**************************************************************************
1350 * widMessage [sample driver]
1352 DWORD WINAPI
widMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
1353 DWORD dwParam1
, DWORD dwParam2
)
1355 TRACE(wave
, "(%u, %04X, %08lX, %08lX, %08lX);\n",
1356 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1359 case WIDM_OPEN
: return widOpen(wDevID
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
1360 case WIDM_CLOSE
: return widClose(wDevID
);
1361 case WIDM_ADDBUFFER
: return widAddBuffer(wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
1362 case WIDM_PREPARE
: return widPrepare(wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
1363 case WIDM_UNPREPARE
: return widUnprepare(wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
1364 case WIDM_GETDEVCAPS
: return widGetDevCaps(wDevID
, (LPWAVEINCAPS16
)dwParam1
,dwParam2
);
1365 case WIDM_GETNUMDEVS
: return wodGetNumDevs(); /* same number of devices in output as in input */
1366 case WIDM_GETPOS
: return widGetPosition(wDevID
, (LPMMTIME16
)dwParam1
, dwParam2
);
1367 case WIDM_RESET
: return widReset(wDevID
);
1368 case WIDM_START
: return widStart(wDevID
);
1369 case WIDM_PAUSE
: return widStop(wDevID
);
1370 case WIDM_STOP
: return widStop(wDevID
);
1372 FIXME(wave
, "unknown message %u!\n", wMsg
);
1374 return MMSYSERR_NOTSUPPORTED
;
1377 /*======================================================================*
1378 * Low level WAVE implemantation - DriverProc *
1379 *======================================================================*/
1381 /**************************************************************************
1382 * WAVE_DriverProc [sample driver]
1384 LONG
WAVE_DriverProc(DWORD dwDevID
, HDRVR16 hDriv
, DWORD wMsg
,
1385 DWORD dwParam1
, DWORD dwParam2
)
1387 TRACE(wave
, "(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1389 case DRV_LOAD
: return 1;
1390 case DRV_FREE
: return 1;
1391 case DRV_OPEN
: return 1;
1392 case DRV_CLOSE
: return 1;
1393 case DRV_ENABLE
: return 1;
1394 case DRV_DISABLE
: return 1;
1395 case DRV_QUERYCONFIGURE
: return 1;
1396 case DRV_CONFIGURE
: MessageBoxA(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK
); return 1;
1397 case DRV_INSTALL
: return DRVCNF_RESTART
;
1398 case DRV_REMOVE
: return DRVCNF_RESTART
;
1400 FIXME(wave
, "is probably wrong msg=0x%04lx\n", wMsg
);
1401 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1403 return MMSYSERR_NOTENABLED
;
1406 #else /* !HAVE_OSS */
1408 /**************************************************************************
1409 * wodMessage [sample driver]
1411 DWORD WINAPI
wodMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
1412 DWORD dwParam1
, DWORD dwParam2
)
1414 FIXME(wave
, "(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1415 return MMSYSERR_NOTENABLED
;
1418 /**************************************************************************
1419 * widMessage [sample driver]
1421 DWORD WINAPI
widMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
1422 DWORD dwParam1
, DWORD dwParam2
)
1424 FIXME(wave
, "(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1425 return MMSYSERR_NOTENABLED
;
1428 /**************************************************************************
1429 * WAVE_DriverProc [sample driver]
1431 LONG
WAVE_DriverProc(DWORD dwDevID
, HDRVR16 hDriv
, DWORD wMsg
,
1432 DWORD dwParam1
, DWORD dwParam2
)
1434 return MMSYSERR_NOTENABLED
;
1436 #endif /* HAVE_OSS */