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)
7 * 2000 Eric Pouech (loops in waveOut)
11 * pause in waveOut does not work correctly
12 * implement async handling in waveIn
15 /*#define EMULATE_SB16*/
25 #include <sys/ioctl.h>
28 #include "wine/winuser16.h"
34 #include "debugtools.h"
36 DEFAULT_DEBUG_CHANNEL(wave);
38 /* Allow 1% deviation for sample rates (some ES137x cards) */
39 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
43 #define SOUND_DEV "/dev/dsp"
44 #define MIXER_DEV "/dev/mixer"
46 #define MAX_WAVEOUTDRV (1)
47 #define MAX_WAVEINDRV (1)
49 /* state diagram for waveOut writing:
51 * +---------+-------------+---------------+---------------------------------+
52 * | state | function | event | new state |
53 * +---------+-------------+---------------+---------------------------------+
54 * | | open() | | STOPPED |
55 * | PAUSED | write() | | PAUSED |
56 * | STOPPED | write() | <thrd create> | PLAYING |
57 * | PLAYING | write() | HEADER | PLAYING |
58 * | (other) | write() | <error> | |
59 * | (any) | pause() | PAUSING | PAUSED |
60 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
61 * | (any) | reset() | RESETTING | STOPPED |
62 * | (any) | close() | CLOSING | CLOSED |
63 * +---------+-------------+---------------+---------------------------------+
66 /* states of the playing device */
67 #define WINE_WS_PLAYING 0
68 #define WINE_WS_PAUSED 1
69 #define WINE_WS_STOPPED 2
70 #define WINE_WS_CLOSED 3
72 /* events to be send to device */
73 #define WINE_WM_PAUSING (WM_USER + 1)
74 #define WINE_WM_RESTARTING (WM_USER + 2)
75 #define WINE_WM_RESETTING (WM_USER + 3)
76 #define WINE_WM_CLOSING (WM_USER + 4)
77 #define WINE_WM_HEADER (WM_USER + 5)
81 volatile int state; /* one of the WINE_WS_ manifest constants */
82 DWORD dwFragmentSize; /* size of OSS buffer fragment */
83 WAVEOPENDESC waveDesc;
86 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
87 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
88 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
90 DWORD dwLastFragDone; /* time in ms, when last played fragment will be actually played */
91 DWORD dwPlayedTotal; /* number of bytes played since opening */
93 /* info on current lpQueueHdr->lpWaveHdr */
94 DWORD dwOffCurrHdr; /* offset in lpPlayPtr->lpData for fragments */
95 DWORD dwRemain; /* number of bytes to write to end the current fragment */
97 /* synchronization stuff */
107 DWORD dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */
108 WAVEOPENDESC waveDesc;
110 PCMWAVEFORMAT format;
111 LPWAVEHDR lpQueueHdr;
112 DWORD dwTotalRecorded;
116 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
117 static WINE_WAVEIN WInDev [MAX_WAVEINDRV ];
119 /*======================================================================*
120 * Low level WAVE implemantation *
121 *======================================================================*/
123 static LONG OSS_Init(void)
133 /* start with output device */
135 /* FIXME: only one device is supported */
136 memset(&WOutDev[0].caps, 0, sizeof(WOutDev[0].caps));
138 if (access(SOUND_DEV,0) != 0 ||
139 (audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0)) == -1) {
140 TRACE("Couldn't open out %s\n", SOUND_DEV);
144 ioctl(audio, SNDCTL_DSP_RESET, 0);
146 /* FIXME: some programs compare this string against the content of the registry
147 * for MM drivers. The name have to match in order the program to work
148 * (e.g. MS win9x mplayer.exe)
151 WOutDev[0].caps.wMid = 0x0002;
152 WOutDev[0].caps.wPid = 0x0104;
153 strcpy(WOutDev[0].caps.szPname, "SB16 Wave Out");
155 WOutDev[0].caps.wMid = 0x00FF; /* Manufac ID */
156 WOutDev[0].caps.wPid = 0x0001; /* Product ID */
157 /* strcpy(WOutDev[0].caps.szPname, "OpenSoundSystem WAVOUT Driver");*/
158 strcpy(WOutDev[0].caps.szPname, "CS4236/37/38");
160 WOutDev[0].caps.vDriverVersion = 0x0100;
161 WOutDev[0].caps.dwFormats = 0x00000000;
162 WOutDev[0].caps.dwSupport = WAVECAPS_VOLUME;
164 IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
165 TRACE("OSS dsp out mask=%08x\n", mask);
167 /* First bytespersampl, then stereo */
168 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
170 WOutDev[0].caps.wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
171 if (WOutDev[0].caps.wChannels > 1) WOutDev[0].caps.dwSupport |= WAVECAPS_LRVOLUME;
174 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
175 if (mask & AFMT_U8) {
176 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4M08;
177 if (WOutDev[0].caps.wChannels > 1)
178 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4S08;
180 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
181 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4M16;
182 if (WOutDev[0].caps.wChannels > 1)
183 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4S16;
187 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
188 if (mask & AFMT_U8) {
189 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2M08;
190 if (WOutDev[0].caps.wChannels > 1)
191 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2S08;
193 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
194 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2M16;
195 if (WOutDev[0].caps.wChannels > 1)
196 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_2S16;
200 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
201 if (mask & AFMT_U8) {
202 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1M08;
203 if (WOutDev[0].caps.wChannels > 1)
204 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1S08;
206 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
207 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1M16;
208 if (WOutDev[0].caps.wChannels > 1)
209 WOutDev[0].caps.dwFormats |= WAVE_FORMAT_1S16;
212 if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
213 if ((caps & DSP_CAP_REALTIME) && !(caps && DSP_CAP_BATCH))
214 WOutDev[0].caps.dwFormats |= WAVECAPS_SAMPLEACCURATE;
217 TRACE("out dwFormats = %08lX\n", WOutDev[0].caps.dwFormats);
219 /* then do input device */
223 if (access(SOUND_DEV,0) != 0 ||
224 (audio = open(SOUND_DEV, O_RDONLY|O_NDELAY, 0)) == -1) {
225 TRACE("Couldn't open in %s (%d)\n", SOUND_DEV, errno);
229 ioctl(audio, SNDCTL_DSP_RESET, 0);
232 WInDev[0].caps.wMid = 0x0002;
233 WInDev[0].caps.wPid = 0x0004;
234 strcpy(WInDev[0].caps.szPname, "SB16 Wave In");
236 WInDev[0].caps.wMid = 0x00FF; /* Manufac ID */
237 WInDev[0].caps.wPid = 0x0001; /* Product ID */
238 strcpy(WInDev[0].caps.szPname, "OpenSoundSystem WAVIN Driver");
240 WInDev[0].caps.dwFormats = 0x00000000;
241 WInDev[0].caps.wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
243 IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
244 TRACE("OSS in dsp mask=%08x\n", mask);
246 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
248 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
249 if (mask & AFMT_U8) {
250 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4M08;
251 if (WInDev[0].caps.wChannels > 1)
252 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4S08;
254 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
255 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4M16;
256 if (WInDev[0].caps.wChannels > 1)
257 WInDev[0].caps.dwFormats |= WAVE_FORMAT_4S16;
261 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
262 if (mask & AFMT_U8) {
263 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2M08;
264 if (WInDev[0].caps.wChannels > 1)
265 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2S08;
267 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
268 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2M16;
269 if (WInDev[0].caps.wChannels > 1)
270 WInDev[0].caps.dwFormats |= WAVE_FORMAT_2S16;
274 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
275 if (mask & AFMT_U8) {
276 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1M08;
277 if (WInDev[0].caps.wChannels > 1)
278 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1S08;
280 if ((mask & AFMT_S16_LE) && bytespersmpl > 1) {
281 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1M16;
282 if (WInDev[0].caps.wChannels > 1)
283 WInDev[0].caps.dwFormats |= WAVE_FORMAT_1S16;
287 TRACE("in dwFormats = %08lX\n", WInDev[0].caps.dwFormats);
292 /**************************************************************************
293 * OSS_NotifyClient [internal]
295 static DWORD OSS_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1,
298 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
304 if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
306 if (WOutDev[wDevID].wFlags != DCB_NULL &&
307 !DriverCallback(WOutDev[wDevID].waveDesc.dwCallback,
308 WOutDev[wDevID].wFlags,
309 WOutDev[wDevID].waveDesc.hWave,
311 WOutDev[wDevID].waveDesc.dwInstance,
314 WARN("can't notify client !\n");
315 return MMSYSERR_NOERROR;
322 if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
324 if (WInDev[wDevID].wFlags != DCB_NULL &&
325 !DriverCallback(WInDev[wDevID].waveDesc.dwCallback,
326 WInDev[wDevID].wFlags,
327 WInDev[wDevID].waveDesc.hWave,
329 WInDev[wDevID].waveDesc.dwInstance,
332 WARN("can't notify client !\n");
333 return MMSYSERR_NOERROR;
337 FIXME("Unknown CB message %u\n", wMsg);
343 /*======================================================================*
344 * Low level WAVE OUT implemantation *
345 *======================================================================*/
347 /**************************************************************************
348 * wodPlayer_WriteFragments [internal]
350 * wodPlayer helper. Writes as many fragments it can to unixdev.
351 * Returns TRUE in case of buffer underrun.
353 static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
361 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) {
362 ERR("ioctl failed (%d)\n", errno);
366 TRACE("Fragments %d/%d\n", info.fragments, info.fragstotal);
368 if (!info.fragments) /* output queue is full, wait a bit */
371 lpWaveHdr = wwo->lpPlayPtr;
373 if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */
374 wwo->dwLastFragDone && /* first fragment has been played */
375 info.fragments + 2 > info.fragstotal) { /* done with all waveOutWrite()' fragments */
376 /* FIXME: should do better handling here */
377 TRACE("Oooch, buffer underrun !\n");
378 return TRUE; /* force resetting of waveOut device */
380 return FALSE; /* wait a bit */
383 if (wwo->dwOffCurrHdr == 0) {
384 TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
385 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
386 if (wwo->lpLoopPtr) {
387 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
389 wwo->lpLoopPtr = lpWaveHdr;
394 lpData = lpWaveHdr->lpData;
396 /* finish current wave hdr ? */
397 if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) {
398 DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
400 /* write end of current wave hdr */
401 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
402 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
404 if (count > 0 || toWrite == 0) {
405 DWORD tc = GetTickCount();
407 if (wwo->dwLastFragDone /* + guard time ?? */ < tc)
408 wwo->dwLastFragDone = tc;
409 wwo->dwLastFragDone += (toWrite * 1000) / wwo->format.wf.nAvgBytesPerSec;
411 lpWaveHdr->reserved = wwo->dwLastFragDone;
412 TRACE("Tagging hdr %p with %08lx\n", lpWaveHdr, wwo->dwLastFragDone);
414 /* WAVEHDR written, go to next one */
415 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
416 if (--wwo->lpLoopPtr->dwLoops > 0) {
417 wwo->lpPlayPtr = wwo->lpLoopPtr;
419 /* last one played */
420 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
421 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop\n");
422 /* shall we consider the END flag for the closing loop or for
423 * the opening one or for both ???
424 * code assumes for closing loop only
426 wwo->lpLoopPtr = lpWaveHdr;
428 wwo->lpLoopPtr = NULL;
430 wwo->lpPlayPtr = lpWaveHdr->lpNext;
433 wwo->lpPlayPtr = lpWaveHdr->lpNext;
435 wwo->dwOffCurrHdr = 0;
436 if ((wwo->dwRemain -= count) == 0) {
437 wwo->dwRemain = wwo->dwFragmentSize;
440 continue; /* try to go to use next wavehdr */
442 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
443 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
445 DWORD tc = GetTickCount();
447 if (wwo->dwLastFragDone /* + guard time ?? */ < tc)
448 wwo->dwLastFragDone = tc;
449 wwo->dwLastFragDone += (wwo->dwRemain * 1000) / wwo->format.wf.nAvgBytesPerSec;
451 TRACE("Tagging frag with %08lx\n", wwo->dwLastFragDone);
453 wwo->dwOffCurrHdr += count;
454 wwo->dwRemain = wwo->dwFragmentSize;
460 /**************************************************************************
461 * wodPlayer_WriteFragments [internal]
463 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
464 * have been played (actually to speaker, not to unixdev fd).
466 static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
469 DWORD tc = GetTickCount();
471 while (wwo->lpQueuePtr &&
473 (wwo->lpQueuePtr != wwo->lpPlayPtr && wwo->lpQueuePtr != wwo->lpLoopPtr))) {
474 lpWaveHdr = wwo->lpQueuePtr;
476 if (lpWaveHdr->reserved > tc && !force) break;
478 wwo->dwPlayedTotal += lpWaveHdr->dwBufferLength;
479 wwo->lpQueuePtr = lpWaveHdr->lpNext;
481 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
482 lpWaveHdr->dwFlags |= WHDR_DONE;
484 TRACE("Notifying client with %p\n", lpWaveHdr);
485 if (OSS_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) {
486 WARN("can't notify client !\n");
491 /**************************************************************************
492 * wodPlayer_Reset [internal]
494 * wodPlayer helper. Resets current output stream.
496 static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
498 /* updates current notify list */
499 wodPlayer_Notify(wwo, uDevID, FALSE);
501 /* flush all possible output */
502 if (ioctl(wwo->unixdev, SNDCTL_DSP_RESET, 0) == -1) {
503 perror("ioctl SNDCTL_DSP_RESET");
505 wwo->state = WINE_WS_STOPPED;
509 wwo->dwOffCurrHdr = 0;
510 wwo->dwRemain = wwo->dwFragmentSize;
512 /* empty notify list */
513 wodPlayer_Notify(wwo, uDevID, TRUE);
515 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
516 wwo->state = WINE_WS_STOPPED;
517 wwo->dwPlayedTotal = 0;
519 /* FIXME: this is not accurate when looping, but can be do better ? */
520 wwo->lpPlayPtr = (wwo->lpLoopPtr) ? wwo->lpLoopPtr : wwo->lpQueuePtr;
521 wwo->state = WINE_WS_PAUSED;
525 /**************************************************************************
526 * wodPlayer [internal]
528 static DWORD CALLBACK wodPlayer(LPVOID pmt)
530 WORD uDevID = (DWORD)pmt;
531 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
536 PeekMessageA(&msg, 0, 0, 0, 0);
537 wwo->state = WINE_WS_STOPPED;
539 wwo->dwLastFragDone = 0;
540 wwo->dwOffCurrHdr = 0;
541 wwo->dwRemain = wwo->dwFragmentSize;
542 wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL;
543 wwo->dwPlayedTotal = 0;
545 TRACE("imhere[0]\n");
546 SetEvent(wwo->hEvent);
548 /* make sleep time to be # of ms to output a fragment */
549 dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
552 /* wait for dwSleepTime or an event in thread's queue */
553 /* FIXME: could improve wait time depending on queue state,
554 * ie, number of queued fragments
556 TRACE("imhere[1]\n");
557 MsgWaitForMultipleObjects(0, NULL, FALSE,
558 (wwo->state == WINE_WS_PLAYING) ?
559 2 * dwSleepTime : /*INFINITE*/100,
561 TRACE("imhere[2] (q=%p p=%p)\n", wwo->lpQueuePtr, wwo->lpPlayPtr);
562 wodPlayer_Notify(wwo, uDevID, FALSE);
563 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
564 switch (msg.message) {
565 case WINE_WM_PAUSING:
566 wodPlayer_Reset(wwo, uDevID, FALSE);
567 wwo->state = WINE_WS_PAUSED;
568 SetEvent(wwo->hEvent);
570 case WINE_WM_RESTARTING:
571 wwo->state = WINE_WS_PLAYING;
572 SetEvent(wwo->hEvent);
575 lpWaveHdr = (LPWAVEHDR)msg.lParam;
577 /* insert buffer at the end of queue */
580 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
583 if (!wwo->lpPlayPtr) wwo->lpPlayPtr = lpWaveHdr;
584 if (wwo->state == WINE_WS_STOPPED)
585 wwo->state = WINE_WS_PLAYING;
587 case WINE_WM_RESETTING:
588 wodPlayer_Reset(wwo, uDevID, TRUE);
589 SetEvent(wwo->hEvent);
591 case WINE_WM_CLOSING:
592 /* sanity check: this should not happen since the device must have been reset before */
593 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
595 wwo->state = WINE_WS_CLOSED;
596 SetEvent(wwo->hEvent);
598 /* shouldn't go here */
600 FIXME("unknown message %d\n", msg.message);
604 if (wwo->state == WINE_WS_PLAYING) {
605 wodPlayer_WriteFragments(wwo);
607 wodPlayer_Notify(wwo, uDevID, FALSE);
610 /* just for not generating compilation warnings... should never be executed */
614 /**************************************************************************
615 * wodGetDevCaps [internal]
617 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
619 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
621 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
623 if (wDevID >= MAX_WAVEOUTDRV) {
624 TRACE("MAX_WAVOUTDRV reached !\n");
625 return MMSYSERR_BADDEVICEID;
628 memcpy(lpCaps, &WOutDev[0].caps, min(dwSize, sizeof(*lpCaps)));
629 return MMSYSERR_NOERROR;
632 /**************************************************************************
635 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
645 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
646 if (lpDesc == NULL) {
647 WARN("Invalid Parameter !\n");
648 return MMSYSERR_INVALPARAM;
650 if (wDevID >= MAX_WAVEOUTDRV) {
651 TRACE("MAX_WAVOUTDRV reached !\n");
652 return MMSYSERR_BADDEVICEID;
654 wodGetDevCaps(wDevID, &woc, sizeof(woc));
656 /* only PCM format is supported so far... */
657 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
658 lpDesc->lpFormat->nChannels == 0 ||
659 lpDesc->lpFormat->nSamplesPerSec == 0) {
660 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
661 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
662 lpDesc->lpFormat->nSamplesPerSec);
663 return WAVERR_BADFORMAT;
666 if (dwFlags & WAVE_FORMAT_QUERY) {
667 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
668 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
669 lpDesc->lpFormat->nSamplesPerSec);
670 return MMSYSERR_NOERROR;
673 WOutDev[wDevID].unixdev = 0;
674 if (access(SOUND_DEV, 0) != 0)
675 return MMSYSERR_NOTENABLED;
676 audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0);
678 WARN("can't open (%d)!\n", errno);
679 return MMSYSERR_ALLOCATED;
682 WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
684 memcpy(&WOutDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
685 memcpy(&WOutDev[wDevID].format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
687 if (WOutDev[wDevID].format.wBitsPerSample == 0) {
688 WOutDev[wDevID].format.wBitsPerSample = 8 *
689 (WOutDev[wDevID].format.wf.nAvgBytesPerSec /
690 WOutDev[wDevID].format.wf.nSamplesPerSec) /
691 WOutDev[wDevID].format.wf.nChannels;
694 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
695 * thus leading to 46ms per fragment, and a turnaround time of 185ms
697 /* 2^10=1024 bytes per fragment, 16 fragments max */
698 audio_fragment = 0x000F000A;
699 sample_rate = WOutDev[wDevID].format.wf.nSamplesPerSec;
700 dsp_stereo = (WOutDev[wDevID].format.wf.nChannels > 1) ? 1 : 0;
701 format = (WOutDev[wDevID].format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8;
703 IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
704 /* First size and stereo then samplerate */
705 IOCTL(audio, SNDCTL_DSP_SETFMT, format);
706 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
707 IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
709 /* paranoid checks */
710 if (format != ((WOutDev[wDevID].format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8))
711 ERR("Can't set format to %d (%d)\n",
712 (WOutDev[wDevID].format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8, format);
713 if (dsp_stereo != (WOutDev[wDevID].format.wf.nChannels > 1) ? 1 : 0)
714 ERR("Can't set stereo to %u (%d)\n",
715 (WOutDev[wDevID].format.wf.nChannels > 1) ? 1 : 0, dsp_stereo);
716 if (!NEAR_MATCH(sample_rate,WOutDev[wDevID].format.wf.nSamplesPerSec))
717 ERR("Can't set sample_rate to %lu (%d)\n",
718 WOutDev[wDevID].format.wf.nSamplesPerSec, sample_rate);
720 /* even if we set fragment size above, read it again, just in case */
721 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
723 WOutDev[wDevID].unixdev = audio;
724 WOutDev[wDevID].dwFragmentSize = fragment_size;
726 WOutDev[wDevID].hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
727 WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(WOutDev[wDevID].dwThreadID));
728 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
730 TRACE("fd=%d fragmentSize=%ld\n",
731 WOutDev[wDevID].unixdev, WOutDev[wDevID].dwFragmentSize);
732 if (WOutDev[wDevID].dwFragmentSize % WOutDev[wDevID].format.wf.nBlockAlign)
733 ERR("Fragment doesn't contain an integral number of data blocks\n");
735 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
736 WOutDev[wDevID].format.wBitsPerSample, WOutDev[wDevID].format.wf.nAvgBytesPerSec,
737 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels,
738 WOutDev[wDevID].format.wf.nBlockAlign);
740 if (OSS_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
741 WARN("can't notify client !\n");
742 return MMSYSERR_INVALPARAM;
744 return MMSYSERR_NOERROR;
747 /**************************************************************************
748 * wodClose [internal]
750 static DWORD wodClose(WORD wDevID)
752 DWORD ret = MMSYSERR_NOERROR;
754 TRACE("(%u);\n", wDevID);
756 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
757 WARN("bad device ID !\n");
758 return MMSYSERR_BADDEVICEID;
761 if (WOutDev[wDevID].lpQueuePtr) {
762 WARN("buffers still playing !\n");
763 ret = WAVERR_STILLPLAYING;
765 TRACE("imhere[3-close]\n");
766 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_CLOSING, 0, 0);
767 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
768 CloseHandle(WOutDev[wDevID].hEvent);
770 close(WOutDev[wDevID].unixdev);
771 WOutDev[wDevID].unixdev = 0;
772 WOutDev[wDevID].dwFragmentSize = 0;
773 if (OSS_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
774 WARN("can't notify client !\n");
775 ret = MMSYSERR_INVALPARAM;
781 /**************************************************************************
782 * wodWrite [internal]
785 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
787 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
789 /* first, do the sanity checks... */
790 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
791 WARN("bad dev ID !\n");
792 return MMSYSERR_BADDEVICEID;
795 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
796 return WAVERR_UNPREPARED;
798 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
799 return WAVERR_STILLPLAYING;
801 lpWaveHdr->dwFlags &= ~WHDR_DONE;
802 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
803 lpWaveHdr->lpNext = 0;
805 TRACE("imhere[3-HEADER]\n");
806 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
808 return MMSYSERR_NOERROR;
811 /**************************************************************************
812 * wodPrepare [internal]
814 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
816 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
818 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
819 WARN("bad device ID !\n");
820 return MMSYSERR_BADDEVICEID;
823 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
824 return WAVERR_STILLPLAYING;
826 lpWaveHdr->dwFlags |= WHDR_PREPARED;
827 lpWaveHdr->dwFlags &= ~WHDR_DONE;
828 return MMSYSERR_NOERROR;
831 /**************************************************************************
832 * wodUnprepare [internal]
834 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
836 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
838 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
839 WARN("bad device ID !\n");
840 return MMSYSERR_BADDEVICEID;
843 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
844 return WAVERR_STILLPLAYING;
846 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
847 lpWaveHdr->dwFlags |= WHDR_DONE;
849 return MMSYSERR_NOERROR;
852 /**************************************************************************
853 * wodPause [internal]
855 static DWORD wodPause(WORD wDevID)
857 TRACE("(%u);!\n", wDevID);
859 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
860 WARN("bad device ID !\n");
861 return MMSYSERR_BADDEVICEID;
864 TRACE("imhere[3-PAUSING]\n");
865 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_PAUSING, 0, 0);
866 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
868 return MMSYSERR_NOERROR;
871 /**************************************************************************
872 * wodRestart [internal]
874 static DWORD wodRestart(WORD wDevID)
876 TRACE("(%u);\n", wDevID);
878 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
879 WARN("bad device ID !\n");
880 return MMSYSERR_BADDEVICEID;
883 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
884 TRACE("imhere[3-RESTARTING]\n");
885 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
886 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
889 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
890 /* FIXME: Myst crashes with this ... hmm -MM
891 if (OSS_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
892 WARN("can't notify client !\n");
893 return MMSYSERR_INVALPARAM;
897 return MMSYSERR_NOERROR;
900 /**************************************************************************
901 * wodReset [internal]
903 static DWORD wodReset(WORD wDevID)
905 TRACE("(%u);\n", wDevID);
907 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
908 WARN("bad device ID !\n");
909 return MMSYSERR_BADDEVICEID;
912 TRACE("imhere[3-RESET]\n");
913 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
914 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
916 return MMSYSERR_NOERROR;
920 /**************************************************************************
921 * wodGetPosition [internal]
923 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
928 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
930 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
931 WARN("bad device ID !\n");
932 return MMSYSERR_BADDEVICEID;
935 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
937 val = WOutDev[wDevID].dwPlayedTotal;
939 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
940 lpTime->wType, WOutDev[wDevID].format.wBitsPerSample,
941 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels,
942 WOutDev[wDevID].format.wf.nAvgBytesPerSec);
943 TRACE("dwTotalPlayed=%lu\n", val);
945 switch (lpTime->wType) {
948 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
951 lpTime->u.sample = val * 8 / WOutDev[wDevID].format.wBitsPerSample;
952 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
955 time = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
956 lpTime->u.smpte.hour = time / 108000;
957 time -= lpTime->u.smpte.hour * 108000;
958 lpTime->u.smpte.min = time / 1800;
959 time -= lpTime->u.smpte.min * 1800;
960 lpTime->u.smpte.sec = time / 30;
961 time -= lpTime->u.smpte.sec * 30;
962 lpTime->u.smpte.frame = time;
963 lpTime->u.smpte.fps = 30;
964 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
965 lpTime->u.smpte.hour, lpTime->u.smpte.min,
966 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
969 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
970 lpTime->wType = TIME_MS;
972 lpTime->u.ms = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
973 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
976 return MMSYSERR_NOERROR;
979 /**************************************************************************
980 * wodGetVolume [internal]
982 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
988 TRACE("(%u, %p);\n", wDevID, lpdwVol);
991 return MMSYSERR_NOTENABLED;
992 if ((mixer = open(MIXER_DEV, O_RDONLY|O_NDELAY)) < 0) {
993 WARN("mixer device not available !\n");
994 return MMSYSERR_NOTENABLED;
996 if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
997 WARN("unable read mixer !\n");
998 return MMSYSERR_NOTENABLED;
1001 left = LOBYTE(volume);
1002 right = HIBYTE(volume);
1003 TRACE("left=%ld right=%ld !\n", left, right);
1004 *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
1005 return MMSYSERR_NOERROR;
1009 /**************************************************************************
1010 * wodSetVolume [internal]
1012 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1018 TRACE("(%u, %08lX);\n", wDevID, dwParam);
1020 left = (LOWORD(dwParam) * 100) / 0xFFFFl;
1021 right = (HIWORD(dwParam) * 100) / 0xFFFFl;
1022 volume = left + (right << 8);
1024 if ((mixer = open(MIXER_DEV, O_WRONLY|O_NDELAY)) < 0) {
1025 WARN("mixer device not available !\n");
1026 return MMSYSERR_NOTENABLED;
1028 if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
1029 WARN("unable set mixer !\n");
1030 return MMSYSERR_NOTENABLED;
1033 return MMSYSERR_NOERROR;
1036 /**************************************************************************
1037 * wodGetNumDevs [internal]
1039 static DWORD wodGetNumDevs(void)
1043 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
1044 int audio = open(SOUND_DEV, O_WRONLY|O_NDELAY, 0);
1055 /**************************************************************************
1056 * OSS_wodMessage [sample driver]
1058 DWORD WINAPI OSS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1059 DWORD dwParam1, DWORD dwParam2)
1061 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1062 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1069 /* FIXME: Pretend this is supported */
1071 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1072 case WODM_CLOSE: return wodClose (wDevID);
1073 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1074 case WODM_PAUSE: return wodPause (wDevID);
1075 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
1076 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
1077 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1078 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1079 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2);
1080 case WODM_GETNUMDEVS: return wodGetNumDevs ();
1081 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
1082 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
1083 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1084 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1085 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
1086 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
1087 case WODM_RESTART: return wodRestart (wDevID);
1088 case WODM_RESET: return wodReset (wDevID);
1090 FIXME("unknown message %d!\n", wMsg);
1092 return MMSYSERR_NOTSUPPORTED;
1095 /*======================================================================*
1096 * Low level WAVE IN implemantation *
1097 *======================================================================*/
1099 /**************************************************************************
1100 * widGetDevCaps [internal]
1102 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
1104 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1106 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1108 if (wDevID >= MAX_WAVEINDRV) {
1109 TRACE("MAX_WAVINDRV reached !\n");
1110 return MMSYSERR_BADDEVICEID;
1113 memcpy(lpCaps, &WInDev[0].caps, min(dwSize, sizeof(*lpCaps)));
1114 return MMSYSERR_NOERROR;
1117 /**************************************************************************
1118 * widOpen [internal]
1120 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1122 int audio, abuf_size, smplrate, samplesize, dsp_stereo;
1123 LPWAVEFORMAT lpFormat;
1125 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1126 if (lpDesc == NULL) {
1127 WARN("Invalid Parameter !\n");
1128 return MMSYSERR_INVALPARAM;
1130 if (wDevID >= MAX_WAVEINDRV) {
1131 TRACE("MAX_WAVINDRV reached !\n");
1132 return MMSYSERR_ALLOCATED;
1135 /* only PCM format is supported so far... */
1136 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1137 lpDesc->lpFormat->nChannels == 0 ||
1138 lpDesc->lpFormat->nSamplesPerSec == 0) {
1139 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1140 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1141 lpDesc->lpFormat->nSamplesPerSec);
1142 return WAVERR_BADFORMAT;
1145 if (dwFlags & WAVE_FORMAT_QUERY) {
1146 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1147 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1148 lpDesc->lpFormat->nSamplesPerSec);
1149 return MMSYSERR_NOERROR;
1152 WInDev[wDevID].unixdev = 0;
1153 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1154 audio = open(SOUND_DEV, O_RDONLY|O_NDELAY, 0);
1156 WARN("can't open (%d)!\n", errno);
1157 return MMSYSERR_ALLOCATED;
1159 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
1160 if (abuf_size < 1024 || abuf_size > 65536) {
1161 if (abuf_size == -1)
1163 WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1164 return MMSYSERR_NOTENABLED;
1166 WARN("SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize %d!\n",abuf_size);
1168 WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1170 if (WInDev[wDevID].lpQueueHdr) {
1171 HeapFree(GetProcessHeap(), 0, WInDev[wDevID].lpQueueHdr);
1172 WInDev[wDevID].lpQueueHdr = NULL;
1174 WInDev[wDevID].unixdev = audio;
1175 WInDev[wDevID].dwFragmentSize = abuf_size;
1176 WInDev[wDevID].dwTotalRecorded = 0;
1177 memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1178 lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat;
1180 memcpy(&WInDev[wDevID].format, lpFormat, sizeof(PCMWAVEFORMAT));
1181 WInDev[wDevID].format.wBitsPerSample = 8; /* <-------------- */
1182 if (WInDev[wDevID].format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1183 if (WInDev[wDevID].format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1184 if (WInDev[wDevID].format.wBitsPerSample == 0) {
1185 WInDev[wDevID].format.wBitsPerSample = 8 *
1186 (WInDev[wDevID].format.wf.nAvgBytesPerSec /
1187 WInDev[wDevID].format.wf.nSamplesPerSec) /
1188 WInDev[wDevID].format.wf.nChannels;
1190 samplesize = WInDev[wDevID].format.wBitsPerSample;
1191 smplrate = WInDev[wDevID].format.wf.nSamplesPerSec;
1192 dsp_stereo = (WInDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
1193 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1194 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1195 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1196 TRACE("wBitsPerSample=%u !\n", WInDev[wDevID].format.wBitsPerSample);
1197 TRACE("nSamplesPerSec=%lu !\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1198 TRACE("nChannels=%u !\n", WInDev[wDevID].format.wf.nChannels);
1199 TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1200 if (OSS_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1201 WARN("can't notify client !\n");
1202 return MMSYSERR_INVALPARAM;
1204 return MMSYSERR_NOERROR;
1207 /**************************************************************************
1208 * widClose [internal]
1210 static DWORD widClose(WORD wDevID)
1212 TRACE("(%u);\n", wDevID);
1213 if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
1214 if (WInDev[wDevID].unixdev == 0) {
1215 WARN("can't close !\n");
1216 return MMSYSERR_NOTENABLED;
1218 if (WInDev[wDevID].lpQueueHdr != NULL) {
1219 WARN("still buffers open !\n");
1220 return WAVERR_STILLPLAYING;
1222 close(WInDev[wDevID].unixdev);
1223 WInDev[wDevID].unixdev = 0;
1224 WInDev[wDevID].dwFragmentSize = 0;
1225 if (OSS_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1226 WARN("can't notify client !\n");
1227 return MMSYSERR_INVALPARAM;
1229 return MMSYSERR_NOERROR;
1232 /**************************************************************************
1233 * widAddBuffer [internal]
1235 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1240 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1241 if (WInDev[wDevID].unixdev == 0) {
1242 WARN("can't do it !\n");
1243 return MMSYSERR_NOTENABLED;
1245 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1246 TRACE("never been prepared !\n");
1247 return WAVERR_UNPREPARED;
1249 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1250 TRACE("header already in use !\n");
1251 return WAVERR_STILLPLAYING;
1253 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1254 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1255 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1256 lpWaveHdr->dwBytesRecorded = 0;
1257 lpWaveHdr->lpNext = NULL;
1258 if (WInDev[wDevID].lpQueueHdr == NULL) {
1259 WInDev[wDevID].lpQueueHdr = lpWaveHdr;
1261 lpWIHdr = WInDev[wDevID].lpQueueHdr;
1262 while (lpWIHdr->lpNext != NULL) {
1263 lpWIHdr = lpWIHdr->lpNext;
1266 lpWIHdr->lpNext = lpWaveHdr;
1269 TRACE("buffer added ! (now %u in queue)\n", count);
1270 return MMSYSERR_NOERROR;
1273 /**************************************************************************
1274 * widPrepare [internal]
1276 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1278 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1279 if (WInDev[wDevID].unixdev == 0) {
1280 WARN("can't prepare !\n");
1281 return MMSYSERR_NOTENABLED;
1283 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1284 return WAVERR_STILLPLAYING;
1285 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1286 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1287 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1288 lpWaveHdr->dwBytesRecorded = 0;
1289 TRACE("header prepared !\n");
1290 return MMSYSERR_NOERROR;
1293 /**************************************************************************
1294 * widUnprepare [internal]
1296 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1298 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1299 if (WInDev[wDevID].unixdev == 0) {
1300 WARN("can't unprepare !\n");
1301 return MMSYSERR_NOTENABLED;
1303 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1304 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1305 lpWaveHdr->dwFlags |= WHDR_DONE;
1307 return MMSYSERR_NOERROR;
1310 /**************************************************************************
1311 * widStart [internal]
1313 static DWORD widStart(WORD wDevID)
1317 LPWAVEHDR *lpWaveHdr;
1320 TRACE("(%u);\n", wDevID);
1321 if (WInDev[wDevID].unixdev == 0) {
1322 WARN("can't start recording !\n");
1323 return MMSYSERR_NOTENABLED;
1326 lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
1327 TRACE("lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
1329 if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
1330 TRACE("never been prepared !\n");
1331 return WAVERR_UNPREPARED;
1334 while (*lpWaveHdr != NULL) {
1335 lpData = (*lpWaveHdr)->lpData;
1336 TRACE("recording buf#%u=%p size=%lu \n",
1337 count, lpData, (*lpWaveHdr)->dwBufferLength);
1339 bytesRead = read(WInDev[wDevID].unixdev, lpData, (*lpWaveHdr)->dwBufferLength);
1341 if (bytesRead == -1)
1342 perror("read from audio device");
1344 TRACE("bytesread=%d (%ld)\n", bytesRead, (*lpWaveHdr)->dwBufferLength);
1345 (*lpWaveHdr)->dwBytesRecorded = bytesRead;
1346 WInDev[wDevID].dwTotalRecorded += (*lpWaveHdr)->dwBytesRecorded;
1347 (*lpWaveHdr)->dwFlags &= ~WHDR_INQUEUE;
1348 (*lpWaveHdr)->dwFlags |= WHDR_DONE;
1350 if (OSS_NotifyClient(wDevID, WIM_DATA, (DWORD)*lpWaveHdr, (*lpWaveHdr)->dwBytesRecorded) != MMSYSERR_NOERROR) {
1351 WARN("can't notify client !\n");
1352 return MMSYSERR_INVALPARAM;
1355 /* removes the current block from the queue */
1356 *lpWaveHdr = (*lpWaveHdr)->lpNext;
1359 TRACE("end of recording !\n");
1360 return MMSYSERR_NOERROR;
1363 /**************************************************************************
1364 * widStop [internal]
1366 static DWORD widStop(WORD wDevID)
1368 TRACE("(%u);\n", wDevID);
1369 if (WInDev[wDevID].unixdev == 0) {
1370 WARN("can't stop !\n");
1371 return MMSYSERR_NOTENABLED;
1373 return MMSYSERR_NOERROR;
1376 /**************************************************************************
1377 * widReset [internal]
1379 static DWORD widReset(WORD wDevID)
1381 TRACE("(%u);\n", wDevID);
1382 if (WInDev[wDevID].unixdev == 0) {
1383 WARN("can't reset !\n");
1384 return MMSYSERR_NOTENABLED;
1386 return MMSYSERR_NOERROR;
1389 /**************************************************************************
1390 * widGetPosition [internal]
1392 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1396 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1397 if (WInDev[wDevID].unixdev == 0) {
1398 WARN("can't get pos !\n");
1399 return MMSYSERR_NOTENABLED;
1401 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1402 TRACE("wType=%04X !\n", lpTime->wType);
1403 TRACE("wBitsPerSample=%u\n", WInDev[wDevID].format.wBitsPerSample);
1404 TRACE("nSamplesPerSec=%lu\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1405 TRACE("nChannels=%u\n", WInDev[wDevID].format.wf.nChannels);
1406 TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1407 switch (lpTime->wType) {
1409 lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
1410 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1413 lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
1414 WInDev[wDevID].format.wBitsPerSample;
1415 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1418 time = WInDev[wDevID].dwTotalRecorded /
1419 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1420 lpTime->u.smpte.hour = time / 108000;
1421 time -= lpTime->u.smpte.hour * 108000;
1422 lpTime->u.smpte.min = time / 1800;
1423 time -= lpTime->u.smpte.min * 1800;
1424 lpTime->u.smpte.sec = time / 30;
1425 time -= lpTime->u.smpte.sec * 30;
1426 lpTime->u.smpte.frame = time;
1427 lpTime->u.smpte.fps = 30;
1428 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1429 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1430 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1433 lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1434 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1435 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1438 FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1439 lpTime->wType = TIME_MS;
1441 return MMSYSERR_NOERROR;
1444 /**************************************************************************
1445 * OSS_widMessage [sample driver]
1447 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1448 DWORD dwParam1, DWORD dwParam2)
1450 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1451 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1454 case DRVM_INIT: return OSS_Init();
1458 /* FIXME: Pretend this is supported */
1460 case WIDM_OPEN: return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1461 case WIDM_CLOSE: return widClose(wDevID);
1462 case WIDM_ADDBUFFER: return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1463 case WIDM_PREPARE: return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1464 case WIDM_UNPREPARE: return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1465 case WIDM_GETDEVCAPS: return widGetDevCaps(wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
1466 case WIDM_GETNUMDEVS: return wodGetNumDevs(); /* same number of devices in output as in input */
1467 case WIDM_GETPOS: return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
1468 case WIDM_RESET: return widReset(wDevID);
1469 case WIDM_START: return widStart(wDevID);
1470 case WIDM_STOP: return widStop(wDevID);
1472 FIXME("unknown message %u!\n", wMsg);
1474 return MMSYSERR_NOTSUPPORTED;
1477 #else /* !HAVE_OSS */
1479 /**************************************************************************
1480 * OSS_wodMessage [sample driver]
1482 DWORD WINAPI OSS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1483 DWORD dwParam1, DWORD dwParam2)
1485 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1486 return MMSYSERR_NOTENABLED;
1489 /**************************************************************************
1490 * OSS_widMessage [sample driver]
1492 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1493 DWORD dwParam1, DWORD dwParam2)
1495 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1496 return MMSYSERR_NOTENABLED;
1499 #endif /* HAVE_OSS */