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*/
23 #include <sys/ioctl.h>
24 #include "wine/winuser16.h"
30 #include "debugtools.h"
32 DEFAULT_DEBUG_CHANNEL(wave)
34 /* Allow 1% deviation for sample rates (some ES137x cards) */
35 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
39 #define SOUND_DEV "/dev/dsp"
40 #define MIXER_DEV "/dev/mixer"
42 #define MAX_WAVEOUTDRV (1)
43 #define MAX_WAVEINDRV (1)
45 /* state diagram for waveOut writing:
47 * +---------+-------------+---------------+---------------------------------+
48 * | state | function | event | new state |
49 * +---------+-------------+---------------+---------------------------------+
50 * | | open() | | STOPPED |
51 * | PAUSED | write() | | PAUSED |
52 * | STOPPED | write() | <thrd create> | PLAYING |
53 * | PLAYING | write() | HEADER | PLAYING |
54 * | (other) | write() | <error> | |
55 * | (any) | pause() | PAUSING | PAUSED |
56 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
57 * | (any) | reset() | RESETTING | STOPPED |
58 * | (any) | close() | CLOSING | CLOSED |
59 * +---------+-------------+---------------+---------------------------------+
62 /* states of the playing device */
63 #define WINE_WS_PLAYING 0
64 #define WINE_WS_PAUSED 1
65 #define WINE_WS_STOPPED 2
66 #define WINE_WS_CLOSED 3
68 /* events to be send to device */
69 #define WINE_WM_PAUSING (WM_USER + 1)
70 #define WINE_WM_RESTARTING (WM_USER + 2)
71 #define WINE_WM_RESETTING (WM_USER + 3)
72 #define WINE_WM_CLOSING (WM_USER + 4)
73 #define WINE_WM_HEADER (WM_USER + 5)
77 volatile int state; /* one of the WINE_WS_ manifest constants */
78 DWORD dwFragmentSize; /* size of OSS buffer fragment */
79 WAVEOPENDESC waveDesc;
82 LPWAVEHDR lpQueueHdr; /* pending buffers for playing */
83 LPWAVEHDR lpNotifyHdr; /* list of wavehdr for which write() has been called, pending for notification */
85 DWORD dwPlayedTotal; /* number of bytes played since opening */
86 DWORD dwPlayed; /* number of bytes played since last DSP_RESET */
87 DWORD dwNotifiedBytes; /* number of bytes for which wavehdr notification has been done */
89 /* info on current lpQueueHdr->lpWaveHdr */
90 DWORD dwOffCurrHdr; /* offset in lpQueueHdr->lpWaveHdr->lpData for fragments */
91 DWORD dwRemain; /* number of bytes to write to end the current fragment */
93 /* synchronization stuff */
98 WORD wMaxFragments; /* max number of fragments that can be written to dsp */
99 WORD wFragsUsedInQueue; /* current number of used fragments inside dsp queue */
105 DWORD dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */
106 WAVEOPENDESC waveDesc;
108 PCMWAVEFORMAT format;
109 LPWAVEHDR lpQueueHdr;
110 DWORD dwTotalRecorded;
113 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
114 static WINE_WAVEIN WInDev [MAX_WAVEOUTDRV];
116 /*======================================================================*
117 * Low level WAVE implemantation *
118 *======================================================================*/
120 /**************************************************************************
121 * WAVE_NotifyClient [internal]
123 static DWORD WAVE_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1,
126 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
132 if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
134 if (WOutDev[wDevID].wFlags != DCB_NULL &&
135 !DriverCallback(WOutDev[wDevID].waveDesc.dwCallback,
136 WOutDev[wDevID].wFlags,
137 WOutDev[wDevID].waveDesc.hWave,
139 WOutDev[wDevID].waveDesc.dwInstance,
142 WARN("can't notify client !\n");
143 return MMSYSERR_NOERROR;
150 if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
152 if (WInDev[wDevID].wFlags != DCB_NULL &&
153 !DriverCallback(WInDev[wDevID].waveDesc.dwCallback,
154 WInDev[wDevID].wFlags,
155 WInDev[wDevID].waveDesc.hWave,
157 WInDev[wDevID].waveDesc.dwInstance,
160 WARN("can't notify client !\n");
161 return MMSYSERR_NOERROR;
165 FIXME("Unknown CB message %u\n", wMsg);
171 /*======================================================================*
172 * Low level WAVE OUT implemantation *
173 *======================================================================*/
175 /**************************************************************************
176 * wodPlayer_WriteFragments [internal]
178 * wodPlayer helper. Writes as many fragments it can to unixdev.
179 * Returns TRUE in case of buffer underrun.
181 static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
188 TRACE("Fragments: %d used on fd %d\n", wwo->wFragsUsedInQueue, wwo->unixdev);
189 if (wwo->wFragsUsedInQueue == wwo->wMaxFragments) /* output queue is full, wait a bit */
192 lpWaveHdr = wwo->lpQueueHdr;
194 if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */
195 wwo->dwNotifiedBytes >= wwo->dwFragmentSize && /* first fragment has been played */
196 wwo->wFragsUsedInQueue < 2) { /* done with all waveOutWrite()' fragments */
197 /* FIXME: should do better handling here */
198 TRACE("Oooch, buffer underrun !\n");
199 return TRUE; /* force resetting of waveOut device */
201 return FALSE; /* wait a bit */
204 if (wwo->dwOffCurrHdr == 0) {
205 TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
206 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
207 FIXME("NIY: loops (%lu) in wavehdr\n", lpWaveHdr->dwLoops);
210 lpData = lpWaveHdr->lpData;
212 /* finish current wave hdr ? */
213 if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) {
214 DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
216 /* write end of current wave hdr */
217 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
218 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
220 if (count > 0 || toWrite == 0) {
223 /* move lpWaveHdr to the end of notify list */
224 for (wh = &(wwo->lpNotifyHdr); *wh; wh = &((*wh)->lpNext));
227 wwo->lpQueueHdr = lpWaveHdr->lpNext;
228 lpWaveHdr->lpNext = 0;
230 wwo->dwOffCurrHdr = 0;
231 if ((wwo->dwRemain -= count) == 0) {
232 wwo->dwRemain = wwo->dwFragmentSize;
233 wwo->wFragsUsedInQueue++;
236 continue; /* try to go to use next wavehdr */
238 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
239 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
241 wwo->dwOffCurrHdr += count;
242 wwo->dwRemain = wwo->dwFragmentSize;
243 wwo->wFragsUsedInQueue++;
249 /**************************************************************************
250 * wodPlayer_WriteFragments [internal]
252 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
253 * have been played (actually to speaker, not to unixdev fd).
255 static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
260 /* get effective number of written bytes */
264 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOPTR, &cinfo) == -1) {
265 perror("ioctl SNDCTL_DSP_GETOPTR");
267 wwo->state = WINE_WS_STOPPED;
270 TRACE("Played %d bytes (played=%ld) on fd %d\n", cinfo.bytes, wwo->dwPlayed, wwo->unixdev);
271 c = cinfo.bytes / wwo->dwFragmentSize - wwo->dwPlayed / wwo->dwFragmentSize;
272 if (wwo->wFragsUsedInQueue > c)
273 wwo->wFragsUsedInQueue -= c;
275 wwo->wFragsUsedInQueue = 0;
276 wwo->dwPlayed = cinfo.bytes;
278 if (force || cinfo.bytes > wwo->dwNotifiedBytes) {
279 /* remove all wavehdr which can be notified */
280 while (wwo->lpNotifyHdr &&
281 (force || (cinfo.bytes >= wwo->dwNotifiedBytes + wwo->lpNotifyHdr->dwBufferLength))) {
282 lpWaveHdr = wwo->lpNotifyHdr;
284 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
285 lpWaveHdr->dwFlags |= WHDR_DONE;
287 wwo->dwNotifiedBytes += lpWaveHdr->dwBufferLength;
288 wwo->lpNotifyHdr = lpWaveHdr->lpNext;
290 TRACE("Notifying client with %p\n", lpWaveHdr);
291 if (WAVE_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) {
292 WARN("can't notify client !\n");
298 /**************************************************************************
299 * wodPlayer_Reset [internal]
301 * wodPlayer helper. Resets current output stream.
303 static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
307 /* updates current notify list */
308 wodPlayer_Notify(wwo, uDevID, FALSE);
310 /* flush all possible output */
311 if (ioctl(wwo->unixdev, SNDCTL_DSP_RESET, 0) == -1) {
312 perror("ioctl SNDCTL_DSP_RESET");
314 wwo->state = WINE_WS_STOPPED;
318 wwo->dwOffCurrHdr = 0;
320 /* empty notify list */
321 wodPlayer_Notify(wwo, uDevID, TRUE);
322 if (wwo->lpNotifyHdr) {
323 ERR("out of sync\n");
325 /* get rid also of all the current queue */
326 for (lpWaveHdr = wwo->lpQueueHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
327 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
328 lpWaveHdr->dwFlags |= WHDR_DONE;
330 if (WAVE_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) {
331 WARN("can't notify client !\n");
335 wwo->state = WINE_WS_STOPPED;
336 wwo->dwPlayedTotal = 0;
338 /* move notify list to begining of lpQueueHdr list */
339 while (wwo->lpNotifyHdr) {
340 lpWaveHdr = wwo->lpNotifyHdr;
341 wwo->lpNotifyHdr = lpWaveHdr->lpNext;
342 lpWaveHdr->lpNext = wwo->lpQueueHdr;
343 wwo->lpQueueHdr = lpWaveHdr;
345 wwo->state = WINE_WS_PAUSED;
346 wwo->dwPlayedTotal += wwo->dwPlayed;
348 wwo->dwNotifiedBytes = wwo->dwPlayed = 0;
349 wwo->wFragsUsedInQueue = 0;
352 /**************************************************************************
353 * wodPlayer [internal]
355 static DWORD CALLBACK wodPlayer(LPVOID pmt)
357 WORD uDevID = (DWORD)pmt;
358 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
363 PeekMessageA(&msg, 0, 0, 0, 0);
364 wwo->state = WINE_WS_STOPPED;
366 wwo->dwNotifiedBytes = 0;
367 wwo->dwOffCurrHdr = 0;
368 wwo->dwRemain = wwo->dwFragmentSize;
369 wwo->lpQueueHdr = NULL;
370 wwo->lpNotifyHdr = NULL;
371 wwo->wFragsUsedInQueue = 0;
372 wwo->dwPlayedTotal = 0;
375 TRACE("imhere[0]\n");
376 SetEvent(wwo->hEvent);
378 /* make sleep time to be # of ms to output a fragment */
379 dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
382 /* wait for dwSleepTime or an event in thread's queue */
383 /* FIXME: could improve wait time depending on queue state,
384 * ie, number of queued fragments
386 TRACE("imhere[1]\n");
387 MsgWaitForMultipleObjects(0, NULL, FALSE,
388 (wwo->state == WINE_WS_PLAYING) ?
389 (MAX(wwo->wFragsUsedInQueue, 4) - 2) * dwSleepTime :
392 TRACE("imhere[2]\n");
393 wodPlayer_Notify(wwo, uDevID, FALSE);
394 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
395 switch (msg.message) {
396 case WINE_WM_PAUSING:
397 wodPlayer_Reset(wwo, uDevID, FALSE);
398 wwo->state = WINE_WS_PAUSED;
399 SetEvent(wwo->hEvent);
401 case WINE_WM_RESTARTING:
402 wwo->state = WINE_WS_PLAYING;
403 SetEvent(wwo->hEvent);
406 lpWaveHdr = (LPWAVEHDR)msg.lParam;
408 lpWaveHdr->dwFlags &= ~WHDR_DONE;
409 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
410 lpWaveHdr->lpNext = 0;
412 /* insert buffer at the end of queue */
415 for (wh = &(wwo->lpQueueHdr); *wh; wh = &((*wh)->lpNext));
418 if (wwo->state == WINE_WS_STOPPED)
419 wwo->state = WINE_WS_PLAYING;
421 case WINE_WM_RESETTING:
422 wodPlayer_Reset(wwo, uDevID, TRUE);
423 SetEvent(wwo->hEvent);
425 case WINE_WM_CLOSING:
426 /* sanity check: this should not happen since the device must have been reset before */
427 if (wwo->lpNotifyHdr || wwo->lpQueueHdr) {
428 ERR("out of sync\n");
431 wwo->state = WINE_WS_CLOSED;
432 SetEvent(wwo->hEvent);
434 /* shouldn't go here */
436 FIXME("unknown message %d\n", msg.message);
440 if (wwo->state == WINE_WS_PLAYING) {
441 wodPlayer_WriteFragments(wwo);
443 wodPlayer_Notify(wwo, uDevID, FALSE);
446 /* just for not generating compilation warnings... should never be executed */
450 /**************************************************************************
451 * wodGetDevCaps [internal]
453 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
463 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
465 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
466 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
468 if (wDevID >= MAX_WAVEOUTDRV) {
469 TRACE("MAX_WAVOUTDRV reached !\n");
470 return MMSYSERR_BADDEVICEID;
473 if (WOutDev[wDevID].unixdev == 0) {
474 audio = open(SOUND_DEV, O_WRONLY, 0);
475 if (audio == -1) return MMSYSERR_ALLOCATED;
477 audio = WOutDev[wDevID].unixdev;
480 /* FIXME: some programs compare this string against the content of the registry
481 * for MM drivers. The name have to match in order the program to work
482 * (e.g. MS win9x mplayer.exe)
485 lpCaps->wMid = 0x0002;
486 lpCaps->wPid = 0x0104;
487 strcpy(lpCaps->szPname, "SB16 Wave Out");
489 lpCaps->wMid = 0x00FF; /* Manufac ID */
490 lpCaps->wPid = 0x0001; /* Product ID */
491 /* strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");*/
492 strcpy(lpCaps->szPname, "CS4236/37/38");
494 lpCaps->vDriverVersion = 0x0100;
495 lpCaps->dwFormats = 0x00000000;
496 lpCaps->dwSupport = WAVECAPS_VOLUME;
498 IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
499 TRACE("OSS dsp mask=%08x\n", mask);
501 IOCTL(audio, SNDCTL_DSP_SETFMT, mask);
502 TRACE("OSS dsp current=%08x\n", mask);
504 /* First bytespersampl, then stereo */
505 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
507 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
508 if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
511 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
512 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
513 if (lpCaps->wChannels > 1)
514 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
515 if (bytespersmpl > 1) {
516 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
517 if (lpCaps->wChannels > 1)
518 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
522 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
523 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
524 if (lpCaps->wChannels > 1)
525 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
526 if (bytespersmpl > 1) {
527 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
528 if (lpCaps->wChannels > 1)
529 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
533 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
534 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
535 if (lpCaps->wChannels > 1)
536 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
537 if (bytespersmpl > 1) {
538 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
539 if (lpCaps->wChannels > 1)
540 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
543 if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
544 if ((caps & DSP_CAP_REALTIME) && !(caps && DSP_CAP_BATCH))
545 lpCaps->dwFormats |= WAVECAPS_SAMPLEACCURATE;
547 if (WOutDev[wDevID].unixdev == 0) {
550 TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
551 return MMSYSERR_NOERROR;
554 /**************************************************************************
557 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
567 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
568 if (lpDesc == NULL) {
569 WARN("Invalid Parameter !\n");
570 return MMSYSERR_INVALPARAM;
572 if (wDevID >= MAX_WAVEOUTDRV) {
573 TRACE("MAX_WAVOUTDRV reached !\n");
574 return MMSYSERR_BADDEVICEID;
576 wodGetDevCaps(wDevID, &woc, sizeof(woc));
578 /* only PCM format is supported so far... */
579 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
580 lpDesc->lpFormat->nChannels == 0 ||
581 lpDesc->lpFormat->nSamplesPerSec == 0) {
582 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
583 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
584 lpDesc->lpFormat->nSamplesPerSec);
585 return WAVERR_BADFORMAT;
588 if (dwFlags & WAVE_FORMAT_QUERY) {
589 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
590 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
591 lpDesc->lpFormat->nSamplesPerSec);
592 return MMSYSERR_NOERROR;
595 WOutDev[wDevID].unixdev = 0;
596 if (access(SOUND_DEV, 0) != 0)
597 return MMSYSERR_NOTENABLED;
598 audio = open(SOUND_DEV, O_WRONLY, 0);
600 WARN("can't open !\n");
601 return MMSYSERR_ALLOCATED ;
604 WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
606 memcpy(&WOutDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
607 memcpy(&WOutDev[wDevID].format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
609 if (WOutDev[wDevID].format.wBitsPerSample == 0) {
610 WOutDev[wDevID].format.wBitsPerSample = 8 *
611 (WOutDev[wDevID].format.wf.nAvgBytesPerSec /
612 WOutDev[wDevID].format.wf.nSamplesPerSec) /
613 WOutDev[wDevID].format.wf.nChannels;
616 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
617 * thus leading to 46ms per fragment, and a turnaround time of 185ms
619 /* 2^10=1024 bytes per fragment, 16 fragments max */
620 audio_fragment = 0x000F000A;
621 sample_size = WOutDev[wDevID].format.wBitsPerSample;
622 sample_rate = WOutDev[wDevID].format.wf.nSamplesPerSec;
623 dsp_stereo = (WOutDev[wDevID].format.wf.nChannels > 1) ? 1 : 0;
625 IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
626 /* First size and stereo then samplerate */
627 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, sample_size);
628 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
629 IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
631 /* paranoid checks */
632 if (sample_size != WOutDev[wDevID].format.wBitsPerSample)
633 ERR("Can't set sample_size to %u (%d)\n",
634 WOutDev[wDevID].format.wBitsPerSample, sample_size);
635 if (dsp_stereo != (WOutDev[wDevID].format.wf.nChannels > 1) ? 1 : 0)
636 ERR("Can't set stereo to %u (%d)\n",
637 (WOutDev[wDevID].format.wf.nChannels > 1) ? 1 : 0, dsp_stereo);
638 if (!NEAR_MATCH(sample_rate,WOutDev[wDevID].format.wf.nSamplesPerSec))
639 ERR("Can't set sample_rate to %lu (%d)\n",
640 WOutDev[wDevID].format.wf.nSamplesPerSec, sample_rate);
642 /* even if we set fragment size above, read it again, just in case */
643 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
645 WOutDev[wDevID].unixdev = audio;
646 WOutDev[wDevID].dwFragmentSize = fragment_size;
647 WOutDev[wDevID].wMaxFragments = HIWORD(audio_fragment) + 1;
649 WOutDev[wDevID].hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
650 WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(WOutDev[wDevID].dwThreadID));
651 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
653 TRACE("fd=%d fragmentSize=%ld\n",
654 WOutDev[wDevID].unixdev, WOutDev[wDevID].dwFragmentSize);
656 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
657 WOutDev[wDevID].format.wBitsPerSample, WOutDev[wDevID].format.wf.nAvgBytesPerSec,
658 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels,
659 WOutDev[wDevID].format.wf.nBlockAlign);
661 if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
662 WARN("can't notify client !\n");
663 return MMSYSERR_INVALPARAM;
665 return MMSYSERR_NOERROR;
668 /**************************************************************************
669 * wodClose [internal]
671 static DWORD wodClose(WORD wDevID)
673 DWORD ret = MMSYSERR_NOERROR;
675 TRACE("(%u);\n", wDevID);
677 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
678 WARN("bad device ID !\n");
679 return MMSYSERR_BADDEVICEID;
682 if (WOutDev[wDevID].lpQueueHdr != NULL || WOutDev[wDevID].lpNotifyHdr != NULL) {
683 WARN("buffers still playing !\n");
684 ret = WAVERR_STILLPLAYING;
686 TRACE("imhere[3-close]\n");
687 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_CLOSING, 0, 0);
688 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
689 CloseHandle(WOutDev[wDevID].hEvent);
691 close(WOutDev[wDevID].unixdev);
692 WOutDev[wDevID].unixdev = 0;
693 WOutDev[wDevID].dwFragmentSize = 0;
694 if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
695 WARN("can't notify client !\n");
696 ret = MMSYSERR_INVALPARAM;
702 /**************************************************************************
703 * wodWrite [internal]
706 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
708 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
710 /* first, do the sanity checks... */
711 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
712 WARN("bad dev ID !\n");
713 return MMSYSERR_BADDEVICEID;
716 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
717 return WAVERR_UNPREPARED;
719 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
720 return WAVERR_STILLPLAYING;
722 TRACE("imhere[3-HEADER]\n");
723 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
725 return MMSYSERR_NOERROR;
728 /**************************************************************************
729 * wodPrepare [internal]
731 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
733 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
735 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
736 WARN("bad device ID !\n");
737 return MMSYSERR_BADDEVICEID;
740 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
741 return WAVERR_STILLPLAYING;
743 lpWaveHdr->dwFlags |= WHDR_PREPARED;
744 lpWaveHdr->dwFlags &= ~WHDR_DONE;
745 return MMSYSERR_NOERROR;
748 /**************************************************************************
749 * wodUnprepare [internal]
751 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
753 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
755 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
756 WARN("bad device ID !\n");
757 return MMSYSERR_BADDEVICEID;
760 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
761 return WAVERR_STILLPLAYING;
763 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
764 lpWaveHdr->dwFlags |= WHDR_DONE;
766 return MMSYSERR_NOERROR;
769 /**************************************************************************
770 * wodPause [internal]
772 static DWORD wodPause(WORD wDevID)
774 TRACE("(%u);!\n", wDevID);
776 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
777 WARN("bad device ID !\n");
778 return MMSYSERR_BADDEVICEID;
781 TRACE("imhere[3-PAUSING]\n");
782 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_PAUSING, 0, 0);
783 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
785 return MMSYSERR_NOERROR;
788 /**************************************************************************
789 * wodRestart [internal]
791 static DWORD wodRestart(WORD wDevID)
793 TRACE("(%u);\n", wDevID);
795 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
796 WARN("bad device ID !\n");
797 return MMSYSERR_BADDEVICEID;
800 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
801 TRACE("imhere[3-RESTARTING]\n");
802 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
803 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
806 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
807 /* FIXME: Myst crashes with this ... hmm -MM
808 if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
809 WARN("can't notify client !\n");
810 return MMSYSERR_INVALPARAM;
814 return MMSYSERR_NOERROR;
817 /**************************************************************************
818 * wodReset [internal]
820 static DWORD wodReset(WORD wDevID)
822 TRACE("(%u);\n", wDevID);
824 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
825 WARN("bad device ID !\n");
826 return MMSYSERR_BADDEVICEID;
829 TRACE("imhere[3-RESET]\n");
830 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
831 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
833 return MMSYSERR_NOERROR;
837 /**************************************************************************
838 * wodGetPosition [internal]
840 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
845 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
847 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
848 WARN("bad device ID !\n");
849 return MMSYSERR_BADDEVICEID;
852 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
854 val = WOutDev[wDevID].dwPlayedTotal + WOutDev[wDevID].dwPlayed;
856 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
857 lpTime->wType, WOutDev[wDevID].format.wBitsPerSample,
858 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels,
859 WOutDev[wDevID].format.wf.nAvgBytesPerSec);
860 TRACE("dwTotalPlayed=%lu\n", val);
862 switch (lpTime->wType) {
865 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
868 lpTime->u.sample = val * 8 / WOutDev[wDevID].format.wBitsPerSample;
869 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
872 time = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
873 lpTime->u.smpte.hour = time / 108000;
874 time -= lpTime->u.smpte.hour * 108000;
875 lpTime->u.smpte.min = time / 1800;
876 time -= lpTime->u.smpte.min * 1800;
877 lpTime->u.smpte.sec = time / 30;
878 time -= lpTime->u.smpte.sec * 30;
879 lpTime->u.smpte.frame = time;
880 lpTime->u.smpte.fps = 30;
881 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
882 lpTime->u.smpte.hour, lpTime->u.smpte.min,
883 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
886 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
887 lpTime->wType = TIME_MS;
889 lpTime->u.ms = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
890 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
893 return MMSYSERR_NOERROR;
896 /**************************************************************************
897 * wodGetVolume [internal]
899 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
905 TRACE("(%u, %p);\n", wDevID, lpdwVol);
908 return MMSYSERR_NOTENABLED;
909 if ((mixer = open(MIXER_DEV, O_RDONLY)) < 0) {
910 WARN("mixer device not available !\n");
911 return MMSYSERR_NOTENABLED;
913 if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
914 WARN("unable read mixer !\n");
915 return MMSYSERR_NOTENABLED;
918 left = LOBYTE(volume);
919 right = HIBYTE(volume);
920 TRACE("left=%ld right=%ld !\n", left, right);
921 *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
922 return MMSYSERR_NOERROR;
926 /**************************************************************************
927 * wodSetVolume [internal]
929 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
935 TRACE("(%u, %08lX);\n", wDevID, dwParam);
937 left = (LOWORD(dwParam) * 100) / 0xFFFFl;
938 right = (HIWORD(dwParam) * 100) / 0xFFFFl;
939 volume = left + (right << 8);
941 if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) {
942 WARN("mixer device not available !\n");
943 return MMSYSERR_NOTENABLED;
945 if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
946 WARN("unable set mixer !\n");
947 return MMSYSERR_NOTENABLED;
950 return MMSYSERR_NOERROR;
953 /**************************************************************************
954 * wodGetNumDevs [internal]
956 static DWORD wodGetNumDevs(void)
960 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
961 int audio = open(SOUND_DEV, O_WRONLY, 0);
972 /**************************************************************************
973 * OSS_wodMessage [sample driver]
975 DWORD WINAPI OSS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
976 DWORD dwParam1, DWORD dwParam2)
978 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
979 wDevID, wMsg, dwUser, dwParam1, dwParam2);
986 /* FIXME: Pretend this is supported */
988 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
989 case WODM_CLOSE: return wodClose (wDevID);
990 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
991 case WODM_PAUSE: return wodPause (wDevID);
992 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
993 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
994 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
995 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
996 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2);
997 case WODM_GETNUMDEVS: return wodGetNumDevs ();
998 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
999 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
1000 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1001 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1002 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
1003 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
1004 case WODM_RESTART: return wodRestart (wDevID);
1005 case WODM_RESET: return wodReset (wDevID);
1007 FIXME("unknown message %d!\n", wMsg);
1009 return MMSYSERR_NOTSUPPORTED;
1012 /*======================================================================*
1013 * Low level WAVE IN implemantation *
1014 *======================================================================*/
1016 /**************************************************************************
1017 * widGetDevCaps [internal]
1019 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
1021 int audio, smplrate, samplesize=16, dsp_stereo=1, bytespersmpl;
1023 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1024 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1025 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1026 audio = open(SOUND_DEV, O_RDONLY, 0);
1027 if (audio == -1) return MMSYSERR_ALLOCATED ;
1029 lpCaps->wMid = 0x0002;
1030 lpCaps->wPid = 0x0004;
1031 strcpy(lpCaps->szPname, "SB16 Wave In");
1033 lpCaps->wMid = 0x00FF; /* Manufac ID */
1034 lpCaps->wPid = 0x0001; /* Product ID */
1035 strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
1037 lpCaps->dwFormats = 0x00000000;
1038 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
1039 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
1041 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1042 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
1043 if (lpCaps->wChannels > 1)
1044 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
1045 if (bytespersmpl > 1) {
1046 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
1047 if (lpCaps->wChannels > 1)
1048 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
1052 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1053 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
1054 if (lpCaps->wChannels > 1)
1055 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
1056 if (bytespersmpl > 1) {
1057 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
1058 if (lpCaps->wChannels > 1)
1059 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
1063 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1064 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
1065 if (lpCaps->wChannels > 1)
1066 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
1067 if (bytespersmpl > 1) {
1068 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
1069 if (lpCaps->wChannels > 1)
1070 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
1074 TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
1075 return MMSYSERR_NOERROR;
1078 /**************************************************************************
1079 * widOpen [internal]
1081 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1083 int audio, abuf_size, smplrate, samplesize, dsp_stereo;
1084 LPWAVEFORMAT lpFormat;
1086 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1087 if (lpDesc == NULL) {
1088 WARN("Invalid Parameter !\n");
1089 return MMSYSERR_INVALPARAM;
1091 if (wDevID >= MAX_WAVEINDRV) {
1092 TRACE("MAX_WAVINDRV reached !\n");
1093 return MMSYSERR_ALLOCATED;
1096 /* only PCM format is supported so far... */
1097 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1098 lpDesc->lpFormat->nChannels == 0 ||
1099 lpDesc->lpFormat->nSamplesPerSec == 0) {
1100 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1101 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1102 lpDesc->lpFormat->nSamplesPerSec);
1103 return WAVERR_BADFORMAT;
1106 if (dwFlags & WAVE_FORMAT_QUERY) {
1107 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1108 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1109 lpDesc->lpFormat->nSamplesPerSec);
1110 return MMSYSERR_NOERROR;
1113 WInDev[wDevID].unixdev = 0;
1114 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1115 audio = open(SOUND_DEV, O_RDONLY, 0);
1117 WARN("can't open !\n");
1118 return MMSYSERR_ALLOCATED;
1120 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
1121 if (abuf_size < 1024 || abuf_size > 65536) {
1122 if (abuf_size == -1)
1123 WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1125 WARN("SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize !\n");
1126 return MMSYSERR_NOTENABLED;
1128 WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1130 if (WInDev[wDevID].lpQueueHdr) {
1131 HeapFree(GetProcessHeap(), 0, WInDev[wDevID].lpQueueHdr);
1132 WInDev[wDevID].lpQueueHdr = NULL;
1134 WInDev[wDevID].unixdev = audio;
1135 WInDev[wDevID].dwFragmentSize = abuf_size;
1136 WInDev[wDevID].dwTotalRecorded = 0;
1137 memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1138 lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat;
1140 memcpy(&WInDev[wDevID].format, lpFormat, sizeof(PCMWAVEFORMAT));
1141 WInDev[wDevID].format.wBitsPerSample = 8; /* <-------------- */
1142 if (WInDev[wDevID].format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1143 if (WInDev[wDevID].format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1144 if (WInDev[wDevID].format.wBitsPerSample == 0) {
1145 WInDev[wDevID].format.wBitsPerSample = 8 *
1146 (WInDev[wDevID].format.wf.nAvgBytesPerSec /
1147 WInDev[wDevID].format.wf.nSamplesPerSec) /
1148 WInDev[wDevID].format.wf.nChannels;
1150 samplesize = WInDev[wDevID].format.wBitsPerSample;
1151 smplrate = WInDev[wDevID].format.wf.nSamplesPerSec;
1152 dsp_stereo = (WInDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
1153 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1154 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1155 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1156 TRACE("wBitsPerSample=%u !\n", WInDev[wDevID].format.wBitsPerSample);
1157 TRACE("nSamplesPerSec=%lu !\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1158 TRACE("nChannels=%u !\n", WInDev[wDevID].format.wf.nChannels);
1159 TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1160 if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1161 WARN("can't notify client !\n");
1162 return MMSYSERR_INVALPARAM;
1164 return MMSYSERR_NOERROR;
1167 /**************************************************************************
1168 * widClose [internal]
1170 static DWORD widClose(WORD wDevID)
1172 TRACE("(%u);\n", wDevID);
1173 if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
1174 if (WInDev[wDevID].unixdev == 0) {
1175 WARN("can't close !\n");
1176 return MMSYSERR_NOTENABLED;
1178 if (WInDev[wDevID].lpQueueHdr != NULL) {
1179 WARN("still buffers open !\n");
1180 return WAVERR_STILLPLAYING;
1182 close(WInDev[wDevID].unixdev);
1183 WInDev[wDevID].unixdev = 0;
1184 WInDev[wDevID].dwFragmentSize = 0;
1185 if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1186 WARN("can't notify client !\n");
1187 return MMSYSERR_INVALPARAM;
1189 return MMSYSERR_NOERROR;
1192 /**************************************************************************
1193 * widAddBuffer [internal]
1195 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1200 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1201 if (WInDev[wDevID].unixdev == 0) {
1202 WARN("can't do it !\n");
1203 return MMSYSERR_NOTENABLED;
1205 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1206 TRACE("never been prepared !\n");
1207 return WAVERR_UNPREPARED;
1209 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1210 TRACE("header already in use !\n");
1211 return WAVERR_STILLPLAYING;
1213 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1214 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1215 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1216 lpWaveHdr->dwBytesRecorded = 0;
1217 if (WInDev[wDevID].lpQueueHdr == NULL) {
1218 WInDev[wDevID].lpQueueHdr = lpWaveHdr;
1220 lpWIHdr = WInDev[wDevID].lpQueueHdr;
1221 while (lpWIHdr->lpNext != NULL) {
1222 lpWIHdr = lpWIHdr->lpNext;
1225 lpWIHdr->lpNext = lpWaveHdr;
1226 lpWaveHdr->lpNext = NULL;
1229 TRACE("buffer added ! (now %u in queue)\n", count);
1230 return MMSYSERR_NOERROR;
1233 /**************************************************************************
1234 * widPrepare [internal]
1236 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1238 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1239 if (WInDev[wDevID].unixdev == 0) {
1240 WARN("can't prepare !\n");
1241 return MMSYSERR_NOTENABLED;
1243 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1244 return WAVERR_STILLPLAYING;
1245 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1246 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1247 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1248 lpWaveHdr->dwBytesRecorded = 0;
1249 TRACE("header prepared !\n");
1250 return MMSYSERR_NOERROR;
1253 /**************************************************************************
1254 * widUnprepare [internal]
1256 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1258 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1259 if (WInDev[wDevID].unixdev == 0) {
1260 WARN("can't unprepare !\n");
1261 return MMSYSERR_NOTENABLED;
1263 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1264 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1265 lpWaveHdr->dwFlags |= WHDR_DONE;
1267 return MMSYSERR_NOERROR;
1270 /**************************************************************************
1271 * widStart [internal]
1273 static DWORD widStart(WORD wDevID)
1277 LPWAVEHDR *lpWaveHdr;
1280 TRACE("(%u);\n", wDevID);
1281 if (WInDev[wDevID].unixdev == 0) {
1282 WARN("can't start recording !\n");
1283 return MMSYSERR_NOTENABLED;
1286 lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
1287 TRACE("lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
1289 if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
1290 TRACE("never been prepared !\n");
1291 return WAVERR_UNPREPARED;
1294 while (*lpWaveHdr != NULL) {
1295 lpData = (*lpWaveHdr)->lpData;
1296 TRACE("recording buf#%u=%p size=%lu \n",
1297 count, lpData, (*lpWaveHdr)->dwBufferLength);
1299 bytesRead = read(WInDev[wDevID].unixdev, lpData, (*lpWaveHdr)->dwBufferLength);
1301 if (bytesRead == -1)
1302 perror("read from audio device");
1304 TRACE("bytesread=%d (%ld)\n", bytesRead, (*lpWaveHdr)->dwBufferLength);
1305 (*lpWaveHdr)->dwBytesRecorded = bytesRead;
1306 WInDev[wDevID].dwTotalRecorded += (*lpWaveHdr)->dwBytesRecorded;
1307 (*lpWaveHdr)->dwFlags &= ~WHDR_INQUEUE;
1308 (*lpWaveHdr)->dwFlags |= WHDR_DONE;
1310 if (WAVE_NotifyClient(wDevID, WIM_DATA, (DWORD)*lpWaveHdr, (*lpWaveHdr)->dwBytesRecorded) != MMSYSERR_NOERROR) {
1311 WARN("can't notify client !\n");
1312 return MMSYSERR_INVALPARAM;
1315 /* removes the current block from the queue */
1316 *lpWaveHdr = (*lpWaveHdr)->lpNext;
1319 TRACE("end of recording !\n");
1320 return MMSYSERR_NOERROR;
1323 /**************************************************************************
1324 * widStop [internal]
1326 static DWORD widStop(WORD wDevID)
1328 TRACE("(%u);\n", wDevID);
1329 if (WInDev[wDevID].unixdev == 0) {
1330 WARN("can't stop !\n");
1331 return MMSYSERR_NOTENABLED;
1333 return MMSYSERR_NOERROR;
1336 /**************************************************************************
1337 * widReset [internal]
1339 static DWORD widReset(WORD wDevID)
1341 TRACE("(%u);\n", wDevID);
1342 if (WInDev[wDevID].unixdev == 0) {
1343 WARN("can't reset !\n");
1344 return MMSYSERR_NOTENABLED;
1346 return MMSYSERR_NOERROR;
1349 /**************************************************************************
1350 * widGetPosition [internal]
1352 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1356 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1357 if (WInDev[wDevID].unixdev == 0) {
1358 WARN("can't get pos !\n");
1359 return MMSYSERR_NOTENABLED;
1361 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1362 TRACE("wType=%04X !\n", lpTime->wType);
1363 TRACE("wBitsPerSample=%u\n", WInDev[wDevID].format.wBitsPerSample);
1364 TRACE("nSamplesPerSec=%lu\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1365 TRACE("nChannels=%u\n", WInDev[wDevID].format.wf.nChannels);
1366 TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1367 switch (lpTime->wType) {
1369 lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
1370 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1373 lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
1374 WInDev[wDevID].format.wBitsPerSample;
1375 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1378 time = WInDev[wDevID].dwTotalRecorded /
1379 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1380 lpTime->u.smpte.hour = time / 108000;
1381 time -= lpTime->u.smpte.hour * 108000;
1382 lpTime->u.smpte.min = time / 1800;
1383 time -= lpTime->u.smpte.min * 1800;
1384 lpTime->u.smpte.sec = time / 30;
1385 time -= lpTime->u.smpte.sec * 30;
1386 lpTime->u.smpte.frame = time;
1387 lpTime->u.smpte.fps = 30;
1388 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1389 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1390 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1393 lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1394 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1395 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1398 FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1399 lpTime->wType = TIME_MS;
1401 return MMSYSERR_NOERROR;
1404 /**************************************************************************
1405 * OSS_widMessage [sample driver]
1407 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1408 DWORD dwParam1, DWORD dwParam2)
1410 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1411 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1418 /* FIXME: Pretend this is supported */
1420 case WIDM_OPEN: return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1421 case WIDM_CLOSE: return widClose(wDevID);
1422 case WIDM_ADDBUFFER: return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1423 case WIDM_PREPARE: return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1424 case WIDM_UNPREPARE: return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1425 case WIDM_GETDEVCAPS: return widGetDevCaps(wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
1426 case WIDM_GETNUMDEVS: return wodGetNumDevs(); /* same number of devices in output as in input */
1427 case WIDM_GETPOS: return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
1428 case WIDM_RESET: return widReset(wDevID);
1429 case WIDM_START: return widStart(wDevID);
1430 case WIDM_STOP: return widStop(wDevID);
1432 FIXME("unknown message %u!\n", wMsg);
1434 return MMSYSERR_NOTSUPPORTED;
1437 #else /* !HAVE_OSS */
1439 /**************************************************************************
1440 * wodMessage [sample driver]
1442 DWORD WINAPI OSS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1443 DWORD dwParam1, DWORD dwParam2)
1445 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1446 return MMSYSERR_NOTENABLED;
1449 /**************************************************************************
1450 * widMessage [sample driver]
1452 DWORD WINAPI OSS_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1453 DWORD dwParam1, DWORD dwParam2)
1455 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1456 return MMSYSERR_NOTENABLED;
1459 #endif /* HAVE_OSS */