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"
42 #include "debugtools.h"
44 DEFAULT_DEBUG_CHANNEL(wave);
46 /* Allow 1% deviation for sample rates (some ES137x cards) */
47 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
51 #define SOUND_DEV "/dev/dsp"
52 #define MIXER_DEV "/dev/mixer"
54 #define MAX_WAVEOUTDRV (1)
55 #define MAX_WAVEINDRV (1)
57 /* state diagram for waveOut writing:
59 * +---------+-------------+---------------+---------------------------------+
60 * | state | function | event | new state |
61 * +---------+-------------+---------------+---------------------------------+
62 * | | open() | | STOPPED |
63 * | PAUSED | write() | | PAUSED |
64 * | STOPPED | write() | <thrd create> | PLAYING |
65 * | PLAYING | write() | HEADER | PLAYING |
66 * | (other) | write() | <error> | |
67 * | (any) | pause() | PAUSING | PAUSED |
68 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
69 * | (any) | reset() | RESETTING | STOPPED |
70 * | (any) | close() | CLOSING | CLOSED |
71 * +---------+-------------+---------------+---------------------------------+
74 /* states of the playing device */
75 #define WINE_WS_PLAYING 0
76 #define WINE_WS_PAUSED 1
77 #define WINE_WS_STOPPED 2
78 #define WINE_WS_CLOSED 3
80 /* events to be send to device */
81 #define WINE_WM_PAUSING (WM_USER + 1)
82 #define WINE_WM_RESTARTING (WM_USER + 2)
83 #define WINE_WM_RESETTING (WM_USER + 3)
84 #define WINE_WM_CLOSING (WM_USER + 4)
85 #define WINE_WM_HEADER (WM_USER + 5)
89 volatile int state; /* one of the WINE_WS_ manifest constants */
90 DWORD dwFragmentSize; /* size of OSS buffer fragment */
91 WAVEOPENDESC waveDesc;
94 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
95 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
96 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
98 DWORD dwLastFragDone; /* time in ms, when last played fragment will be actually played */
99 DWORD dwPlayedTotal; /* number of bytes played since opening */
101 /* info on current lpQueueHdr->lpWaveHdr */
102 DWORD dwOffCurrHdr; /* offset in lpPlayPtr->lpData for fragments */
103 DWORD dwRemain; /* number of bytes to write to end the current fragment */
105 /* synchronization stuff */
111 /* DirectSound stuff */
119 DWORD dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */
120 WAVEOPENDESC waveDesc;
122 PCMWAVEFORMAT format;
123 LPWAVEHDR lpQueuePtr;
124 DWORD dwTotalRecorded;
127 /* synchronization stuff */
133 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
134 static WINE_WAVEIN WInDev [MAX_WAVEINDRV ];
136 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
138 /*======================================================================*
139 * Low level WAVE implemantation *
140 *======================================================================*/
142 LONG OSS_WaveInit(void)
152 /* start with output device */
154 /* FIXME: only one device is supported */
155 memset(&WOutDev[0].caps, 0, sizeof(WOutDev[0].caps));
157 if (access(SOUND_DEV,0) != 0 ||
158 (audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0)) == -1) {
159 TRACE("Couldn't open out %s (%s)\n", SOUND_DEV, strerror(errno));
163 ioctl(audio, SNDCTL_DSP_RESET, 0);
165 /* FIXME: some programs compare this string against the content of the registry
166 * for MM drivers. The name have to match in order the program to work
167 * (e.g. MS win9x mplayer.exe)
170 WOutDev[0].caps.wMid = 0x0002;
171 WOutDev[0].caps.wPid = 0x0104;
172 strcpy(WOutDev[0].caps.szPname, "SB16 Wave Out");
174 WOutDev[0].caps.wMid = 0x00FF; /* Manufac ID */
175 WOutDev[0].caps.wPid = 0x0001; /* Product ID */
176 /* strcpy(WOutDev[0].caps.szPname, "OpenSoundSystem WAVOUT Driver");*/
177 strcpy(WOutDev[0].caps.szPname, "CS4236/37/38");
179 WOutDev[0].caps.vDriverVersion = 0x0100;
180 WOutDev[0].caps.dwFormats = 0x00000000;
181 WOutDev[0].caps.dwSupport = WAVECAPS_VOLUME;
183 IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
184 TRACE("OSS dsp out mask=%08x\n", mask);
186 /* First bytespersampl, then stereo */
187 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
189 WOutDev[0].caps.wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
190 if (WOutDev[0].caps.wChannels > 1) WOutDev[0].caps.dwSupport |= WAVECAPS_LRVOLUME;
193 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
194 if (mask & AFMT_U8) {
195 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4M08;
196 if (WOutDev[0].caps.wChannels > 1)
197 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4S08;
199 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
200 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4M16;
201 if (WOutDev[0].caps.wChannels > 1)
202 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4S16;
206 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
207 if (mask & AFMT_U8) {
208 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2M08;
209 if (WOutDev[0].caps.wChannels > 1)
210 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2S08;
212 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
213 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2M16;
214 if (WOutDev[0].caps.wChannels > 1)
215 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2S16;
219 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
220 if (mask & AFMT_U8) {
221 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1M08;
222 if (WOutDev[0].caps.wChannels > 1)
223 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1S08;
225 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
226 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1M16;
227 if (WOutDev[0].caps.wChannels > 1)
228 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1S16;
231 if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
232 TRACE("OSS dsp out caps=%08X\n", caps);
233 if ((caps & DSP_CAP_REALTIME) && !(caps & DSP_CAP_BATCH)) {
234 WOutDev[0].caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
236 /* well, might as well use the DirectSound cap flag for something */
237 if ((caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP))
238 WOutDev[0].caps.dwSupport |= WAVECAPS_DIRECTSOUND;
242 TRACE("out dwFormats = %08lX, dwSupport = %08lX\n",
243 WOutDev[0].caps.dwFormats, WOutDev[0].caps.dwSupport);
245 /* then do input device */
249 if (access(SOUND_DEV,0) != 0 ||
250 (audio = open(SOUND_DEV, O_RDONLY|O_NDELAY, 0)) == -1) {
251 TRACE("Couldn't open in %s (%s)\n", SOUND_DEV, strerror(errno));
255 ioctl(audio, SNDCTL_DSP_RESET, 0);
258 WInDev[0].caps.wMid = 0x0002;
259 WInDev[0].caps.wPid = 0x0004;
260 strcpy(WInDev[0].caps.szPname, "SB16 Wave In");
262 WInDev[0].caps.wMid = 0x00FF; /* Manufac ID */
263 WInDev[0].caps.wPid = 0x0001; /* Product ID */
264 strcpy(WInDev[0].caps.szPname, "OpenSoundSystem WAVIN Driver");
266 WInDev[0].caps.dwFormats = 0x00000000;
267 WInDev[0].caps.wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
269 IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
270 TRACE("OSS in dsp mask=%08x\n", mask);
272 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
274 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
275 if (mask & AFMT_U8) {
276 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4M08;
277 if (WInDev[0].caps.wChannels > 1)
278 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4S08;
280 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
281 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4M16;
282 if (WInDev[0].caps.wChannels > 1)
283 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4S16;
287 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
288 if (mask & AFMT_U8) {
289 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2M08;
290 if (WInDev[0].caps.wChannels > 1)
291 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2S08;
293 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
294 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2M16;
295 if (WInDev[0].caps.wChannels > 1)
296 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2S16;
300 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
301 if (mask & AFMT_U8) {
302 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1M08;
303 if (WInDev[0].caps.wChannels > 1)
304 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1S08;
306 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
307 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1M16;
308 if (WInDev[0].caps.wChannels > 1)
309 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1S16;
313 TRACE("in dwFormats = %08lX\n", WInDev[0].caps.dwFormats);
318 /**************************************************************************
319 * OSS_NotifyClient [internal]
321 static DWORD OSS_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1,
324 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
330 if (wDevID >= MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
332 if (WOutDev[wDevID].wFlags != DCB_NULL &&
333 !DriverCallback(WOutDev[wDevID].waveDesc.dwCallback,
334 WOutDev[wDevID].wFlags,
335 WOutDev[wDevID].waveDesc.hWave,
337 WOutDev[wDevID].waveDesc.dwInstance,
340 WARN("can't notify client !\n");
341 return MMSYSERR_NOERROR;
348 if (wDevID >= MAX_WAVEINDRV) return MCIERR_INTERNAL;
350 if (WInDev[wDevID].wFlags != DCB_NULL &&
351 !DriverCallback(WInDev[wDevID].waveDesc.dwCallback,
352 WInDev[wDevID].wFlags,
353 WInDev[wDevID].waveDesc.hWave,
355 WInDev[wDevID].waveDesc.dwInstance,
358 WARN("can't notify client !\n");
359 return MMSYSERR_NOERROR;
363 FIXME("Unknown CB message %u\n", wMsg);
369 /*======================================================================*
370 * Low level WAVE OUT implemantation *
371 *======================================================================*/
373 /**************************************************************************
374 * wodPlayer_WriteFragments [internal]
376 * wodPlayer helper. Writes as many fragments it can to unixdev.
377 * Returns TRUE in case of buffer underrun.
379 static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
387 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) {
388 ERR("ioctl failed (%s)\n", strerror(errno));
392 TRACE("Fragments %d/%d\n", info.fragments, info.fragstotal);
394 if (!info.fragments) /* output queue is full, wait a bit */
397 lpWaveHdr = wwo->lpPlayPtr;
399 if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */
400 wwo->dwLastFragDone && /* first fragment has been played */
401 info.fragments + 2 > info.fragstotal) { /* done with all waveOutWrite()' fragments */
402 /* FIXME: should do better handling here */
403 TRACE("Oooch, buffer underrun !\n");
404 return TRUE; /* force resetting of waveOut device */
406 return FALSE; /* wait a bit */
409 if (wwo->dwOffCurrHdr == 0) {
410 TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
411 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
412 if (wwo->lpLoopPtr) {
413 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
415 wwo->lpLoopPtr = lpWaveHdr;
420 lpData = lpWaveHdr->lpData;
422 /* finish current wave hdr ? */
423 if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) {
424 DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
426 /* write end of current wave hdr */
427 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
428 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
430 if (count > 0 || toWrite == 0) {
431 DWORD tc = GetTickCount();
433 if (wwo->dwLastFragDone /* + guard time ?? */ < tc)
434 wwo->dwLastFragDone = tc;
435 wwo->dwLastFragDone += (toWrite * 1000) / wwo->format.wf.nAvgBytesPerSec;
437 lpWaveHdr->reserved = wwo->dwLastFragDone;
438 TRACE("Tagging hdr %p with %08lx\n", lpWaveHdr, wwo->dwLastFragDone);
440 /* WAVEHDR written, go to next one */
441 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
442 if (--wwo->lpLoopPtr->dwLoops > 0) {
443 wwo->lpPlayPtr = wwo->lpLoopPtr;
445 /* last one played */
446 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
447 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop\n");
448 /* shall we consider the END flag for the closing loop or for
449 * the opening one or for both ???
450 * code assumes for closing loop only
452 wwo->lpLoopPtr = lpWaveHdr;
454 wwo->lpLoopPtr = NULL;
456 wwo->lpPlayPtr = lpWaveHdr->lpNext;
459 wwo->lpPlayPtr = lpWaveHdr->lpNext;
461 wwo->dwOffCurrHdr = 0;
462 if ((wwo->dwRemain -= count) == 0) {
463 wwo->dwRemain = wwo->dwFragmentSize;
466 continue; /* try to go to use next wavehdr */
468 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
469 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
471 DWORD tc = GetTickCount();
473 if (wwo->dwLastFragDone /* + guard time ?? */ < tc)
474 wwo->dwLastFragDone = tc;
475 wwo->dwLastFragDone += (wwo->dwRemain * 1000) / wwo->format.wf.nAvgBytesPerSec;
477 TRACE("Tagging frag with %08lx\n", wwo->dwLastFragDone);
479 wwo->dwOffCurrHdr += count;
480 wwo->dwRemain = wwo->dwFragmentSize;
486 /**************************************************************************
487 * wodPlayer_Notify [internal]
489 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
490 * have been played (actually to speaker, not to unixdev fd).
492 static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
495 DWORD tc = GetTickCount();
497 while (wwo->lpQueuePtr &&
499 (wwo->lpQueuePtr != wwo->lpPlayPtr && wwo->lpQueuePtr != wwo->lpLoopPtr))) {
500 lpWaveHdr = wwo->lpQueuePtr;
502 if (lpWaveHdr->reserved > tc && !force) break;
504 wwo->dwPlayedTotal += lpWaveHdr->dwBufferLength;
505 wwo->lpQueuePtr = lpWaveHdr->lpNext;
507 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
508 lpWaveHdr->dwFlags |= WHDR_DONE;
510 TRACE("Notifying client with %p\n", lpWaveHdr);
511 if (OSS_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) {
512 WARN("can't notify client !\n");
517 /**************************************************************************
518 * wodPlayer_Reset [internal]
520 * wodPlayer helper. Resets current output stream.
522 static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
524 /* updates current notify list */
525 wodPlayer_Notify(wwo, uDevID, FALSE);
527 /* flush all possible output */
528 if (ioctl(wwo->unixdev, SNDCTL_DSP_RESET, 0) == -1) {
529 perror("ioctl SNDCTL_DSP_RESET");
531 wwo->state = WINE_WS_STOPPED;
535 wwo->dwOffCurrHdr = 0;
536 wwo->dwRemain = wwo->dwFragmentSize;
538 /* empty notify list */
539 wodPlayer_Notify(wwo, uDevID, TRUE);
541 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
542 wwo->state = WINE_WS_STOPPED;
543 wwo->dwPlayedTotal = 0;
545 /* FIXME: this is not accurate when looping, but can be do better ? */
546 wwo->lpPlayPtr = (wwo->lpLoopPtr) ? wwo->lpLoopPtr : wwo->lpQueuePtr;
547 wwo->state = WINE_WS_PAUSED;
551 /**************************************************************************
552 * wodPlayer [internal]
554 static DWORD CALLBACK wodPlayer(LPVOID pmt)
556 WORD uDevID = (DWORD)pmt;
557 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
562 PeekMessageA(&msg, 0, 0, 0, 0);
563 wwo->state = WINE_WS_STOPPED;
565 wwo->dwLastFragDone = 0;
566 wwo->dwOffCurrHdr = 0;
567 wwo->dwRemain = wwo->dwFragmentSize;
568 wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL;
569 wwo->dwPlayedTotal = 0;
571 TRACE("imhere[0]\n");
572 SetEvent(wwo->hEvent);
574 /* make sleep time to be # of ms to output a fragment */
575 dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
578 /* wait for dwSleepTime or an event in thread's queue */
579 /* FIXME: could improve wait time depending on queue state,
580 * ie, number of queued fragments
582 TRACE("imhere[1]\n");
583 MsgWaitForMultipleObjects(0, NULL, FALSE,
584 (wwo->state == WINE_WS_PLAYING) ?
585 2 * dwSleepTime : /*INFINITE*/100,
587 TRACE("imhere[2] (q=%p p=%p)\n", wwo->lpQueuePtr, wwo->lpPlayPtr);
588 wodPlayer_Notify(wwo, uDevID, FALSE);
589 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
590 switch (msg.message) {
591 case WINE_WM_PAUSING:
592 wodPlayer_Reset(wwo, uDevID, FALSE);
593 wwo->state = WINE_WS_PAUSED;
594 SetEvent(wwo->hEvent);
596 case WINE_WM_RESTARTING:
597 wwo->state = WINE_WS_PLAYING;
598 SetEvent(wwo->hEvent);
601 lpWaveHdr = (LPWAVEHDR)msg.lParam;
603 /* insert buffer at the end of queue */
606 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
609 if (!wwo->lpPlayPtr) wwo->lpPlayPtr = lpWaveHdr;
610 if (wwo->state == WINE_WS_STOPPED)
611 wwo->state = WINE_WS_PLAYING;
613 case WINE_WM_RESETTING:
614 wodPlayer_Reset(wwo, uDevID, TRUE);
615 SetEvent(wwo->hEvent);
617 case WINE_WM_CLOSING:
618 /* sanity check: this should not happen since the device must have been reset before */
619 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
621 wwo->state = WINE_WS_CLOSED;
622 SetEvent(wwo->hEvent);
624 /* shouldn't go here */
626 FIXME("unknown message %d\n", msg.message);
630 if (wwo->state == WINE_WS_PLAYING) {
631 wodPlayer_WriteFragments(wwo);
633 wodPlayer_Notify(wwo, uDevID, FALSE);
636 /* just for not generating compilation warnings... should never be executed */
640 /**************************************************************************
641 * wodGetDevCaps [internal]
643 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
645 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
647 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
649 if (wDevID >= MAX_WAVEOUTDRV) {
650 TRACE("MAX_WAVOUTDRV reached !\n");
651 return MMSYSERR_BADDEVICEID;
654 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
655 return MMSYSERR_NOERROR;
658 /**************************************************************************
661 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
671 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
672 if (lpDesc == NULL) {
673 WARN("Invalid Parameter !\n");
674 return MMSYSERR_INVALPARAM;
676 if (wDevID >= MAX_WAVEOUTDRV) {
677 TRACE("MAX_WAVOUTDRV reached !\n");
678 return MMSYSERR_BADDEVICEID;
681 /* only PCM format is supported so far... */
682 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
683 lpDesc->lpFormat->nChannels == 0 ||
684 lpDesc->lpFormat->nSamplesPerSec == 0) {
685 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
686 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
687 lpDesc->lpFormat->nSamplesPerSec);
688 return WAVERR_BADFORMAT;
691 if (dwFlags & WAVE_FORMAT_QUERY) {
692 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
693 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
694 lpDesc->lpFormat->nSamplesPerSec);
695 return MMSYSERR_NOERROR;
698 wwo = &WOutDev[wDevID];
700 if (dwFlags & WAVE_DIRECTSOUND) {
701 if (!(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
702 /* not supported, ignore it */
703 dwFlags &= ~WAVE_DIRECTSOUND;
708 if (access(SOUND_DEV, 0) != 0)
709 return MMSYSERR_NOTENABLED;
710 if (dwFlags & WAVE_DIRECTSOUND)
711 /* we want to be able to mmap() the device, which means it must be opened readable,
712 * otherwise mmap() will fail (at least under Linux) */
713 audio = open(SOUND_DEV, O_RDWR|O_NDELAY, 0);
715 audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0);
717 WARN("can't open (%s)!\n", strerror(errno));
718 return MMSYSERR_ALLOCATED;
721 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
723 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
724 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
726 if (WOutDev[wDevID].format.wBitsPerSample == 0) {
727 WOutDev[wDevID].format.wBitsPerSample = 8 *
728 (WOutDev[wDevID].format.wf.nAvgBytesPerSec /
729 WOutDev[wDevID].format.wf.nSamplesPerSec) /
730 WOutDev[wDevID].format.wf.nChannels;
733 if (dwFlags & WAVE_DIRECTSOUND) {
734 /* with DirectSound, fragments are irrelevant, but a large buffer isn't...
735 * so let's choose a full 64KB for DirectSound */
736 audio_fragment = 0x0020000B;
738 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
739 * thus leading to 46ms per fragment, and a turnaround time of 185ms
741 /* 2^10=1024 bytes per fragment, 16 fragments max */
742 audio_fragment = 0x000F000A;
744 sample_rate = wwo->format.wf.nSamplesPerSec;
745 dsp_stereo = (wwo->format.wf.nChannels > 1) ? 1 : 0;
746 format = (wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8;
748 IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
749 /* First size and stereo then samplerate */
750 IOCTL(audio, SNDCTL_DSP_SETFMT, format);
751 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
752 IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
754 /* paranoid checks */
755 if (format != ((wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8))
756 ERR("Can't set format to %d (%d)\n",
757 (wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8, format);
758 if (dsp_stereo != (wwo->format.wf.nChannels > 1) ? 1 : 0)
759 ERR("Can't set stereo to %u (%d)\n",
760 (wwo->format.wf.nChannels > 1) ? 1 : 0, dsp_stereo);
761 if (!NEAR_MATCH(sample_rate,wwo->format.wf.nSamplesPerSec))
762 ERR("Can't set sample_rate to %lu (%d)\n",
763 wwo->format.wf.nSamplesPerSec, sample_rate);
765 /* even if we set fragment size above, read it again, just in case */
766 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
768 wwo->unixdev = audio;
769 wwo->dwFragmentSize = fragment_size;
771 if (!(dwFlags & WAVE_DIRECTSOUND)) {
772 wwo->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
773 wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
774 WaitForSingleObject(wwo->hEvent, INFINITE);
776 wwo->hEvent = INVALID_HANDLE_VALUE;
777 wwo->hThread = INVALID_HANDLE_VALUE;
781 TRACE("fd=%d fragmentSize=%ld\n",
782 wwo->unixdev, wwo->dwFragmentSize);
783 if (wwo->dwFragmentSize % wwo->format.wf.nBlockAlign)
784 ERR("Fragment doesn't contain an integral number of data blocks\n");
786 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
787 wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
788 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
789 wwo->format.wf.nBlockAlign);
791 if (OSS_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
792 WARN("can't notify client !\n");
793 return MMSYSERR_INVALPARAM;
795 return MMSYSERR_NOERROR;
798 /**************************************************************************
799 * wodClose [internal]
801 static DWORD wodClose(WORD wDevID)
803 DWORD ret = MMSYSERR_NOERROR;
806 TRACE("(%u);\n", wDevID);
808 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
809 WARN("bad device ID !\n");
810 return MMSYSERR_BADDEVICEID;
813 wwo = &WOutDev[wDevID];
814 if (wwo->lpQueuePtr) {
815 WARN("buffers still playing !\n");
816 ret = WAVERR_STILLPLAYING;
818 TRACE("imhere[3-close]\n");
819 if (wwo->hEvent != INVALID_HANDLE_VALUE) {
820 PostThreadMessageA(wwo->dwThreadID, WINE_WM_CLOSING, 0, 0);
821 WaitForSingleObject(wwo->hEvent, INFINITE);
822 CloseHandle(wwo->hEvent);
825 munmap(wwo->mapping, wwo->maplen);
831 wwo->dwFragmentSize = 0;
832 if (OSS_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
833 WARN("can't notify client !\n");
834 ret = MMSYSERR_INVALPARAM;
840 /**************************************************************************
841 * wodWrite [internal]
844 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
846 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
848 /* first, do the sanity checks... */
849 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
850 WARN("bad dev ID !\n");
851 return MMSYSERR_BADDEVICEID;
854 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
855 return WAVERR_UNPREPARED;
857 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
858 return WAVERR_STILLPLAYING;
860 lpWaveHdr->dwFlags &= ~WHDR_DONE;
861 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
862 lpWaveHdr->lpNext = 0;
864 TRACE("imhere[3-HEADER]\n");
865 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
867 return MMSYSERR_NOERROR;
870 /**************************************************************************
871 * wodPrepare [internal]
873 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
875 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
877 if (wDevID >= MAX_WAVEOUTDRV) {
878 WARN("bad device ID !\n");
879 return MMSYSERR_BADDEVICEID;
882 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
883 return WAVERR_STILLPLAYING;
885 lpWaveHdr->dwFlags |= WHDR_PREPARED;
886 lpWaveHdr->dwFlags &= ~WHDR_DONE;
887 return MMSYSERR_NOERROR;
890 /**************************************************************************
891 * wodUnprepare [internal]
893 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
895 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
897 if (wDevID >= MAX_WAVEOUTDRV) {
898 WARN("bad device ID !\n");
899 return MMSYSERR_BADDEVICEID;
902 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
903 return WAVERR_STILLPLAYING;
905 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
906 lpWaveHdr->dwFlags |= WHDR_DONE;
908 return MMSYSERR_NOERROR;
911 /**************************************************************************
912 * wodPause [internal]
914 static DWORD wodPause(WORD wDevID)
916 TRACE("(%u);!\n", wDevID);
918 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
919 WARN("bad device ID !\n");
920 return MMSYSERR_BADDEVICEID;
923 TRACE("imhere[3-PAUSING]\n");
924 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_PAUSING, 0, 0);
925 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
927 return MMSYSERR_NOERROR;
930 /**************************************************************************
931 * wodRestart [internal]
933 static DWORD wodRestart(WORD wDevID)
935 TRACE("(%u);\n", wDevID);
937 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
938 WARN("bad device ID !\n");
939 return MMSYSERR_BADDEVICEID;
942 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
943 TRACE("imhere[3-RESTARTING]\n");
944 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
945 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
948 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
949 /* FIXME: Myst crashes with this ... hmm -MM
950 if (OSS_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
951 WARN("can't notify client !\n");
952 return MMSYSERR_INVALPARAM;
956 return MMSYSERR_NOERROR;
959 /**************************************************************************
960 * wodReset [internal]
962 static DWORD wodReset(WORD wDevID)
964 TRACE("(%u);\n", wDevID);
966 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
967 WARN("bad device ID !\n");
968 return MMSYSERR_BADDEVICEID;
971 TRACE("imhere[3-RESET]\n");
972 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
973 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
975 return MMSYSERR_NOERROR;
979 /**************************************************************************
980 * wodGetPosition [internal]
982 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
988 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
990 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
991 WARN("bad device ID !\n");
992 return MMSYSERR_BADDEVICEID;
995 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
997 wwo = &WOutDev[wDevID];
998 val = wwo->dwPlayedTotal;
1000 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
1001 lpTime->wType, wwo->format.wBitsPerSample,
1002 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1003 wwo->format.wf.nAvgBytesPerSec);
1004 TRACE("dwTotalPlayed=%lu\n", val);
1006 switch (lpTime->wType) {
1009 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1012 lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample;
1013 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1016 time = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1017 lpTime->u.smpte.hour = time / 108000;
1018 time -= lpTime->u.smpte.hour * 108000;
1019 lpTime->u.smpte.min = time / 1800;
1020 time -= lpTime->u.smpte.min * 1800;
1021 lpTime->u.smpte.sec = time / 30;
1022 time -= lpTime->u.smpte.sec * 30;
1023 lpTime->u.smpte.frame = time;
1024 lpTime->u.smpte.fps = 30;
1025 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1026 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1027 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1030 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
1031 lpTime->wType = TIME_MS;
1033 lpTime->u.ms = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1034 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1037 return MMSYSERR_NOERROR;
1040 /**************************************************************************
1041 * wodGetVolume [internal]
1043 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1049 TRACE("(%u, %p);\n", wDevID, lpdwVol);
1051 if (lpdwVol == NULL)
1052 return MMSYSERR_NOTENABLED;
1053 if ((mixer = open(MIXER_DEV, O_RDONLY|O_NDELAY)) < 0) {
1054 WARN("mixer device not available !\n");
1055 return MMSYSERR_NOTENABLED;
1057 if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
1058 WARN("unable read mixer !\n");
1059 return MMSYSERR_NOTENABLED;
1062 left = LOBYTE(volume);
1063 right = HIBYTE(volume);
1064 TRACE("left=%ld right=%ld !\n", left, right);
1065 *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
1066 return MMSYSERR_NOERROR;
1070 /**************************************************************************
1071 * wodSetVolume [internal]
1073 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1079 TRACE("(%u, %08lX);\n", wDevID, dwParam);
1081 left = (LOWORD(dwParam) * 100) / 0xFFFFl;
1082 right = (HIWORD(dwParam) * 100) / 0xFFFFl;
1083 volume = left + (right << 8);
1085 if ((mixer = open(MIXER_DEV, O_WRONLY|O_NDELAY)) < 0) {
1086 WARN("mixer device not available !\n");
1087 return MMSYSERR_NOTENABLED;
1089 if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
1090 WARN("unable set mixer !\n");
1091 return MMSYSERR_NOTENABLED;
1093 TRACE("volume=%04x\n", (unsigned)volume);
1096 return MMSYSERR_NOERROR;
1099 /**************************************************************************
1100 * wodGetNumDevs [internal]
1102 static DWORD wodGetNumDevs(void)
1106 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
1107 int audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0);
1118 /**************************************************************************
1119 * OSS_wodMessage [sample driver]
1121 DWORD WINAPI OSS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1122 DWORD dwParam1, DWORD dwParam2)
1124 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1125 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1132 /* FIXME: Pretend this is supported */
1134 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1135 case WODM_CLOSE: return wodClose (wDevID);
1136 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1137 case WODM_PAUSE: return wodPause (wDevID);
1138 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
1139 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
1140 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1141 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1142 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2);
1143 case WODM_GETNUMDEVS: return wodGetNumDevs ();
1144 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
1145 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
1146 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1147 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1148 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
1149 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
1150 case WODM_RESTART: return wodRestart (wDevID);
1151 case WODM_RESET: return wodReset (wDevID);
1152 case 0x810: return wodDsCreate(wDevID, (PIDSDRIVER*)dwParam1);
1154 FIXME("unknown message %d!\n", wMsg);
1156 return MMSYSERR_NOTSUPPORTED;
1159 /*======================================================================*
1160 * Low level DSOUND implementation *
1161 *======================================================================*/
1163 typedef struct IDsDriverImpl IDsDriverImpl;
1164 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
1166 struct IDsDriverImpl
1168 /* IUnknown fields */
1169 ICOM_VFIELD(IDsDriver);
1171 /* IDsDriverImpl fields */
1173 IDsDriverBufferImpl*primary;
1176 struct IDsDriverBufferImpl
1178 /* IUnknown fields */
1179 ICOM_VFIELD(IDsDriverBuffer);
1181 /* IDsDriverBufferImpl fields */
1186 static HRESULT DSDB_MapPrimary(IDsDriverBufferImpl *dsdb)
1188 WINE_WAVEOUT *wwo = &(WOutDev[dsdb->drv->wDevID]);
1189 if (!wwo->mapping) {
1190 wwo->mapping = mmap(NULL, wwo->maplen, PROT_WRITE, MAP_SHARED,
1192 if (wwo->mapping == (LPBYTE)-1) {
1193 ERR("(%p): Could not map sound device for direct access (errno=%d)\n", dsdb, errno);
1194 return DSERR_GENERIC;
1196 TRACE("(%p): sound device has been mapped for direct access at %p, size=%ld\n", dsdb, wwo->mapping, wwo->maplen);
1201 static HRESULT DSDB_UnmapPrimary(IDsDriverBufferImpl *dsdb)
1203 WINE_WAVEOUT *wwo = &(WOutDev[dsdb->drv->wDevID]);
1205 if (munmap(wwo->mapping, wwo->maplen) < 0) {
1206 ERR("(%p): Could not unmap sound device (errno=%d)\n", dsdb, errno);
1207 return DSERR_GENERIC;
1209 wwo->mapping = NULL;
1210 TRACE("(%p): sound device unmapped\n", dsdb);
1215 static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
1217 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1218 FIXME("(): stub!\n");
1219 return DSERR_UNSUPPORTED;
1222 static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface)
1224 ICOM_THIS(IDsDriverBufferImpl,iface);
1229 static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface)
1231 ICOM_THIS(IDsDriverBufferImpl,iface);
1234 if (This == This->drv->primary)
1235 This->drv->primary = NULL;
1236 DSDB_UnmapPrimary(This);
1237 HeapFree(GetProcessHeap(),0,This);
1241 static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
1242 LPVOID*ppvAudio1,LPDWORD pdwLen1,
1243 LPVOID*ppvAudio2,LPDWORD pdwLen2,
1244 DWORD dwWritePosition,DWORD dwWriteLen,
1247 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1248 /* since we (GetDriverDesc flags) have specified DSDDESC_DONTNEEDPRIMARYLOCK,
1249 * and that we don't support secondary buffers, this method will never be called */
1250 TRACE("(%p): stub\n",iface);
1251 return DSERR_UNSUPPORTED;
1254 static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
1255 LPVOID pvAudio1,DWORD dwLen1,
1256 LPVOID pvAudio2,DWORD dwLen2)
1258 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1259 TRACE("(%p): stub\n",iface);
1260 return DSERR_UNSUPPORTED;
1263 static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface,
1264 LPWAVEFORMATEX pwfx)
1266 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1268 TRACE("(%p,%p)\n",iface,pwfx);
1269 /* On our request (GetDriverDesc flags), DirectSound has by now used
1270 * waveOutClose/waveOutOpen to set the format...
1271 * unfortunately, this means our mmap() is now gone...
1272 * so we need to somehow signal to our DirectSound implementation
1273 * that it should completely recreate this HW buffer...
1274 * this unexpected error code should do the trick... */
1275 return DSERR_BUFFERLOST;
1278 static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq)
1280 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1281 TRACE("(%p,%ld): stub\n",iface,dwFreq);
1282 return DSERR_UNSUPPORTED;
1285 static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan)
1287 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
1288 FIXME("(%p,%p): stub!\n",iface,pVolPan);
1289 return DSERR_UNSUPPORTED;
1292 static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos)
1294 /* ICOM_THIS(IDsDriverImpl,iface); */
1295 TRACE("(%p,%ld): stub\n",iface,dwNewPos);
1296 return DSERR_UNSUPPORTED;
1299 static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
1300 LPDWORD lpdwPlay, LPDWORD lpdwWrite)
1302 ICOM_THIS(IDsDriverBufferImpl,iface);
1306 TRACE("(%p)\n",iface);
1307 if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_GETOPTR, &info) < 0) {
1308 ERR("ioctl failed (%d)\n", errno);
1309 return DSERR_GENERIC;
1311 ptr = info.ptr & ~3; /* align the pointer, just in case */
1312 if (lpdwPlay) *lpdwPlay = ptr;
1314 /* add some safety margin (not strictly necessary, but...) */
1315 *lpdwWrite = ptr + 32;
1316 while (*lpdwWrite > This->buflen)
1317 *lpdwWrite -= This->buflen;
1319 TRACE("playpos=%ld, writepos=%ld\n", lpdwPlay?*lpdwPlay:0, lpdwWrite?*lpdwWrite:0);
1320 return DSERR_UNSUPPORTED;
1323 static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags)
1325 ICOM_THIS(IDsDriverBufferImpl,iface);
1326 int enable = PCM_ENABLE_OUTPUT;
1327 TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags);
1328 if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
1329 ERR("ioctl failed (%d)\n", errno);
1330 return DSERR_GENERIC;
1335 static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface)
1337 ICOM_THIS(IDsDriverBufferImpl,iface);
1339 TRACE("(%p)\n",iface);
1340 /* no more playing */
1341 if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
1342 ERR("ioctl failed (%d)\n", errno);
1343 return DSERR_GENERIC;
1346 /* the play position must be reset to the beginning of the buffer */
1347 if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_RESET, 0) < 0) {
1348 ERR("ioctl failed (%d)\n", errno);
1349 return DSERR_GENERIC;
1355 static ICOM_VTABLE(IDsDriverBuffer) dsdbvt =
1357 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1358 IDsDriverBufferImpl_QueryInterface,
1359 IDsDriverBufferImpl_AddRef,
1360 IDsDriverBufferImpl_Release,
1361 IDsDriverBufferImpl_Lock,
1362 IDsDriverBufferImpl_Unlock,
1363 IDsDriverBufferImpl_SetFormat,
1364 IDsDriverBufferImpl_SetFrequency,
1365 IDsDriverBufferImpl_SetVolumePan,
1366 IDsDriverBufferImpl_SetPosition,
1367 IDsDriverBufferImpl_GetPosition,
1368 IDsDriverBufferImpl_Play,
1369 IDsDriverBufferImpl_Stop
1372 static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj)
1374 /* ICOM_THIS(IDsDriverImpl,iface); */
1375 FIXME("(%p): stub!\n",iface);
1376 return DSERR_UNSUPPORTED;
1379 static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface)
1381 ICOM_THIS(IDsDriverImpl,iface);
1386 static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface)
1388 ICOM_THIS(IDsDriverImpl,iface);
1391 HeapFree(GetProcessHeap(),0,This);
1395 static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc)
1397 ICOM_THIS(IDsDriverImpl,iface);
1398 TRACE("(%p,%p)\n",iface,pDesc);
1399 pDesc->dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
1400 DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK;
1401 strcpy(pDesc->szDesc,"WineOSS DirectSound Driver");
1402 strcpy(pDesc->szDrvName,"wineoss.drv");
1403 pDesc->dnDevNode = WOutDev[This->wDevID].waveDesc.dnDevNode;
1405 pDesc->wReserved = 0;
1406 pDesc->ulDeviceNum = This->wDevID;
1407 pDesc->dwHeapType = DSDHEAP_NOHEAP;
1408 pDesc->pvDirectDrawHeap = NULL;
1409 pDesc->dwMemStartAddress = 0;
1410 pDesc->dwMemEndAddress = 0;
1411 pDesc->dwMemAllocExtra = 0;
1412 pDesc->pvReserved1 = NULL;
1413 pDesc->pvReserved2 = NULL;
1417 static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface)
1419 ICOM_THIS(IDsDriverImpl,iface);
1422 TRACE("(%p)\n",iface);
1423 /* make sure the card doesn't start playing before we want it to */
1424 if (ioctl(WOutDev[This->wDevID].unixdev, SNDCTL_DSP_SETTRIGGER, &enable) < 0) {
1425 ERR("ioctl failed (%d)\n", errno);
1426 return DSERR_GENERIC;
1431 static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface)
1433 ICOM_THIS(IDsDriverImpl,iface);
1434 TRACE("(%p)\n",iface);
1435 if (This->primary) {
1436 ERR("problem with DirectSound: primary not released\n");
1437 return DSERR_GENERIC;
1442 static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps)
1444 /* ICOM_THIS(IDsDriverImpl,iface); */
1445 TRACE("(%p,%p)\n",iface,pCaps);
1446 memset(pCaps, 0, sizeof(*pCaps));
1447 /* FIXME: need to check actual capabilities */
1448 pCaps->dwFlags = DSCAPS_PRIMARYMONO | DSCAPS_PRIMARYSTEREO |
1449 DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARY16BIT;
1450 pCaps->dwPrimaryBuffers = 1;
1451 /* the other fields only apply to secondary buffers, which we don't support
1452 * (unless we want to mess with wavetable synthesizers and MIDI) */
1456 static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
1457 LPWAVEFORMATEX pwfx,
1458 DWORD dwFlags, DWORD dwCardAddress,
1459 LPDWORD pdwcbBufferSize,
1463 ICOM_THIS(IDsDriverImpl,iface);
1464 IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
1466 audio_buf_info info;
1468 TRACE("(%p,%p,%lx,%lx)\n",iface,pwfx,dwFlags,dwCardAddress);
1469 /* we only support primary buffers */
1470 if (!(dwFlags & DSBCAPS_PRIMARYBUFFER))
1471 return DSERR_UNSUPPORTED;
1473 return DSERR_ALLOCATED;
1474 if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN))
1475 return DSERR_CONTROLUNAVAIL;
1477 *ippdsdb = (IDsDriverBufferImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverBufferImpl));
1478 if (*ippdsdb == NULL)
1479 return DSERR_OUTOFMEMORY;
1480 ICOM_VTBL(*ippdsdb) = &dsdbvt;
1481 (*ippdsdb)->ref = 1;
1482 (*ippdsdb)->drv = This;
1484 /* check how big the DMA buffer is now */
1485 if (ioctl(WOutDev[This->wDevID].unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) {
1486 ERR("ioctl failed (%d)\n", errno);
1487 HeapFree(GetProcessHeap(),0,*ippdsdb);
1489 return DSERR_GENERIC;
1491 WOutDev[This->wDevID].maplen = (*ippdsdb)->buflen = info.fragstotal * info.fragsize;
1493 /* map the DMA buffer */
1494 err = DSDB_MapPrimary(*ippdsdb);
1496 HeapFree(GetProcessHeap(),0,*ippdsdb);
1501 /* primary buffer is ready to go */
1502 *pdwcbBufferSize = WOutDev[This->wDevID].maplen;
1503 *ppbBuffer = WOutDev[This->wDevID].mapping;
1505 This->primary = *ippdsdb;
1510 static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
1511 PIDSDRIVERBUFFER pBuffer,
1514 /* ICOM_THIS(IDsDriverImpl,iface); */
1515 TRACE("(%p,%p): stub\n",iface,pBuffer);
1516 return DSERR_INVALIDCALL;
1519 static ICOM_VTABLE(IDsDriver) dsdvt =
1521 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1522 IDsDriverImpl_QueryInterface,
1523 IDsDriverImpl_AddRef,
1524 IDsDriverImpl_Release,
1525 IDsDriverImpl_GetDriverDesc,
1527 IDsDriverImpl_Close,
1528 IDsDriverImpl_GetCaps,
1529 IDsDriverImpl_CreateSoundBuffer,
1530 IDsDriverImpl_DuplicateSoundBuffer
1533 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
1535 IDsDriverImpl** idrv = (IDsDriverImpl**)drv;
1537 /* the HAL isn't much better than the HEL if we can't do mmap() */
1538 if (!(WOutDev[wDevID].caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
1539 ERR("DirectSound flag not set\n");
1540 MESSAGE("This sound card's driver does not support direct access\n");
1541 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
1542 return MMSYSERR_NOTSUPPORTED;
1545 *idrv = (IDsDriverImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverImpl));
1547 return MMSYSERR_NOMEM;
1548 ICOM_VTBL(*idrv) = &dsdvt;
1551 (*idrv)->wDevID = wDevID;
1552 (*idrv)->primary = NULL;
1553 return MMSYSERR_NOERROR;
1556 /*======================================================================*
1557 * Low level WAVE IN implemantation *
1558 *======================================================================*/
1560 /**************************************************************************
1561 * widGetDevCaps [internal]
1563 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
1565 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1567 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1569 if (wDevID >= MAX_WAVEINDRV) {
1570 TRACE("MAX_WAVINDRV reached !\n");
1571 return MMSYSERR_BADDEVICEID;
1574 memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1575 return MMSYSERR_NOERROR;
1578 /**************************************************************************
1579 * widRecorder [internal]
1581 static DWORD CALLBACK widRecorder(LPVOID pmt)
1583 WORD uDevID = (DWORD)pmt;
1584 WINE_WAVEIN* wwi = (WINE_WAVEIN*)&WInDev[uDevID];
1590 PeekMessageA(&msg, 0, 0, 0, 0);
1591 wwi->state = WINE_WS_STOPPED;
1592 wwi->dwTotalRecorded = 0;
1594 TRACE("imhere[0]\n");
1595 SetEvent(wwi->hEvent);
1597 /* make sleep time to be # of ms to output a fragment */
1598 dwSleepTime = (wwi->dwFragmentSize * 1000) / wwi->format.wf.nAvgBytesPerSec;
1601 /* wait for dwSleepTime or an event in thread's queue */
1602 /* FIXME: could improve wait time depending on queue state,
1603 * ie, number of queued fragments
1605 TRACE("imhere[1]\n");
1607 if (wwi->lpQueuePtr != NULL) {
1608 lpWaveHdr = wwi->lpQueuePtr;
1609 TRACE("recording buf=%p size=%lu/read=%lu \n",
1610 lpWaveHdr->lpData, wwi->lpQueuePtr->dwBufferLength, lpWaveHdr->dwBytesRecorded);
1612 bytesRead = read(wwi->unixdev, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
1613 lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
1615 if (bytesRead > 0) {
1616 TRACE("Read=%lu (%ld)\n", bytesRead, lpWaveHdr->dwBufferLength);
1617 lpWaveHdr->dwBytesRecorded += bytesRead;
1618 wwi->dwTotalRecorded += bytesRead;
1619 if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength) {
1620 /* removes the current block from the queue */
1621 wwi->lpQueuePtr = lpWaveHdr->lpNext;
1623 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1624 lpWaveHdr->dwFlags |= WHDR_DONE;
1626 if (OSS_NotifyClient(uDevID, WIM_DATA, (DWORD)lpWaveHdr, lpWaveHdr->dwBytesRecorded) != MMSYSERR_NOERROR) {
1627 WARN("can't notify client !\n");
1632 TRACE("No data (%s)\n", strerror(errno));
1636 MsgWaitForMultipleObjects(0, NULL, FALSE, dwSleepTime, QS_POSTMESSAGE);
1637 TRACE("imhere[2] (q=%p)\n", wwi->lpQueuePtr);
1639 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
1640 switch (msg.message) {
1641 case WINE_WM_PAUSING:
1642 wwi->state = WINE_WS_PAUSED;
1643 SetEvent(wwi->hEvent);
1645 case WINE_WM_RESTARTING:
1646 wwi->state = WINE_WS_PLAYING;
1647 SetEvent(wwi->hEvent);
1649 case WINE_WM_HEADER:
1650 lpWaveHdr = (LPWAVEHDR)msg.lParam;
1651 lpWaveHdr->lpNext = 0;
1653 /* insert buffer at the end of queue */
1656 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
1659 if (wwi->state == WINE_WS_STOPPED)
1660 wwi->state = WINE_WS_PLAYING;
1662 case WINE_WM_RESETTING:
1663 wwi->state = WINE_WS_STOPPED;
1664 /* return all buffers to the app */
1665 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
1666 TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
1667 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1668 lpWaveHdr->dwFlags |= WHDR_DONE;
1670 if (OSS_NotifyClient(uDevID, WIM_DATA, (DWORD)lpWaveHdr,
1671 lpWaveHdr->dwBytesRecorded) != MMSYSERR_NOERROR) {
1672 WARN("can't notify client !\n");
1675 wwi->lpQueuePtr = NULL;
1676 SetEvent(wwi->hEvent);
1678 case WINE_WM_CLOSING:
1680 wwi->state = WINE_WS_CLOSED;
1681 SetEvent(wwi->hEvent);
1683 /* shouldn't go here */
1685 FIXME("unknown message %d\n", msg.message);
1691 /* just for not generating compilation warnings... should never be executed */
1696 /**************************************************************************
1697 * widOpen [internal]
1699 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1701 int audio, abuf_size, smplrate, samplesize, dsp_stereo;
1702 LPWAVEFORMAT lpFormat;
1705 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1706 if (lpDesc == NULL) {
1707 WARN("Invalid Parameter !\n");
1708 return MMSYSERR_INVALPARAM;
1710 if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_BADDEVICEID;
1712 /* only PCM format is supported so far... */
1713 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1714 lpDesc->lpFormat->nChannels == 0 ||
1715 lpDesc->lpFormat->nSamplesPerSec == 0) {
1716 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1717 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1718 lpDesc->lpFormat->nSamplesPerSec);
1719 return WAVERR_BADFORMAT;
1722 if (dwFlags & WAVE_FORMAT_QUERY) {
1723 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1724 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1725 lpDesc->lpFormat->nSamplesPerSec);
1726 return MMSYSERR_NOERROR;
1729 wwi = &WInDev[wDevID];
1731 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1732 audio = open(SOUND_DEV, O_RDONLY|O_NDELAY, 0);
1734 WARN("can't open (%s)!\n", strerror(errno));
1735 return MMSYSERR_ALLOCATED;
1737 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
1738 if (abuf_size < 1024 || abuf_size > 65536) {
1739 if (abuf_size == -1)
1741 WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1743 return MMSYSERR_NOTENABLED;
1745 WARN("SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize %d!\n",abuf_size);
1747 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1749 if (wwi->lpQueuePtr) {
1750 WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr);
1751 wwi->lpQueuePtr = NULL;
1753 wwi->unixdev = audio;
1754 wwi->dwFragmentSize = abuf_size;
1755 wwi->dwTotalRecorded = 0;
1756 memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1757 lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat;
1759 memcpy(&wwi->format, lpFormat, sizeof(PCMWAVEFORMAT));
1760 wwi->format.wBitsPerSample = 8; /* <-------------- */
1761 if (wwi->format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1762 if (wwi->format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1763 if (wwi->format.wBitsPerSample == 0) {
1764 wwi->format.wBitsPerSample = 8 *
1765 (wwi->format.wf.nAvgBytesPerSec /
1766 wwi->format.wf.nSamplesPerSec) /
1767 wwi->format.wf.nChannels;
1769 samplesize = wwi->format.wBitsPerSample;
1770 smplrate = wwi->format.wf.nSamplesPerSec;
1771 dsp_stereo = (wwi->format.wf.nChannels > 1) ? TRUE : FALSE;
1772 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1773 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1774 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1775 TRACE("wBitsPerSample=%u !\n", wwi->format.wBitsPerSample);
1776 TRACE("nSamplesPerSec=%lu !\n", wwi->format.wf.nSamplesPerSec);
1777 TRACE("nChannels=%u !\n", wwi->format.wf.nChannels);
1778 TRACE("nAvgBytesPerSec=%lu\n", wwi->format.wf.nAvgBytesPerSec);
1780 wwi->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1781 wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
1782 WaitForSingleObject(wwi->hEvent, INFINITE);
1784 if (OSS_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1785 WARN("can't notify client !\n");
1786 return MMSYSERR_INVALPARAM;
1788 return MMSYSERR_NOERROR;
1791 /**************************************************************************
1792 * widClose [internal]
1794 static DWORD widClose(WORD wDevID)
1798 TRACE("(%u);\n", wDevID);
1799 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == 0) {
1800 WARN("can't close !\n");
1801 return MMSYSERR_INVALHANDLE;
1804 wwi = &WInDev[wDevID];
1806 if (wwi->lpQueuePtr != NULL) {
1807 WARN("still buffers open !\n");
1808 return WAVERR_STILLPLAYING;
1811 PostThreadMessageA(wwi->dwThreadID, WINE_WM_CLOSING, 0, 0);
1812 WaitForSingleObject(wwi->hEvent, INFINITE);
1813 CloseHandle(wwi->hEvent);
1814 close(wwi->unixdev);
1816 wwi->dwFragmentSize = 0;
1817 if (OSS_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1818 WARN("can't notify client !\n");
1819 return MMSYSERR_INVALPARAM;
1821 return MMSYSERR_NOERROR;
1824 /**************************************************************************
1825 * widAddBuffer [internal]
1827 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1829 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1831 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == 0) {
1832 WARN("can't do it !\n");
1833 return MMSYSERR_INVALHANDLE;
1835 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1836 TRACE("never been prepared !\n");
1837 return WAVERR_UNPREPARED;
1839 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1840 TRACE("header already in use !\n");
1841 return WAVERR_STILLPLAYING;
1843 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1844 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1845 lpWaveHdr->dwBytesRecorded = 0;
1846 lpWaveHdr->lpNext = NULL;
1847 PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
1848 return MMSYSERR_NOERROR;
1851 /**************************************************************************
1852 * widPrepare [internal]
1854 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1856 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1858 if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE;
1860 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1861 return WAVERR_STILLPLAYING;
1863 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1864 lpWaveHdr->dwFlags &= ~(WHDR_INQUEUE|WHDR_DONE);
1865 lpWaveHdr->dwBytesRecorded = 0;
1866 TRACE("header prepared !\n");
1867 return MMSYSERR_NOERROR;
1870 /**************************************************************************
1871 * widUnprepare [internal]
1873 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1875 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1876 if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE;
1878 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1879 return WAVERR_STILLPLAYING;
1881 lpWaveHdr->dwFlags &= ~(WHDR_PREPARED|WHDR_INQUEUE);
1882 lpWaveHdr->dwFlags |= WHDR_DONE;
1884 return MMSYSERR_NOERROR;
1887 /**************************************************************************
1888 * widStart [internal]
1890 static DWORD widStart(WORD wDevID)
1892 TRACE("(%u);\n", wDevID);
1893 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == 0) {
1894 WARN("can't start recording !\n");
1895 return MMSYSERR_INVALHANDLE;
1898 PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
1899 WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE);
1901 return MMSYSERR_NOERROR;
1904 /**************************************************************************
1905 * widStop [internal]
1907 static DWORD widStop(WORD wDevID)
1909 TRACE("(%u);\n", wDevID);
1910 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == 0) {
1911 WARN("can't stop !\n");
1912 return MMSYSERR_INVALHANDLE;
1914 /* FIXME: reset aint stop */
1915 PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
1916 WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE);
1918 return MMSYSERR_NOERROR;
1921 /**************************************************************************
1922 * widReset [internal]
1924 static DWORD widReset(WORD wDevID)
1926 TRACE("(%u);\n", wDevID);
1927 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == 0) {
1928 WARN("can't reset !\n");
1929 return MMSYSERR_INVALHANDLE;
1931 PostThreadMessageA(WInDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
1932 WaitForSingleObject(WInDev[wDevID].hEvent, INFINITE);
1933 return MMSYSERR_NOERROR;
1936 /**************************************************************************
1937 * widGetPosition [internal]
1939 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1944 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1946 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].unixdev == 0) {
1947 WARN("can't get pos !\n");
1948 return MMSYSERR_INVALHANDLE;
1950 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1952 wwi = &WInDev[wDevID];
1954 TRACE("wType=%04X !\n", lpTime->wType);
1955 TRACE("wBitsPerSample=%u\n", wwi->format.wBitsPerSample);
1956 TRACE("nSamplesPerSec=%lu\n", wwi->format.wf.nSamplesPerSec);
1957 TRACE("nChannels=%u\n", wwi->format.wf.nChannels);
1958 TRACE("nAvgBytesPerSec=%lu\n", wwi->format.wf.nAvgBytesPerSec);
1959 switch (lpTime->wType) {
1961 lpTime->u.cb = wwi->dwTotalRecorded;
1962 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1965 lpTime->u.sample = wwi->dwTotalRecorded * 8 /
1966 wwi->format.wBitsPerSample;
1967 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1970 time = wwi->dwTotalRecorded /
1971 (wwi->format.wf.nAvgBytesPerSec / 1000);
1972 lpTime->u.smpte.hour = time / 108000;
1973 time -= lpTime->u.smpte.hour * 108000;
1974 lpTime->u.smpte.min = time / 1800;
1975 time -= lpTime->u.smpte.min * 1800;
1976 lpTime->u.smpte.sec = time / 30;
1977 time -= lpTime->u.smpte.sec * 30;
1978 lpTime->u.smpte.frame = time;
1979 lpTime->u.smpte.fps = 30;
1980 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1981 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1982 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1985 lpTime->u.ms = wwi->dwTotalRecorded /
1986 (wwi->format.wf.nAvgBytesPerSec / 1000);
1987 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1990 FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1991 lpTime->wType = TIME_MS;
1993 return MMSYSERR_NOERROR;
1996 /**************************************************************************
1997 * OSS_widMessage [sample driver]
1999 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2000 DWORD dwParam1, DWORD dwParam2)
2002 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
2003 wDevID, wMsg, dwUser, dwParam1, dwParam2);
2010 /* FIXME: Pretend this is supported */
2012 case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
2013 case WIDM_CLOSE: return widClose (wDevID);
2014 case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2015 case WIDM_PREPARE: return widPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2016 case WIDM_UNPREPARE: return widUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2017 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
2018 case WIDM_GETNUMDEVS: return wodGetNumDevs (); /* same number of devices in output as in input */
2019 case WIDM_GETPOS: return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
2020 case WIDM_RESET: return widReset (wDevID);
2021 case WIDM_START: return widStart (wDevID);
2022 case WIDM_STOP: return widStop (wDevID);
2024 FIXME("unknown message %u!\n", wMsg);
2026 return MMSYSERR_NOTSUPPORTED;
2029 #else /* !HAVE_OSS */
2031 /**************************************************************************
2032 * OSS_wodMessage [sample driver]
2034 DWORD WINAPI OSS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2035 DWORD dwParam1, DWORD dwParam2)
2037 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2038 return MMSYSERR_NOTENABLED;
2041 /**************************************************************************
2042 * OSS_widMessage [sample driver]
2044 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2045 DWORD dwParam1, DWORD dwParam2)
2047 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2048 return MMSYSERR_NOTENABLED;
2051 #endif /* HAVE_OSS */