1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
5 * Copyright 1994 Martin Ayotte
6 * 1999 Eric Pouech (async playing in waveOut)
10 * pause in waveOut does not work correctly
11 * implement async handling in waveIn
14 /*#define EMULATE_SB16*/
24 #include <sys/ioctl.h>
27 #include "wine/winuser16.h"
33 #include "debugtools.h"
35 DEFAULT_DEBUG_CHANNEL(wave);
37 /* Allow 1% deviation for sample rates (some ES137x cards) */
38 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
42 #define SOUND_DEV "/dev/dsp"
43 #define MIXER_DEV "/dev/mixer"
45 #define MAX_WAVEOUTDRV (1)
46 #define MAX_WAVEINDRV (1)
48 /* state diagram for waveOut writing:
50 * +---------+-------------+---------------+---------------------------------+
51 * | state | function | event | new state |
52 * +---------+-------------+---------------+---------------------------------+
53 * | | open() | | STOPPED |
54 * | PAUSED | write() | | PAUSED |
55 * | STOPPED | write() | <thrd create> | PLAYING |
56 * | PLAYING | write() | HEADER | PLAYING |
57 * | (other) | write() | <error> | |
58 * | (any) | pause() | PAUSING | PAUSED |
59 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
60 * | (any) | reset() | RESETTING | STOPPED |
61 * | (any) | close() | CLOSING | CLOSED |
62 * +---------+-------------+---------------+---------------------------------+
65 /* states of the playing device */
66 #define WINE_WS_PLAYING 0
67 #define WINE_WS_PAUSED 1
68 #define WINE_WS_STOPPED 2
69 #define WINE_WS_CLOSED 3
71 /* events to be send to device */
72 #define WINE_WM_PAUSING (WM_USER + 1)
73 #define WINE_WM_RESTARTING (WM_USER + 2)
74 #define WINE_WM_RESETTING (WM_USER + 3)
75 #define WINE_WM_CLOSING (WM_USER + 4)
76 #define WINE_WM_HEADER (WM_USER + 5)
80 volatile int state; /* one of the WINE_WS_ manifest constants */
81 DWORD dwFragmentSize; /* size of OSS buffer fragment */
82 WAVEOPENDESC waveDesc;
85 LPWAVEHDR lpQueueHdr; /* pending buffers for playing */
86 LPWAVEHDR lpNotifyHdr; /* list of wavehdr for which write() has been called, pending for notification */
88 DWORD dwPlayedTotal; /* number of bytes played since opening */
89 DWORD dwPlayed; /* number of bytes played since last DSP_RESET */
90 DWORD dwNotifiedBytes; /* number of bytes for which wavehdr notification has been done */
92 /* info on current lpQueueHdr->lpWaveHdr */
93 DWORD dwOffCurrHdr; /* offset in lpQueueHdr->lpWaveHdr->lpData for fragments */
94 DWORD dwRemain; /* number of bytes to write to end the current fragment */
96 /* synchronization stuff */
101 WORD wMaxFragments; /* max number of fragments that can be written to dsp */
102 WORD wFragsUsedInQueue; /* current number of used fragments inside dsp queue */
108 DWORD dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */
109 WAVEOPENDESC waveDesc;
111 PCMWAVEFORMAT format;
112 LPWAVEHDR lpQueueHdr;
113 DWORD dwTotalRecorded;
116 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
117 static WINE_WAVEIN WInDev [MAX_WAVEOUTDRV];
119 /*======================================================================*
120 * Low level WAVE implemantation *
121 *======================================================================*/
123 /**************************************************************************
124 * WAVE_NotifyClient [internal]
126 static DWORD WAVE_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1,
129 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
135 if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
137 if (WOutDev[wDevID].wFlags != DCB_NULL &&
138 !DriverCallback(WOutDev[wDevID].waveDesc.dwCallback,
139 WOutDev[wDevID].wFlags,
140 WOutDev[wDevID].waveDesc.hWave,
142 WOutDev[wDevID].waveDesc.dwInstance,
145 WARN("can't notify client !\n");
146 return MMSYSERR_NOERROR;
153 if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
155 if (WInDev[wDevID].wFlags != DCB_NULL &&
156 !DriverCallback(WInDev[wDevID].waveDesc.dwCallback,
157 WInDev[wDevID].wFlags,
158 WInDev[wDevID].waveDesc.hWave,
160 WInDev[wDevID].waveDesc.dwInstance,
163 WARN("can't notify client !\n");
164 return MMSYSERR_NOERROR;
168 FIXME("Unknown CB message %u\n", wMsg);
174 /*======================================================================*
175 * Low level WAVE OUT implemantation *
176 *======================================================================*/
178 /**************************************************************************
179 * wodPlayer_WriteFragments [internal]
181 * wodPlayer helper. Writes as many fragments it can to unixdev.
182 * Returns TRUE in case of buffer underrun.
184 static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
191 TRACE("Fragments: %d used on fd %d\n", wwo->wFragsUsedInQueue, wwo->unixdev);
192 if (wwo->wFragsUsedInQueue == wwo->wMaxFragments) /* output queue is full, wait a bit */
195 lpWaveHdr = wwo->lpQueueHdr;
197 if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */
198 wwo->dwNotifiedBytes >= wwo->dwFragmentSize && /* first fragment has been played */
199 wwo->wFragsUsedInQueue < 2) { /* done with all waveOutWrite()' fragments */
200 /* FIXME: should do better handling here */
201 TRACE("Oooch, buffer underrun !\n");
202 return TRUE; /* force resetting of waveOut device */
204 return FALSE; /* wait a bit */
207 if (wwo->dwOffCurrHdr == 0) {
208 TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
209 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
210 FIXME("NIY: loops (%lu) in wavehdr\n", lpWaveHdr->dwLoops);
213 lpData = lpWaveHdr->lpData;
215 /* finish current wave hdr ? */
216 if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) {
217 DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
219 /* write end of current wave hdr */
220 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
221 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
223 if (count > 0 || toWrite == 0) {
226 /* move lpWaveHdr to the end of notify list */
227 for (wh = &(wwo->lpNotifyHdr); *wh; wh = &((*wh)->lpNext));
230 wwo->lpQueueHdr = lpWaveHdr->lpNext;
231 lpWaveHdr->lpNext = 0;
233 wwo->dwOffCurrHdr = 0;
234 if ((wwo->dwRemain -= count) == 0) {
235 wwo->dwRemain = wwo->dwFragmentSize;
236 wwo->wFragsUsedInQueue++;
239 continue; /* try to go to use next wavehdr */
241 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
242 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
244 wwo->dwOffCurrHdr += count;
245 wwo->dwRemain = wwo->dwFragmentSize;
246 wwo->wFragsUsedInQueue++;
252 /**************************************************************************
253 * wodPlayer_WriteFragments [internal]
255 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
256 * have been played (actually to speaker, not to unixdev fd).
258 static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
263 /* get effective number of written bytes */
267 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOPTR, &cinfo) == -1) {
268 perror("ioctl SNDCTL_DSP_GETOPTR");
270 wwo->state = WINE_WS_STOPPED;
273 TRACE("Played %d bytes (played=%ld) on fd %d\n", cinfo.bytes, wwo->dwPlayed, wwo->unixdev);
274 c = cinfo.bytes / wwo->dwFragmentSize - wwo->dwPlayed / wwo->dwFragmentSize;
275 if (wwo->wFragsUsedInQueue > c)
276 wwo->wFragsUsedInQueue -= c;
278 wwo->wFragsUsedInQueue = 0;
279 wwo->dwPlayed = cinfo.bytes;
281 if (force || cinfo.bytes > wwo->dwNotifiedBytes) {
282 /* remove all wavehdr which can be notified */
283 while (wwo->lpNotifyHdr &&
284 (force || (cinfo.bytes >= wwo->dwNotifiedBytes + wwo->lpNotifyHdr->dwBufferLength))) {
285 lpWaveHdr = wwo->lpNotifyHdr;
287 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
288 lpWaveHdr->dwFlags |= WHDR_DONE;
290 wwo->dwNotifiedBytes += lpWaveHdr->dwBufferLength;
291 wwo->lpNotifyHdr = lpWaveHdr->lpNext;
293 TRACE("Notifying client with %p\n", lpWaveHdr);
294 if (WAVE_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) {
295 WARN("can't notify client !\n");
301 /**************************************************************************
302 * wodPlayer_Reset [internal]
304 * wodPlayer helper. Resets current output stream.
306 static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
310 /* updates current notify list */
311 wodPlayer_Notify(wwo, uDevID, FALSE);
313 /* flush all possible output */
314 if (ioctl(wwo->unixdev, SNDCTL_DSP_RESET, 0) == -1) {
315 perror("ioctl SNDCTL_DSP_RESET");
317 wwo->state = WINE_WS_STOPPED;
321 wwo->dwOffCurrHdr = 0;
323 /* empty notify list */
324 wodPlayer_Notify(wwo, uDevID, TRUE);
325 if (wwo->lpNotifyHdr) {
326 ERR("out of sync\n");
328 /* get rid also of all the current queue */
329 for (lpWaveHdr = wwo->lpQueueHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
330 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
331 lpWaveHdr->dwFlags |= WHDR_DONE;
333 if (WAVE_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) {
334 WARN("can't notify client !\n");
338 wwo->state = WINE_WS_STOPPED;
339 wwo->dwPlayedTotal = 0;
341 /* move notify list to begining of lpQueueHdr list */
342 while (wwo->lpNotifyHdr) {
343 lpWaveHdr = wwo->lpNotifyHdr;
344 wwo->lpNotifyHdr = lpWaveHdr->lpNext;
345 lpWaveHdr->lpNext = wwo->lpQueueHdr;
346 wwo->lpQueueHdr = lpWaveHdr;
348 wwo->state = WINE_WS_PAUSED;
349 wwo->dwPlayedTotal += wwo->dwPlayed;
351 wwo->dwNotifiedBytes = wwo->dwPlayed = 0;
352 wwo->wFragsUsedInQueue = 0;
355 /**************************************************************************
356 * wodPlayer [internal]
358 static DWORD CALLBACK wodPlayer(LPVOID pmt)
360 WORD uDevID = (DWORD)pmt;
361 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
366 PeekMessageA(&msg, 0, 0, 0, 0);
367 wwo->state = WINE_WS_STOPPED;
369 wwo->dwNotifiedBytes = 0;
370 wwo->dwOffCurrHdr = 0;
371 wwo->dwRemain = wwo->dwFragmentSize;
372 wwo->lpQueueHdr = NULL;
373 wwo->lpNotifyHdr = NULL;
374 wwo->wFragsUsedInQueue = 0;
375 wwo->dwPlayedTotal = 0;
378 TRACE("imhere[0]\n");
379 SetEvent(wwo->hEvent);
381 /* make sleep time to be # of ms to output a fragment */
382 dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
385 /* wait for dwSleepTime or an event in thread's queue */
386 /* FIXME: could improve wait time depending on queue state,
387 * ie, number of queued fragments
389 TRACE("imhere[1]\n");
390 MsgWaitForMultipleObjects(0, NULL, FALSE,
391 (wwo->state == WINE_WS_PLAYING) ?
392 (MAX(wwo->wFragsUsedInQueue, 4) - 2) * dwSleepTime :
395 TRACE("imhere[2]\n");
396 wodPlayer_Notify(wwo, uDevID, FALSE);
397 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
398 switch (msg.message) {
399 case WINE_WM_PAUSING:
400 wodPlayer_Reset(wwo, uDevID, FALSE);
401 wwo->state = WINE_WS_PAUSED;
402 SetEvent(wwo->hEvent);
404 case WINE_WM_RESTARTING:
405 wwo->state = WINE_WS_PLAYING;
406 SetEvent(wwo->hEvent);
409 lpWaveHdr = (LPWAVEHDR)msg.lParam;
411 lpWaveHdr->dwFlags &= ~WHDR_DONE;
412 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
413 lpWaveHdr->lpNext = 0;
415 /* insert buffer at the end of queue */
418 for (wh = &(wwo->lpQueueHdr); *wh; wh = &((*wh)->lpNext));
421 if (wwo->state == WINE_WS_STOPPED)
422 wwo->state = WINE_WS_PLAYING;
424 case WINE_WM_RESETTING:
425 wodPlayer_Reset(wwo, uDevID, TRUE);
426 SetEvent(wwo->hEvent);
428 case WINE_WM_CLOSING:
429 /* sanity check: this should not happen since the device must have been reset before */
430 if (wwo->lpNotifyHdr || wwo->lpQueueHdr) {
431 ERR("out of sync\n");
434 wwo->state = WINE_WS_CLOSED;
435 SetEvent(wwo->hEvent);
437 /* shouldn't go here */
439 FIXME("unknown message %d\n", msg.message);
443 if (wwo->state == WINE_WS_PLAYING) {
444 wodPlayer_WriteFragments(wwo);
446 wodPlayer_Notify(wwo, uDevID, FALSE);
449 /* just for not generating compilation warnings... should never be executed */
453 /**************************************************************************
454 * wodGetDevCaps [internal]
456 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
466 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
468 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
469 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
471 if (wDevID >= MAX_WAVEOUTDRV) {
472 TRACE("MAX_WAVOUTDRV reached !\n");
473 return MMSYSERR_BADDEVICEID;
476 if (WOutDev[wDevID].unixdev == 0) {
477 audio = open(SOUND_DEV, O_WRONLY, 0);
478 if (audio == -1) return MMSYSERR_ALLOCATED;
480 audio = WOutDev[wDevID].unixdev;
483 /* FIXME: some programs compare this string against the content of the registry
484 * for MM drivers. The name have to match in order the program to work
485 * (e.g. MS win9x mplayer.exe)
488 lpCaps->wMid = 0x0002;
489 lpCaps->wPid = 0x0104;
490 strcpy(lpCaps->szPname, "SB16 Wave Out");
492 lpCaps->wMid = 0x00FF; /* Manufac ID */
493 lpCaps->wPid = 0x0001; /* Product ID */
494 /* strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");*/
495 strcpy(lpCaps->szPname, "CS4236/37/38");
497 lpCaps->vDriverVersion = 0x0100;
498 lpCaps->dwFormats = 0x00000000;
499 lpCaps->dwSupport = WAVECAPS_VOLUME;
501 IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
502 TRACE("OSS dsp mask=%08x\n", mask);
504 IOCTL(audio, SNDCTL_DSP_SETFMT, mask);
505 TRACE("OSS dsp current=%08x\n", mask);
507 /* First bytespersampl, then stereo */
508 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
510 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
511 if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
514 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
515 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
516 if (lpCaps->wChannels > 1)
517 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
518 if (bytespersmpl > 1) {
519 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
520 if (lpCaps->wChannels > 1)
521 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
525 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
526 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
527 if (lpCaps->wChannels > 1)
528 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
529 if (bytespersmpl > 1) {
530 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
531 if (lpCaps->wChannels > 1)
532 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
536 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
537 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
538 if (lpCaps->wChannels > 1)
539 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
540 if (bytespersmpl > 1) {
541 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
542 if (lpCaps->wChannels > 1)
543 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
546 if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
547 if ((caps & DSP_CAP_REALTIME) && !(caps && DSP_CAP_BATCH))
548 lpCaps->dwFormats |= WAVECAPS_SAMPLEACCURATE;
550 if (WOutDev[wDevID].unixdev == 0) {
553 TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
554 return MMSYSERR_NOERROR;
557 /**************************************************************************
560 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
570 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
571 if (lpDesc == NULL) {
572 WARN("Invalid Parameter !\n");
573 return MMSYSERR_INVALPARAM;
575 if (wDevID >= MAX_WAVEOUTDRV) {
576 TRACE("MAX_WAVOUTDRV reached !\n");
577 return MMSYSERR_BADDEVICEID;
579 wodGetDevCaps(wDevID, &woc, sizeof(woc));
581 /* only PCM format is supported so far... */
582 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
583 lpDesc->lpFormat->nChannels == 0 ||
584 lpDesc->lpFormat->nSamplesPerSec == 0) {
585 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
586 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
587 lpDesc->lpFormat->nSamplesPerSec);
588 return WAVERR_BADFORMAT;
591 if (dwFlags & WAVE_FORMAT_QUERY) {
592 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
593 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
594 lpDesc->lpFormat->nSamplesPerSec);
595 return MMSYSERR_NOERROR;
598 WOutDev[wDevID].unixdev = 0;
599 if (access(SOUND_DEV, 0) != 0)
600 return MMSYSERR_NOTENABLED;
601 audio = open(SOUND_DEV, O_WRONLY, 0);
603 WARN("can't open !\n");
604 return MMSYSERR_ALLOCATED ;
607 WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
609 memcpy(&WOutDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
610 memcpy(&WOutDev[wDevID].format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
612 if (WOutDev[wDevID].format.wBitsPerSample == 0) {
613 WOutDev[wDevID].format.wBitsPerSample = 8 *
614 (WOutDev[wDevID].format.wf.nAvgBytesPerSec /
615 WOutDev[wDevID].format.wf.nSamplesPerSec) /
616 WOutDev[wDevID].format.wf.nChannels;
619 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
620 * thus leading to 46ms per fragment, and a turnaround time of 185ms
622 /* 2^10=1024 bytes per fragment, 16 fragments max */
623 audio_fragment = 0x000F000A;
624 sample_size = WOutDev[wDevID].format.wBitsPerSample;
625 sample_rate = WOutDev[wDevID].format.wf.nSamplesPerSec;
626 dsp_stereo = (WOutDev[wDevID].format.wf.nChannels > 1) ? 1 : 0;
628 IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
629 /* First size and stereo then samplerate */
630 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, sample_size);
631 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
632 IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
634 /* paranoid checks */
635 if (sample_size != WOutDev[wDevID].format.wBitsPerSample)
636 ERR("Can't set sample_size to %u (%d)\n",
637 WOutDev[wDevID].format.wBitsPerSample, sample_size);
638 if (dsp_stereo != (WOutDev[wDevID].format.wf.nChannels > 1) ? 1 : 0)
639 ERR("Can't set stereo to %u (%d)\n",
640 (WOutDev[wDevID].format.wf.nChannels > 1) ? 1 : 0, dsp_stereo);
641 if (!NEAR_MATCH(sample_rate,WOutDev[wDevID].format.wf.nSamplesPerSec))
642 ERR("Can't set sample_rate to %lu (%d)\n",
643 WOutDev[wDevID].format.wf.nSamplesPerSec, sample_rate);
645 /* even if we set fragment size above, read it again, just in case */
646 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
648 WOutDev[wDevID].unixdev = audio;
649 WOutDev[wDevID].dwFragmentSize = fragment_size;
650 WOutDev[wDevID].wMaxFragments = HIWORD(audio_fragment) + 1;
652 WOutDev[wDevID].hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
653 WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(WOutDev[wDevID].dwThreadID));
654 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
656 TRACE("fd=%d fragmentSize=%ld\n",
657 WOutDev[wDevID].unixdev, WOutDev[wDevID].dwFragmentSize);
659 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
660 WOutDev[wDevID].format.wBitsPerSample, WOutDev[wDevID].format.wf.nAvgBytesPerSec,
661 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels,
662 WOutDev[wDevID].format.wf.nBlockAlign);
664 if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
665 WARN("can't notify client !\n");
666 return MMSYSERR_INVALPARAM;
668 return MMSYSERR_NOERROR;
671 /**************************************************************************
672 * wodClose [internal]
674 static DWORD wodClose(WORD wDevID)
676 DWORD ret = MMSYSERR_NOERROR;
678 TRACE("(%u);\n", wDevID);
680 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
681 WARN("bad device ID !\n");
682 return MMSYSERR_BADDEVICEID;
685 if (WOutDev[wDevID].lpQueueHdr != NULL || WOutDev[wDevID].lpNotifyHdr != NULL) {
686 WARN("buffers still playing !\n");
687 ret = WAVERR_STILLPLAYING;
689 TRACE("imhere[3-close]\n");
690 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_CLOSING, 0, 0);
691 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
692 CloseHandle(WOutDev[wDevID].hEvent);
694 close(WOutDev[wDevID].unixdev);
695 WOutDev[wDevID].unixdev = 0;
696 WOutDev[wDevID].dwFragmentSize = 0;
697 if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
698 WARN("can't notify client !\n");
699 ret = MMSYSERR_INVALPARAM;
705 /**************************************************************************
706 * wodWrite [internal]
709 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
711 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
713 /* first, do the sanity checks... */
714 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
715 WARN("bad dev ID !\n");
716 return MMSYSERR_BADDEVICEID;
719 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
720 return WAVERR_UNPREPARED;
722 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
723 return WAVERR_STILLPLAYING;
725 TRACE("imhere[3-HEADER]\n");
726 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
728 return MMSYSERR_NOERROR;
731 /**************************************************************************
732 * wodPrepare [internal]
734 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
736 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
738 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
739 WARN("bad device ID !\n");
740 return MMSYSERR_BADDEVICEID;
743 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
744 return WAVERR_STILLPLAYING;
746 lpWaveHdr->dwFlags |= WHDR_PREPARED;
747 lpWaveHdr->dwFlags &= ~WHDR_DONE;
748 return MMSYSERR_NOERROR;
751 /**************************************************************************
752 * wodUnprepare [internal]
754 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
756 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
758 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
759 WARN("bad device ID !\n");
760 return MMSYSERR_BADDEVICEID;
763 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
764 return WAVERR_STILLPLAYING;
766 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
767 lpWaveHdr->dwFlags |= WHDR_DONE;
769 return MMSYSERR_NOERROR;
772 /**************************************************************************
773 * wodPause [internal]
775 static DWORD wodPause(WORD wDevID)
777 TRACE("(%u);!\n", wDevID);
779 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
780 WARN("bad device ID !\n");
781 return MMSYSERR_BADDEVICEID;
784 TRACE("imhere[3-PAUSING]\n");
785 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_PAUSING, 0, 0);
786 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
788 return MMSYSERR_NOERROR;
791 /**************************************************************************
792 * wodRestart [internal]
794 static DWORD wodRestart(WORD wDevID)
796 TRACE("(%u);\n", wDevID);
798 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
799 WARN("bad device ID !\n");
800 return MMSYSERR_BADDEVICEID;
803 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
804 TRACE("imhere[3-RESTARTING]\n");
805 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
806 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
809 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
810 /* FIXME: Myst crashes with this ... hmm -MM
811 if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
812 WARN("can't notify client !\n");
813 return MMSYSERR_INVALPARAM;
817 return MMSYSERR_NOERROR;
820 /**************************************************************************
821 * wodReset [internal]
823 static DWORD wodReset(WORD wDevID)
825 TRACE("(%u);\n", wDevID);
827 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
828 WARN("bad device ID !\n");
829 return MMSYSERR_BADDEVICEID;
832 TRACE("imhere[3-RESET]\n");
833 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
834 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
836 return MMSYSERR_NOERROR;
840 /**************************************************************************
841 * wodGetPosition [internal]
843 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
848 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
850 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
851 WARN("bad device ID !\n");
852 return MMSYSERR_BADDEVICEID;
855 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
857 val = WOutDev[wDevID].dwPlayedTotal + WOutDev[wDevID].dwPlayed;
859 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
860 lpTime->wType, WOutDev[wDevID].format.wBitsPerSample,
861 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels,
862 WOutDev[wDevID].format.wf.nAvgBytesPerSec);
863 TRACE("dwTotalPlayed=%lu\n", val);
865 switch (lpTime->wType) {
868 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
871 lpTime->u.sample = val * 8 / WOutDev[wDevID].format.wBitsPerSample;
872 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
875 time = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
876 lpTime->u.smpte.hour = time / 108000;
877 time -= lpTime->u.smpte.hour * 108000;
878 lpTime->u.smpte.min = time / 1800;
879 time -= lpTime->u.smpte.min * 1800;
880 lpTime->u.smpte.sec = time / 30;
881 time -= lpTime->u.smpte.sec * 30;
882 lpTime->u.smpte.frame = time;
883 lpTime->u.smpte.fps = 30;
884 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
885 lpTime->u.smpte.hour, lpTime->u.smpte.min,
886 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
889 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
890 lpTime->wType = TIME_MS;
892 lpTime->u.ms = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
893 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
896 return MMSYSERR_NOERROR;
899 /**************************************************************************
900 * wodGetVolume [internal]
902 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
908 TRACE("(%u, %p);\n", wDevID, lpdwVol);
911 return MMSYSERR_NOTENABLED;
912 if ((mixer = open(MIXER_DEV, O_RDONLY)) < 0) {
913 WARN("mixer device not available !\n");
914 return MMSYSERR_NOTENABLED;
916 if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
917 WARN("unable read mixer !\n");
918 return MMSYSERR_NOTENABLED;
921 left = LOBYTE(volume);
922 right = HIBYTE(volume);
923 TRACE("left=%ld right=%ld !\n", left, right);
924 *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
925 return MMSYSERR_NOERROR;
929 /**************************************************************************
930 * wodSetVolume [internal]
932 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
938 TRACE("(%u, %08lX);\n", wDevID, dwParam);
940 left = (LOWORD(dwParam) * 100) / 0xFFFFl;
941 right = (HIWORD(dwParam) * 100) / 0xFFFFl;
942 volume = left + (right << 8);
944 if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) {
945 WARN("mixer device not available !\n");
946 return MMSYSERR_NOTENABLED;
948 if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
949 WARN("unable set mixer !\n");
950 return MMSYSERR_NOTENABLED;
953 return MMSYSERR_NOERROR;
956 /**************************************************************************
957 * wodGetNumDevs [internal]
959 static DWORD wodGetNumDevs(void)
963 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
964 int audio = open(SOUND_DEV, O_WRONLY, 0);
975 /**************************************************************************
976 * OSS_wodMessage [sample driver]
978 DWORD WINAPI OSS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
979 DWORD dwParam1, DWORD dwParam2)
981 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
982 wDevID, wMsg, dwUser, dwParam1, dwParam2);
989 /* FIXME: Pretend this is supported */
991 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
992 case WODM_CLOSE: return wodClose (wDevID);
993 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
994 case WODM_PAUSE: return wodPause (wDevID);
995 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
996 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
997 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
998 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
999 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2);
1000 case WODM_GETNUMDEVS: return wodGetNumDevs ();
1001 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
1002 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
1003 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1004 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1005 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
1006 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
1007 case WODM_RESTART: return wodRestart (wDevID);
1008 case WODM_RESET: return wodReset (wDevID);
1010 FIXME("unknown message %d!\n", wMsg);
1012 return MMSYSERR_NOTSUPPORTED;
1015 /*======================================================================*
1016 * Low level WAVE IN implemantation *
1017 *======================================================================*/
1019 /**************************************************************************
1020 * widGetDevCaps [internal]
1022 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
1024 int audio, smplrate, samplesize=16, dsp_stereo=1, bytespersmpl;
1026 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1027 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1028 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1029 audio = open(SOUND_DEV, O_RDONLY, 0);
1030 if (audio == -1) return MMSYSERR_ALLOCATED ;
1032 lpCaps->wMid = 0x0002;
1033 lpCaps->wPid = 0x0004;
1034 strcpy(lpCaps->szPname, "SB16 Wave In");
1036 lpCaps->wMid = 0x00FF; /* Manufac ID */
1037 lpCaps->wPid = 0x0001; /* Product ID */
1038 strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
1040 lpCaps->dwFormats = 0x00000000;
1041 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
1042 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
1044 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1045 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
1046 if (lpCaps->wChannels > 1)
1047 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
1048 if (bytespersmpl > 1) {
1049 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
1050 if (lpCaps->wChannels > 1)
1051 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
1055 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1056 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
1057 if (lpCaps->wChannels > 1)
1058 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
1059 if (bytespersmpl > 1) {
1060 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
1061 if (lpCaps->wChannels > 1)
1062 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
1066 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1067 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
1068 if (lpCaps->wChannels > 1)
1069 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
1070 if (bytespersmpl > 1) {
1071 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
1072 if (lpCaps->wChannels > 1)
1073 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
1077 TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
1078 return MMSYSERR_NOERROR;
1081 /**************************************************************************
1082 * widOpen [internal]
1084 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1086 int audio, abuf_size, smplrate, samplesize, dsp_stereo;
1087 LPWAVEFORMAT lpFormat;
1089 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1090 if (lpDesc == NULL) {
1091 WARN("Invalid Parameter !\n");
1092 return MMSYSERR_INVALPARAM;
1094 if (wDevID >= MAX_WAVEINDRV) {
1095 TRACE("MAX_WAVINDRV reached !\n");
1096 return MMSYSERR_ALLOCATED;
1099 /* only PCM format is supported so far... */
1100 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1101 lpDesc->lpFormat->nChannels == 0 ||
1102 lpDesc->lpFormat->nSamplesPerSec == 0) {
1103 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1104 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1105 lpDesc->lpFormat->nSamplesPerSec);
1106 return WAVERR_BADFORMAT;
1109 if (dwFlags & WAVE_FORMAT_QUERY) {
1110 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1111 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1112 lpDesc->lpFormat->nSamplesPerSec);
1113 return MMSYSERR_NOERROR;
1116 WInDev[wDevID].unixdev = 0;
1117 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1118 audio = open(SOUND_DEV, O_RDONLY, 0);
1120 WARN("can't open !\n");
1121 return MMSYSERR_ALLOCATED;
1123 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
1124 if (abuf_size < 1024 || abuf_size > 65536) {
1125 if (abuf_size == -1)
1127 WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1128 return MMSYSERR_NOTENABLED;
1130 WARN("SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize %d!\n",abuf_size);
1132 WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1134 if (WInDev[wDevID].lpQueueHdr) {
1135 HeapFree(GetProcessHeap(), 0, WInDev[wDevID].lpQueueHdr);
1136 WInDev[wDevID].lpQueueHdr = NULL;
1138 WInDev[wDevID].unixdev = audio;
1139 WInDev[wDevID].dwFragmentSize = abuf_size;
1140 WInDev[wDevID].dwTotalRecorded = 0;
1141 memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1142 lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat;
1144 memcpy(&WInDev[wDevID].format, lpFormat, sizeof(PCMWAVEFORMAT));
1145 WInDev[wDevID].format.wBitsPerSample = 8; /* <-------------- */
1146 if (WInDev[wDevID].format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1147 if (WInDev[wDevID].format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1148 if (WInDev[wDevID].format.wBitsPerSample == 0) {
1149 WInDev[wDevID].format.wBitsPerSample = 8 *
1150 (WInDev[wDevID].format.wf.nAvgBytesPerSec /
1151 WInDev[wDevID].format.wf.nSamplesPerSec) /
1152 WInDev[wDevID].format.wf.nChannels;
1154 samplesize = WInDev[wDevID].format.wBitsPerSample;
1155 smplrate = WInDev[wDevID].format.wf.nSamplesPerSec;
1156 dsp_stereo = (WInDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
1157 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1158 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1159 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1160 TRACE("wBitsPerSample=%u !\n", WInDev[wDevID].format.wBitsPerSample);
1161 TRACE("nSamplesPerSec=%lu !\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1162 TRACE("nChannels=%u !\n", WInDev[wDevID].format.wf.nChannels);
1163 TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1164 if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1165 WARN("can't notify client !\n");
1166 return MMSYSERR_INVALPARAM;
1168 return MMSYSERR_NOERROR;
1171 /**************************************************************************
1172 * widClose [internal]
1174 static DWORD widClose(WORD wDevID)
1176 TRACE("(%u);\n", wDevID);
1177 if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
1178 if (WInDev[wDevID].unixdev == 0) {
1179 WARN("can't close !\n");
1180 return MMSYSERR_NOTENABLED;
1182 if (WInDev[wDevID].lpQueueHdr != NULL) {
1183 WARN("still buffers open !\n");
1184 return WAVERR_STILLPLAYING;
1186 close(WInDev[wDevID].unixdev);
1187 WInDev[wDevID].unixdev = 0;
1188 WInDev[wDevID].dwFragmentSize = 0;
1189 if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1190 WARN("can't notify client !\n");
1191 return MMSYSERR_INVALPARAM;
1193 return MMSYSERR_NOERROR;
1196 /**************************************************************************
1197 * widAddBuffer [internal]
1199 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1204 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1205 if (WInDev[wDevID].unixdev == 0) {
1206 WARN("can't do it !\n");
1207 return MMSYSERR_NOTENABLED;
1209 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1210 TRACE("never been prepared !\n");
1211 return WAVERR_UNPREPARED;
1213 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1214 TRACE("header already in use !\n");
1215 return WAVERR_STILLPLAYING;
1217 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1218 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1219 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1220 lpWaveHdr->dwBytesRecorded = 0;
1221 lpWaveHdr->lpNext = NULL;
1222 if (WInDev[wDevID].lpQueueHdr == NULL) {
1223 WInDev[wDevID].lpQueueHdr = lpWaveHdr;
1225 lpWIHdr = WInDev[wDevID].lpQueueHdr;
1226 while (lpWIHdr->lpNext != NULL) {
1227 lpWIHdr = lpWIHdr->lpNext;
1230 lpWIHdr->lpNext = lpWaveHdr;
1233 TRACE("buffer added ! (now %u in queue)\n", count);
1234 return MMSYSERR_NOERROR;
1237 /**************************************************************************
1238 * widPrepare [internal]
1240 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1242 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1243 if (WInDev[wDevID].unixdev == 0) {
1244 WARN("can't prepare !\n");
1245 return MMSYSERR_NOTENABLED;
1247 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1248 return WAVERR_STILLPLAYING;
1249 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1250 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1251 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1252 lpWaveHdr->dwBytesRecorded = 0;
1253 TRACE("header prepared !\n");
1254 return MMSYSERR_NOERROR;
1257 /**************************************************************************
1258 * widUnprepare [internal]
1260 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1262 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1263 if (WInDev[wDevID].unixdev == 0) {
1264 WARN("can't unprepare !\n");
1265 return MMSYSERR_NOTENABLED;
1267 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1268 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1269 lpWaveHdr->dwFlags |= WHDR_DONE;
1271 return MMSYSERR_NOERROR;
1274 /**************************************************************************
1275 * widStart [internal]
1277 static DWORD widStart(WORD wDevID)
1281 LPWAVEHDR *lpWaveHdr;
1284 TRACE("(%u);\n", wDevID);
1285 if (WInDev[wDevID].unixdev == 0) {
1286 WARN("can't start recording !\n");
1287 return MMSYSERR_NOTENABLED;
1290 lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
1291 TRACE("lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
1293 if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
1294 TRACE("never been prepared !\n");
1295 return WAVERR_UNPREPARED;
1298 while (*lpWaveHdr != NULL) {
1299 lpData = (*lpWaveHdr)->lpData;
1300 TRACE("recording buf#%u=%p size=%lu \n",
1301 count, lpData, (*lpWaveHdr)->dwBufferLength);
1303 bytesRead = read(WInDev[wDevID].unixdev, lpData, (*lpWaveHdr)->dwBufferLength);
1305 if (bytesRead == -1)
1306 perror("read from audio device");
1308 TRACE("bytesread=%d (%ld)\n", bytesRead, (*lpWaveHdr)->dwBufferLength);
1309 (*lpWaveHdr)->dwBytesRecorded = bytesRead;
1310 WInDev[wDevID].dwTotalRecorded += (*lpWaveHdr)->dwBytesRecorded;
1311 (*lpWaveHdr)->dwFlags &= ~WHDR_INQUEUE;
1312 (*lpWaveHdr)->dwFlags |= WHDR_DONE;
1314 if (WAVE_NotifyClient(wDevID, WIM_DATA, (DWORD)*lpWaveHdr, (*lpWaveHdr)->dwBytesRecorded) != MMSYSERR_NOERROR) {
1315 WARN("can't notify client !\n");
1316 return MMSYSERR_INVALPARAM;
1319 /* removes the current block from the queue */
1320 *lpWaveHdr = (*lpWaveHdr)->lpNext;
1323 TRACE("end of recording !\n");
1324 return MMSYSERR_NOERROR;
1327 /**************************************************************************
1328 * widStop [internal]
1330 static DWORD widStop(WORD wDevID)
1332 TRACE("(%u);\n", wDevID);
1333 if (WInDev[wDevID].unixdev == 0) {
1334 WARN("can't stop !\n");
1335 return MMSYSERR_NOTENABLED;
1337 return MMSYSERR_NOERROR;
1340 /**************************************************************************
1341 * widReset [internal]
1343 static DWORD widReset(WORD wDevID)
1345 TRACE("(%u);\n", wDevID);
1346 if (WInDev[wDevID].unixdev == 0) {
1347 WARN("can't reset !\n");
1348 return MMSYSERR_NOTENABLED;
1350 return MMSYSERR_NOERROR;
1353 /**************************************************************************
1354 * widGetPosition [internal]
1356 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1360 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1361 if (WInDev[wDevID].unixdev == 0) {
1362 WARN("can't get pos !\n");
1363 return MMSYSERR_NOTENABLED;
1365 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1366 TRACE("wType=%04X !\n", lpTime->wType);
1367 TRACE("wBitsPerSample=%u\n", WInDev[wDevID].format.wBitsPerSample);
1368 TRACE("nSamplesPerSec=%lu\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1369 TRACE("nChannels=%u\n", WInDev[wDevID].format.wf.nChannels);
1370 TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1371 switch (lpTime->wType) {
1373 lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
1374 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1377 lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
1378 WInDev[wDevID].format.wBitsPerSample;
1379 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1382 time = WInDev[wDevID].dwTotalRecorded /
1383 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1384 lpTime->u.smpte.hour = time / 108000;
1385 time -= lpTime->u.smpte.hour * 108000;
1386 lpTime->u.smpte.min = time / 1800;
1387 time -= lpTime->u.smpte.min * 1800;
1388 lpTime->u.smpte.sec = time / 30;
1389 time -= lpTime->u.smpte.sec * 30;
1390 lpTime->u.smpte.frame = time;
1391 lpTime->u.smpte.fps = 30;
1392 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1393 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1394 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1397 lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1398 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1399 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1402 FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1403 lpTime->wType = TIME_MS;
1405 return MMSYSERR_NOERROR;
1408 /**************************************************************************
1409 * OSS_widMessage [sample driver]
1411 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1412 DWORD dwParam1, DWORD dwParam2)
1414 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1415 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1422 /* FIXME: Pretend this is supported */
1424 case WIDM_OPEN: return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1425 case WIDM_CLOSE: return widClose(wDevID);
1426 case WIDM_ADDBUFFER: return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1427 case WIDM_PREPARE: return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1428 case WIDM_UNPREPARE: return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1429 case WIDM_GETDEVCAPS: return widGetDevCaps(wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
1430 case WIDM_GETNUMDEVS: return wodGetNumDevs(); /* same number of devices in output as in input */
1431 case WIDM_GETPOS: return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
1432 case WIDM_RESET: return widReset(wDevID);
1433 case WIDM_START: return widStart(wDevID);
1434 case WIDM_STOP: return widStop(wDevID);
1436 FIXME("unknown message %u!\n", wMsg);
1438 return MMSYSERR_NOTSUPPORTED;
1441 #else /* !HAVE_OSS */
1443 /**************************************************************************
1444 * wodMessage [sample driver]
1446 DWORD WINAPI OSS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1447 DWORD dwParam1, DWORD dwParam2)
1449 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1450 return MMSYSERR_NOTENABLED;
1453 /**************************************************************************
1454 * widMessage [sample driver]
1456 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1457 DWORD dwParam1, DWORD dwParam2)
1459 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1460 return MMSYSERR_NOTENABLED;
1463 #endif /* HAVE_OSS */