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/waveIn)
7 * 2000 Eric Pouech (loops in waveOut)
11 * pause in waveOut does not work correctly
12 * full duplex (in/out) is not working (device is opened twice for Out
13 * and In) (OSS is known for its poor duplex capabilities, alsa is
17 /*#define EMULATE_SB16*/
27 #include <sys/ioctl.h>
28 #ifdef HAVE_SYS_MMAN_H
29 # include <sys/mman.h>
34 #include "wine/winuser16.h"
41 #include "debugtools.h"
43 DEFAULT_DEBUG_CHANNEL(wave);
45 /* Allow 1% deviation for sample rates (some ES137x cards) */
46 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
50 #define SOUND_DEV "/dev/dsp"
51 #define MIXER_DEV "/dev/mixer"
53 #define MAX_WAVEOUTDRV (1)
54 #define MAX_WAVEINDRV (1)
56 /* state diagram for waveOut writing:
58 * +---------+-------------+---------------+---------------------------------+
59 * | state | function | event | new state |
60 * +---------+-------------+---------------+---------------------------------+
61 * | | open() | | STOPPED |
62 * | PAUSED | write() | | PAUSED |
63 * | STOPPED | write() | <thrd create> | PLAYING |
64 * | PLAYING | write() | HEADER | PLAYING |
65 * | (other) | write() | <error> | |
66 * | (any) | pause() | PAUSING | PAUSED |
67 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
68 * | (any) | reset() | RESETTING | STOPPED |
69 * | (any) | close() | CLOSING | CLOSED |
70 * +---------+-------------+---------------+---------------------------------+
73 /* states of the playing device */
74 #define WINE_WS_PLAYING 0
75 #define WINE_WS_PAUSED 1
76 #define WINE_WS_STOPPED 2
77 #define WINE_WS_CLOSED 3
79 /* events to be send to device */
80 #define WINE_WM_PAUSING (WM_USER + 1)
81 #define WINE_WM_RESTARTING (WM_USER + 2)
82 #define WINE_WM_RESETTING (WM_USER + 3)
83 #define WINE_WM_CLOSING (WM_USER + 4)
84 #define WINE_WM_HEADER (WM_USER + 5)
86 #define WINE_WM_FIRST WINE_WM_PAUSING
87 #define WINE_WM_LAST WINE_WM_HEADER
96 volatile int state; /* one of the WINE_WS_ manifest constants */
97 DWORD dwFragmentSize; /* size of OSS buffer fragment */
98 WAVEOPENDESC waveDesc;
100 PCMWAVEFORMAT format;
101 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
102 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
103 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
105 DWORD dwLastFragDone; /* time in ms, when last played fragment will be actually played */
106 DWORD dwPlayedTotal; /* number of bytes played since opening */
108 /* info on current lpQueueHdr->lpWaveHdr */
109 DWORD dwOffCurrHdr; /* offset in lpPlayPtr->lpData for fragments */
110 DWORD dwRemain; /* number of bytes to write to end the current fragment */
112 /* synchronization stuff */
116 #define WWO_RING_BUFFER_SIZE 30
117 WWO_MSG messages[WWO_RING_BUFFER_SIZE];
121 CRITICAL_SECTION msg_crst;
124 /* DirectSound stuff */
132 DWORD dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */
133 WAVEOPENDESC waveDesc;
135 PCMWAVEFORMAT format;
136 LPWAVEHDR lpQueuePtr;
137 DWORD dwTotalRecorded;
139 BOOL bTriggerSupport;
141 /* synchronization stuff */
147 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
148 static WINE_WAVEIN WInDev [MAX_WAVEINDRV ];
150 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
152 /*======================================================================*
153 * Low level WAVE implementation *
154 *======================================================================*/
156 LONG OSS_WaveInit(void)
168 /* start with output device */
170 /* initialize all device handles to -1 */
171 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
173 WOutDev[i].unixdev = -1;
176 /* FIXME: only one device is supported */
177 memset(&WOutDev[0].caps, 0, sizeof(WOutDev[0].caps));
179 if (access(SOUND_DEV,0) != 0 ||
180 (audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0)) == -1) {
181 WARN("Couldn't open out %s (%s)\n", SOUND_DEV, strerror(errno));
185 ioctl(audio, SNDCTL_DSP_RESET, 0);
187 /* FIXME: some programs compare this string against the content of the registry
188 * for MM drivers. The names have to match in order for the program to work
189 * (e.g. MS win9x mplayer.exe)
192 WOutDev[0].caps.wMid = 0x0002;
193 WOutDev[0].caps.wPid = 0x0104;
194 strcpy(WOutDev[0].caps.szPname, "SB16 Wave Out");
196 WOutDev[0].caps.wMid = 0x00FF; /* Manufac ID */
197 WOutDev[0].caps.wPid = 0x0001; /* Product ID */
198 /* strcpy(WOutDev[0].caps.szPname, "OpenSoundSystem WAVOUT Driver");*/
199 strcpy(WOutDev[0].caps.szPname, "CS4236/37/38");
201 WOutDev[0].caps.vDriverVersion = 0x0100;
202 WOutDev[0].caps.dwFormats = 0x00000000;
203 WOutDev[0].caps.dwSupport = WAVECAPS_VOLUME;
205 IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
206 TRACE("OSS dsp out mask=%08x\n", mask);
208 /* First bytespersampl, then stereo */
209 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
211 WOutDev[0].caps.wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
212 if (WOutDev[0].caps.wChannels > 1) WOutDev[0].caps.dwSupport |= WAVECAPS_LRVOLUME;
215 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
216 if (mask & AFMT_U8) {
217 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4M08;
218 if (WOutDev[0].caps.wChannels > 1)
219 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4S08;
221 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
222 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4M16;
223 if (WOutDev[0].caps.wChannels > 1)
224 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4S16;
228 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
229 if (mask & AFMT_U8) {
230 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2M08;
231 if (WOutDev[0].caps.wChannels > 1)
232 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2S08;
234 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
235 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2M16;
236 if (WOutDev[0].caps.wChannels > 1)
237 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2S16;
241 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
242 if (mask & AFMT_U8) {
243 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1M08;
244 if (WOutDev[0].caps.wChannels > 1)
245 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1S08;
247 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
248 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1M16;
249 if (WOutDev[0].caps.wChannels > 1)
250 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1S16;
253 if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
254 TRACE("OSS dsp out caps=%08X\n", caps);
255 if ((caps & DSP_CAP_REALTIME) && !(caps & DSP_CAP_BATCH)) {
256 WOutDev[0].caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
258 /* well, might as well use the DirectSound cap flag for something */
259 if ((caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP))
260 WOutDev[0].caps.dwSupport |= WAVECAPS_DIRECTSOUND;
264 TRACE("out dwFormats = %08lX, dwSupport = %08lX\n",
265 WOutDev[0].caps.dwFormats, WOutDev[0].caps.dwSupport);
267 /* then do input device */
271 for (i = 0; i < MAX_WAVEINDRV; ++i)
273 WInDev[i].unixdev = -1;
276 memset(&WInDev[0].caps, 0, sizeof(WInDev[0].caps));
278 if (access(SOUND_DEV,0) != 0 ||
279 (audio = open(SOUND_DEV, O_RDONLY|O_NDELAY, 0)) == -1) {
280 WARN("Couldn't open in %s (%s)\n", SOUND_DEV, strerror(errno));
284 ioctl(audio, SNDCTL_DSP_RESET, 0);
287 WInDev[0].caps.wMid = 0x0002;
288 WInDev[0].caps.wPid = 0x0004;
289 strcpy(WInDev[0].caps.szPname, "SB16 Wave In");
291 WInDev[0].caps.wMid = 0x00FF; /* Manufac ID */
292 WInDev[0].caps.wPid = 0x0001; /* Product ID */
293 strcpy(WInDev[0].caps.szPname, "OpenSoundSystem WAVIN Driver");
295 WInDev[0].caps.dwFormats = 0x00000000;
296 WInDev[0].caps.wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
298 WInDev[0].bTriggerSupport = FALSE;
299 if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
300 TRACE("OSS dsp in caps=%08X\n", caps);
301 if (caps & DSP_CAP_TRIGGER)
302 WInDev[0].bTriggerSupport = TRUE;
305 IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
306 TRACE("OSS in dsp mask=%08x\n", mask);
308 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
310 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
311 if (mask & AFMT_U8) {
312 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4M08;
313 if (WInDev[0].caps.wChannels > 1)
314 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4S08;
316 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
317 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4M16;
318 if (WInDev[0].caps.wChannels > 1)
319 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4S16;
323 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
324 if (mask & AFMT_U8) {
325 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2M08;
326 if (WInDev[0].caps.wChannels > 1)
327 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2S08;
329 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
330 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2M16;
331 if (WInDev[0].caps.wChannels > 1)
332 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2S16;
336 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
337 if (mask & AFMT_U8) {
338 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1M08;
339 if (WInDev[0].caps.wChannels > 1)
340 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1S08;
342 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
343 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1M16;
344 if (WInDev[0].caps.wChannels > 1)
345 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1S16;
349 TRACE("in dwFormats = %08lX\n", WInDev[0].caps.dwFormats);
354 /**************************************************************************
355 * OSS_NotifyClient [internal]
357 static DWORD OSS_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1,
360 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
366 if (wDevID >= MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
368 if (WOutDev[wDevID].wFlags != DCB_NULL &&
369 !DriverCallback(WOutDev[wDevID].waveDesc.dwCallback,
370 WOutDev[wDevID].wFlags,
371 WOutDev[wDevID].waveDesc.hWave,
373 WOutDev[wDevID].waveDesc.dwInstance,
376 WARN("can't notify client !\n");
377 return MMSYSERR_NOERROR;
384 if (wDevID >= MAX_WAVEINDRV) return MCIERR_INTERNAL;
386 if (WInDev[wDevID].wFlags != DCB_NULL &&
387 !DriverCallback(WInDev[wDevID].waveDesc.dwCallback,
388 WInDev[wDevID].wFlags,
389 WInDev[wDevID].waveDesc.hWave,
391 WInDev[wDevID].waveDesc.dwInstance,
394 WARN("can't notify client !\n");
395 return MMSYSERR_NOERROR;
399 FIXME("Unknown CB message %u\n", wMsg);
405 /*======================================================================*
406 * Low level WAVE OUT implementation *
407 *======================================================================*/
409 /**************************************************************************
410 * wodPlayer_WriteFragments [internal]
412 * wodPlayer helper. Writes as many fragments as it can to unixdev.
413 * Returns TRUE in case of buffer underrun.
415 static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
423 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) {
424 ERR("ioctl failed (%s)\n", strerror(errno));
428 TRACE("info={frag=%d fsize=%d ftotal=%d bytes=%d}\n", info.fragments, info.fragsize, info.fragstotal, info.bytes);
430 if (!info.fragments) /* output queue is full, wait a bit */
433 lpWaveHdr = wwo->lpPlayPtr;
435 if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */
436 wwo->dwLastFragDone && /* first fragment has been played */
437 info.fragments + 2 > info.fragstotal) { /* done with all waveOutWrite()' fragments */
438 /* FIXME: should do better handling here */
439 WARN("Oooch, buffer underrun !\n");
440 return TRUE; /* force resetting of waveOut device */
442 return FALSE; /* wait a bit */
445 if (wwo->dwOffCurrHdr == 0) {
446 TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
447 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
448 if (wwo->lpLoopPtr) {
449 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
451 wwo->lpLoopPtr = lpWaveHdr;
456 lpData = lpWaveHdr->lpData;
458 /* finish current wave hdr ? */
459 if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) {
460 DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
462 /* write end of current wave hdr */
463 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
464 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
466 if (count > 0 || toWrite == 0) {
467 DWORD tc = GetTickCount();
469 if (wwo->dwLastFragDone /* + guard time ?? */ < tc)
470 wwo->dwLastFragDone = tc;
471 wwo->dwLastFragDone += (toWrite * 1000) / wwo->format.wf.nAvgBytesPerSec;
473 lpWaveHdr->reserved = wwo->dwLastFragDone;
474 TRACE("Tagging hdr %p with %08lx\n", lpWaveHdr, wwo->dwLastFragDone);
476 /* WAVEHDR written, go to next one */
477 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
478 if (--wwo->lpLoopPtr->dwLoops > 0) {
479 wwo->lpPlayPtr = wwo->lpLoopPtr;
481 /* last one played */
482 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
483 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
484 /* shall we consider the END flag for the closing loop or for
485 * the opening one or for both ???
486 * code assumes for closing loop only
488 wwo->lpLoopPtr = lpWaveHdr;
490 wwo->lpLoopPtr = NULL;
492 wwo->lpPlayPtr = lpWaveHdr->lpNext;
495 wwo->lpPlayPtr = lpWaveHdr->lpNext;
497 wwo->dwOffCurrHdr = 0;
498 if ((wwo->dwRemain -= count) == 0) {
499 wwo->dwRemain = wwo->dwFragmentSize;
502 continue; /* try to go to use next wavehdr */
504 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
505 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
507 DWORD tc = GetTickCount();
509 if (wwo->dwLastFragDone /* + guard time ?? */ < tc)
510 wwo->dwLastFragDone = tc;
511 wwo->dwLastFragDone += (wwo->dwRemain * 1000) / wwo->format.wf.nAvgBytesPerSec;
513 TRACE("Tagging frag with %08lx\n", wwo->dwLastFragDone);
515 wwo->dwOffCurrHdr += count;
516 wwo->dwRemain = wwo->dwFragmentSize;
523 int wodPlayer_Message(WINE_WAVEOUT *wwo, int msg, DWORD param)
525 EnterCriticalSection(&wwo->msg_crst);
526 if ((wwo->msg_tosave == wwo->msg_toget) /* buffer overflow ? */
527 && (wwo->messages[wwo->msg_toget].msg))
529 ERR("buffer overflow !?\n");
530 LeaveCriticalSection(&wwo->msg_crst);
534 wwo->messages[wwo->msg_tosave].msg = msg;
535 wwo->messages[wwo->msg_tosave].param = param;
537 if (wwo->msg_tosave > WWO_RING_BUFFER_SIZE-1)
539 LeaveCriticalSection(&wwo->msg_crst);
540 /* signal a new message */
541 SetEvent(wwo->msg_event);
545 int wodPlayer_RetrieveMessage(WINE_WAVEOUT *wwo, int *msg, DWORD *param)
547 EnterCriticalSection(&wwo->msg_crst);
549 if (wwo->msg_toget == wwo->msg_tosave) /* buffer empty ? */
551 LeaveCriticalSection(&wwo->msg_crst);
555 *msg = wwo->messages[wwo->msg_toget].msg;
556 wwo->messages[wwo->msg_toget].msg = 0;
557 *param = wwo->messages[wwo->msg_toget].param;
559 if (wwo->msg_toget > WWO_RING_BUFFER_SIZE-1)
561 LeaveCriticalSection(&wwo->msg_crst);
565 /**************************************************************************
566 * wodPlayer_Notify [internal]
568 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
569 * have been played (actually to speaker, not to unixdev fd).
571 static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
574 DWORD tc = GetTickCount();
576 while (wwo->lpQueuePtr &&
578 (wwo->lpQueuePtr != wwo->lpPlayPtr && wwo->lpQueuePtr != wwo->lpLoopPtr))) {
579 lpWaveHdr = wwo->lpQueuePtr;
581 if (lpWaveHdr->reserved > tc && !force) break;
583 wwo->dwPlayedTotal += lpWaveHdr->dwBufferLength;
584 wwo->lpQueuePtr = lpWaveHdr->lpNext;
586 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
587 lpWaveHdr->dwFlags |= WHDR_DONE;
589 TRACE("Notifying client with %p\n", lpWaveHdr);
590 if (OSS_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) {
591 WARN("can't notify client !\n");
596 /**************************************************************************
597 * wodPlayer_Reset [internal]
599 * wodPlayer helper. Resets current output stream.
601 static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
603 /* updates current notify list */
604 wodPlayer_Notify(wwo, uDevID, FALSE);
606 /* flush all possible output */
607 if (ioctl(wwo->unixdev, SNDCTL_DSP_RESET, 0) == -1) {
608 perror("ioctl SNDCTL_DSP_RESET");
610 wwo->state = WINE_WS_STOPPED;
614 wwo->dwOffCurrHdr = 0;
615 wwo->dwRemain = wwo->dwFragmentSize;
617 /* empty notify list */
618 wodPlayer_Notify(wwo, uDevID, TRUE);
620 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
621 wwo->state = WINE_WS_STOPPED;
622 wwo->dwPlayedTotal = 0;
624 /* FIXME: this is not accurate when looping, but can be do better ? */
625 wwo->lpPlayPtr = (wwo->lpLoopPtr) ? wwo->lpLoopPtr : wwo->lpQueuePtr;
626 wwo->state = WINE_WS_PAUSED;
630 /**************************************************************************
631 * wodPlayer [internal]
633 static DWORD CALLBACK wodPlayer(LPVOID pmt)
635 WORD uDevID = (DWORD)pmt;
636 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
643 wwo->state = WINE_WS_STOPPED;
645 wwo->dwLastFragDone = 0;
646 wwo->dwOffCurrHdr = 0;
647 wwo->dwRemain = wwo->dwFragmentSize;
648 wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL;
649 wwo->dwPlayedTotal = 0;
651 TRACE("imhere[0]\n");
652 SetEvent(wwo->hEvent);
655 /* wait for dwSleepTime or an event in thread's queue
657 * - is wait time calculation optimal ?
658 * - these 100 ms parts should be changed, but Eric reports
659 * that the wodPlayer thread might lock up if we use INFINITE
660 * (strange !), so I better don't change that now... */
661 if (wwo->state != WINE_WS_PLAYING)
666 if (tc < wwo->dwLastFragDone)
668 /* calculate sleep time depending on when the last fragment
670 dwSleepTime = (wwo->dwLastFragDone - tc)*7/10;
671 if (dwSleepTime > 100)
678 TRACE("imhere[1]\n");
680 WaitForSingleObject(wwo->msg_event, dwSleepTime);
681 TRACE("imhere[2] (q=%p p=%p)\n", wwo->lpQueuePtr, wwo->lpPlayPtr);
682 while (wodPlayer_RetrieveMessage(wwo, &msg, ¶m)) {
684 case WINE_WM_PAUSING:
685 wodPlayer_Reset(wwo, uDevID, FALSE);
686 wwo->state = WINE_WS_PAUSED;
687 SetEvent(wwo->hEvent);
689 case WINE_WM_RESTARTING:
690 wwo->state = WINE_WS_PLAYING;
691 SetEvent(wwo->hEvent);
694 lpWaveHdr = (LPWAVEHDR)param;
696 /* insert buffer at the end of queue */
699 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
702 if (!wwo->lpPlayPtr) wwo->lpPlayPtr = lpWaveHdr;
703 if (wwo->state == WINE_WS_STOPPED)
704 wwo->state = WINE_WS_PLAYING;
706 case WINE_WM_RESETTING:
707 wodPlayer_Reset(wwo, uDevID, TRUE);
708 SetEvent(wwo->hEvent);
710 case WINE_WM_CLOSING:
711 /* sanity check: this should not happen since the device must have been reset before */
712 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
714 wwo->state = WINE_WS_CLOSED;
715 SetEvent(wwo->hEvent);
717 /* shouldn't go here */
719 FIXME("unknown message %d\n", msg);
723 if (wwo->state == WINE_WS_PLAYING) {
724 wodPlayer_WriteFragments(wwo);
726 wodPlayer_Notify(wwo, uDevID, FALSE);
729 /* just for not generating compilation warnings... should never be executed */
733 /**************************************************************************
734 * wodGetDevCaps [internal]
736 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
738 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
740 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
742 if (wDevID >= MAX_WAVEOUTDRV) {
743 TRACE("MAX_WAVOUTDRV reached !\n");
744 return MMSYSERR_BADDEVICEID;
747 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
748 return MMSYSERR_NOERROR;
751 /**************************************************************************
754 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
764 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
765 if (lpDesc == NULL) {
766 WARN("Invalid Parameter !\n");
767 return MMSYSERR_INVALPARAM;
769 if (wDevID >= MAX_WAVEOUTDRV) {
770 TRACE("MAX_WAVOUTDRV reached !\n");
771 return MMSYSERR_BADDEVICEID;
774 /* only PCM format is supported so far... */
775 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
776 lpDesc->lpFormat->nChannels == 0 ||
777 lpDesc->lpFormat->nSamplesPerSec == 0) {
778 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
779 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
780 lpDesc->lpFormat->nSamplesPerSec);
781 return WAVERR_BADFORMAT;
784 if (dwFlags & WAVE_FORMAT_QUERY) {
785 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
786 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
787 lpDesc->lpFormat->nSamplesPerSec);
788 return MMSYSERR_NOERROR;
791 wwo = &WOutDev[wDevID];
793 if ((dwFlags & WAVE_DIRECTSOUND) && !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
794 /* not supported, ignore it */
795 dwFlags &= ~WAVE_DIRECTSOUND;
797 if (access(SOUND_DEV, 0) != 0)
798 return MMSYSERR_NOTENABLED;
799 if (dwFlags & WAVE_DIRECTSOUND)
800 /* we want to be able to mmap() the device, which means it must be opened readable,
801 * otherwise mmap() will fail (at least under Linux) */
802 audio = open(SOUND_DEV, O_RDWR|O_NDELAY, 0);
804 audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0);
806 WARN("can't open sound device %s (%s)!\n", SOUND_DEV, strerror(errno));
807 return MMSYSERR_ALLOCATED;
809 fcntl(audio, F_SETFD, 1); /* set close on exec flag */
810 wwo->unixdev = audio;
811 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
813 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
814 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
816 if (wwo->format.wBitsPerSample == 0) {
817 WARN("Resetting zeroed wBitsPerSample\n");
818 wwo->format.wBitsPerSample = 8 *
819 (wwo->format.wf.nAvgBytesPerSec /
820 wwo->format.wf.nSamplesPerSec) /
821 wwo->format.wf.nChannels;
824 if (dwFlags & WAVE_DIRECTSOUND) {
825 /* with DirectSound, fragments are irrelevant, but a large buffer isn't...
826 * so let's choose a full 64KB (32 * 2^11) for DirectSound */
827 audio_fragment = 0x0020000B;
829 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
830 * thus leading to 46ms per fragment, and a turnaround time of 185ms
832 /* 16 fragments max, 2^10=1024 bytes per fragment */
833 audio_fragment = 0x000F000A;
835 sample_rate = wwo->format.wf.nSamplesPerSec;
836 dsp_stereo = (wwo->format.wf.nChannels > 1) ? 1 : 0;
837 format = (wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8;
839 IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
840 /* First size and stereo then samplerate */
841 IOCTL(audio, SNDCTL_DSP_SETFMT, format);
842 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
843 IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
845 /* paranoid checks */
846 if (format != ((wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8))
847 ERR("Can't set format to %d (%d)\n",
848 (wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8, format);
849 if (dsp_stereo != (wwo->format.wf.nChannels > 1) ? 1 : 0)
850 ERR("Can't set stereo to %u (%d)\n",
851 (wwo->format.wf.nChannels > 1) ? 1 : 0, dsp_stereo);
852 if (!NEAR_MATCH(sample_rate,wwo->format.wf.nSamplesPerSec))
853 ERR("Can't set sample_rate to %lu (%d)\n",
854 wwo->format.wf.nSamplesPerSec, sample_rate);
856 /* even if we set fragment size above, read it again, just in case */
857 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
858 if (fragment_size == -1) {
859 WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
862 return MMSYSERR_NOTENABLED;
864 wwo->dwFragmentSize = fragment_size;
868 wwo->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);
869 memset(wwo->messages, 0, sizeof(WWO_MSG)*WWO_RING_BUFFER_SIZE);
870 InitializeCriticalSection(&wwo->msg_crst);
872 if (!(dwFlags & WAVE_DIRECTSOUND)) {
873 wwo->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
874 wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
875 WaitForSingleObject(wwo->hEvent, INFINITE);
877 wwo->hEvent = INVALID_HANDLE_VALUE;
878 wwo->hThread = INVALID_HANDLE_VALUE;
882 TRACE("fd=%d fragmentSize=%ld\n",
883 wwo->unixdev, wwo->dwFragmentSize);
884 if (wwo->dwFragmentSize % wwo->format.wf.nBlockAlign)
885 ERR("Fragment doesn't contain an integral number of data blocks\n");
887 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
888 wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
889 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
890 wwo->format.wf.nBlockAlign);
892 if (OSS_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
893 WARN("can't notify client !\n");
894 return MMSYSERR_INVALPARAM;
896 return MMSYSERR_NOERROR;
899 /**************************************************************************
900 * wodClose [internal]
902 static DWORD wodClose(WORD wDevID)
904 DWORD ret = MMSYSERR_NOERROR;
907 TRACE("(%u);\n", wDevID);
909 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) {
910 WARN("bad device ID !\n");
911 return MMSYSERR_BADDEVICEID;
914 wwo = &WOutDev[wDevID];
915 if (wwo->lpQueuePtr) {
916 WARN("buffers still playing !\n");
917 ret = WAVERR_STILLPLAYING;
919 TRACE("imhere[3-close]\n");
920 if (wwo->hEvent != INVALID_HANDLE_VALUE) {
921 wodPlayer_Message(wwo, WINE_WM_CLOSING, 0);
922 WaitForSingleObject(wwo->hEvent, INFINITE);
923 CloseHandle(wwo->hEvent);
926 munmap(wwo->mapping, wwo->maplen);
932 wwo->dwFragmentSize = 0;
933 if (OSS_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
934 WARN("can't notify client !\n");
935 ret = MMSYSERR_INVALPARAM;
941 /**************************************************************************
942 * wodWrite [internal]
945 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
947 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
949 /* first, do the sanity checks... */
950 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) {
951 WARN("bad dev ID !\n");
952 return MMSYSERR_BADDEVICEID;
955 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
956 return WAVERR_UNPREPARED;
958 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
959 return WAVERR_STILLPLAYING;
961 lpWaveHdr->dwFlags &= ~WHDR_DONE;
962 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
963 lpWaveHdr->lpNext = 0;
965 TRACE("imhere[3-HEADER]\n");
966 wodPlayer_Message(&WOutDev[wDevID], WINE_WM_HEADER, (DWORD)lpWaveHdr);
968 return MMSYSERR_NOERROR;
971 /**************************************************************************
972 * wodPrepare [internal]
974 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
976 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
978 if (wDevID >= MAX_WAVEOUTDRV) {
979 WARN("bad device ID !\n");
980 return MMSYSERR_BADDEVICEID;
983 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
984 return WAVERR_STILLPLAYING;
986 lpWaveHdr->dwFlags |= WHDR_PREPARED;
987 lpWaveHdr->dwFlags &= ~WHDR_DONE;
988 return MMSYSERR_NOERROR;
991 /**************************************************************************
992 * wodUnprepare [internal]
994 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
996 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
998 if (wDevID >= MAX_WAVEOUTDRV) {
999 WARN("bad device ID !\n");
1000 return MMSYSERR_BADDEVICEID;
1003 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1004 return WAVERR_STILLPLAYING;
1006 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1007 lpWaveHdr->dwFlags |= WHDR_DONE;
1009 return MMSYSERR_NOERROR;
1012 /**************************************************************************
1013 * wodPause [internal]
1015 static DWORD wodPause(WORD wDevID)
1017 TRACE("(%u);!\n", wDevID);
1019 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) {
1020 WARN("bad device ID !\n");
1021 return MMSYSERR_BADDEVICEID;
1024 TRACE("imhere[3-PAUSING]\n");
1025 wodPlayer_Message(&WOutDev[wDevID], WINE_WM_PAUSING, 0);
1026 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
1028 return MMSYSERR_NOERROR;
1031 /**************************************************************************
1032 * wodRestart [internal]
1034 static DWORD wodRestart(WORD wDevID)
1036 TRACE("(%u);\n", wDevID);
1038 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) {
1039 WARN("bad device ID !\n");
1040 return MMSYSERR_BADDEVICEID;
1043 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
1044 TRACE("imhere[3-RESTARTING]\n");
1045 wodPlayer_Message(&WOutDev[wDevID], WINE_WM_RESTARTING, 0);
1046 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
1049 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1050 /* FIXME: Myst crashes with this ... hmm -MM
1051 if (OSS_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
1052 WARN("can't notify client !\n");
1053 return MMSYSERR_INVALPARAM;
1057 return MMSYSERR_NOERROR;
1060 /**************************************************************************
1061 * wodReset [internal]
1063 static DWORD wodReset(WORD wDevID)
1065 TRACE("(%u);\n", wDevID);
1067 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) {
1068 WARN("bad device ID !\n");
1069 return MMSYSERR_BADDEVICEID;
1072 TRACE("imhere[3-RESET]\n");
1073 wodPlayer_Message(&WOutDev[wDevID], WINE_WM_RESETTING, 0);
1074 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
1076 return MMSYSERR_NOERROR;
1080 /**************************************************************************
1081 * wodGetPosition [internal]
1083 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1089 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1091 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == -1) {
1092 WARN("bad device ID !\n");
1093 return MMSYSERR_BADDEVICEID;
1096 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1098 wwo = &WOutDev[wDevID];
1099 val = wwo->dwPlayedTotal;
1101 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
1102 lpTime->wType, wwo->format.wBitsPerSample,
1103 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1104 wwo->format.wf.nAvgBytesPerSec);
1105 TRACE("dwTotalPlayed=%lu\n", val);
1107 switch (lpTime->wType) {
1110 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1113 lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample;
1114 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1117 time = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1118 lpTime->u.smpte.hour = time / 108000;
1119 time -= lpTime->u.smpte.hour * 108000;
1120 lpTime->u.smpte.min = time / 1800;
1121 time -= lpTime->u.smpte.min * 1800;
1122 lpTime->u.smpte.sec = time / 30;
1123 time -= lpTime->u.smpte.sec * 30;
1124 lpTime->u.smpte.frame = time;
1125 lpTime->u.smpte.fps = 30;
1126 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1127 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1128 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1131 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
1132 lpTime->wType = TIME_MS;
1134 lpTime->u.ms = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1135 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1138 return MMSYSERR_NOERROR;
1141 /**************************************************************************
1142 * wodGetVolume [internal]
1144 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1150 TRACE("(%u, %p);\n", wDevID, lpdwVol);
1152 if (lpdwVol == NULL)
1153 return MMSYSERR_NOTENABLED;
1154 if ((mixer = open(MIXER_DEV, O_RDONLY|O_NDELAY)) < 0) {
1155 WARN("mixer device not available !\n");
1156 return MMSYSERR_NOTENABLED;
1158 if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
1159 WARN("unable read mixer !\n");
1160 return MMSYSERR_NOTENABLED;
1163 left = LOBYTE(volume);
1164 right = HIBYTE(volume);
1165 TRACE("left=%ld right=%ld !\n", left, right);
1166 *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
1167 return MMSYSERR_NOERROR;
1171 /**************************************************************************
1172 * wodSetVolume [internal]
1174 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1180 TRACE("(%u, %08lX);\n", wDevID, dwParam);
1182 left = (LOWORD(dwParam) * 100) / 0xFFFFl;
1183 right = (HIWORD(dwParam) * 100) / 0xFFFFl;
1184 volume = left + (right << 8);
1186 if ((mixer = open(MIXER_DEV, O_WRONLY|O_NDELAY)) < 0) {
1187 WARN("mixer device not available !\n");
1188 return MMSYSERR_NOTENABLED;
1190 if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
1191 WARN("unable set mixer !\n");
1192 return MMSYSERR_NOTENABLED;
1194 TRACE("volume=%04x\n", (unsigned)volume);
1197 return MMSYSERR_NOERROR;
1200 /**************************************************************************
1201 * wodGetNumDevs [internal]
1203 static DWORD wodGetNumDevs(void)
1207 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
1208 int audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0);
1219 /**************************************************************************
1220 * OSS_wodMessage [sample driver]
1222 DWORD WINAPI OSS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1223 DWORD dwParam1, DWORD dwParam2)
1225 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1226 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1233 /* FIXME: Pretend this is supported */
1235 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1236 case WODM_CLOSE: return wodClose (wDevID);
1237 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1238 case WODM_PAUSE: return wodPause (wDevID);
1239 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
1240 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
1241 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1242 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1243 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2);
1244 case WODM_GETNUMDEVS: return wodGetNumDevs ();
1245 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
1246 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
1247 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1248 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1249 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
1250 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
1251 case WODM_RESTART: return wodRestart (wDevID);
1252 case WODM_RESET: return wodReset (wDevID);
1253 case 0x810: return wodDsCreate(wDevID, (PIDSDRIVER*)dwParam1);
1255 FIXME("unknown message %d!\n", wMsg);
1257 return MMSYSERR_NOTSUPPORTED;
1260 /*======================================================================*
1261 * Low level DSOUND implementation *
1262 *======================================================================*/
1264 typedef struct IDsDriverImpl IDsDriverImpl;
1265 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
1267 struct IDsDriverImpl
1269 /* IUnknown fields */
1270 ICOM_VFIELD(IDsDriver);
1272 /* IDsDriverImpl fields */
1274 IDsDriverBufferImpl*primary;
1277 struct IDsDriverBufferImpl
1279 /* IUnknown fields */
1280 ICOM_VFIELD(IDsDriverBuffer);
1282 /* IDsDriverBufferImpl fields */
1287 static HRESULT DSDB_MapPrimary(IDsDriverBufferImpl *dsdb)
1289 WINE_WAVEOUT *wwo = &(WOutDev[dsdb->drv->wDevID]);
1290 if (!wwo->mapping) {
1291 wwo->mapping = mmap(NULL, wwo->maplen, PROT_WRITE, MAP_SHARED,
1293 if (wwo->mapping == (LPBYTE)-1) {
1294 ERR("(%p): Could not map sound device for direct access (errno=%d)\n", dsdb, errno);
1295 return DSERR_GENERIC;
1297 TRACE("(%p): sound device has been mapped for direct access at %p, size=%ld\n", dsdb, wwo->mapping, wwo->maplen);
1299 /* for some reason, es1371 and sblive! sometimes have junk in here. */
1300 memset(wwo->mapping,0,wwo->maplen); /* clear it, or we get junk noise */
1305 static HRESULT DSDB_UnmapPrimary(IDsDriverBufferImpl *dsdb)
1307 WINE_WAVEOUT *wwo = &(WOutDev[dsdb->drv->wDevID]);
1309 if (munmap(wwo->mapping, wwo->maplen) < 0) {
1310 ERR("(%p): Could not unmap sound device (errno=%d)\n", dsdb, errno);
1311 return DSERR_GENERIC;
1313 wwo->mapping = NULL;
1314 TRACE("(%p): sound device unmapped\n", dsdb);
1319 static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
1321 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1322 FIXME("(): stub!\n");
1323 return DSERR_UNSUPPORTED;
1326 static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface)
1328 ICOM_THIS(IDsDriverBufferImpl,iface);
1333 static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface)
1335 ICOM_THIS(IDsDriverBufferImpl,iface);
1338 if (This == This->drv->primary)
1339 This->drv->primary = NULL;
1340 DSDB_UnmapPrimary(This);
1341 HeapFree(GetProcessHeap(),0,This);
1345 static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
1346 LPVOID*ppvAudio1,LPDWORD pdwLen1,
1347 LPVOID*ppvAudio2,LPDWORD pdwLen2,
1348 DWORD dwWritePosition,DWORD dwWriteLen,
1351 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1352 /* since we (GetDriverDesc flags) have specified DSDDESC_DONTNEEDPRIMARYLOCK,
1353 * and that we don't support secondary buffers, this method will never be called */
1354 TRACE("(%p): stub\n",iface);
1355 return DSERR_UNSUPPORTED;
1358 static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
1359 LPVOID pvAudio1,DWORD dwLen1,
1360 LPVOID pvAudio2,DWORD dwLen2)
1362 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1363 TRACE("(%p): stub\n",iface);
1364 return DSERR_UNSUPPORTED;
1367 static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface,
1368 LPWAVEFORMATEX pwfx)
1370 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1372 TRACE("(%p,%p)\n",iface,pwfx);
1373 /* On our request (GetDriverDesc flags), DirectSound has by now used
1374 * waveOutClose/waveOutOpen to set the format...
1375 * unfortunately, this means our mmap() is now gone...
1376 * so we need to somehow signal to our DirectSound implementation
1377 * that it should completely recreate this HW buffer...
1378 * this unexpected error code should do the trick... */
1379 return DSERR_BUFFERLOST;
1382 static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq)
1384 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1385 TRACE("(%p,%ld): stub\n",iface,dwFreq);
1386 return DSERR_UNSUPPORTED;
1389 static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan)
1391 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1392 FIXME("(%p,%p): stub!\n",iface,pVolPan);
1393 return DSERR_UNSUPPORTED;
1396 static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos)
1398 /* ICOM_THIS(IDsDriverImpl,iface); */
1399 TRACE("(%p,%ld): stub\n",iface,dwNewPos);
1400 return DSERR_UNSUPPORTED;
1403 static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
1404 LPDWORD lpdwPlay, LPDWORD lpdwWrite)
1406 ICOM_THIS(IDsDriverBufferImpl,iface);
1410 TRACE("(%p)\n",iface);
1411 if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_GETOPTR, &info) < 0) {
1412 ERR("ioctl failed (%d)\n", errno);
1413 return DSERR_GENERIC;
1415 ptr = info.ptr & ~3; /* align the pointer, just in case */
1416 if (lpdwPlay) *lpdwPlay = ptr;
1418 /* add some safety margin (not strictly necessary, but...) */
1419 *lpdwWrite = ptr + 32;
1420 while (*lpdwWrite > This->buflen)
1421 *lpdwWrite -= This->buflen;
1423 TRACE("playpos=%ld, writepos=%ld\n", lpdwPlay?*lpdwPlay:0, lpdwWrite?*lpdwWrite:0);
1424 return DSERR_UNSUPPORTED;
1427 static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags)
1429 ICOM_THIS(IDsDriverBufferImpl,iface);
1430 int enable = PCM_ENABLE_OUTPUT;
1431 TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags);
1432 if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
1433 ERR("ioctl failed (%d)\n", errno);
1434 return DSERR_GENERIC;
1439 static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface)
1441 ICOM_THIS(IDsDriverBufferImpl,iface);
1443 TRACE("(%p)\n",iface);
1444 /* no more playing */
1445 if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
1446 ERR("ioctl failed (%d)\n", errno);
1447 return DSERR_GENERIC;
1450 /* the play position must be reset to the beginning of the buffer */
1451 if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_RESET, 0) < 0) {
1452 ERR("ioctl failed (%d)\n", errno);
1453 return DSERR_GENERIC;
1459 static ICOM_VTABLE(IDsDriverBuffer) dsdbvt =
1461 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1462 IDsDriverBufferImpl_QueryInterface,
1463 IDsDriverBufferImpl_AddRef,
1464 IDsDriverBufferImpl_Release,
1465 IDsDriverBufferImpl_Lock,
1466 IDsDriverBufferImpl_Unlock,
1467 IDsDriverBufferImpl_SetFormat,
1468 IDsDriverBufferImpl_SetFrequency,
1469 IDsDriverBufferImpl_SetVolumePan,
1470 IDsDriverBufferImpl_SetPosition,
1471 IDsDriverBufferImpl_GetPosition,
1472 IDsDriverBufferImpl_Play,
1473 IDsDriverBufferImpl_Stop
1476 static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj)
1478 /* ICOM_THIS(IDsDriverImpl,iface); */
1479 FIXME("(%p): stub!\n",iface);
1480 return DSERR_UNSUPPORTED;
1483 static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface)
1485 ICOM_THIS(IDsDriverImpl,iface);
1490 static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface)
1492 ICOM_THIS(IDsDriverImpl,iface);
1495 HeapFree(GetProcessHeap(),0,This);
1499 static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc)
1501 ICOM_THIS(IDsDriverImpl,iface);
1502 TRACE("(%p,%p)\n",iface,pDesc);
1503 pDesc->dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
1504 DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK;
1505 strcpy(pDesc->szDesc,"WineOSS DirectSound Driver");
1506 strcpy(pDesc->szDrvName,"wineoss.drv");
1507 pDesc->dnDevNode = WOutDev[This->wDevID].waveDesc.dnDevNode;
1509 pDesc->wReserved = 0;
1510 pDesc->ulDeviceNum = This->wDevID;
1511 pDesc->dwHeapType = DSDHEAP_NOHEAP;
1512 pDesc->pvDirectDrawHeap = NULL;
1513 pDesc->dwMemStartAddress = 0;
1514 pDesc->dwMemEndAddress = 0;
1515 pDesc->dwMemAllocExtra = 0;
1516 pDesc->pvReserved1 = NULL;
1517 pDesc->pvReserved2 = NULL;
1521 static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface)
1523 ICOM_THIS(IDsDriverImpl,iface);
1526 TRACE("(%p)\n",iface);
1527 /* make sure the card doesn't start playing before we want it to */
1528 if (ioctl(WOutDev[This->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
1529 ERR("ioctl failed (%d)\n", errno);
1530 return DSERR_GENERIC;
1535 static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface)
1537 ICOM_THIS(IDsDriverImpl,iface);
1538 TRACE("(%p)\n",iface);
1539 if (This->primary) {
1540 ERR("problem with DirectSound: primary not released\n");
1541 return DSERR_GENERIC;
1546 static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps)
1548 /* ICOM_THIS(IDsDriverImpl,iface); */
1549 TRACE("(%p,%p)\n",iface,pCaps);
1550 memset(pCaps, 0, sizeof(*pCaps));
1551 /* FIXME: need to check actual capabilities */
1552 pCaps->dwFlags = DSCAPS_PRIMARYMONO | DSCAPS_PRIMARYSTEREO |
1553 DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARY16BIT;
1554 pCaps->dwPrimaryBuffers = 1;
1555 /* the other fields only apply to secondary buffers, which we don't support
1556 * (unless we want to mess with wavetable synthesizers and MIDI) */
1560 static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
1561 LPWAVEFORMATEX pwfx,
1562 DWORD dwFlags, DWORD dwCardAddress,
1563 LPDWORD pdwcbBufferSize,
1567 ICOM_THIS(IDsDriverImpl,iface);
1568 IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
1570 audio_buf_info info;
1572 TRACE("(%p,%p,%lx,%lx)\n",iface,pwfx,dwFlags,dwCardAddress);
1573 /* we only support primary buffers */
1574 if (!(dwFlags & DSBCAPS_PRIMARYBUFFER))
1575 return DSERR_UNSUPPORTED;
1577 return DSERR_ALLOCATED;
1578 if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN))
1579 return DSERR_CONTROLUNAVAIL;
1581 *ippdsdb = (IDsDriverBufferImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverBufferImpl));
1582 if (*ippdsdb == NULL)
1583 return DSERR_OUTOFMEMORY;
1584 ICOM_VTBL(*ippdsdb) = &dsdbvt;
1585 (*ippdsdb)->ref = 1;
1586 (*ippdsdb)->drv = This;
1588 /* check how big the DMA buffer is now */
1589 if (ioctl(WOutDev[This->wDevID].unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) {
1590 ERR("ioctl failed (%d)\n", errno);
1591 HeapFree(GetProcessHeap(),0,*ippdsdb);
1593 return DSERR_GENERIC;
1595 WOutDev[This->wDevID].maplen = (*ippdsdb)->buflen = info.fragstotal * info.fragsize;
1597 /* map the DMA buffer */
1598 err = DSDB_MapPrimary(*ippdsdb);
1600 HeapFree(GetProcessHeap(),0,*ippdsdb);
1605 /* primary buffer is ready to go */
1606 *pdwcbBufferSize = WOutDev[This->wDevID].maplen;
1607 *ppbBuffer = WOutDev[This->wDevID].mapping;
1609 This->primary = *ippdsdb;
1614 static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
1615 PIDSDRIVERBUFFER pBuffer,
1618 /* ICOM_THIS(IDsDriverImpl,iface); */
1619 TRACE("(%p,%p): stub\n",iface,pBuffer);
1620 return DSERR_INVALIDCALL;
1623 static ICOM_VTABLE(IDsDriver) dsdvt =
1625 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1626 IDsDriverImpl_QueryInterface,
1627 IDsDriverImpl_AddRef,
1628 IDsDriverImpl_Release,
1629 IDsDriverImpl_GetDriverDesc,
1631 IDsDriverImpl_Close,
1632 IDsDriverImpl_GetCaps,
1633 IDsDriverImpl_CreateSoundBuffer,
1634 IDsDriverImpl_DuplicateSoundBuffer
1637 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
1639 IDsDriverImpl** idrv = (IDsDriverImpl**)drv;
1641 /* the HAL isn't much better than the HEL if we can't do mmap() */
1642 if (!(WOutDev[wDevID].caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
1643 ERR("DirectSound flag not set\n");
1644 MESSAGE("This sound card's driver does not support direct access\n");
1645 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
1646 return MMSYSERR_NOTSUPPORTED;
1649 *idrv = (IDsDriverImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverImpl));
1651 return MMSYSERR_NOMEM;
1652 ICOM_VTBL(*idrv) = &dsdvt;
1655 (*idrv)->wDevID = wDevID;
1656 (*idrv)->primary = NULL;
1657 return MMSYSERR_NOERROR;
1660 /*======================================================================*
1661 * Low level WAVE IN implementation *
1662 *======================================================================*/
1664 /**************************************************************************
1665 * widGetDevCaps [internal]
1667 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
1669 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1671 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1673 if (wDevID >= MAX_WAVEINDRV) {
1674 TRACE("MAX_WAVINDRV reached !\n");
1675 return MMSYSERR_BADDEVICEID;
1678 memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1679 return MMSYSERR_NOERROR;
1682 /**************************************************************************
1683 * widRecorder [internal]
1685 static DWORD CALLBACK widRecorder(LPVOID pmt)
1687 WORD uDevID = (DWORD)pmt;
1688 WINE_WAVEIN* wwi = (WINE_WAVEIN*)&WInDev[uDevID];
1694 audio_buf_info info;
1696 LPVOID buffer = HeapAlloc(GetProcessHeap(),
1698 wwi->dwFragmentSize);
1700 LPVOID pOffset = buffer;
1702 PeekMessageA(&msg, 0, 0, 0, 0);
1703 wwi->state = WINE_WS_STOPPED;
1704 wwi->dwTotalRecorded = 0;
1706 SetEvent(wwi->hEvent);
1708 /* make sleep time to be # of ms to output a fragment */
1709 dwSleepTime = (wwi->dwFragmentSize * 1000) / wwi->format.wf.nAvgBytesPerSec;
1710 TRACE("sleeptime=%ld ms\n", dwSleepTime);
1713 /* wait for dwSleepTime or an event in thread's queue */
1714 /* FIXME: could improve wait time depending on queue state,
1715 * ie, number of queued fragments
1718 if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING)
1720 lpWaveHdr = wwi->lpQueuePtr;
1723 ioctl(wwi->unixdev, SNDCTL_DSP_GETISPACE, &info);
1724 TRACE("info={frag=%d fsize=%d ftotal=%d bytes=%d}\n", info.fragments, info.fragsize, info.fragstotal, info.bytes);
1727 /* read all the fragments accumulated so far */
1728 while ((info.fragments > 0) && (wwi->lpQueuePtr))
1732 if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded > wwi->dwFragmentSize)
1734 /* directly read fragment in wavehdr */
1735 bytesRead = read(wwi->unixdev,
1736 lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
1737 wwi->dwFragmentSize);
1739 TRACE("bytesRead=%ld (direct)\n", bytesRead);
1740 if (bytesRead != (DWORD) -1)
1742 /* update number of bytes recorded in current buffer and by this device */
1743 lpWaveHdr->dwBytesRecorded += bytesRead;
1744 wwi->dwTotalRecorded += bytesRead;
1746 /* buffer is full. notify client */
1747 if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
1749 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1750 lpWaveHdr->dwFlags |= WHDR_DONE;
1752 if (OSS_NotifyClient(uDevID,
1755 lpWaveHdr->dwBytesRecorded) != MMSYSERR_NOERROR)
1757 WARN("can't notify client !\n");
1759 lpWaveHdr = wwi->lpQueuePtr = lpWaveHdr->lpNext;
1765 /* read the fragment in a local buffer */
1766 bytesRead = read(wwi->unixdev, buffer, wwi->dwFragmentSize);
1769 TRACE("bytesRead=%ld (local)\n", bytesRead);
1771 /* copy data in client buffers */
1772 while (bytesRead != (DWORD) -1 && bytesRead > 0)
1774 DWORD dwToCopy = min (bytesRead, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
1776 memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
1780 /* update number of bytes recorded in current buffer and by this device */
1781 lpWaveHdr->dwBytesRecorded += dwToCopy;
1782 wwi->dwTotalRecorded += dwToCopy;
1783 bytesRead -= dwToCopy;
1784 pOffset += dwToCopy;
1786 /* client buffer is full. notify client */
1787 if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
1789 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1790 lpWaveHdr->dwFlags |= WHDR_DONE;
1792 if (OSS_NotifyClient(uDevID,
1795 lpWaveHdr->dwBytesRecorded) != MMSYSERR_NOERROR)
1797 WARN("can't notify client !\n");
1800 if (lpWaveHdr->lpNext)
1802 lpWaveHdr = lpWaveHdr->lpNext;
1803 wwi->lpQueuePtr = lpWaveHdr;
1807 /* no more buffer to copy data to, but we did read more.
1808 * what hasn't been copied will be dropped
1810 if (bytesRead) WARN("buffer over run! %lu bytes dropped.\n", bytesRead);
1811 wwi->lpQueuePtr = NULL;
1820 MsgWaitForMultipleObjects(0, NULL, FALSE, dwSleepTime, QS_POSTMESSAGE);
1822 while (PeekMessageA(&msg, 0, WINE_WM_FIRST, WINE_WM_LAST, PM_REMOVE)) {
1824 TRACE("msg=0x%x wParam=0x%x lParam=0x%lx\n", msg.message, msg.wParam, msg.lParam);
1825 switch (msg.message) {
1826 case WINE_WM_PAUSING:
1827 wwi->state = WINE_WS_PAUSED;
1828 /*FIXME("Device should stop recording");*/
1829 SetEvent(wwi->hEvent);
1831 case WINE_WM_RESTARTING:
1833 int enable = PCM_ENABLE_INPUT;
1834 wwi->state = WINE_WS_PLAYING;
1836 if (wwi->bTriggerSupport)
1838 /* start the recording */
1839 if (ioctl(wwi->unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0)
1841 ERR("ioctl(SNDCTL_DSP_SETTRIGGER) failed (%d)\n", errno);
1846 unsigned char data[4];
1847 /* read 4 bytes to start the recording */
1848 read(wwi->unixdev, data, 4);
1851 SetEvent(wwi->hEvent);
1854 case WINE_WM_HEADER:
1855 lpWaveHdr = (LPWAVEHDR)msg.lParam;
1856 lpWaveHdr->lpNext = 0;
1858 /* insert buffer at the end of queue */
1861 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
1865 case WINE_WM_RESETTING:
1866 wwi->state = WINE_WS_STOPPED;
1867 /* return all buffers to the app */
1868 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
1869 TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
1870 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1871 lpWaveHdr->dwFlags |= WHDR_DONE;
1873 if (OSS_NotifyClient(uDevID, WIM_DATA, (DWORD)lpWaveHdr,
1874 lpWaveHdr->dwBytesRecorded) != MMSYSERR_NOERROR) {
1875 WARN("can't notify client !\n");
1878 wwi->lpQueuePtr = NULL;
1879 SetEvent(wwi->hEvent);
1881 case WINE_WM_CLOSING:
1883 wwi->state = WINE_WS_CLOSED;
1884 SetEvent(wwi->hEvent);
1885 HeapFree(GetProcessHeap(), 0, buffer);
1887 /* shouldn't go here */
1889 FIXME("unknown message %d\n", msg.message);
1895 /* just for not generating compilation warnings... should never be executed */
1900 /**************************************************************************
1901 * widOpen [internal]
1903 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1912 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1913 if (lpDesc == NULL) {
1914 WARN("Invalid Parameter !\n");
1915 return MMSYSERR_INVALPARAM;
1917 if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_BADDEVICEID;
1919 /* only PCM format is supported so far... */
1920 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1921 lpDesc->lpFormat->nChannels == 0 ||
1922 lpDesc->lpFormat->nSamplesPerSec == 0) {
1923 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1924 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1925 lpDesc->lpFormat->nSamplesPerSec);
1926 return WAVERR_BADFORMAT;
1929 if (dwFlags & WAVE_FORMAT_QUERY) {
1930 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1931 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1932 lpDesc->lpFormat->nSamplesPerSec);
1933 return MMSYSERR_NOERROR;
1936 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1937 audio = open(SOUND_DEV, O_RDONLY|O_NDELAY, 0);
1939 WARN("can't open sound device %s (%s)!\n", SOUND_DEV, strerror(errno));
1940 return MMSYSERR_ALLOCATED;
1942 fcntl(audio, F_SETFD, 1); /* set close on exec flag */
1944 wwi = &WInDev[wDevID];
1945 if (wwi->lpQueuePtr) {
1946 WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr);
1947 wwi->lpQueuePtr = NULL;
1949 wwi->unixdev = audio;
1950 wwi->dwTotalRecorded = 0;
1951 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1953 memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1954 memcpy(&wwi->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1956 if (wwi->format.wBitsPerSample == 0) {
1957 WARN("Resetting zeroed wBitsPerSample\n");
1958 wwi->format.wBitsPerSample = 8 *
1959 (wwi->format.wf.nAvgBytesPerSec /
1960 wwi->format.wf.nSamplesPerSec) /
1961 wwi->format.wf.nChannels;
1964 sample_rate = wwi->format.wf.nSamplesPerSec;
1965 dsp_stereo = (wwi->format.wf.nChannels > 1) ? TRUE : FALSE;
1966 format = (wwi->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8;
1968 IOCTL(audio, SNDCTL_DSP_SETFMT, format);
1969 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1970 IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
1972 /* paranoid checks */
1973 if (format != ((wwi->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8))
1974 ERR("Can't set format to %d (%d)\n",
1975 (wwi->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8, format);
1976 if (dsp_stereo != (wwi->format.wf.nChannels > 1) ? 1 : 0)
1977 ERR("Can't set stereo to %u (%d)\n",
1978 (wwi->format.wf.nChannels > 1) ? 1 : 0, dsp_stereo);
1979 if (!NEAR_MATCH(sample_rate, wwi->format.wf.nSamplesPerSec))
1980 ERR("Can't set sample_rate to %lu (%d)\n",
1981 wwi->format.wf.nSamplesPerSec, sample_rate);
1983 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
1984 if (fragment_size == -1) {
1985 WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1988 return MMSYSERR_NOTENABLED;
1990 wwi->dwFragmentSize = fragment_size;
1992 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1993 wwi->format.wBitsPerSample, wwi->format.wf.nAvgBytesPerSec,
1994 wwi->format.wf.nSamplesPerSec, wwi->format.wf.nChannels,
1995 wwi->format.wf.nBlockAlign);
1997 wwi->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1998 wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
1999 WaitForSingleObject(wwi->hEvent, INFINITE);
2001 if (OSS_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
2002 WARN("can't notify client !\n");
2003 return MMSYSERR_INVALPARAM;
2005 return MMSYSERR_NOERROR;
2008 /**************************************************************************
2009 * widClose [internal]
2011 static DWORD widClose(WORD wDevID)
2015 TRACE("(%u);\n", wDevID);
2016 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == -1) {
2017 WARN("can't close !\n");
2018 return MMSYSERR_INVALHANDLE;
2021 wwi = &WInDev[wDevID];
2023 if (wwi->lpQueuePtr != NULL) {
2024 WARN("still buffers open !\n");
2025 return WAVERR_STILLPLAYING;
2028 PostThreadMessageA(wwi->dwThreadID, WINE_WM_CLOSING, 0, 0);
2029 WaitForSingleObject(wwi->hEvent, INFINITE);
2030 CloseHandle(wwi->hEvent);
2031 close(wwi->unixdev);
2033 wwi->dwFragmentSize = 0;
2034 if (OSS_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
2035 WARN("can't notify client !\n");
2036 return MMSYSERR_INVALPARAM;
2038 return MMSYSERR_NOERROR;
2041 /**************************************************************************
2042 * widAddBuffer [internal]
2044 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
2046 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
2048 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == -1) {
2049 WARN("can't do it !\n");
2050 return MMSYSERR_INVALHANDLE;
2052 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
2053 TRACE("never been prepared !\n");
2054 return WAVERR_UNPREPARED;
2056 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
2057 TRACE("header already in use !\n");
2058 return WAVERR_STILLPLAYING;
2061 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
2062 lpWaveHdr->dwFlags &= ~WHDR_DONE;
2063 lpWaveHdr->dwBytesRecorded = 0;
2064 lpWaveHdr->lpNext = NULL;
2066 PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
2067 return MMSYSERR_NOERROR;
2070 /**************************************************************************
2071 * widPrepare [internal]
2073 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
2075 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
2077 if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE;
2079 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
2080 return WAVERR_STILLPLAYING;
2082 lpWaveHdr->dwFlags |= WHDR_PREPARED;
2083 lpWaveHdr->dwFlags &= ~(WHDR_INQUEUE|WHDR_DONE);
2084 lpWaveHdr->dwBytesRecorded = 0;
2085 TRACE("header prepared !\n");
2086 return MMSYSERR_NOERROR;
2089 /**************************************************************************
2090 * widUnprepare [internal]
2092 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
2094 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
2095 if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE;
2097 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
2098 return WAVERR_STILLPLAYING;
2100 lpWaveHdr->dwFlags &= ~(WHDR_PREPARED|WHDR_INQUEUE);
2101 lpWaveHdr->dwFlags |= WHDR_DONE;
2103 return MMSYSERR_NOERROR;
2106 /**************************************************************************
2107 * widStart [internal]
2109 static DWORD widStart(WORD wDevID)
2111 TRACE("(%u);\n", wDevID);
2112 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == -1) {
2113 WARN("can't start recording !\n");
2114 return MMSYSERR_INVALHANDLE;
2117 PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
2118 WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE);
2119 return MMSYSERR_NOERROR;
2122 /**************************************************************************
2123 * widStop [internal]
2125 static DWORD widStop(WORD wDevID)
2127 TRACE("(%u);\n", wDevID);
2128 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == -1) {
2129 WARN("can't stop !\n");
2130 return MMSYSERR_INVALHANDLE;
2132 /* FIXME: reset aint stop */
2133 PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
2134 WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE);
2136 return MMSYSERR_NOERROR;
2139 /**************************************************************************
2140 * widReset [internal]
2142 static DWORD widReset(WORD wDevID)
2144 TRACE("(%u);\n", wDevID);
2145 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == -1) {
2146 WARN("can't reset !\n");
2147 return MMSYSERR_INVALHANDLE;
2149 PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
2150 WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE);
2151 return MMSYSERR_NOERROR;
2154 /**************************************************************************
2155 * widGetPosition [internal]
2157 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
2162 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
2164 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == -1) {
2165 WARN("can't get pos !\n");
2166 return MMSYSERR_INVALHANDLE;
2168 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
2170 wwi = &WInDev[wDevID];
2172 TRACE("wType=%04X !\n", lpTime->wType);
2173 TRACE("wBitsPerSample=%u\n", wwi->format.wBitsPerSample);
2174 TRACE("nSamplesPerSec=%lu\n", wwi->format.wf.nSamplesPerSec);
2175 TRACE("nChannels=%u\n", wwi->format.wf.nChannels);
2176 TRACE("nAvgBytesPerSec=%lu\n", wwi->format.wf.nAvgBytesPerSec);
2177 switch (lpTime->wType) {
2179 lpTime->u.cb = wwi->dwTotalRecorded;
2180 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
2183 lpTime->u.sample = wwi->dwTotalRecorded * 8 /
2184 wwi->format.wBitsPerSample;
2185 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
2188 time = wwi->dwTotalRecorded /
2189 (wwi->format.wf.nAvgBytesPerSec / 1000);
2190 lpTime->u.smpte.hour = time / 108000;
2191 time -= lpTime->u.smpte.hour * 108000;
2192 lpTime->u.smpte.min = time / 1800;
2193 time -= lpTime->u.smpte.min * 1800;
2194 lpTime->u.smpte.sec = time / 30;
2195 time -= lpTime->u.smpte.sec * 30;
2196 lpTime->u.smpte.frame = time;
2197 lpTime->u.smpte.fps = 30;
2198 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
2199 lpTime->u.smpte.hour, lpTime->u.smpte.min,
2200 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
2203 lpTime->u.ms = wwi->dwTotalRecorded /
2204 (wwi->format.wf.nAvgBytesPerSec / 1000);
2205 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
2208 FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
2209 lpTime->wType = TIME_MS;
2211 return MMSYSERR_NOERROR;
2214 /**************************************************************************
2215 * OSS_widMessage [sample driver]
2217 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2218 DWORD dwParam1, DWORD dwParam2)
2220 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
2221 wDevID, wMsg, dwUser, dwParam1, dwParam2);
2228 /* FIXME: Pretend this is supported */
2230 case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
2231 case WIDM_CLOSE: return widClose (wDevID);
2232 case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2233 case WIDM_PREPARE: return widPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2234 case WIDM_UNPREPARE: return widUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2235 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
2236 case WIDM_GETNUMDEVS: return wodGetNumDevs (); /* same number of devices in output as in input */
2237 case WIDM_GETPOS: return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
2238 case WIDM_RESET: return widReset (wDevID);
2239 case WIDM_START: return widStart (wDevID);
2240 case WIDM_STOP: return widStop (wDevID);
2242 FIXME("unknown message %u!\n", wMsg);
2244 return MMSYSERR_NOTSUPPORTED;
2247 #else /* !HAVE_OSS */
2249 /**************************************************************************
2250 * OSS_wodMessage [sample driver]
2252 DWORD WINAPI OSS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2253 DWORD dwParam1, DWORD dwParam2)
2255 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2256 return MMSYSERR_NOTENABLED;
2259 /**************************************************************************
2260 * OSS_widMessage [sample driver]
2262 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2263 DWORD dwParam1, DWORD dwParam2)
2265 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2266 return MMSYSERR_NOTENABLED;
2269 #endif /* HAVE_OSS */