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"
27 #include "debugtools.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:
41 * +---------+-------------+---------------+---------------------------------+
42 * | state | function | event | new state |
43 * +---------+-------------+---------------+---------------------------------+
44 * | | open() | | STOPPED |
45 * | PAUSED | write() | | PAUSED |
46 * | STOPPED | write() | <thrd create> | PLAYING |
47 * | PLAYING | write() | HEADER | PLAYING |
48 * | (other) | write() | <error> | |
49 * | (any) | pause() | PAUSING | PAUSED |
50 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
51 * | (any) | reset() | RESETTING | STOPPED |
52 * | (any) | close() | CLOSING | CLOSED |
53 * +---------+-------------+---------------+---------------------------------+
56 /* states of the playing device */
57 #define WINE_WS_PLAYING 0
58 #define WINE_WS_PAUSED 1
59 #define WINE_WS_STOPPED 2
60 #define WINE_WS_CLOSED 3
62 /* events to be send to device */
63 #define WINE_WM_PAUSING (WM_USER + 1)
64 #define WINE_WM_RESTARTING (WM_USER + 2)
65 #define WINE_WM_RESETTING (WM_USER + 3)
66 #define WINE_WM_CLOSING (WM_USER + 4)
67 #define WINE_WM_HEADER (WM_USER + 5)
71 volatile int state
; /* one of the WINE_WS_ manifest constants */
72 DWORD dwFragmentSize
; /* size of OSS buffer fragment */
73 WAVEOPENDESC waveDesc
;
76 LPWAVEHDR lpQueueHdr
; /* pending buffers for playing */
77 LPWAVEHDR lpNotifyHdr
; /* list of wavehdr for which write() has been called, pending for notification */
79 DWORD dwPlayedTotal
; /* number of bytes played since opening */
80 DWORD dwPlayed
; /* number of bytes played since last DSP_RESET */
81 DWORD dwNotifiedBytes
; /* number of bytes for which wavehdr notification has been done */
83 /* info on current lpQueueHdr->lpWaveHdr */
84 DWORD dwOffCurrHdr
; /* offset in lpQueueHdr->lpWaveHdr->lpData for fragments */
85 DWORD dwRemain
; /* number of bytes to write to end the current fragment */
87 /* synchronization stuff */
92 WORD wMaxFragments
; /* max number of fragments that can be written to dsp */
93 WORD wFragsUsedInQueue
; /* current number of used fragments inside dsp queue */
99 DWORD dwFragmentSize
; /* OpenSound '/dev/dsp' give us that size */
100 WAVEOPENDESC waveDesc
;
102 PCMWAVEFORMAT format
;
103 LPWAVEHDR lpQueueHdr
;
104 DWORD dwTotalRecorded
;
107 static WINE_WAVEOUT WOutDev
[MAX_WAVEOUTDRV
];
108 static WINE_WAVEIN WInDev
[MAX_WAVEOUTDRV
];
110 /*======================================================================*
111 * Low level WAVE implemantation *
112 *======================================================================*/
114 /**************************************************************************
115 * WAVE_NotifyClient [internal]
117 static DWORD
WAVE_NotifyClient(UINT16 wDevID
, WORD wMsg
,
118 DWORD dwParam1
, DWORD dwParam2
)
120 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID
, wMsg
, dwParam1
, dwParam2
);
126 if (wDevID
> MAX_WAVEOUTDRV
) return MCIERR_INTERNAL
;
128 if (WOutDev
[wDevID
].wFlags
!= DCB_NULL
&&
129 !DriverCallback16(WOutDev
[wDevID
].waveDesc
.dwCallBack
,
130 WOutDev
[wDevID
].wFlags
,
131 WOutDev
[wDevID
].waveDesc
.hWave
,
133 WOutDev
[wDevID
].waveDesc
.dwInstance
,
136 WARN("can't notify client !\n");
137 return MMSYSERR_NOERROR
;
144 if (wDevID
> MAX_WAVEINDRV
) return MCIERR_INTERNAL
;
146 if (WInDev
[wDevID
].wFlags
!= DCB_NULL
&&
147 !DriverCallback16(WInDev
[wDevID
].waveDesc
.dwCallBack
,
148 WInDev
[wDevID
].wFlags
,
149 WInDev
[wDevID
].waveDesc
.hWave
,
151 WInDev
[wDevID
].waveDesc
.dwInstance
,
154 WARN("can't notify client !\n");
155 return MMSYSERR_NOERROR
;
159 FIXME("Unknown CB message %u\n", wMsg
);
165 /*======================================================================*
166 * Low level WAVE OUT implemantation *
167 *======================================================================*/
169 /**************************************************************************
170 * wodPlayer_WriteFragments [internal]
172 * wodPlayer helper. Writes as many fragments it can to unixdev.
173 * Returns TRUE in case of buffer underrun.
175 static BOOL
wodPlayer_WriteFragments(WINE_WAVEOUT
* wwo
)
182 TRACE("Fragments: %d used on fd %d\n", wwo
->wFragsUsedInQueue
, wwo
->unixdev
);
183 if (wwo
->wFragsUsedInQueue
== wwo
->wMaxFragments
) /* output queue is full, wait a bit */
186 lpWaveHdr
= wwo
->lpQueueHdr
;
188 if (wwo
->dwRemain
> 0 && /* still data to send to complete current fragment */
189 wwo
->dwNotifiedBytes
>= wwo
->dwFragmentSize
&& /* first fragment has been played */
190 wwo
->wFragsUsedInQueue
< 2) { /* done with all waveOutWrite()' fragments */
191 /* FIXME: should do better handling here */
192 TRACE("Oooch, buffer underrun !\n");
193 return TRUE
; /* force resetting of waveOut device */
195 return FALSE
; /* wait a bit */
198 if (wwo
->dwOffCurrHdr
== 0) {
199 TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr
, lpWaveHdr
->dwBufferLength
);
200 if (lpWaveHdr
->dwFlags
& WHDR_BEGINLOOP
)
201 FIXME("NIY: loops (%lu) in wavehdr\n", lpWaveHdr
->dwLoops
);
204 lpData
= ((DWORD
)lpWaveHdr
== lpWaveHdr
->reserved
) ?
205 (LPBYTE
)lpWaveHdr
->lpData
: (LPBYTE
)PTR_SEG_TO_LIN(lpWaveHdr
->lpData
);
207 /* finish current wave hdr ? */
208 if (wwo
->dwOffCurrHdr
+ wwo
->dwRemain
>= lpWaveHdr
->dwBufferLength
) {
209 DWORD toWrite
= lpWaveHdr
->dwBufferLength
- wwo
->dwOffCurrHdr
;
211 /* write end of current wave hdr */
212 count
= write(wwo
->unixdev
, lpData
+ wwo
->dwOffCurrHdr
, toWrite
);
213 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData
, wwo
->dwOffCurrHdr
, toWrite
, count
);
218 /* move lpWaveHdr to the end of notify list */
219 for (wh
= &(wwo
->lpNotifyHdr
); *wh
; wh
= &((*wh
)->lpNext
));
222 wwo
->lpQueueHdr
= lpWaveHdr
->lpNext
;
223 lpWaveHdr
->lpNext
= 0;
225 wwo
->dwOffCurrHdr
= 0;
226 if ((wwo
->dwRemain
-= count
) == 0) {
227 wwo
->dwRemain
= wwo
->dwFragmentSize
;
228 wwo
->wFragsUsedInQueue
++;
231 continue; /* try to go to use next wavehdr */
233 count
= write(wwo
->unixdev
, lpData
+ wwo
->dwOffCurrHdr
, wwo
->dwRemain
);
234 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData
, wwo
->dwOffCurrHdr
, wwo
->dwRemain
, count
);
236 wwo
->dwOffCurrHdr
+= count
;
237 wwo
->dwRemain
= wwo
->dwFragmentSize
;
238 wwo
->wFragsUsedInQueue
++;
244 /**************************************************************************
245 * wodPlayer_WriteFragments [internal]
247 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
248 * have been played (actually to speaker, not to unixdev fd).
250 static void wodPlayer_Notify(WINE_WAVEOUT
* wwo
, WORD uDevID
, BOOL force
)
255 /* get effective number of written bytes */
257 if (ioctl(wwo
->unixdev
, SNDCTL_DSP_GETOPTR
, &cinfo
) == -1) {
258 perror("ioctl SNDCTL_DSP_GETOPTR");
260 wwo
->state
= WINE_WS_STOPPED
;
263 TRACE("Played %d bytes (played=%ld) on fd %d\n", cinfo
.bytes
, wwo
->dwPlayed
, wwo
->unixdev
);
264 wwo
->wFragsUsedInQueue
-= cinfo
.bytes
/ wwo
->dwFragmentSize
- wwo
->dwPlayed
/ wwo
->dwFragmentSize
;
265 wwo
->dwPlayed
= cinfo
.bytes
;
267 if (force
|| cinfo
.bytes
> wwo
->dwNotifiedBytes
) {
268 /* remove all wavehdr which can be notified */
269 while (wwo
->lpNotifyHdr
&&
270 (force
|| (cinfo
.bytes
>= wwo
->dwNotifiedBytes
+ wwo
->lpNotifyHdr
->dwBufferLength
))) {
271 lpWaveHdr
= wwo
->lpNotifyHdr
;
273 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
274 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
276 wwo
->dwNotifiedBytes
+= lpWaveHdr
->dwBufferLength
;
277 wwo
->lpNotifyHdr
= lpWaveHdr
->lpNext
;
279 TRACE("Notifying client with %p\n", lpWaveHdr
);
280 if (WAVE_NotifyClient(uDevID
, WOM_DONE
, lpWaveHdr
->reserved
, 0) != MMSYSERR_NOERROR
) {
281 WARN("can't notify client !\n");
287 /**************************************************************************
288 * wodPlayer_Reset [internal]
290 * wodPlayer helper. Resets current output stream.
292 static void wodPlayer_Reset(WINE_WAVEOUT
* wwo
, WORD uDevID
, BOOL reset
)
296 /* updates current notify list */
297 wodPlayer_Notify(wwo
, uDevID
, FALSE
);
299 /* flush all possible output */
300 if (ioctl(wwo
->unixdev
, SNDCTL_DSP_RESET
, 0) == -1) {
301 perror("ioctl SNDCTL_DSP_RESET");
303 wwo
->state
= WINE_WS_STOPPED
;
307 wwo
->dwOffCurrHdr
= 0;
309 /* empty notify list */
310 wodPlayer_Notify(wwo
, uDevID
, TRUE
);
311 if (wwo
->lpNotifyHdr
) {
312 ERR("out of sync\n");
314 /* get rid also of all the current queue */
315 for (lpWaveHdr
= wwo
->lpQueueHdr
; lpWaveHdr
; lpWaveHdr
= lpWaveHdr
->lpNext
) {
316 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
317 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
319 if (WAVE_NotifyClient(uDevID
, WOM_DONE
, lpWaveHdr
->reserved
, 0) != MMSYSERR_NOERROR
) {
320 WARN("can't notify client !\n");
324 wwo
->state
= WINE_WS_STOPPED
;
325 wwo
->dwPlayedTotal
= 0;
327 /* move notify list to begining of lpQueueHdr list */
328 while (wwo
->lpNotifyHdr
) {
329 lpWaveHdr
= wwo
->lpNotifyHdr
;
330 wwo
->lpNotifyHdr
= lpWaveHdr
->lpNext
;
331 lpWaveHdr
->lpNext
= wwo
->lpQueueHdr
;
332 wwo
->lpQueueHdr
= lpWaveHdr
;
334 wwo
->state
= WINE_WS_PAUSED
;
335 wwo
->dwPlayedTotal
+= wwo
->dwPlayed
;
337 wwo
->dwNotifiedBytes
= wwo
->dwPlayed
= 0;
338 wwo
->wFragsUsedInQueue
= 0;
341 /**************************************************************************
342 * wodPlayer [internal]
344 static DWORD CALLBACK
wodPlayer(LPVOID pmt
)
346 WORD uDevID
= (DWORD
)pmt
;
347 WINE_WAVEOUT
* wwo
= (WINE_WAVEOUT
*)&WOutDev
[uDevID
];
352 PeekMessageA(&msg
, 0, 0, 0, 0);
353 wwo
->state
= WINE_WS_STOPPED
;
355 wwo
->dwNotifiedBytes
= 0;
356 wwo
->dwOffCurrHdr
= 0;
357 wwo
->dwRemain
= wwo
->dwFragmentSize
;
358 wwo
->lpQueueHdr
= NULL
;
359 wwo
->lpNotifyHdr
= NULL
;
360 wwo
->wFragsUsedInQueue
= 0;
361 wwo
->dwPlayedTotal
= 0;
364 TRACE("imhere[0]\n");
365 SetEvent(wwo
->hEvent
);
367 /* make sleep time to be # of ms to output a fragment */
368 dwSleepTime
= (wwo
->dwFragmentSize
* 1000) / wwo
->format
.wf
.nAvgBytesPerSec
;
371 /* wait for dwSleepTime or an event in thread's queue */
372 /* FIXME: could improve wait time depending on queue state,
373 * ie, number of queued fragments
375 TRACE("imhere[1]\n");
376 MsgWaitForMultipleObjects(0, NULL
, FALSE
,
377 (wwo
->state
== WINE_WS_PLAYING
) ?
378 (MAX(wwo
->wFragsUsedInQueue
, 4) - 2) * dwSleepTime
:
381 TRACE("imhere[2]\n");
382 wodPlayer_Notify(wwo
, uDevID
, FALSE
);
383 while (PeekMessageA(&msg
, 0, 0, 0, PM_REMOVE
)) {
384 switch (msg
.message
) {
385 case WINE_WM_PAUSING
:
386 wodPlayer_Reset(wwo
, uDevID
, FALSE
);
387 wwo
->state
= WINE_WS_PAUSED
;
388 SetEvent(wwo
->hEvent
);
390 case WINE_WM_RESTARTING
:
391 wwo
->state
= WINE_WS_PLAYING
;
392 SetEvent(wwo
->hEvent
);
395 lpWaveHdr
= (LPWAVEHDR
)msg
.lParam
;
397 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
398 lpWaveHdr
->dwFlags
|= WHDR_INQUEUE
;
399 lpWaveHdr
->lpNext
= 0;
401 /* insert buffer at the end of queue */
404 for (wh
= &(wwo
->lpQueueHdr
); *wh
; wh
= &((*wh
)->lpNext
));
407 if (wwo
->state
== WINE_WS_STOPPED
)
408 wwo
->state
= WINE_WS_PLAYING
;
410 case WINE_WM_RESETTING
:
411 wodPlayer_Reset(wwo
, uDevID
, TRUE
);
412 SetEvent(wwo
->hEvent
);
414 case WINE_WM_CLOSING
:
415 /* sanity check: this should not happen since the device must have been reset before */
416 if (wwo
->lpNotifyHdr
|| wwo
->lpQueueHdr
) {
417 ERR("out of sync\n");
420 wwo
->state
= WINE_WS_CLOSED
;
421 SetEvent(wwo
->hEvent
);
423 /* shouldn't go here */
425 FIXME("unknown message %d\n", msg
.message
);
429 if (wwo
->state
== WINE_WS_PLAYING
) {
430 wodPlayer_WriteFragments(wwo
);
432 wodPlayer_Notify(wwo
, uDevID
, FALSE
);
435 /* just for not generating compilation warnings... should never be executed */
439 /**************************************************************************
440 * wodGetDevCaps [internal]
442 static DWORD
wodGetDevCaps(WORD wDevID
, LPWAVEOUTCAPS16 lpCaps
, DWORD dwSize
)
452 TRACE("(%u, %p, %lu);\n", wDevID
, lpCaps
, dwSize
);
454 if (lpCaps
== NULL
) return MMSYSERR_NOTENABLED
;
455 if (access(SOUND_DEV
,0) != 0) return MMSYSERR_NOTENABLED
;
457 if (wDevID
>= MAX_WAVEOUTDRV
) {
458 TRACE("MAX_WAVOUTDRV reached !\n");
459 return MMSYSERR_BADDEVICEID
;
462 if (WOutDev
[wDevID
].unixdev
== 0) {
463 audio
= open(SOUND_DEV
, O_WRONLY
, 0);
464 if (audio
== -1) return MMSYSERR_ALLOCATED
;
466 audio
= WOutDev
[wDevID
].unixdev
;
469 /* FIXME: some programs compare this string against the content of the registry
470 * for MM drivers. The name have to match in order the program to work
471 * (e.g. MS win9x mplayer.exe)
474 lpCaps
->wMid
= 0x0002;
475 lpCaps
->wPid
= 0x0104;
476 strcpy(lpCaps
->szPname
, "SB16 Wave Out");
478 lpCaps
->wMid
= 0x00FF; /* Manufac ID */
479 lpCaps
->wPid
= 0x0001; /* Product ID */
480 /* strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");*/
481 strcpy(lpCaps
->szPname
, "CS4236/37/38");
483 lpCaps
->vDriverVersion
= 0x0100;
484 lpCaps
->dwFormats
= 0x00000000;
485 lpCaps
->dwSupport
= WAVECAPS_VOLUME
;
487 IOCTL(audio
, SNDCTL_DSP_GETFMTS
, mask
);
488 TRACE("OSS dsp mask=%08x\n", mask
);
490 IOCTL(audio
, SNDCTL_DSP_SETFMT
, mask
);
491 TRACE("OSS dsp current=%08x\n", mask
);
493 /* First bytespersampl, then stereo */
494 bytespersmpl
= (IOCTL(audio
, SNDCTL_DSP_SAMPLESIZE
, samplesize
) != 0) ? 1 : 2;
496 lpCaps
->wChannels
= (IOCTL(audio
, SNDCTL_DSP_STEREO
, dsp_stereo
) != 0) ? 1 : 2;
497 if (lpCaps
->wChannels
> 1) lpCaps
->dwSupport
|= WAVECAPS_LRVOLUME
;
500 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
501 lpCaps
->dwFormats
|= WAVE_FORMAT_4M08
;
502 if (lpCaps
->wChannels
> 1)
503 lpCaps
->dwFormats
|= WAVE_FORMAT_4S08
;
504 if (bytespersmpl
> 1) {
505 lpCaps
->dwFormats
|= WAVE_FORMAT_4M16
;
506 if (lpCaps
->wChannels
> 1)
507 lpCaps
->dwFormats
|= WAVE_FORMAT_4S16
;
511 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
512 lpCaps
->dwFormats
|= WAVE_FORMAT_2M08
;
513 if (lpCaps
->wChannels
> 1)
514 lpCaps
->dwFormats
|= WAVE_FORMAT_2S08
;
515 if (bytespersmpl
> 1) {
516 lpCaps
->dwFormats
|= WAVE_FORMAT_2M16
;
517 if (lpCaps
->wChannels
> 1)
518 lpCaps
->dwFormats
|= WAVE_FORMAT_2S16
;
522 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
523 lpCaps
->dwFormats
|= WAVE_FORMAT_1M08
;
524 if (lpCaps
->wChannels
> 1)
525 lpCaps
->dwFormats
|= WAVE_FORMAT_1S08
;
526 if (bytespersmpl
> 1) {
527 lpCaps
->dwFormats
|= WAVE_FORMAT_1M16
;
528 if (lpCaps
->wChannels
> 1)
529 lpCaps
->dwFormats
|= WAVE_FORMAT_1S16
;
532 if (IOCTL(audio
, SNDCTL_DSP_GETCAPS
, caps
) == 0) {
533 if ((caps
& DSP_CAP_REALTIME
) && !(caps
&& DSP_CAP_BATCH
))
534 lpCaps
->dwFormats
|= WAVECAPS_SAMPLEACCURATE
;
536 if (WOutDev
[wDevID
].unixdev
== 0) {
539 TRACE("dwFormats = %08lX\n", lpCaps
->dwFormats
);
540 return MMSYSERR_NOERROR
;
543 /**************************************************************************
546 static DWORD
wodOpen(WORD wDevID
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
556 TRACE("(%u, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
557 if (lpDesc
== NULL
) {
558 WARN("Invalid Parameter !\n");
559 return MMSYSERR_INVALPARAM
;
561 if (wDevID
>= MAX_WAVEOUTDRV
) {
562 TRACE("MAX_WAVOUTDRV reached !\n");
563 return MMSYSERR_BADDEVICEID
;
565 wodGetDevCaps(wDevID
, &woc
, sizeof(woc
));
567 /* only PCM format is supported so far... */
568 if (lpDesc
->lpFormat
->wFormatTag
!= WAVE_FORMAT_PCM
||
569 lpDesc
->lpFormat
->nChannels
== 0 ||
570 lpDesc
->lpFormat
->nSamplesPerSec
== 0) {
571 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
572 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
573 lpDesc
->lpFormat
->nSamplesPerSec
);
574 return WAVERR_BADFORMAT
;
577 if (dwFlags
& WAVE_FORMAT_QUERY
)
578 return MMSYSERR_NOERROR
;
580 WOutDev
[wDevID
].unixdev
= 0;
581 if (access(SOUND_DEV
, 0) != 0)
582 return MMSYSERR_NOTENABLED
;
583 audio
= open(SOUND_DEV
, O_WRONLY
, 0);
585 WARN("can't open !\n");
586 return MMSYSERR_ALLOCATED
;
589 WOutDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
591 memcpy(&WOutDev
[wDevID
].waveDesc
, lpDesc
, sizeof(WAVEOPENDESC
));
592 memcpy(&WOutDev
[wDevID
].format
, lpDesc
->lpFormat
, sizeof(PCMWAVEFORMAT
));
594 if (WOutDev
[wDevID
].format
.wBitsPerSample
== 0) {
595 WOutDev
[wDevID
].format
.wBitsPerSample
= 8 *
596 (WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/
597 WOutDev
[wDevID
].format
.wf
.nSamplesPerSec
) /
598 WOutDev
[wDevID
].format
.wf
.nChannels
;
601 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
602 * thus leading to 46ms per fragment, and a turnaround time of 185ms
604 /* 2^10=1024 bytes per fragment, 16 fragments max */
605 audio_fragment
= 0x000F000A;
606 sample_size
= WOutDev
[wDevID
].format
.wBitsPerSample
;
607 sample_rate
= WOutDev
[wDevID
].format
.wf
.nSamplesPerSec
;
608 dsp_stereo
= (WOutDev
[wDevID
].format
.wf
.nChannels
> 1) ? TRUE
: FALSE
;
610 IOCTL(audio
, SNDCTL_DSP_SETFRAGMENT
, audio_fragment
);
611 /* First size and stereo then samplerate */
612 IOCTL(audio
, SNDCTL_DSP_SAMPLESIZE
, sample_size
);
613 IOCTL(audio
, SNDCTL_DSP_STEREO
, dsp_stereo
);
614 IOCTL(audio
, SNDCTL_DSP_SPEED
, sample_rate
);
616 /* even if we set fragment size above, read it again, just in case */
617 IOCTL(audio
, SNDCTL_DSP_GETBLKSIZE
, fragment_size
);
619 WOutDev
[wDevID
].unixdev
= audio
;
620 WOutDev
[wDevID
].dwFragmentSize
= fragment_size
;
621 WOutDev
[wDevID
].wMaxFragments
= HIWORD(audio_fragment
) + 1;
623 WOutDev
[wDevID
].hEvent
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
624 WOutDev
[wDevID
].hThread
= CreateThread(NULL
, 0, wodPlayer
, (LPVOID
)(DWORD
)wDevID
, 0, &(WOutDev
[wDevID
].dwThreadID
));
625 WaitForSingleObject(WOutDev
[wDevID
].hEvent
, INFINITE
);
627 TRACE("fd=%d fragmentSize=%ld\n",
628 WOutDev
[wDevID
].unixdev
, WOutDev
[wDevID
].dwFragmentSize
);
630 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u !\n",
631 WOutDev
[wDevID
].format
.wBitsPerSample
, WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
,
632 WOutDev
[wDevID
].format
.wf
.nSamplesPerSec
, WOutDev
[wDevID
].format
.wf
.nChannels
);
634 if (WAVE_NotifyClient(wDevID
, WOM_OPEN
, 0L, 0L) != MMSYSERR_NOERROR
) {
635 WARN("can't notify client !\n");
636 return MMSYSERR_INVALPARAM
;
638 return MMSYSERR_NOERROR
;
641 /**************************************************************************
642 * wodClose [internal]
644 static DWORD
wodClose(WORD wDevID
)
646 DWORD ret
= MMSYSERR_NOERROR
;
648 TRACE("(%u);\n", wDevID
);
650 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
651 WARN("bad device ID !\n");
652 return MMSYSERR_BADDEVICEID
;
655 if (WOutDev
[wDevID
].lpQueueHdr
!= NULL
|| WOutDev
[wDevID
].lpNotifyHdr
!= NULL
) {
656 WARN("buffers still playing !\n");
657 ret
= WAVERR_STILLPLAYING
;
659 TRACE("imhere[3-close]\n");
660 PostThreadMessageA(WOutDev
[wDevID
].dwThreadID
, WINE_WM_CLOSING
, 0, 0);
661 WaitForSingleObject(WOutDev
[wDevID
].hEvent
, INFINITE
);
662 CloseHandle(WOutDev
[wDevID
].hEvent
);
664 close(WOutDev
[wDevID
].unixdev
);
665 WOutDev
[wDevID
].unixdev
= 0;
666 WOutDev
[wDevID
].dwFragmentSize
= 0;
667 if (WAVE_NotifyClient(wDevID
, WOM_CLOSE
, 0L, 0L) != MMSYSERR_NOERROR
) {
668 WARN("can't notify client !\n");
669 ret
= MMSYSERR_INVALPARAM
;
675 /**************************************************************************
676 * wodWrite [internal]
679 static DWORD
wodWrite(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
681 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
683 /* first, do the sanity checks... */
684 if (wDevID
>= MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
685 WARN("bad dev ID !\n");
686 return MMSYSERR_BADDEVICEID
;
689 if (lpWaveHdr
->lpData
== NULL
|| !(lpWaveHdr
->dwFlags
& WHDR_PREPARED
))
690 return WAVERR_UNPREPARED
;
692 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
693 return WAVERR_STILLPLAYING
;
695 TRACE("imhere[3-HEADER]\n");
696 PostThreadMessageA(WOutDev
[wDevID
].dwThreadID
, WINE_WM_HEADER
, 0, (DWORD
)lpWaveHdr
);
698 return MMSYSERR_NOERROR
;
701 /**************************************************************************
702 * wodPrepare [internal]
704 static DWORD
wodPrepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
706 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
708 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
709 WARN("bad device ID !\n");
710 return MMSYSERR_BADDEVICEID
;
713 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
714 return WAVERR_STILLPLAYING
;
716 lpWaveHdr
->dwFlags
|= WHDR_PREPARED
;
717 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
718 return MMSYSERR_NOERROR
;
721 /**************************************************************************
722 * wodUnprepare [internal]
724 static DWORD
wodUnprepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
726 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
728 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
729 WARN("bad device ID !\n");
730 return MMSYSERR_BADDEVICEID
;
733 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
734 return WAVERR_STILLPLAYING
;
736 lpWaveHdr
->dwFlags
&= ~WHDR_PREPARED
;
737 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
739 return MMSYSERR_NOERROR
;
742 /**************************************************************************
743 * wodPause [internal]
745 static DWORD
wodPause(WORD wDevID
)
747 TRACE("(%u);!\n", wDevID
);
749 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
750 WARN("bad device ID !\n");
751 return MMSYSERR_BADDEVICEID
;
754 TRACE("imhere[3-PAUSING]\n");
755 PostThreadMessageA(WOutDev
[wDevID
].dwThreadID
, WINE_WM_PAUSING
, 0, 0);
756 WaitForSingleObject(WOutDev
[wDevID
].hEvent
, INFINITE
);
758 return MMSYSERR_NOERROR
;
761 /**************************************************************************
762 * wodRestart [internal]
764 static DWORD
wodRestart(WORD wDevID
)
766 TRACE("(%u);\n", wDevID
);
768 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
769 WARN("bad device ID !\n");
770 return MMSYSERR_BADDEVICEID
;
773 if (WOutDev
[wDevID
].state
== WINE_WS_PAUSED
) {
774 TRACE("imhere[3-RESTARTING]\n");
775 PostThreadMessageA(WOutDev
[wDevID
].dwThreadID
, WINE_WM_RESTARTING
, 0, 0);
776 WaitForSingleObject(WOutDev
[wDevID
].hEvent
, INFINITE
);
779 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
780 /* FIXME: Myst crashes with this ... hmm -MM
781 if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
782 WARN("can't notify client !\n");
783 return MMSYSERR_INVALPARAM;
787 return MMSYSERR_NOERROR
;
790 /**************************************************************************
791 * wodReset [internal]
793 static DWORD
wodReset(WORD wDevID
)
795 TRACE("(%u);\n", wDevID
);
797 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
798 WARN("bad device ID !\n");
799 return MMSYSERR_BADDEVICEID
;
802 TRACE("imhere[3-RESET]\n");
803 PostThreadMessageA(WOutDev
[wDevID
].dwThreadID
, WINE_WM_RESETTING
, 0, 0);
804 WaitForSingleObject(WOutDev
[wDevID
].hEvent
, INFINITE
);
806 return MMSYSERR_NOERROR
;
810 /**************************************************************************
811 * wodGetPosition [internal]
813 static DWORD
wodGetPosition(WORD wDevID
, LPMMTIME16 lpTime
, DWORD uSize
)
818 TRACE("(%u, %p, %lu);\n", wDevID
, lpTime
, uSize
);
820 if (wDevID
> MAX_WAVEOUTDRV
|| WOutDev
[wDevID
].unixdev
== 0) {
821 WARN("bad device ID !\n");
822 return MMSYSERR_BADDEVICEID
;
825 if (lpTime
== NULL
) return MMSYSERR_INVALPARAM
;
827 val
= WOutDev
[wDevID
].dwPlayedTotal
+ WOutDev
[wDevID
].dwPlayed
;
829 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
830 lpTime
->wType
, WOutDev
[wDevID
].format
.wBitsPerSample
,
831 WOutDev
[wDevID
].format
.wf
.nSamplesPerSec
, WOutDev
[wDevID
].format
.wf
.nChannels
,
832 WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
);
833 TRACE("dwTotalPlayed=%lu\n", val
);
835 switch (lpTime
->wType
) {
838 TRACE("TIME_BYTES=%lu\n", lpTime
->u
.cb
);
841 lpTime
->u
.sample
= val
* 8 / WOutDev
[wDevID
].format
.wBitsPerSample
;
842 TRACE("TIME_SAMPLES=%lu\n", lpTime
->u
.sample
);
845 time
= val
/ (WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/ 1000);
846 lpTime
->u
.smpte
.hour
= time
/ 108000;
847 time
-= lpTime
->u
.smpte
.hour
* 108000;
848 lpTime
->u
.smpte
.min
= time
/ 1800;
849 time
-= lpTime
->u
.smpte
.min
* 1800;
850 lpTime
->u
.smpte
.sec
= time
/ 30;
851 time
-= lpTime
->u
.smpte
.sec
* 30;
852 lpTime
->u
.smpte
.frame
= time
;
853 lpTime
->u
.smpte
.fps
= 30;
854 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
855 lpTime
->u
.smpte
.hour
, lpTime
->u
.smpte
.min
,
856 lpTime
->u
.smpte
.sec
, lpTime
->u
.smpte
.frame
);
859 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime
->wType
);
860 lpTime
->wType
= TIME_MS
;
862 lpTime
->u
.ms
= val
/ (WOutDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/ 1000);
863 TRACE("TIME_MS=%lu\n", lpTime
->u
.ms
);
866 return MMSYSERR_NOERROR
;
869 /**************************************************************************
870 * wodGetVolume [internal]
872 static DWORD
wodGetVolume(WORD wDevID
, LPDWORD lpdwVol
)
878 TRACE("(%u, %p);\n", wDevID
, lpdwVol
);
881 return MMSYSERR_NOTENABLED
;
882 if ((mixer
= open(MIXER_DEV
, O_RDONLY
)) < 0) {
883 WARN("mixer device not available !\n");
884 return MMSYSERR_NOTENABLED
;
886 if (ioctl(mixer
, SOUND_MIXER_READ_PCM
, &volume
) == -1) {
887 WARN("unable read mixer !\n");
888 return MMSYSERR_NOTENABLED
;
891 left
= LOBYTE(volume
);
892 right
= HIBYTE(volume
);
893 TRACE("left=%ld right=%ld !\n", left
, right
);
894 *lpdwVol
= ((left
* 0xFFFFl
) / 100) + (((right
* 0xFFFFl
) / 100) << 16);
895 return MMSYSERR_NOERROR
;
899 /**************************************************************************
900 * wodSetVolume [internal]
902 static DWORD
wodSetVolume(WORD wDevID
, DWORD dwParam
)
908 TRACE("(%u, %08lX);\n", wDevID
, dwParam
);
910 left
= (LOWORD(dwParam
) * 100) / 0xFFFFl
;
911 right
= (HIWORD(dwParam
) * 100) / 0xFFFFl
;
912 volume
= left
+ (right
<< 8);
914 if ((mixer
= open(MIXER_DEV
, O_WRONLY
)) < 0) {
915 WARN("mixer device not available !\n");
916 return MMSYSERR_NOTENABLED
;
918 if (ioctl(mixer
, SOUND_MIXER_WRITE_PCM
, &volume
) == -1) {
919 WARN("unable set mixer !\n");
920 return MMSYSERR_NOTENABLED
;
923 return MMSYSERR_NOERROR
;
926 /**************************************************************************
927 * wodGetNumDevs [internal]
929 static DWORD
wodGetNumDevs(void)
933 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
934 int audio
= open(SOUND_DEV
, O_WRONLY
, 0);
945 /**************************************************************************
946 * wodMessage [sample driver]
948 DWORD WINAPI
wodMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
949 DWORD dwParam1
, DWORD dwParam2
)
951 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
952 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
955 case WODM_OPEN
: return wodOpen (wDevID
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
956 case WODM_CLOSE
: return wodClose (wDevID
);
957 case WODM_WRITE
: return wodWrite (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
958 case WODM_PAUSE
: return wodPause (wDevID
);
959 /* case WODM_STOP: */
960 case WODM_GETPOS
: return wodGetPosition (wDevID
, (LPMMTIME16
)dwParam1
, dwParam2
);
961 case WODM_BREAKLOOP
: return MMSYSERR_NOTSUPPORTED
;
962 case WODM_PREPARE
: return wodPrepare (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
963 case WODM_UNPREPARE
: return wodUnprepare (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
964 case WODM_GETDEVCAPS
: return wodGetDevCaps (wDevID
, (LPWAVEOUTCAPS16
)dwParam1
, dwParam2
);
965 case WODM_GETNUMDEVS
: return wodGetNumDevs ();
966 case WODM_GETPITCH
: return MMSYSERR_NOTSUPPORTED
;
967 case WODM_SETPITCH
: return MMSYSERR_NOTSUPPORTED
;
968 case WODM_GETPLAYBACKRATE
: return MMSYSERR_NOTSUPPORTED
;
969 case WODM_SETPLAYBACKRATE
: return MMSYSERR_NOTSUPPORTED
;
970 case WODM_GETVOLUME
: return wodGetVolume (wDevID
, (LPDWORD
)dwParam1
);
971 case WODM_SETVOLUME
: return wodSetVolume (wDevID
, dwParam1
);
972 case WODM_RESTART
: return wodRestart (wDevID
);
973 case WODM_RESET
: return wodReset (wDevID
);
975 FIXME("unknown message %d!\n", wMsg
);
977 return MMSYSERR_NOTSUPPORTED
;
980 /*======================================================================*
981 * Low level WAVE IN implemantation *
982 *======================================================================*/
984 /**************************************************************************
985 * widGetDevCaps [internal]
987 static DWORD
widGetDevCaps(WORD wDevID
, LPWAVEINCAPS16 lpCaps
, DWORD dwSize
)
989 int audio
,smplrate
,samplesize
=16,dsp_stereo
=1,bytespersmpl
;
991 TRACE("(%u, %p, %lu);\n", wDevID
, lpCaps
, dwSize
);
992 if (lpCaps
== NULL
) return MMSYSERR_NOTENABLED
;
993 if (access(SOUND_DEV
,0) != 0) return MMSYSERR_NOTENABLED
;
994 audio
= open(SOUND_DEV
, O_RDONLY
, 0);
995 if (audio
== -1) return MMSYSERR_ALLOCATED
;
997 lpCaps
->wMid
= 0x0002;
998 lpCaps
->wPid
= 0x0004;
999 strcpy(lpCaps
->szPname
, "SB16 Wave In");
1001 lpCaps
->wMid
= 0x00FF; /* Manufac ID */
1002 lpCaps
->wPid
= 0x0001; /* Product ID */
1003 strcpy(lpCaps
->szPname
, "OpenSoundSystem WAVIN Driver");
1005 lpCaps
->dwFormats
= 0x00000000;
1006 lpCaps
->wChannels
= (IOCTL(audio
, SNDCTL_DSP_STEREO
, dsp_stereo
) != 0) ? 1 : 2;
1007 bytespersmpl
= (IOCTL(audio
, SNDCTL_DSP_SAMPLESIZE
, samplesize
) != 0) ? 1 : 2;
1009 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
1010 lpCaps
->dwFormats
|= WAVE_FORMAT_4M08
;
1011 if (lpCaps
->wChannels
> 1)
1012 lpCaps
->dwFormats
|= WAVE_FORMAT_4S08
;
1013 if (bytespersmpl
> 1) {
1014 lpCaps
->dwFormats
|= WAVE_FORMAT_4M16
;
1015 if (lpCaps
->wChannels
> 1)
1016 lpCaps
->dwFormats
|= WAVE_FORMAT_4S16
;
1020 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
1021 lpCaps
->dwFormats
|= WAVE_FORMAT_2M08
;
1022 if (lpCaps
->wChannels
> 1)
1023 lpCaps
->dwFormats
|= WAVE_FORMAT_2S08
;
1024 if (bytespersmpl
> 1) {
1025 lpCaps
->dwFormats
|= WAVE_FORMAT_2M16
;
1026 if (lpCaps
->wChannels
> 1)
1027 lpCaps
->dwFormats
|= WAVE_FORMAT_2S16
;
1031 if (IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
) == 0) {
1032 lpCaps
->dwFormats
|= WAVE_FORMAT_1M08
;
1033 if (lpCaps
->wChannels
> 1)
1034 lpCaps
->dwFormats
|= WAVE_FORMAT_1S08
;
1035 if (bytespersmpl
> 1) {
1036 lpCaps
->dwFormats
|= WAVE_FORMAT_1M16
;
1037 if (lpCaps
->wChannels
> 1)
1038 lpCaps
->dwFormats
|= WAVE_FORMAT_1S16
;
1042 TRACE("dwFormats = %08lX\n", lpCaps
->dwFormats
);
1043 return MMSYSERR_NOERROR
;
1046 /**************************************************************************
1047 * widOpen [internal]
1049 static DWORD
widOpen(WORD wDevID
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
1051 int audio
,abuf_size
,smplrate
,samplesize
,dsp_stereo
;
1052 LPWAVEFORMAT lpFormat
;
1054 TRACE("(%u, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
1055 if (lpDesc
== NULL
) {
1056 WARN("Invalid Parameter !\n");
1057 return MMSYSERR_INVALPARAM
;
1059 if (wDevID
>= MAX_WAVEINDRV
) {
1060 TRACE("MAX_WAVINDRV reached !\n");
1061 return MMSYSERR_ALLOCATED
;
1063 WInDev
[wDevID
].unixdev
= 0;
1064 if (access(SOUND_DEV
,0) != 0) return MMSYSERR_NOTENABLED
;
1065 audio
= open(SOUND_DEV
, O_RDONLY
, 0);
1067 WARN("can't open !\n");
1068 return MMSYSERR_ALLOCATED
;
1070 IOCTL(audio
, SNDCTL_DSP_GETBLKSIZE
, abuf_size
);
1071 if (abuf_size
< 1024 || abuf_size
> 65536) {
1072 if (abuf_size
== -1)
1073 WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1075 WARN("SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize !\n");
1076 return MMSYSERR_NOTENABLED
;
1078 WInDev
[wDevID
].wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
1080 if (WInDev
[wDevID
].lpQueueHdr
) {
1081 HeapFree(GetProcessHeap(), 0, WInDev
[wDevID
].lpQueueHdr
);
1082 WInDev
[wDevID
].lpQueueHdr
= NULL
;
1084 WInDev
[wDevID
].unixdev
= audio
;
1085 WInDev
[wDevID
].dwFragmentSize
= abuf_size
;
1086 WInDev
[wDevID
].dwTotalRecorded
= 0;
1087 memcpy(&WInDev
[wDevID
].waveDesc
, lpDesc
, sizeof(WAVEOPENDESC
));
1088 lpFormat
= (LPWAVEFORMAT
) lpDesc
->lpFormat
;
1089 if (lpFormat
->wFormatTag
!= WAVE_FORMAT_PCM
) {
1090 WARN("Bad format %04X !\n",
1091 lpFormat
->wFormatTag
);
1092 return WAVERR_BADFORMAT
;
1094 memcpy(&WInDev
[wDevID
].format
, lpFormat
, sizeof(PCMWAVEFORMAT
));
1095 WInDev
[wDevID
].format
.wBitsPerSample
= 8; /* <-------------- */
1096 if (WInDev
[wDevID
].format
.wf
.nChannels
== 0) return WAVERR_BADFORMAT
;
1097 if (WInDev
[wDevID
].format
.wf
.nSamplesPerSec
== 0) return WAVERR_BADFORMAT
;
1098 if (WInDev
[wDevID
].format
.wBitsPerSample
== 0) {
1099 WInDev
[wDevID
].format
.wBitsPerSample
= 8 *
1100 (WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/
1101 WInDev
[wDevID
].format
.wf
.nSamplesPerSec
) /
1102 WInDev
[wDevID
].format
.wf
.nChannels
;
1104 samplesize
= WInDev
[wDevID
].format
.wBitsPerSample
;
1105 smplrate
= WInDev
[wDevID
].format
.wf
.nSamplesPerSec
;
1106 dsp_stereo
= (WInDev
[wDevID
].format
.wf
.nChannels
> 1) ? TRUE
: FALSE
;
1107 IOCTL(audio
, SNDCTL_DSP_SPEED
, smplrate
);
1108 IOCTL(audio
, SNDCTL_DSP_SAMPLESIZE
, samplesize
);
1109 IOCTL(audio
, SNDCTL_DSP_STEREO
, dsp_stereo
);
1110 TRACE("wBitsPerSample=%u !\n", WInDev
[wDevID
].format
.wBitsPerSample
);
1111 TRACE("nSamplesPerSec=%lu !\n", WInDev
[wDevID
].format
.wf
.nSamplesPerSec
);
1112 TRACE("nChannels=%u !\n", WInDev
[wDevID
].format
.wf
.nChannels
);
1113 TRACE("nAvgBytesPerSec=%lu\n", WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
);
1114 if (WAVE_NotifyClient(wDevID
, WIM_OPEN
, 0L, 0L) != MMSYSERR_NOERROR
) {
1115 WARN("can't notify client !\n");
1116 return MMSYSERR_INVALPARAM
;
1118 return MMSYSERR_NOERROR
;
1121 /**************************************************************************
1122 * widClose [internal]
1124 static DWORD
widClose(WORD wDevID
)
1126 TRACE("(%u);\n", wDevID
);
1127 if (wDevID
> MAX_WAVEINDRV
) return MMSYSERR_INVALPARAM
;
1128 if (WInDev
[wDevID
].unixdev
== 0) {
1129 WARN("can't close !\n");
1130 return MMSYSERR_NOTENABLED
;
1132 if (WInDev
[wDevID
].lpQueueHdr
!= NULL
) {
1133 WARN("still buffers open !\n");
1134 return WAVERR_STILLPLAYING
;
1136 close(WInDev
[wDevID
].unixdev
);
1137 WInDev
[wDevID
].unixdev
= 0;
1138 WInDev
[wDevID
].dwFragmentSize
= 0;
1139 if (WAVE_NotifyClient(wDevID
, WIM_CLOSE
, 0L, 0L) != MMSYSERR_NOERROR
) {
1140 WARN("can't notify client !\n");
1141 return MMSYSERR_INVALPARAM
;
1143 return MMSYSERR_NOERROR
;
1146 /**************************************************************************
1147 * widAddBuffer [internal]
1149 static DWORD
widAddBuffer(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
1154 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
1155 if (WInDev
[wDevID
].unixdev
== 0) {
1156 WARN("can't do it !\n");
1157 return MMSYSERR_NOTENABLED
;
1159 if (!(lpWaveHdr
->dwFlags
& WHDR_PREPARED
)) {
1160 TRACE("never been prepared !\n");
1161 return WAVERR_UNPREPARED
;
1163 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
) {
1164 TRACE("header already in use !\n");
1165 return WAVERR_STILLPLAYING
;
1167 lpWaveHdr
->dwFlags
|= WHDR_PREPARED
;
1168 lpWaveHdr
->dwFlags
|= WHDR_INQUEUE
;
1169 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
1170 lpWaveHdr
->dwBytesRecorded
= 0;
1171 if (WInDev
[wDevID
].lpQueueHdr
== NULL
) {
1172 WInDev
[wDevID
].lpQueueHdr
= lpWaveHdr
;
1174 lpWIHdr
= WInDev
[wDevID
].lpQueueHdr
;
1175 while (lpWIHdr
->lpNext
!= NULL
) {
1176 lpWIHdr
= lpWIHdr
->lpNext
;
1179 lpWIHdr
->lpNext
= lpWaveHdr
;
1180 lpWaveHdr
->lpNext
= NULL
;
1183 TRACE("buffer added ! (now %u in queue)\n", count
);
1184 return MMSYSERR_NOERROR
;
1187 /**************************************************************************
1188 * widPrepare [internal]
1190 static DWORD
widPrepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
1192 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
1193 if (WInDev
[wDevID
].unixdev
== 0) {
1194 WARN("can't prepare !\n");
1195 return MMSYSERR_NOTENABLED
;
1197 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
1198 return WAVERR_STILLPLAYING
;
1199 lpWaveHdr
->dwFlags
|= WHDR_PREPARED
;
1200 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
1201 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
1202 lpWaveHdr
->dwBytesRecorded
= 0;
1203 TRACE("header prepared !\n");
1204 return MMSYSERR_NOERROR
;
1207 /**************************************************************************
1208 * widUnprepare [internal]
1210 static DWORD
widUnprepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
1212 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
1213 if (WInDev
[wDevID
].unixdev
== 0) {
1214 WARN("can't unprepare !\n");
1215 return MMSYSERR_NOTENABLED
;
1217 lpWaveHdr
->dwFlags
&= ~WHDR_PREPARED
;
1218 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
1219 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
1221 return MMSYSERR_NOERROR
;
1224 /**************************************************************************
1225 * widStart [internal]
1227 static DWORD
widStart(WORD wDevID
)
1231 LPWAVEHDR
*lpWaveHdr
;
1234 TRACE("(%u);\n", wDevID
);
1235 if (WInDev
[wDevID
].unixdev
== 0) {
1236 WARN("can't start recording !\n");
1237 return MMSYSERR_NOTENABLED
;
1240 lpWaveHdr
= &(WInDev
[wDevID
].lpQueueHdr
);
1241 TRACE("lpWaveHdr = %08lx\n",(DWORD
)lpWaveHdr
);
1243 if (!*lpWaveHdr
|| !(*lpWaveHdr
)->lpData
) {
1244 TRACE("never been prepared !\n");
1245 return WAVERR_UNPREPARED
;
1248 while (*lpWaveHdr
!= NULL
) {
1250 lpData
= ((DWORD
)*lpWaveHdr
== (*lpWaveHdr
)->reserved
) ?
1251 (LPBYTE
)(*lpWaveHdr
)->lpData
: (LPBYTE
)PTR_SEG_TO_LIN((*lpWaveHdr
)->lpData
);
1253 TRACE("recording buf#%u=%p size=%lu \n",
1254 count
, lpData
, (*lpWaveHdr
)->dwBufferLength
);
1256 bytesRead
= read(WInDev
[wDevID
].unixdev
, lpData
, (*lpWaveHdr
)->dwBufferLength
);
1258 if (bytesRead
== -1)
1259 perror("read from audio device");
1261 TRACE("bytesread=%d (%ld)\n", bytesRead
, (*lpWaveHdr
)->dwBufferLength
);
1262 (*lpWaveHdr
)->dwBytesRecorded
= bytesRead
;
1263 WInDev
[wDevID
].dwTotalRecorded
+= (*lpWaveHdr
)->dwBytesRecorded
;
1264 (*lpWaveHdr
)->dwFlags
&= ~WHDR_INQUEUE
;
1265 (*lpWaveHdr
)->dwFlags
|= WHDR_DONE
;
1267 if (WAVE_NotifyClient(wDevID
, WIM_DATA
, (*lpWaveHdr
)->reserved
, (*lpWaveHdr
)->dwBytesRecorded
) != MMSYSERR_NOERROR
) {
1268 WARN("can't notify client !\n");
1269 return MMSYSERR_INVALPARAM
;
1271 /* removes the current block from the queue */
1272 *lpWaveHdr
= (*lpWaveHdr
)->lpNext
;
1275 TRACE("end of recording !\n");
1276 return MMSYSERR_NOERROR
;
1279 /**************************************************************************
1280 * widStop [internal]
1282 static DWORD
widStop(WORD wDevID
)
1284 TRACE("(%u);\n", wDevID
);
1285 if (WInDev
[wDevID
].unixdev
== 0) {
1286 WARN("can't stop !\n");
1287 return MMSYSERR_NOTENABLED
;
1289 return MMSYSERR_NOERROR
;
1292 /**************************************************************************
1293 * widReset [internal]
1295 static DWORD
widReset(WORD wDevID
)
1297 TRACE("(%u);\n", wDevID
);
1298 if (WInDev
[wDevID
].unixdev
== 0) {
1299 WARN("can't reset !\n");
1300 return MMSYSERR_NOTENABLED
;
1302 return MMSYSERR_NOERROR
;
1305 /**************************************************************************
1306 * widGetPosition [internal]
1308 static DWORD
widGetPosition(WORD wDevID
, LPMMTIME16 lpTime
, DWORD uSize
)
1312 TRACE("(%u, %p, %lu);\n", wDevID
, lpTime
, uSize
);
1313 if (WInDev
[wDevID
].unixdev
== 0) {
1314 WARN("can't get pos !\n");
1315 return MMSYSERR_NOTENABLED
;
1317 if (lpTime
== NULL
) return MMSYSERR_INVALPARAM
;
1318 TRACE("wType=%04X !\n", lpTime
->wType
);
1319 TRACE("wBitsPerSample=%u\n", WInDev
[wDevID
].format
.wBitsPerSample
);
1320 TRACE("nSamplesPerSec=%lu\n", WInDev
[wDevID
].format
.wf
.nSamplesPerSec
);
1321 TRACE("nChannels=%u\n", WInDev
[wDevID
].format
.wf
.nChannels
);
1322 TRACE("nAvgBytesPerSec=%lu\n", WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
);
1323 switch (lpTime
->wType
) {
1325 lpTime
->u
.cb
= WInDev
[wDevID
].dwTotalRecorded
;
1326 TRACE("TIME_BYTES=%lu\n", lpTime
->u
.cb
);
1329 lpTime
->u
.sample
= WInDev
[wDevID
].dwTotalRecorded
* 8 /
1330 WInDev
[wDevID
].format
.wBitsPerSample
;
1331 TRACE("TIME_SAMPLES=%lu\n", lpTime
->u
.sample
);
1334 time
= WInDev
[wDevID
].dwTotalRecorded
/
1335 (WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/ 1000);
1336 lpTime
->u
.smpte
.hour
= time
/ 108000;
1337 time
-= lpTime
->u
.smpte
.hour
* 108000;
1338 lpTime
->u
.smpte
.min
= time
/ 1800;
1339 time
-= lpTime
->u
.smpte
.min
* 1800;
1340 lpTime
->u
.smpte
.sec
= time
/ 30;
1341 time
-= lpTime
->u
.smpte
.sec
* 30;
1342 lpTime
->u
.smpte
.frame
= time
;
1343 lpTime
->u
.smpte
.fps
= 30;
1344 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1345 lpTime
->u
.smpte
.hour
, lpTime
->u
.smpte
.min
,
1346 lpTime
->u
.smpte
.sec
, lpTime
->u
.smpte
.frame
);
1349 lpTime
->u
.ms
= WInDev
[wDevID
].dwTotalRecorded
/
1350 (WInDev
[wDevID
].format
.wf
.nAvgBytesPerSec
/ 1000);
1351 TRACE("TIME_MS=%lu\n", lpTime
->u
.ms
);
1354 FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime
->wType
);
1355 lpTime
->wType
= TIME_MS
;
1357 return MMSYSERR_NOERROR
;
1360 /**************************************************************************
1361 * widMessage [sample driver]
1363 DWORD WINAPI
widMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
1364 DWORD dwParam1
, DWORD dwParam2
)
1366 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1367 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1370 case WIDM_OPEN
: return widOpen(wDevID
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
1371 case WIDM_CLOSE
: return widClose(wDevID
);
1372 case WIDM_ADDBUFFER
: return widAddBuffer(wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
1373 case WIDM_PREPARE
: return widPrepare(wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
1374 case WIDM_UNPREPARE
: return widUnprepare(wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
1375 case WIDM_GETDEVCAPS
: return widGetDevCaps(wDevID
, (LPWAVEINCAPS16
)dwParam1
,dwParam2
);
1376 case WIDM_GETNUMDEVS
: return wodGetNumDevs(); /* same number of devices in output as in input */
1377 case WIDM_GETPOS
: return widGetPosition(wDevID
, (LPMMTIME16
)dwParam1
, dwParam2
);
1378 case WIDM_RESET
: return widReset(wDevID
);
1379 case WIDM_START
: return widStart(wDevID
);
1380 case WIDM_PAUSE
: return widStop(wDevID
);
1381 case WIDM_STOP
: return widStop(wDevID
);
1383 FIXME("unknown message %u!\n", wMsg
);
1385 return MMSYSERR_NOTSUPPORTED
;
1388 /*======================================================================*
1389 * Low level WAVE implemantation - DriverProc *
1390 *======================================================================*/
1392 /**************************************************************************
1393 * WAVE_DriverProc [sample driver]
1395 LONG CALLBACK
WAVE_DriverProc(DWORD dwDevID
, HDRVR16 hDriv
, DWORD wMsg
,
1396 DWORD dwParam1
, DWORD dwParam2
)
1398 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1400 case DRV_LOAD
: return 1;
1401 case DRV_FREE
: return 1;
1402 case DRV_OPEN
: return 1;
1403 case DRV_CLOSE
: return 1;
1404 case DRV_ENABLE
: return 1;
1405 case DRV_DISABLE
: return 1;
1406 case DRV_QUERYCONFIGURE
: return 1;
1407 case DRV_CONFIGURE
: MessageBoxA(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK
); return 1;
1408 case DRV_INSTALL
: return DRVCNF_RESTART
;
1409 case DRV_REMOVE
: return DRVCNF_RESTART
;
1411 FIXME("is probably wrong msg=0x%04lx\n", wMsg
);
1412 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
1414 return MMSYSERR_NOTENABLED
;
1417 #else /* !HAVE_OSS */
1419 /**************************************************************************
1420 * wodMessage [sample driver]
1422 DWORD WINAPI
wodMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
1423 DWORD dwParam1
, DWORD dwParam2
)
1425 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1426 return MMSYSERR_NOTENABLED
;
1429 /**************************************************************************
1430 * widMessage [sample driver]
1432 DWORD WINAPI
widMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
1433 DWORD dwParam1
, DWORD dwParam2
)
1435 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
1436 return MMSYSERR_NOTENABLED
;
1439 /**************************************************************************
1440 * WAVE_DriverProc [sample driver]
1442 LONG CALLBACK
WAVE_DriverProc(DWORD dwDevID
, HDRVR16 hDriv
, DWORD wMsg
,
1443 DWORD dwParam1
, DWORD dwParam2
)
1445 return MMSYSERR_NOTENABLED
;
1447 #endif /* HAVE_OSS */