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*/
21 #include <sys/ioctl.h>
22 #include "wine/winuser16.h"
28 #include "debugtools.h"
30 DEFAULT_DEBUG_CHANNEL(wave)
34 #define SOUND_DEV "/dev/dsp"
35 #define MIXER_DEV "/dev/mixer"
37 #define MAX_WAVEOUTDRV (1)
38 #define MAX_WAVEINDRV (1)
40 /* state diagram for waveOut writing:
42 * +---------+-------------+---------------+---------------------------------+
43 * | state | function | event | new state |
44 * +---------+-------------+---------------+---------------------------------+
45 * | | open() | | STOPPED |
46 * | PAUSED | write() | | PAUSED |
47 * | STOPPED | write() | <thrd create> | PLAYING |
48 * | PLAYING | write() | HEADER | PLAYING |
49 * | (other) | write() | <error> | |
50 * | (any) | pause() | PAUSING | PAUSED |
51 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
52 * | (any) | reset() | RESETTING | STOPPED |
53 * | (any) | close() | CLOSING | CLOSED |
54 * +---------+-------------+---------------+---------------------------------+
57 /* states of the playing device */
58 #define WINE_WS_PLAYING 0
59 #define WINE_WS_PAUSED 1
60 #define WINE_WS_STOPPED 2
61 #define WINE_WS_CLOSED 3
63 /* events to be send to device */
64 #define WINE_WM_PAUSING (WM_USER + 1)
65 #define WINE_WM_RESTARTING (WM_USER + 2)
66 #define WINE_WM_RESETTING (WM_USER + 3)
67 #define WINE_WM_CLOSING (WM_USER + 4)
68 #define WINE_WM_HEADER (WM_USER + 5)
72 volatile int state; /* one of the WINE_WS_ manifest constants */
73 DWORD dwFragmentSize; /* size of OSS buffer fragment */
74 WAVEOPENDESC waveDesc;
77 LPWAVEHDR lpQueueHdr; /* pending buffers for playing */
78 LPWAVEHDR lpNotifyHdr; /* list of wavehdr for which write() has been called, pending for notification */
80 DWORD dwPlayedTotal; /* number of bytes played since opening */
81 DWORD dwPlayed; /* number of bytes played since last DSP_RESET */
82 DWORD dwNotifiedBytes; /* number of bytes for which wavehdr notification has been done */
84 /* info on current lpQueueHdr->lpWaveHdr */
85 DWORD dwOffCurrHdr; /* offset in lpQueueHdr->lpWaveHdr->lpData for fragments */
86 DWORD dwRemain; /* number of bytes to write to end the current fragment */
88 /* synchronization stuff */
93 WORD wMaxFragments; /* max number of fragments that can be written to dsp */
94 WORD wFragsUsedInQueue; /* current number of used fragments inside dsp queue */
100 DWORD dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */
101 WAVEOPENDESC waveDesc;
103 PCMWAVEFORMAT format;
104 LPWAVEHDR lpQueueHdr;
105 DWORD dwTotalRecorded;
108 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
109 static WINE_WAVEIN WInDev [MAX_WAVEOUTDRV];
111 /*======================================================================*
112 * Low level WAVE implemantation *
113 *======================================================================*/
115 /**************************************************************************
116 * WAVE_NotifyClient [internal]
118 static DWORD WAVE_NotifyClient(UINT16 wDevID, WORD wMsg,
119 DWORD dwParam1, DWORD dwParam2)
121 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
127 if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
129 if (WOutDev[wDevID].wFlags != DCB_NULL &&
130 !DriverCallback16(WOutDev[wDevID].waveDesc.dwCallBack,
131 WOutDev[wDevID].wFlags,
132 WOutDev[wDevID].waveDesc.hWave,
134 WOutDev[wDevID].waveDesc.dwInstance,
137 WARN("can't notify client !\n");
138 return MMSYSERR_NOERROR;
145 if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
147 if (WInDev[wDevID].wFlags != DCB_NULL &&
148 !DriverCallback16(WInDev[wDevID].waveDesc.dwCallBack,
149 WInDev[wDevID].wFlags,
150 WInDev[wDevID].waveDesc.hWave,
152 WInDev[wDevID].waveDesc.dwInstance,
155 WARN("can't notify client !\n");
156 return MMSYSERR_NOERROR;
160 FIXME("Unknown CB message %u\n", wMsg);
166 /*======================================================================*
167 * Low level WAVE OUT implemantation *
168 *======================================================================*/
170 /**************************************************************************
171 * wodPlayer_WriteFragments [internal]
173 * wodPlayer helper. Writes as many fragments it can to unixdev.
174 * Returns TRUE in case of buffer underrun.
176 static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
183 TRACE("Fragments: %d used on fd %d\n", wwo->wFragsUsedInQueue, wwo->unixdev);
184 if (wwo->wFragsUsedInQueue == wwo->wMaxFragments) /* output queue is full, wait a bit */
187 lpWaveHdr = wwo->lpQueueHdr;
189 if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */
190 wwo->dwNotifiedBytes >= wwo->dwFragmentSize && /* first fragment has been played */
191 wwo->wFragsUsedInQueue < 2) { /* done with all waveOutWrite()' fragments */
192 /* FIXME: should do better handling here */
193 TRACE("Oooch, buffer underrun !\n");
194 return TRUE; /* force resetting of waveOut device */
196 return FALSE; /* wait a bit */
199 if (wwo->dwOffCurrHdr == 0) {
200 TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
201 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
202 FIXME("NIY: loops (%lu) in wavehdr\n", lpWaveHdr->dwLoops);
205 lpData = ((DWORD)lpWaveHdr == lpWaveHdr->reserved) ?
206 (LPBYTE)lpWaveHdr->lpData : (LPBYTE)PTR_SEG_TO_LIN(lpWaveHdr->lpData);
208 /* finish current wave hdr ? */
209 if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) {
210 DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
212 /* write end of current wave hdr */
213 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
214 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
219 /* move lpWaveHdr to the end of notify list */
220 for (wh = &(wwo->lpNotifyHdr); *wh; wh = &((*wh)->lpNext));
223 wwo->lpQueueHdr = lpWaveHdr->lpNext;
224 lpWaveHdr->lpNext = 0;
226 wwo->dwOffCurrHdr = 0;
227 if ((wwo->dwRemain -= count) == 0) {
228 wwo->dwRemain = wwo->dwFragmentSize;
229 wwo->wFragsUsedInQueue++;
232 continue; /* try to go to use next wavehdr */
234 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
235 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
237 wwo->dwOffCurrHdr += count;
238 wwo->dwRemain = wwo->dwFragmentSize;
239 wwo->wFragsUsedInQueue++;
245 /**************************************************************************
246 * wodPlayer_WriteFragments [internal]
248 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
249 * have been played (actually to speaker, not to unixdev fd).
251 static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
256 /* get effective number of written bytes */
258 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOPTR, &cinfo) == -1) {
259 perror("ioctl SNDCTL_DSP_GETOPTR");
261 wwo->state = WINE_WS_STOPPED;
264 TRACE("Played %d bytes (played=%ld) on fd %d\n", cinfo.bytes, wwo->dwPlayed, wwo->unixdev);
265 wwo->wFragsUsedInQueue -= cinfo.bytes / wwo->dwFragmentSize - wwo->dwPlayed / wwo->dwFragmentSize;
266 wwo->dwPlayed = cinfo.bytes;
268 if (force || cinfo.bytes > wwo->dwNotifiedBytes) {
269 /* remove all wavehdr which can be notified */
270 while (wwo->lpNotifyHdr &&
271 (force || (cinfo.bytes >= wwo->dwNotifiedBytes + wwo->lpNotifyHdr->dwBufferLength))) {
272 lpWaveHdr = wwo->lpNotifyHdr;
274 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
275 lpWaveHdr->dwFlags |= WHDR_DONE;
277 wwo->dwNotifiedBytes += lpWaveHdr->dwBufferLength;
278 wwo->lpNotifyHdr = lpWaveHdr->lpNext;
280 TRACE("Notifying client with %p\n", lpWaveHdr);
281 if (WAVE_NotifyClient(uDevID, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
282 WARN("can't notify client !\n");
288 /**************************************************************************
289 * wodPlayer_Reset [internal]
291 * wodPlayer helper. Resets current output stream.
293 static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
297 /* updates current notify list */
298 wodPlayer_Notify(wwo, uDevID, FALSE);
300 /* flush all possible output */
301 if (ioctl(wwo->unixdev, SNDCTL_DSP_RESET, 0) == -1) {
302 perror("ioctl SNDCTL_DSP_RESET");
304 wwo->state = WINE_WS_STOPPED;
308 wwo->dwOffCurrHdr = 0;
310 /* empty notify list */
311 wodPlayer_Notify(wwo, uDevID, TRUE);
312 if (wwo->lpNotifyHdr) {
313 ERR("out of sync\n");
315 /* get rid also of all the current queue */
316 for (lpWaveHdr = wwo->lpQueueHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
317 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
318 lpWaveHdr->dwFlags |= WHDR_DONE;
320 if (WAVE_NotifyClient(uDevID, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
321 WARN("can't notify client !\n");
325 wwo->state = WINE_WS_STOPPED;
326 wwo->dwPlayedTotal = 0;
328 /* move notify list to begining of lpQueueHdr list */
329 while (wwo->lpNotifyHdr) {
330 lpWaveHdr = wwo->lpNotifyHdr;
331 wwo->lpNotifyHdr = lpWaveHdr->lpNext;
332 lpWaveHdr->lpNext = wwo->lpQueueHdr;
333 wwo->lpQueueHdr = lpWaveHdr;
335 wwo->state = WINE_WS_PAUSED;
336 wwo->dwPlayedTotal += wwo->dwPlayed;
338 wwo->dwNotifiedBytes = wwo->dwPlayed = 0;
339 wwo->wFragsUsedInQueue = 0;
342 /**************************************************************************
343 * wodPlayer [internal]
345 static DWORD CALLBACK wodPlayer(LPVOID pmt)
347 WORD uDevID = (DWORD)pmt;
348 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
353 PeekMessageA(&msg, 0, 0, 0, 0);
354 wwo->state = WINE_WS_STOPPED;
356 wwo->dwNotifiedBytes = 0;
357 wwo->dwOffCurrHdr = 0;
358 wwo->dwRemain = wwo->dwFragmentSize;
359 wwo->lpQueueHdr = NULL;
360 wwo->lpNotifyHdr = NULL;
361 wwo->wFragsUsedInQueue = 0;
362 wwo->dwPlayedTotal = 0;
365 TRACE("imhere[0]\n");
366 SetEvent(wwo->hEvent);
368 /* make sleep time to be # of ms to output a fragment */
369 dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
372 /* wait for dwSleepTime or an event in thread's queue */
373 /* FIXME: could improve wait time depending on queue state,
374 * ie, number of queued fragments
376 TRACE("imhere[1]\n");
377 MsgWaitForMultipleObjects(0, NULL, FALSE,
378 (wwo->state == WINE_WS_PLAYING) ?
379 (MAX(wwo->wFragsUsedInQueue, 4) - 2) * dwSleepTime :
382 TRACE("imhere[2]\n");
383 wodPlayer_Notify(wwo, uDevID, FALSE);
384 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
385 switch (msg.message) {
386 case WINE_WM_PAUSING:
387 wodPlayer_Reset(wwo, uDevID, FALSE);
388 wwo->state = WINE_WS_PAUSED;
389 SetEvent(wwo->hEvent);
391 case WINE_WM_RESTARTING:
392 wwo->state = WINE_WS_PLAYING;
393 SetEvent(wwo->hEvent);
396 lpWaveHdr = (LPWAVEHDR)msg.lParam;
398 lpWaveHdr->dwFlags &= ~WHDR_DONE;
399 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
400 lpWaveHdr->lpNext = 0;
402 /* insert buffer at the end of queue */
405 for (wh = &(wwo->lpQueueHdr); *wh; wh = &((*wh)->lpNext));
408 if (wwo->state == WINE_WS_STOPPED)
409 wwo->state = WINE_WS_PLAYING;
411 case WINE_WM_RESETTING:
412 wodPlayer_Reset(wwo, uDevID, TRUE);
413 SetEvent(wwo->hEvent);
415 case WINE_WM_CLOSING:
416 /* sanity check: this should not happen since the device must have been reset before */
417 if (wwo->lpNotifyHdr || wwo->lpQueueHdr) {
418 ERR("out of sync\n");
421 wwo->state = WINE_WS_CLOSED;
422 SetEvent(wwo->hEvent);
424 /* shouldn't go here */
426 FIXME("unknown message %d\n", msg.message);
430 if (wwo->state == WINE_WS_PLAYING) {
431 wodPlayer_WriteFragments(wwo);
433 wodPlayer_Notify(wwo, uDevID, FALSE);
436 /* just for not generating compilation warnings... should never be executed */
440 /**************************************************************************
441 * wodGetDevCaps [internal]
443 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS16 lpCaps, DWORD dwSize)
453 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
455 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
456 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
458 if (wDevID >= MAX_WAVEOUTDRV) {
459 TRACE("MAX_WAVOUTDRV reached !\n");
460 return MMSYSERR_BADDEVICEID;
463 if (WOutDev[wDevID].unixdev == 0) {
464 audio = open(SOUND_DEV, O_WRONLY, 0);
465 if (audio == -1) return MMSYSERR_ALLOCATED;
467 audio = WOutDev[wDevID].unixdev;
470 /* FIXME: some programs compare this string against the content of the registry
471 * for MM drivers. The name have to match in order the program to work
472 * (e.g. MS win9x mplayer.exe)
475 lpCaps->wMid = 0x0002;
476 lpCaps->wPid = 0x0104;
477 strcpy(lpCaps->szPname, "SB16 Wave Out");
479 lpCaps->wMid = 0x00FF; /* Manufac ID */
480 lpCaps->wPid = 0x0001; /* Product ID */
481 /* strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");*/
482 strcpy(lpCaps->szPname, "CS4236/37/38");
484 lpCaps->vDriverVersion = 0x0100;
485 lpCaps->dwFormats = 0x00000000;
486 lpCaps->dwSupport = WAVECAPS_VOLUME;
488 IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
489 TRACE("OSS dsp mask=%08x\n", mask);
491 IOCTL(audio, SNDCTL_DSP_SETFMT, mask);
492 TRACE("OSS dsp current=%08x\n", mask);
494 /* First bytespersampl, then stereo */
495 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
497 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
498 if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
501 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
502 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
503 if (lpCaps->wChannels > 1)
504 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
505 if (bytespersmpl > 1) {
506 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
507 if (lpCaps->wChannels > 1)
508 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
512 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
513 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
514 if (lpCaps->wChannels > 1)
515 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
516 if (bytespersmpl > 1) {
517 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
518 if (lpCaps->wChannels > 1)
519 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
523 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
524 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
525 if (lpCaps->wChannels > 1)
526 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
527 if (bytespersmpl > 1) {
528 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
529 if (lpCaps->wChannels > 1)
530 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
533 if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
534 if ((caps & DSP_CAP_REALTIME) && !(caps && DSP_CAP_BATCH))
535 lpCaps->dwFormats |= WAVECAPS_SAMPLEACCURATE;
537 if (WOutDev[wDevID].unixdev == 0) {
540 TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
541 return MMSYSERR_NOERROR;
544 /**************************************************************************
547 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
557 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
558 if (lpDesc == NULL) {
559 WARN("Invalid Parameter !\n");
560 return MMSYSERR_INVALPARAM;
562 if (wDevID >= MAX_WAVEOUTDRV) {
563 TRACE("MAX_WAVOUTDRV reached !\n");
564 return MMSYSERR_BADDEVICEID;
566 wodGetDevCaps(wDevID, &woc, sizeof(woc));
568 /* only PCM format is supported so far... */
569 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
570 lpDesc->lpFormat->nChannels == 0 ||
571 lpDesc->lpFormat->nSamplesPerSec == 0) {
572 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
573 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
574 lpDesc->lpFormat->nSamplesPerSec);
575 return WAVERR_BADFORMAT;
578 if (dwFlags & WAVE_FORMAT_QUERY)
579 return MMSYSERR_NOERROR;
581 WOutDev[wDevID].unixdev = 0;
582 if (access(SOUND_DEV, 0) != 0)
583 return MMSYSERR_NOTENABLED;
584 audio = open(SOUND_DEV, O_WRONLY, 0);
586 WARN("can't open !\n");
587 return MMSYSERR_ALLOCATED ;
590 WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
592 memcpy(&WOutDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
593 memcpy(&WOutDev[wDevID].format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
595 if (WOutDev[wDevID].format.wBitsPerSample == 0) {
596 WOutDev[wDevID].format.wBitsPerSample = 8 *
597 (WOutDev[wDevID].format.wf.nAvgBytesPerSec /
598 WOutDev[wDevID].format.wf.nSamplesPerSec) /
599 WOutDev[wDevID].format.wf.nChannels;
602 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
603 * thus leading to 46ms per fragment, and a turnaround time of 185ms
605 /* 2^10=1024 bytes per fragment, 16 fragments max */
606 audio_fragment = 0x000F000A;
607 sample_size = WOutDev[wDevID].format.wBitsPerSample;
608 sample_rate = WOutDev[wDevID].format.wf.nSamplesPerSec;
609 dsp_stereo = (WOutDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
611 IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
612 /* First size and stereo then samplerate */
613 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, sample_size);
614 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
615 IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
617 /* even if we set fragment size above, read it again, just in case */
618 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
620 WOutDev[wDevID].unixdev = audio;
621 WOutDev[wDevID].dwFragmentSize = fragment_size;
622 WOutDev[wDevID].wMaxFragments = HIWORD(audio_fragment) + 1;
624 WOutDev[wDevID].hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
625 WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(WOutDev[wDevID].dwThreadID));
626 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
628 TRACE("fd=%d fragmentSize=%ld\n",
629 WOutDev[wDevID].unixdev, WOutDev[wDevID].dwFragmentSize);
631 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u !\n",
632 WOutDev[wDevID].format.wBitsPerSample, WOutDev[wDevID].format.wf.nAvgBytesPerSec,
633 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels);
635 if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
636 WARN("can't notify client !\n");
637 return MMSYSERR_INVALPARAM;
639 return MMSYSERR_NOERROR;
642 /**************************************************************************
643 * wodClose [internal]
645 static DWORD wodClose(WORD wDevID)
647 DWORD ret = MMSYSERR_NOERROR;
649 TRACE("(%u);\n", wDevID);
651 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
652 WARN("bad device ID !\n");
653 return MMSYSERR_BADDEVICEID;
656 if (WOutDev[wDevID].lpQueueHdr != NULL || WOutDev[wDevID].lpNotifyHdr != NULL) {
657 WARN("buffers still playing !\n");
658 ret = WAVERR_STILLPLAYING;
660 TRACE("imhere[3-close]\n");
661 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_CLOSING, 0, 0);
662 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
663 CloseHandle(WOutDev[wDevID].hEvent);
665 close(WOutDev[wDevID].unixdev);
666 WOutDev[wDevID].unixdev = 0;
667 WOutDev[wDevID].dwFragmentSize = 0;
668 if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
669 WARN("can't notify client !\n");
670 ret = MMSYSERR_INVALPARAM;
676 /**************************************************************************
677 * wodWrite [internal]
680 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
682 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
684 /* first, do the sanity checks... */
685 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
686 WARN("bad dev ID !\n");
687 return MMSYSERR_BADDEVICEID;
690 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
691 return WAVERR_UNPREPARED;
693 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
694 return WAVERR_STILLPLAYING;
696 TRACE("imhere[3-HEADER]\n");
697 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
699 return MMSYSERR_NOERROR;
702 /**************************************************************************
703 * wodPrepare [internal]
705 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
707 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
709 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
710 WARN("bad device ID !\n");
711 return MMSYSERR_BADDEVICEID;
714 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
715 return WAVERR_STILLPLAYING;
717 lpWaveHdr->dwFlags |= WHDR_PREPARED;
718 lpWaveHdr->dwFlags &= ~WHDR_DONE;
719 return MMSYSERR_NOERROR;
722 /**************************************************************************
723 * wodUnprepare [internal]
725 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
727 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
729 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
730 WARN("bad device ID !\n");
731 return MMSYSERR_BADDEVICEID;
734 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
735 return WAVERR_STILLPLAYING;
737 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
738 lpWaveHdr->dwFlags |= WHDR_DONE;
740 return MMSYSERR_NOERROR;
743 /**************************************************************************
744 * wodPause [internal]
746 static DWORD wodPause(WORD wDevID)
748 TRACE("(%u);!\n", wDevID);
750 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
751 WARN("bad device ID !\n");
752 return MMSYSERR_BADDEVICEID;
755 TRACE("imhere[3-PAUSING]\n");
756 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_PAUSING, 0, 0);
757 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
759 return MMSYSERR_NOERROR;
762 /**************************************************************************
763 * wodRestart [internal]
765 static DWORD wodRestart(WORD wDevID)
767 TRACE("(%u);\n", wDevID);
769 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
770 WARN("bad device ID !\n");
771 return MMSYSERR_BADDEVICEID;
774 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
775 TRACE("imhere[3-RESTARTING]\n");
776 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
777 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
780 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
781 /* FIXME: Myst crashes with this ... hmm -MM
782 if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
783 WARN("can't notify client !\n");
784 return MMSYSERR_INVALPARAM;
788 return MMSYSERR_NOERROR;
791 /**************************************************************************
792 * wodReset [internal]
794 static DWORD wodReset(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 TRACE("imhere[3-RESET]\n");
804 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
805 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
807 return MMSYSERR_NOERROR;
811 /**************************************************************************
812 * wodGetPosition [internal]
814 static DWORD wodGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
819 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
821 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
822 WARN("bad device ID !\n");
823 return MMSYSERR_BADDEVICEID;
826 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
828 val = WOutDev[wDevID].dwPlayedTotal + WOutDev[wDevID].dwPlayed;
830 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
831 lpTime->wType, WOutDev[wDevID].format.wBitsPerSample,
832 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels,
833 WOutDev[wDevID].format.wf.nAvgBytesPerSec);
834 TRACE("dwTotalPlayed=%lu\n", val);
836 switch (lpTime->wType) {
839 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
842 lpTime->u.sample = val * 8 / WOutDev[wDevID].format.wBitsPerSample;
843 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
846 time = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
847 lpTime->u.smpte.hour = time / 108000;
848 time -= lpTime->u.smpte.hour * 108000;
849 lpTime->u.smpte.min = time / 1800;
850 time -= lpTime->u.smpte.min * 1800;
851 lpTime->u.smpte.sec = time / 30;
852 time -= lpTime->u.smpte.sec * 30;
853 lpTime->u.smpte.frame = time;
854 lpTime->u.smpte.fps = 30;
855 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
856 lpTime->u.smpte.hour, lpTime->u.smpte.min,
857 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
860 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
861 lpTime->wType = TIME_MS;
863 lpTime->u.ms = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
864 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
867 return MMSYSERR_NOERROR;
870 /**************************************************************************
871 * wodGetVolume [internal]
873 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
879 TRACE("(%u, %p);\n", wDevID, lpdwVol);
882 return MMSYSERR_NOTENABLED;
883 if ((mixer = open(MIXER_DEV, O_RDONLY)) < 0) {
884 WARN("mixer device not available !\n");
885 return MMSYSERR_NOTENABLED;
887 if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
888 WARN("unable read mixer !\n");
889 return MMSYSERR_NOTENABLED;
892 left = LOBYTE(volume);
893 right = HIBYTE(volume);
894 TRACE("left=%ld right=%ld !\n", left, right);
895 *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
896 return MMSYSERR_NOERROR;
900 /**************************************************************************
901 * wodSetVolume [internal]
903 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
909 TRACE("(%u, %08lX);\n", wDevID, dwParam);
911 left = (LOWORD(dwParam) * 100) / 0xFFFFl;
912 right = (HIWORD(dwParam) * 100) / 0xFFFFl;
913 volume = left + (right << 8);
915 if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) {
916 WARN("mixer device not available !\n");
917 return MMSYSERR_NOTENABLED;
919 if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
920 WARN("unable set mixer !\n");
921 return MMSYSERR_NOTENABLED;
924 return MMSYSERR_NOERROR;
927 /**************************************************************************
928 * wodGetNumDevs [internal]
930 static DWORD wodGetNumDevs(void)
934 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
935 int audio = open(SOUND_DEV, O_WRONLY, 0);
946 /**************************************************************************
947 * wodMessage [sample driver]
949 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
950 DWORD dwParam1, DWORD dwParam2)
952 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
953 wDevID, wMsg, dwUser, dwParam1, dwParam2);
956 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
957 case WODM_CLOSE: return wodClose (wDevID);
958 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
959 case WODM_PAUSE: return wodPause (wDevID);
960 /* case WODM_STOP: */
961 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME16)dwParam1, dwParam2);
962 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
963 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
964 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
965 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPS16)dwParam1, dwParam2);
966 case WODM_GETNUMDEVS: return wodGetNumDevs ();
967 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
968 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
969 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
970 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
971 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
972 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
973 case WODM_RESTART: return wodRestart (wDevID);
974 case WODM_RESET: return wodReset (wDevID);
976 FIXME("unknown message %d!\n", wMsg);
978 return MMSYSERR_NOTSUPPORTED;
981 /*======================================================================*
982 * Low level WAVE IN implemantation *
983 *======================================================================*/
985 /**************************************************************************
986 * widGetDevCaps [internal]
988 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS16 lpCaps, DWORD dwSize)
990 int audio,smplrate,samplesize=16,dsp_stereo=1,bytespersmpl;
992 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
993 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
994 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
995 audio = open(SOUND_DEV, O_RDONLY, 0);
996 if (audio == -1) return MMSYSERR_ALLOCATED ;
998 lpCaps->wMid = 0x0002;
999 lpCaps->wPid = 0x0004;
1000 strcpy(lpCaps->szPname, "SB16 Wave In");
1002 lpCaps->wMid = 0x00FF; /* Manufac ID */
1003 lpCaps->wPid = 0x0001; /* Product ID */
1004 strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
1006 lpCaps->dwFormats = 0x00000000;
1007 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
1008 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
1010 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1011 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
1012 if (lpCaps->wChannels > 1)
1013 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
1014 if (bytespersmpl > 1) {
1015 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
1016 if (lpCaps->wChannels > 1)
1017 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
1021 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1022 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
1023 if (lpCaps->wChannels > 1)
1024 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
1025 if (bytespersmpl > 1) {
1026 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
1027 if (lpCaps->wChannels > 1)
1028 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
1032 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1033 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
1034 if (lpCaps->wChannels > 1)
1035 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
1036 if (bytespersmpl > 1) {
1037 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
1038 if (lpCaps->wChannels > 1)
1039 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
1043 TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
1044 return MMSYSERR_NOERROR;
1047 /**************************************************************************
1048 * widOpen [internal]
1050 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1052 int audio,abuf_size,smplrate,samplesize,dsp_stereo;
1053 LPWAVEFORMAT lpFormat;
1055 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1056 if (lpDesc == NULL) {
1057 WARN("Invalid Parameter !\n");
1058 return MMSYSERR_INVALPARAM;
1060 if (wDevID >= MAX_WAVEINDRV) {
1061 TRACE("MAX_WAVINDRV reached !\n");
1062 return MMSYSERR_ALLOCATED;
1064 WInDev[wDevID].unixdev = 0;
1065 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1066 audio = open(SOUND_DEV, O_RDONLY, 0);
1068 WARN("can't open !\n");
1069 return MMSYSERR_ALLOCATED;
1071 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
1072 if (abuf_size < 1024 || abuf_size > 65536) {
1073 if (abuf_size == -1)
1074 WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1076 WARN("SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize !\n");
1077 return MMSYSERR_NOTENABLED;
1079 WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1081 if (WInDev[wDevID].lpQueueHdr) {
1082 HeapFree(GetProcessHeap(), 0, WInDev[wDevID].lpQueueHdr);
1083 WInDev[wDevID].lpQueueHdr = NULL;
1085 WInDev[wDevID].unixdev = audio;
1086 WInDev[wDevID].dwFragmentSize = abuf_size;
1087 WInDev[wDevID].dwTotalRecorded = 0;
1088 memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1089 lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat;
1090 if (lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
1091 WARN("Bad format %04X !\n",
1092 lpFormat->wFormatTag);
1093 return WAVERR_BADFORMAT;
1095 memcpy(&WInDev[wDevID].format, lpFormat, sizeof(PCMWAVEFORMAT));
1096 WInDev[wDevID].format.wBitsPerSample = 8; /* <-------------- */
1097 if (WInDev[wDevID].format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1098 if (WInDev[wDevID].format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1099 if (WInDev[wDevID].format.wBitsPerSample == 0) {
1100 WInDev[wDevID].format.wBitsPerSample = 8 *
1101 (WInDev[wDevID].format.wf.nAvgBytesPerSec /
1102 WInDev[wDevID].format.wf.nSamplesPerSec) /
1103 WInDev[wDevID].format.wf.nChannels;
1105 samplesize = WInDev[wDevID].format.wBitsPerSample;
1106 smplrate = WInDev[wDevID].format.wf.nSamplesPerSec;
1107 dsp_stereo = (WInDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
1108 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1109 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1110 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1111 TRACE("wBitsPerSample=%u !\n", WInDev[wDevID].format.wBitsPerSample);
1112 TRACE("nSamplesPerSec=%lu !\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1113 TRACE("nChannels=%u !\n", WInDev[wDevID].format.wf.nChannels);
1114 TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1115 if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1116 WARN("can't notify client !\n");
1117 return MMSYSERR_INVALPARAM;
1119 return MMSYSERR_NOERROR;
1122 /**************************************************************************
1123 * widClose [internal]
1125 static DWORD widClose(WORD wDevID)
1127 TRACE("(%u);\n", wDevID);
1128 if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
1129 if (WInDev[wDevID].unixdev == 0) {
1130 WARN("can't close !\n");
1131 return MMSYSERR_NOTENABLED;
1133 if (WInDev[wDevID].lpQueueHdr != NULL) {
1134 WARN("still buffers open !\n");
1135 return WAVERR_STILLPLAYING;
1137 close(WInDev[wDevID].unixdev);
1138 WInDev[wDevID].unixdev = 0;
1139 WInDev[wDevID].dwFragmentSize = 0;
1140 if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1141 WARN("can't notify client !\n");
1142 return MMSYSERR_INVALPARAM;
1144 return MMSYSERR_NOERROR;
1147 /**************************************************************************
1148 * widAddBuffer [internal]
1150 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1155 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1156 if (WInDev[wDevID].unixdev == 0) {
1157 WARN("can't do it !\n");
1158 return MMSYSERR_NOTENABLED;
1160 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1161 TRACE("never been prepared !\n");
1162 return WAVERR_UNPREPARED;
1164 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1165 TRACE("header already in use !\n");
1166 return WAVERR_STILLPLAYING;
1168 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1169 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1170 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1171 lpWaveHdr->dwBytesRecorded = 0;
1172 if (WInDev[wDevID].lpQueueHdr == NULL) {
1173 WInDev[wDevID].lpQueueHdr = lpWaveHdr;
1175 lpWIHdr = WInDev[wDevID].lpQueueHdr;
1176 while (lpWIHdr->lpNext != NULL) {
1177 lpWIHdr = lpWIHdr->lpNext;
1180 lpWIHdr->lpNext = lpWaveHdr;
1181 lpWaveHdr->lpNext = NULL;
1184 TRACE("buffer added ! (now %u in queue)\n", count);
1185 return MMSYSERR_NOERROR;
1188 /**************************************************************************
1189 * widPrepare [internal]
1191 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1193 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1194 if (WInDev[wDevID].unixdev == 0) {
1195 WARN("can't prepare !\n");
1196 return MMSYSERR_NOTENABLED;
1198 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1199 return WAVERR_STILLPLAYING;
1200 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1201 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1202 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1203 lpWaveHdr->dwBytesRecorded = 0;
1204 TRACE("header prepared !\n");
1205 return MMSYSERR_NOERROR;
1208 /**************************************************************************
1209 * widUnprepare [internal]
1211 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1213 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1214 if (WInDev[wDevID].unixdev == 0) {
1215 WARN("can't unprepare !\n");
1216 return MMSYSERR_NOTENABLED;
1218 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1219 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1220 lpWaveHdr->dwFlags |= WHDR_DONE;
1222 return MMSYSERR_NOERROR;
1225 /**************************************************************************
1226 * widStart [internal]
1228 static DWORD widStart(WORD wDevID)
1232 LPWAVEHDR *lpWaveHdr;
1235 TRACE("(%u);\n", wDevID);
1236 if (WInDev[wDevID].unixdev == 0) {
1237 WARN("can't start recording !\n");
1238 return MMSYSERR_NOTENABLED;
1241 lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
1242 TRACE("lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
1244 if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
1245 TRACE("never been prepared !\n");
1246 return WAVERR_UNPREPARED;
1249 while (*lpWaveHdr != NULL) {
1251 lpData = ((DWORD)*lpWaveHdr == (*lpWaveHdr)->reserved) ?
1252 (LPBYTE)(*lpWaveHdr)->lpData : (LPBYTE)PTR_SEG_TO_LIN((*lpWaveHdr)->lpData);
1254 TRACE("recording buf#%u=%p size=%lu \n",
1255 count, lpData, (*lpWaveHdr)->dwBufferLength);
1257 bytesRead = read(WInDev[wDevID].unixdev, lpData, (*lpWaveHdr)->dwBufferLength);
1259 if (bytesRead == -1)
1260 perror("read from audio device");
1262 TRACE("bytesread=%d (%ld)\n", bytesRead, (*lpWaveHdr)->dwBufferLength);
1263 (*lpWaveHdr)->dwBytesRecorded = bytesRead;
1264 WInDev[wDevID].dwTotalRecorded += (*lpWaveHdr)->dwBytesRecorded;
1265 (*lpWaveHdr)->dwFlags &= ~WHDR_INQUEUE;
1266 (*lpWaveHdr)->dwFlags |= WHDR_DONE;
1268 if (WAVE_NotifyClient(wDevID, WIM_DATA, (*lpWaveHdr)->reserved, (*lpWaveHdr)->dwBytesRecorded) != MMSYSERR_NOERROR) {
1269 WARN("can't notify client !\n");
1270 return MMSYSERR_INVALPARAM;
1272 /* removes the current block from the queue */
1273 *lpWaveHdr = (*lpWaveHdr)->lpNext;
1276 TRACE("end of recording !\n");
1277 return MMSYSERR_NOERROR;
1280 /**************************************************************************
1281 * widStop [internal]
1283 static DWORD widStop(WORD wDevID)
1285 TRACE("(%u);\n", wDevID);
1286 if (WInDev[wDevID].unixdev == 0) {
1287 WARN("can't stop !\n");
1288 return MMSYSERR_NOTENABLED;
1290 return MMSYSERR_NOERROR;
1293 /**************************************************************************
1294 * widReset [internal]
1296 static DWORD widReset(WORD wDevID)
1298 TRACE("(%u);\n", wDevID);
1299 if (WInDev[wDevID].unixdev == 0) {
1300 WARN("can't reset !\n");
1301 return MMSYSERR_NOTENABLED;
1303 return MMSYSERR_NOERROR;
1306 /**************************************************************************
1307 * widGetPosition [internal]
1309 static DWORD widGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
1313 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1314 if (WInDev[wDevID].unixdev == 0) {
1315 WARN("can't get pos !\n");
1316 return MMSYSERR_NOTENABLED;
1318 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1319 TRACE("wType=%04X !\n", lpTime->wType);
1320 TRACE("wBitsPerSample=%u\n", WInDev[wDevID].format.wBitsPerSample);
1321 TRACE("nSamplesPerSec=%lu\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1322 TRACE("nChannels=%u\n", WInDev[wDevID].format.wf.nChannels);
1323 TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1324 switch (lpTime->wType) {
1326 lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
1327 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1330 lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
1331 WInDev[wDevID].format.wBitsPerSample;
1332 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1335 time = WInDev[wDevID].dwTotalRecorded /
1336 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1337 lpTime->u.smpte.hour = time / 108000;
1338 time -= lpTime->u.smpte.hour * 108000;
1339 lpTime->u.smpte.min = time / 1800;
1340 time -= lpTime->u.smpte.min * 1800;
1341 lpTime->u.smpte.sec = time / 30;
1342 time -= lpTime->u.smpte.sec * 30;
1343 lpTime->u.smpte.frame = time;
1344 lpTime->u.smpte.fps = 30;
1345 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1346 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1347 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1350 lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1351 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1352 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1355 FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1356 lpTime->wType = TIME_MS;
1358 return MMSYSERR_NOERROR;
1361 /**************************************************************************
1362 * widMessage [sample driver]
1364 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1365 DWORD dwParam1, DWORD dwParam2)
1367 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1368 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1371 case WIDM_OPEN: return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1372 case WIDM_CLOSE: return widClose(wDevID);
1373 case WIDM_ADDBUFFER: return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1374 case WIDM_PREPARE: return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1375 case WIDM_UNPREPARE: return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1376 case WIDM_GETDEVCAPS: return widGetDevCaps(wDevID, (LPWAVEINCAPS16)dwParam1,dwParam2);
1377 case WIDM_GETNUMDEVS: return wodGetNumDevs(); /* same number of devices in output as in input */
1378 case WIDM_GETPOS: return widGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
1379 case WIDM_RESET: return widReset(wDevID);
1380 case WIDM_START: return widStart(wDevID);
1381 case WIDM_STOP: return widStop(wDevID);
1383 FIXME("unknown message %u!\n", wMsg);
1385 return MMSYSERR_NOTSUPPORTED;
1388 /*======================================================================*
1389 * Low level WAVE implemantation - DriverProc *
1390 *======================================================================*/
1392 /**************************************************************************
1393 * WAVE_DriverProc [sample driver]
1395 LONG CALLBACK WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1396 DWORD dwParam1, DWORD dwParam2)
1398 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1400 case DRV_LOAD: return 1;
1401 case DRV_FREE: return 1;
1402 case DRV_OPEN: return 1;
1403 case DRV_CLOSE: return 1;
1404 case DRV_ENABLE: return 1;
1405 case DRV_DISABLE: return 1;
1406 case DRV_QUERYCONFIGURE: return 1;
1407 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
1408 case DRV_INSTALL: return DRVCNF_RESTART;
1409 case DRV_REMOVE: return DRVCNF_RESTART;
1411 FIXME("is probably wrong msg=0x%04lx\n", wMsg);
1412 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1414 return MMSYSERR_NOTENABLED;
1417 #else /* !HAVE_OSS */
1419 /**************************************************************************
1420 * wodMessage [sample driver]
1422 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1423 DWORD dwParam1, DWORD dwParam2)
1425 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1426 return MMSYSERR_NOTENABLED;
1429 /**************************************************************************
1430 * widMessage [sample driver]
1432 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1433 DWORD dwParam1, DWORD dwParam2)
1435 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1436 return MMSYSERR_NOTENABLED;
1439 /**************************************************************************
1440 * WAVE_DriverProc [sample driver]
1442 LONG CALLBACK WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1443 DWORD dwParam1, DWORD dwParam2)
1445 return MMSYSERR_NOTENABLED;
1447 #endif /* HAVE_OSS */