Check if in depth-conversion mode before returning an error message
[wine/multimedia.git] / multimedia / audio.c
blob870f26959ce50cd1f475932c0d694a5e2398ca33
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3 * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
5 * Copyright 1994 Martin Ayotte
6 * 1999 Eric Pouech (async playing in waveOut)
7 */
8 /*
9 * FIXME:
10 * pause in waveOut does not work correctly
11 * implement async handling in waveIn
14 /*#define EMULATE_SB16*/
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <sys/ioctl.h>
22 #include "wine/winuser16.h"
23 #include "driver.h"
24 #include "multimedia.h"
25 #include "heap.h"
26 #include "ldt.h"
27 #include "debug.h"
29 DEFAULT_DEBUG_CHANNEL(wave)
31 #ifdef HAVE_OSS
33 #define SOUND_DEV "/dev/dsp"
34 #define MIXER_DEV "/dev/mixer"
36 #define MAX_WAVEOUTDRV (1)
37 #define MAX_WAVEINDRV (1)
39 /* state diagram for waveOut writing:
40 * FIXME: I'm not 100% pleased with the handling of RESETTING and CLOSING which are
41 * temporary states.
42 * Basically, they are events handled in waveOutXXXX functions (open, write, pause, restart),
43 * and the others (resetn close) which are handled by the player. We (I ?) use transient
44 * states (RESETTING and CLOSING) to send the event from the waveOutXXX functions to
45 * the player. Not very good.
47 * open() STOPPED
48 * PAUSED write() PAUSED
49 * STOPPED write() PLAYING
50 * (other) write() <error>
51 * (any) pause() PAUSED
52 * PAUSED restart() PLAYING if hdr, STOPPED otherwise
53 * reset() RESETTING => STOPPED
54 * close() CLOSING => CLOSED
57 #define WINE_WS_PLAYING 0
58 #define WINE_WS_PAUSED 1
59 #define WINE_WS_STOPPED 2
60 #define WINE_WS_RESETTING 3
61 #define WINE_WS_CLOSING 4
62 #define WINE_WS_CLOSED 5
64 typedef struct {
65 int unixdev;
66 volatile int state; /* one of the WINE_WS_ manifest constants */
67 DWORD dwFragmentSize;
68 WAVEOPENDESC waveDesc;
69 WORD wFlags;
70 PCMWAVEFORMAT format;
71 LPWAVEHDR lpQueueHdr;
72 DWORD dwTotalPlayed;
73 HANDLE hThread;
74 CRITICAL_SECTION critSect;
75 /* used only by wodPlayer */
76 DWORD dwOffCurrHdr; /* offset in lpQueueHdr->lpWaveHdr->lpData for fragments */
77 DWORD dwRemain; /* number of bytes to write to end the current fragment */
78 DWORD dwNotifiedBytes; /* number of bytes for which wavehdr notification has been done */
79 WAVEHDR* lpNotifyHdr; /* list of wavehdr for which write() has been called, pending for notification */
80 } WINE_WAVEOUT;
82 typedef struct {
83 int unixdev;
84 volatile int state;
85 DWORD dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */
86 WAVEOPENDESC waveDesc;
87 WORD wFlags;
88 PCMWAVEFORMAT format;
89 LPWAVEHDR lpQueueHdr;
90 DWORD dwTotalRecorded;
91 } WINE_WAVEIN;
93 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
94 static WINE_WAVEIN WInDev [MAX_WAVEOUTDRV];
96 /*======================================================================*
97 * Low level WAVE implemantation *
98 *======================================================================*/
100 /**************************************************************************
101 * WAVE_NotifyClient [internal]
103 static DWORD WAVE_NotifyClient(UINT16 wDevID, WORD wMsg,
104 DWORD dwParam1, DWORD dwParam2)
106 TRACE(wave, "wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
108 switch (wMsg) {
109 case WOM_OPEN:
110 case WOM_CLOSE:
111 case WOM_DONE:
112 if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
114 if (WOutDev[wDevID].wFlags != DCB_NULL &&
115 !DriverCallback16(WOutDev[wDevID].waveDesc.dwCallBack,
116 WOutDev[wDevID].wFlags,
117 WOutDev[wDevID].waveDesc.hWave,
118 wMsg,
119 WOutDev[wDevID].waveDesc.dwInstance,
120 dwParam1,
121 dwParam2)) {
122 WARN(wave, "can't notify client !\n");
123 return MMSYSERR_NOERROR;
125 break;
127 case WIM_OPEN:
128 case WIM_CLOSE:
129 case WIM_DATA:
130 if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
132 if (WInDev[wDevID].wFlags != DCB_NULL &&
133 !DriverCallback16(WInDev[wDevID].waveDesc.dwCallBack,
134 WInDev[wDevID].wFlags,
135 WInDev[wDevID].waveDesc.hWave,
136 wMsg,
137 WInDev[wDevID].waveDesc.dwInstance,
138 dwParam1,
139 dwParam2)) {
140 WARN(wave, "can't notify client !\n");
141 return MMSYSERR_NOERROR;
143 break;
144 default:
145 FIXME(wave, "Unknown CB message %u\n", wMsg);
146 break;
148 return 0;
151 /*======================================================================*
152 * Low level WAVE OUT implemantation *
153 *======================================================================*/
155 /**************************************************************************
156 * wodPlayer_WriteFragments [internal]
158 * wodPlayer helper. Writes as many fragments it can to unixdev.
159 * Returns TRUE in case of buffer underrun.
161 static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
163 LPWAVEHDR lpWaveHdr;
164 LPWAVEHDR wh;
165 LPBYTE lpData;
166 int count;
167 audio_buf_info abinfo;
169 for (;;) {
170 /* get number of writable fragments */
171 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &abinfo) == -1) {
172 perror("ioctl SNDCTL_DSP_GETOSPACE");
173 wwo->hThread = 0;
174 LeaveCriticalSection(&wwo->critSect);
175 ExitThread(-1);
177 TRACE(wave, "Can write %d fragments on fd %d\n", abinfo.fragments, wwo->unixdev);
179 if (abinfo.fragments == 0) /* output queue is full, wait a bit */
180 return FALSE;
182 lpWaveHdr = wwo->lpQueueHdr;
183 if (!lpWaveHdr) {
184 if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */
185 wwo->dwNotifiedBytes >= wwo->dwFragmentSize && /* first fragment has been played */
186 abinfo.fragstotal - abinfo.fragments < 2) { /* done with all waveOutWrite()' fragments */
188 /* FIXME: should do better handling here */
189 TRACE(wave, "Oooch, buffer underrun !\n");
190 return TRUE; /* force resetting of waveOut device */
192 return FALSE; /* wait a bit */
195 if (wwo->dwOffCurrHdr == 0) {
196 TRACE(wave, "Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
197 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
198 FIXME(wave, "NIY: loops (%lu) in wavehdr\n", lpWaveHdr->dwLoops);
201 lpData = ((DWORD)lpWaveHdr == lpWaveHdr->reserved) ?
202 (LPBYTE)lpWaveHdr->lpData : (LPBYTE)PTR_SEG_TO_LIN(lpWaveHdr->lpData);
204 /* finish current wave hdr ? */
205 if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) {
206 DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
208 /* write end of current wave hdr */
209 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
210 TRACE(wave, "write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
212 /* move lpWaveHdr to notify list */
213 if (wwo->lpNotifyHdr) {
214 for (wh = wwo->lpNotifyHdr; wh->lpNext != NULL; wh = wh->lpNext);
215 wh->lpNext = lpWaveHdr;
216 } else {
217 wwo->lpNotifyHdr = lpWaveHdr;
219 wwo->lpQueueHdr = lpWaveHdr->lpNext;
220 lpWaveHdr->lpNext = 0;
222 wwo->dwOffCurrHdr = 0;
223 if ((wwo->dwRemain -= toWrite) == 0)
224 wwo->dwRemain = wwo->dwFragmentSize;
225 continue; /* try to go to use next wavehdr */
226 } else {
227 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
228 TRACE(wave, "write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
229 if (count > 0) {
230 wwo->dwOffCurrHdr += wwo->dwRemain;
231 wwo->dwRemain = wwo->dwFragmentSize;
237 /**************************************************************************
238 * wodPlayer_WriteFragments [internal]
240 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
241 * have been played (actually to speaker, not to unixdev fd).
243 static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
245 LPWAVEHDR lpWaveHdr;
246 count_info cinfo;
248 /* get effective number of written bytes */
249 if (!force) {
250 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOPTR, &cinfo) == -1) {
251 perror("ioctl SNDCTL_DSP_GETOPTR");
252 wwo->hThread = 0;
253 LeaveCriticalSection(&wwo->critSect);
254 ExitThread(-1);
256 TRACE(wave, "Played %d bytes on fd %d\n", cinfo.bytes, wwo->unixdev);
257 /* FIXME: this will not work if a RESET or SYNC occurs somewhere */
258 wwo->dwTotalPlayed = cinfo.bytes;
260 if (force || cinfo.bytes > wwo->dwNotifiedBytes) {
261 /* remove all wavehdr which can be notified */
262 while (wwo->lpNotifyHdr &&
263 (force || (cinfo.bytes >= wwo->dwNotifiedBytes + wwo->lpNotifyHdr->dwBufferLength))) {
264 lpWaveHdr = wwo->lpNotifyHdr;
266 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
267 lpWaveHdr->dwFlags |= WHDR_DONE;
268 if (!force)
269 wwo->dwNotifiedBytes += lpWaveHdr->dwBufferLength;
270 wwo->lpNotifyHdr = lpWaveHdr->lpNext;
272 /* FIXME: should we keep the wwo->critsect locked during callback ? */
273 /* normally yes, only a limited set of functions can be called...
274 * the ones which can be called inside an i386 interruption
275 * so this should be fine
277 LeaveCriticalSection(&wwo->critSect);
278 TRACE(wave, "Notifying client with %p\n", lpWaveHdr);
279 if (WAVE_NotifyClient(uDevID, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
280 WARN(wave, "can't notify client !\n");
282 EnterCriticalSection(&wwo->critSect);
287 /**************************************************************************
288 * wodPlayer [internal]
290 static DWORD WINAPI wodPlayer(LPVOID pmt)
292 WORD uDevID = (DWORD)pmt;
293 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
294 int laststate = -1;
295 WAVEHDR* lpWaveHdr;
296 DWORD dwSleepTime;
298 wwo->dwNotifiedBytes = 0;
299 wwo->dwOffCurrHdr = 0;
300 wwo->dwRemain = wwo->dwFragmentSize;
301 wwo->lpNotifyHdr = NULL;
303 /* make sleep time to be # of ms to output a fragment */
304 dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
306 for (;;) {
308 EnterCriticalSection(&wwo->critSect);
310 switch (wwo->state) {
311 case WINE_WS_PAUSED:
312 wodPlayer_Notify(wwo, uDevID, FALSE);
313 if (laststate != WINE_WS_PAUSED) {
314 if (ioctl(wwo->unixdev, SNDCTL_DSP_POST, 0) == -1) {
315 perror("ioctl SNDCTL_DSP_POST");
316 wwo->hThread = 0;
317 LeaveCriticalSection(&wwo->critSect);
318 ExitThread(-1);
321 break;
322 case WINE_WS_STOPPED:
323 /* should we kill the Thread here and to restart it later when needed ? */
324 break;
325 case WINE_WS_PLAYING:
326 if (!wodPlayer_WriteFragments(wwo)) {
327 wodPlayer_Notify(wwo, uDevID, FALSE);
328 break;
330 /* fall thru */
331 case WINE_WS_RESETTING:
332 /* updates current notify list */
333 wodPlayer_Notify(wwo, uDevID, FALSE);
334 /* flush all possible output */
335 if (ioctl(wwo->unixdev, SNDCTL_DSP_SYNC, 0) == -1) {
336 perror("ioctl SNDCTL_DSP_SYNC");
337 wwo->hThread = 0;
338 LeaveCriticalSection(&wwo->critSect);
339 ExitThread(-1);
341 /* DSP_SYNC resets the number of send bytes */
342 wwo->dwNotifiedBytes = 0;
343 /* empty notify list */
344 wodPlayer_Notify(wwo, uDevID, TRUE);
345 if (wwo->lpNotifyHdr) {
346 ERR(wave, "out of sync\n");
348 wwo->dwOffCurrHdr = 0;
349 /* get rid also of all the current queue */
350 for (lpWaveHdr = wwo->lpQueueHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
351 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
352 lpWaveHdr->dwFlags |= WHDR_DONE;
354 if (WAVE_NotifyClient((DWORD)pmt, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
355 WARN(wave, "can't notify client !\n");
358 wwo->lpQueueHdr = 0;
359 wwo->state = WINE_WS_STOPPED;
360 break;
361 case WINE_WS_CLOSING:
362 /* this should not happen since the device must have been reset before */
363 if (wwo->lpNotifyHdr || wwo->lpQueueHdr) {
364 ERR(wave, "out of sync\n");
366 wwo->hThread = 0;
367 wwo->state = WINE_WS_CLOSED;
368 LeaveCriticalSection(&wwo->critSect);
369 DeleteCriticalSection(&wwo->critSect);
370 ExitThread(0);
371 /* shouldn't go here */
372 default:
373 FIXME(wave, "unknown state %d\n", wwo->state);
374 break;
376 laststate = wwo->state;
377 LeaveCriticalSection(&wwo->critSect);
378 /* sleep for one fragment to be sent */
379 Sleep(dwSleepTime);
381 ExitThread(0);
384 /**************************************************************************
385 * wodGetDevCaps [internal]
387 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS16 lpCaps, DWORD dwSize)
389 int audio;
390 int smplrate;
391 int samplesize = 16;
392 int dsp_stereo = 1;
393 int bytespersmpl;
394 int caps;
396 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
398 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
399 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
401 if (wDevID >= MAX_WAVEOUTDRV) {
402 TRACE(wave, "MAX_WAVOUTDRV reached !\n");
403 return MMSYSERR_BADDEVICEID;
406 if (WOutDev[wDevID].unixdev == 0) {
407 audio = open(SOUND_DEV, O_WRONLY, 0);
408 if (audio == -1) return MMSYSERR_ALLOCATED;
409 } else {
410 audio = WOutDev[wDevID].unixdev;
413 /* FIXME: some programs compare this string against the content of the registry
414 * for MM drivers. The name have to match in order the program to work
415 * (e.g. MS win9x mplayer.exe)
417 #ifdef EMULATE_SB16
418 lpCaps->wMid = 0x0002;
419 lpCaps->wPid = 0x0104;
420 strcpy(lpCaps->szPname, "SB16 Wave Out");
421 #else
422 lpCaps->wMid = 0x00FF; /* Manufac ID */
423 lpCaps->wPid = 0x0001; /* Product ID */
424 /* strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");*/
425 strcpy(lpCaps->szPname, "CS4236/37/38");
426 #endif
427 lpCaps->vDriverVersion = 0x0100;
428 lpCaps->dwFormats = 0x00000000;
429 lpCaps->dwSupport = WAVECAPS_VOLUME;
431 /* First bytespersampl, then stereo */
432 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
434 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
435 if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
437 smplrate = 44100;
438 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
439 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
440 if (lpCaps->wChannels > 1)
441 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
442 if (bytespersmpl > 1) {
443 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
444 if (lpCaps->wChannels > 1)
445 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
448 smplrate = 22050;
449 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
450 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
451 if (lpCaps->wChannels > 1)
452 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
453 if (bytespersmpl > 1) {
454 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
455 if (lpCaps->wChannels > 1)
456 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
459 smplrate = 11025;
460 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
461 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
462 if (lpCaps->wChannels > 1)
463 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
464 if (bytespersmpl > 1) {
465 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
466 if (lpCaps->wChannels > 1)
467 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
470 if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
471 if ((caps & DSP_CAP_REALTIME) && !(caps && DSP_CAP_BATCH))
472 lpCaps->dwFormats |= WAVECAPS_SAMPLEACCURATE;
474 if (WOutDev[wDevID].unixdev == 0) {
475 close(audio);
477 TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
478 return MMSYSERR_NOERROR;
482 /**************************************************************************
483 * wodOpen [internal]
485 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
487 int audio;
488 int sample_rate;
489 int sample_size;
490 int dsp_stereo;
491 int audio_fragment;
492 int fragment_size;
494 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
495 if (lpDesc == NULL) {
496 WARN(wave, "Invalid Parameter !\n");
497 return MMSYSERR_INVALPARAM;
499 if (wDevID >= MAX_WAVEOUTDRV) {
500 TRACE(wave, "MAX_WAVOUTDRV reached !\n");
501 return MMSYSERR_BADDEVICEID;
503 /* only PCM format is supported so far... */
504 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
505 lpDesc->lpFormat->nChannels == 0 ||
506 lpDesc->lpFormat->nSamplesPerSec == 0) {
507 WARN(wave, "Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
508 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
509 lpDesc->lpFormat->nSamplesPerSec);
510 return WAVERR_BADFORMAT;
513 if (dwFlags & WAVE_FORMAT_QUERY)
514 return MMSYSERR_NOERROR;
516 WOutDev[wDevID].unixdev = 0;
517 if (access(SOUND_DEV, 0) != 0)
518 return MMSYSERR_NOTENABLED;
519 audio = open(SOUND_DEV, O_WRONLY, 0);
520 if (audio == -1) {
521 WARN(wave, "can't open !\n");
522 return MMSYSERR_ALLOCATED ;
525 WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
527 memcpy(&WOutDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
528 memcpy(&WOutDev[wDevID].format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
530 if (WOutDev[wDevID].format.wBitsPerSample == 0) {
531 WOutDev[wDevID].format.wBitsPerSample = 8 *
532 (WOutDev[wDevID].format.wf.nAvgBytesPerSec /
533 WOutDev[wDevID].format.wf.nSamplesPerSec) /
534 WOutDev[wDevID].format.wf.nChannels;
537 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
538 * thus leading to 46ms per fragment, and a turnaround time of 185ms
540 /* 2^10=1024 bytes per fragment, 16 fragments max */
541 audio_fragment = 0x000F000A;
542 sample_size = WOutDev[wDevID].format.wBitsPerSample;
543 sample_rate = WOutDev[wDevID].format.wf.nSamplesPerSec;
544 dsp_stereo = (WOutDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
546 IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
547 /* First size and stereo then samplerate */
548 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, sample_size);
549 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
550 IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
552 /* even if we set fragment size above, read it again, just in case */
553 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
555 WOutDev[wDevID].lpQueueHdr = NULL;
556 WOutDev[wDevID].unixdev = audio;
557 WOutDev[wDevID].dwTotalPlayed = 0;
558 WOutDev[wDevID].dwFragmentSize = fragment_size;
559 WOutDev[wDevID].state = WINE_WS_STOPPED;
560 WOutDev[wDevID].hThread = 0;
562 /* FIXME: do we need to make critsect global ? */
563 InitializeCriticalSection(&WOutDev[wDevID].critSect);
564 MakeCriticalSectionGlobal(&WOutDev[wDevID].critSect);
566 TRACE(wave, "fd=%d fragmentSize=%ld\n",
567 WOutDev[wDevID].unixdev, WOutDev[wDevID].dwFragmentSize);
569 TRACE(wave, "wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u !\n",
570 WOutDev[wDevID].format.wBitsPerSample, WOutDev[wDevID].format.wf.nAvgBytesPerSec,
571 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels);
573 if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
574 WARN(wave, "can't notify client !\n");
575 return MMSYSERR_INVALPARAM;
577 return MMSYSERR_NOERROR;
580 /**************************************************************************
581 * wodClose [internal]
583 static DWORD wodClose(WORD wDevID)
585 DWORD ret = MMSYSERR_NOERROR;
587 TRACE(wave, "(%u);\n", wDevID);
589 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
590 WARN(wave, "bad device ID !\n");
591 return MMSYSERR_BADDEVICEID;
594 if (WOutDev[wDevID].lpQueueHdr != NULL || WOutDev[wDevID].lpNotifyHdr != NULL) {
595 WARN(wave, "buffers still playing !\n");
596 ret = WAVERR_STILLPLAYING;
597 } else {
598 EnterCriticalSection(&WOutDev[wDevID].critSect);
599 WOutDev[wDevID].state = WINE_WS_CLOSING;
600 LeaveCriticalSection(&WOutDev[wDevID].critSect);
602 while (WOutDev[wDevID].state != WINE_WS_CLOSED)
603 Sleep(10);
605 close(WOutDev[wDevID].unixdev);
606 WOutDev[wDevID].unixdev = 0;
607 WOutDev[wDevID].dwFragmentSize = 0;
608 if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
609 WARN(wave, "can't notify client !\n");
610 ret = MMSYSERR_INVALPARAM;
613 return ret;
616 /**************************************************************************
617 * wodWrite [internal]
620 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
622 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
624 /* first, do the sanity checks... */
625 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
626 WARN(wave, "bad dev ID !\n");
627 return MMSYSERR_BADDEVICEID;
630 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
631 return WAVERR_UNPREPARED;
633 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
634 return WAVERR_STILLPLAYING;
636 lpWaveHdr->dwFlags &= ~WHDR_DONE;
637 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
638 lpWaveHdr->lpNext = 0;
640 EnterCriticalSection(&WOutDev[wDevID].critSect);
641 /* insert buffer in queue */
642 if (WOutDev[wDevID].lpQueueHdr) {
643 WAVEHDR* wh;
644 for (wh = WOutDev[wDevID].lpQueueHdr; wh->lpNext; wh = wh->lpNext);
645 wh->lpNext = lpWaveHdr;
646 } else {
647 WOutDev[wDevID].lpQueueHdr = lpWaveHdr;
649 /* starts thread if wod is not paused */
650 switch (WOutDev[wDevID].state) {
651 case WINE_WS_PAUSED:
652 case WINE_WS_PLAYING:
653 /* nothing to do */
654 break;
655 case WINE_WS_STOPPED:
656 WOutDev[wDevID].state = WINE_WS_PLAYING;
657 if (WOutDev[wDevID].hThread == 0) {
658 WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, NULL);
660 break;
661 default:
662 FIXME(wave, "Unsupported state %d\n", WOutDev[wDevID].state);
664 LeaveCriticalSection(&WOutDev[wDevID].critSect);
666 return MMSYSERR_NOERROR;
669 /**************************************************************************
670 * wodPrepare [internal]
672 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
674 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
676 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
677 WARN(wave, "bad device ID !\n");
678 return MMSYSERR_BADDEVICEID;
681 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
682 return WAVERR_STILLPLAYING;
684 lpWaveHdr->dwFlags |= WHDR_PREPARED;
685 lpWaveHdr->dwFlags &= ~WHDR_DONE;
686 return MMSYSERR_NOERROR;
689 /**************************************************************************
690 * wodUnprepare [internal]
692 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
694 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
696 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
697 WARN(wave, "bad device ID !\n");
698 return MMSYSERR_BADDEVICEID;
701 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
702 return WAVERR_STILLPLAYING;
704 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
705 lpWaveHdr->dwFlags |= WHDR_DONE;
707 return MMSYSERR_NOERROR;
710 /**************************************************************************
711 * wodPause [internal]
713 static DWORD wodPause(WORD wDevID)
715 TRACE(wave, "(%u);!\n", wDevID);
717 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
718 WARN(wave, "bad device ID !\n");
719 return MMSYSERR_BADDEVICEID;
722 EnterCriticalSection(&WOutDev[wDevID].critSect);
723 WOutDev[wDevID].state = WINE_WS_PAUSED;
724 LeaveCriticalSection(&WOutDev[wDevID].critSect);
726 return MMSYSERR_NOERROR;
729 /**************************************************************************
730 * wodStop [internal]
732 static DWORD wodStop(WORD wDevID)
734 FIXME(wave, "(%u); NIY!\n", wDevID);
736 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
737 WARN(wave, "bad device ID !\n");
738 return MMSYSERR_BADDEVICEID;
741 return MMSYSERR_NOERROR;
744 /**************************************************************************
745 * wodRestart [internal]
747 static DWORD wodRestart(WORD wDevID)
749 TRACE(wave, "(%u);\n", wDevID);
751 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
752 WARN(wave, "bad device ID !\n");
753 return MMSYSERR_BADDEVICEID;
756 EnterCriticalSection(&WOutDev[wDevID].critSect);
757 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
758 if (WOutDev[wDevID].lpQueueHdr != NULL) {
759 WOutDev[wDevID].state = WINE_WS_PLAYING;
760 if (WOutDev[wDevID].hThread == 0)
761 WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, NULL);
762 } else {
763 WOutDev[wDevID].state = WINE_WS_STOPPED;
766 LeaveCriticalSection(&WOutDev[wDevID].critSect);
768 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
769 /* FIXME: Myst crashes with this ... hmm -MM
770 if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
771 WARN(wave, "can't notify client !\n");
772 return MMSYSERR_INVALPARAM;
776 return MMSYSERR_NOERROR;
779 /**************************************************************************
780 * wodReset [internal]
782 static DWORD wodReset(WORD wDevID)
784 TRACE(wave, "(%u);\n", wDevID);
786 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
787 WARN(wave, "bad device ID !\n");
788 return MMSYSERR_BADDEVICEID;
791 EnterCriticalSection(&WOutDev[wDevID].critSect);
792 if (WOutDev[wDevID].hThread == 0) {
793 WOutDev[wDevID].state = WINE_WS_STOPPED;
794 } else {
795 WOutDev[wDevID].state = WINE_WS_RESETTING;
797 LeaveCriticalSection(&WOutDev[wDevID].critSect);
799 while (WOutDev[wDevID].state != WINE_WS_STOPPED)
800 Sleep(10);
802 return MMSYSERR_NOERROR;
806 /**************************************************************************
807 * wodGetPosition [internal]
809 static DWORD wodGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
811 int time;
813 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpTime, uSize);
815 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
816 WARN(wave, "bad device ID !\n");
817 return MMSYSERR_BADDEVICEID;
820 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
821 TRACE(wave, "wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
822 lpTime->wType, WOutDev[wDevID].format.wBitsPerSample,
823 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels,
824 WOutDev[wDevID].format.wf.nAvgBytesPerSec);
825 TRACE(wave, "dwTotalPlayed=%lu\n", WOutDev[wDevID].dwTotalPlayed);
827 switch (lpTime->wType) {
828 case TIME_BYTES:
829 lpTime->u.cb = WOutDev[wDevID].dwTotalPlayed;
830 TRACE(wave, "TIME_BYTES=%lu\n", lpTime->u.cb);
831 break;
832 case TIME_SAMPLES:
833 lpTime->u.sample = WOutDev[wDevID].dwTotalPlayed * 8 /
834 WOutDev[wDevID].format.wBitsPerSample;
835 TRACE(wave, "TIME_SAMPLES=%lu\n", lpTime->u.sample);
836 break;
837 case TIME_SMPTE:
838 time = WOutDev[wDevID].dwTotalPlayed /
839 (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
840 lpTime->u.smpte.hour = time / 108000;
841 time -= lpTime->u.smpte.hour * 108000;
842 lpTime->u.smpte.min = time / 1800;
843 time -= lpTime->u.smpte.min * 1800;
844 lpTime->u.smpte.sec = time / 30;
845 time -= lpTime->u.smpte.sec * 30;
846 lpTime->u.smpte.frame = time;
847 lpTime->u.smpte.fps = 30;
848 TRACE(wave, "TIME_SMPTE=%02u:%02u:%02u:%02u\n",
849 lpTime->u.smpte.hour, lpTime->u.smpte.min,
850 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
851 break;
852 default:
853 FIXME(wave, "Format %d not supported ! use TIME_MS !\n", lpTime->wType);
854 lpTime->wType = TIME_MS;
855 case TIME_MS:
856 lpTime->u.ms = WOutDev[wDevID].dwTotalPlayed /
857 (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
858 TRACE(wave, "TIME_MS=%lu\n", lpTime->u.ms);
859 break;
861 return MMSYSERR_NOERROR;
864 /**************************************************************************
865 * wodGetVolume [internal]
867 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
869 int mixer;
870 int volume, left, right;
872 TRACE(wave, "(%u, %p);\n", wDevID, lpdwVol);
874 if (lpdwVol == NULL)
875 return MMSYSERR_NOTENABLED;
876 if ((mixer = open(MIXER_DEV, O_RDONLY)) < 0) {
877 WARN(wave, "mixer device not available !\n");
878 return MMSYSERR_NOTENABLED;
880 if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
881 WARN(wave, "unable read mixer !\n");
882 return MMSYSERR_NOTENABLED;
884 close(mixer);
885 left = volume & 0x7F;
886 right = (volume >> 8) & 0x7F;
887 TRACE(wave, "left=%d right=%d !\n", left, right);
888 *lpdwVol = MAKELONG(left << 9, right << 9);
889 return MMSYSERR_NOERROR;
893 /**************************************************************************
894 * wodSetVolume [internal]
896 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
898 int mixer;
899 int volume;
900 TRACE(wave, "(%u, %08lX);\n", wDevID, dwParam);
901 volume = (LOWORD(dwParam) >> 9 & 0x7F) +
902 ((HIWORD(dwParam) >> 9 & 0x7F) << 8);
903 if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) {
904 WARN(wave, "mixer device not available !\n");
905 return MMSYSERR_NOTENABLED;
907 if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
908 WARN(wave, "unable set mixer !\n");
909 return MMSYSERR_NOTENABLED;
911 close(mixer);
912 return MMSYSERR_NOERROR;
915 /**************************************************************************
916 * wodGetNumDevs [internal]
918 static DWORD wodGetNumDevs(void)
920 DWORD ret = 1;
922 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
923 int audio = open(SOUND_DEV, O_WRONLY, 0);
925 if (audio == -1) {
926 if (errno != EBUSY)
927 ret = 0;
928 } else {
929 close(audio);
931 return ret;
934 /**************************************************************************
935 * wodMessage [sample driver]
937 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
938 DWORD dwParam1, DWORD dwParam2)
940 TRACE(wave, "(%u, %04X, %08lX, %08lX, %08lX);\n",
941 wDevID, wMsg, dwUser, dwParam1, dwParam2);
943 switch (wMsg) {
944 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
945 case WODM_CLOSE: return wodClose (wDevID);
946 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
947 case WODM_PAUSE: return wodPause (wDevID);
948 case WODM_STOP: return wodStop (wDevID);
949 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME16)dwParam1, dwParam2);
950 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
951 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
952 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
953 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPS16)dwParam1, dwParam2);
954 case WODM_GETNUMDEVS: return wodGetNumDevs ();
955 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
956 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
957 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
958 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
959 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
960 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
961 case WODM_RESTART: return wodRestart (wDevID);
962 case WODM_RESET: return wodReset (wDevID);
963 default:
964 FIXME(wave, "unknown message %d!\n", wMsg);
966 return MMSYSERR_NOTSUPPORTED;
969 /*======================================================================*
970 * Low level WAVE IN implemantation *
971 *======================================================================*/
973 /**************************************************************************
974 * widGetDevCaps [internal]
976 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS16 lpCaps, DWORD dwSize)
978 int audio,smplrate,samplesize=16,dsp_stereo=1,bytespersmpl;
980 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
981 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
982 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
983 audio = open(SOUND_DEV, O_RDONLY, 0);
984 if (audio == -1) return MMSYSERR_ALLOCATED ;
985 #ifdef EMULATE_SB16
986 lpCaps->wMid = 0x0002;
987 lpCaps->wPid = 0x0004;
988 strcpy(lpCaps->szPname, "SB16 Wave In");
989 #else
990 lpCaps->wMid = 0x00FF; /* Manufac ID */
991 lpCaps->wPid = 0x0001; /* Product ID */
992 strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
993 #endif
994 lpCaps->dwFormats = 0x00000000;
995 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
996 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
997 smplrate = 44100;
998 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
999 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
1000 if (lpCaps->wChannels > 1)
1001 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
1002 if (bytespersmpl > 1) {
1003 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
1004 if (lpCaps->wChannels > 1)
1005 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
1008 smplrate = 22050;
1009 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1010 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
1011 if (lpCaps->wChannels > 1)
1012 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
1013 if (bytespersmpl > 1) {
1014 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
1015 if (lpCaps->wChannels > 1)
1016 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
1019 smplrate = 11025;
1020 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1021 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
1022 if (lpCaps->wChannels > 1)
1023 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
1024 if (bytespersmpl > 1) {
1025 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
1026 if (lpCaps->wChannels > 1)
1027 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
1030 close(audio);
1031 TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
1032 return MMSYSERR_NOERROR;
1035 /**************************************************************************
1036 * widOpen [internal]
1038 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1040 int audio,abuf_size,smplrate,samplesize,dsp_stereo;
1041 LPWAVEFORMAT lpFormat;
1043 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1044 if (lpDesc == NULL) {
1045 WARN(wave, "Invalid Parameter !\n");
1046 return MMSYSERR_INVALPARAM;
1048 if (wDevID >= MAX_WAVEINDRV) {
1049 TRACE(wave, "MAX_WAVINDRV reached !\n");
1050 return MMSYSERR_ALLOCATED;
1052 WInDev[wDevID].unixdev = 0;
1053 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1054 audio = open(SOUND_DEV, O_RDONLY, 0);
1055 if (audio == -1) {
1056 WARN(wave, "can't open !\n");
1057 return MMSYSERR_ALLOCATED;
1059 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
1060 if (abuf_size < 1024 || abuf_size > 65536) {
1061 if (abuf_size == -1)
1062 WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1063 else
1064 WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize !\n");
1065 return MMSYSERR_NOTENABLED;
1067 WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1069 if (WInDev[wDevID].lpQueueHdr) {
1070 HeapFree(GetProcessHeap(), 0, WInDev[wDevID].lpQueueHdr);
1071 WInDev[wDevID].lpQueueHdr = NULL;
1073 WInDev[wDevID].unixdev = audio;
1074 WInDev[wDevID].dwFragmentSize = abuf_size;
1075 WInDev[wDevID].dwTotalRecorded = 0;
1076 memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1077 lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat;
1078 if (lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
1079 WARN(wave, "Bad format %04X !\n",
1080 lpFormat->wFormatTag);
1081 return WAVERR_BADFORMAT;
1083 memcpy(&WInDev[wDevID].format, lpFormat, sizeof(PCMWAVEFORMAT));
1084 WInDev[wDevID].format.wBitsPerSample = 8; /* <-------------- */
1085 if (WInDev[wDevID].format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1086 if (WInDev[wDevID].format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1087 if (WInDev[wDevID].format.wBitsPerSample == 0) {
1088 WInDev[wDevID].format.wBitsPerSample = 8 *
1089 (WInDev[wDevID].format.wf.nAvgBytesPerSec /
1090 WInDev[wDevID].format.wf.nSamplesPerSec) /
1091 WInDev[wDevID].format.wf.nChannels;
1093 samplesize = WInDev[wDevID].format.wBitsPerSample;
1094 smplrate = WInDev[wDevID].format.wf.nSamplesPerSec;
1095 dsp_stereo = (WInDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
1096 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1097 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1098 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1099 TRACE(wave, "wBitsPerSample=%u !\n", WInDev[wDevID].format.wBitsPerSample);
1100 TRACE(wave, "nSamplesPerSec=%lu !\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1101 TRACE(wave, "nChannels=%u !\n", WInDev[wDevID].format.wf.nChannels);
1102 TRACE(wave, "nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1103 if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1104 WARN(wave, "can't notify client !\n");
1105 return MMSYSERR_INVALPARAM;
1107 return MMSYSERR_NOERROR;
1110 /**************************************************************************
1111 * widClose [internal]
1113 static DWORD widClose(WORD wDevID)
1115 TRACE(wave, "(%u);\n", wDevID);
1116 if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
1117 if (WInDev[wDevID].unixdev == 0) {
1118 WARN(wave, "can't close !\n");
1119 return MMSYSERR_NOTENABLED;
1121 if (WInDev[wDevID].lpQueueHdr != NULL) {
1122 WARN(wave, "still buffers open !\n");
1123 return WAVERR_STILLPLAYING;
1125 close(WInDev[wDevID].unixdev);
1126 WInDev[wDevID].unixdev = 0;
1127 WInDev[wDevID].dwFragmentSize = 0;
1128 if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1129 WARN(wave, "can't notify client !\n");
1130 return MMSYSERR_INVALPARAM;
1132 return MMSYSERR_NOERROR;
1135 /**************************************************************************
1136 * widAddBuffer [internal]
1138 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1140 int count = 1;
1141 LPWAVEHDR lpWIHdr;
1143 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1144 if (WInDev[wDevID].unixdev == 0) {
1145 WARN(wave, "can't do it !\n");
1146 return MMSYSERR_NOTENABLED;
1148 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1149 TRACE(wave, "never been prepared !\n");
1150 return WAVERR_UNPREPARED;
1152 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1153 TRACE(wave, "header already in use !\n");
1154 return WAVERR_STILLPLAYING;
1156 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1157 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1158 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1159 lpWaveHdr->dwBytesRecorded = 0;
1160 if (WInDev[wDevID].lpQueueHdr == NULL) {
1161 WInDev[wDevID].lpQueueHdr = lpWaveHdr;
1162 } else {
1163 lpWIHdr = WInDev[wDevID].lpQueueHdr;
1164 while (lpWIHdr->lpNext != NULL) {
1165 lpWIHdr = lpWIHdr->lpNext;
1166 count++;
1168 lpWIHdr->lpNext = lpWaveHdr;
1169 lpWaveHdr->lpNext = NULL;
1170 count++;
1172 TRACE(wave, "buffer added ! (now %u in queue)\n", count);
1173 return MMSYSERR_NOERROR;
1176 /**************************************************************************
1177 * widPrepare [internal]
1179 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1181 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1182 if (WInDev[wDevID].unixdev == 0) {
1183 WARN(wave, "can't prepare !\n");
1184 return MMSYSERR_NOTENABLED;
1186 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1187 return WAVERR_STILLPLAYING;
1188 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1189 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1190 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1191 lpWaveHdr->dwBytesRecorded = 0;
1192 TRACE(wave, "header prepared !\n");
1193 return MMSYSERR_NOERROR;
1196 /**************************************************************************
1197 * widUnprepare [internal]
1199 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1201 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1202 if (WInDev[wDevID].unixdev == 0) {
1203 WARN(wave, "can't unprepare !\n");
1204 return MMSYSERR_NOTENABLED;
1206 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1207 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1208 lpWaveHdr->dwFlags |= WHDR_DONE;
1210 return MMSYSERR_NOERROR;
1213 /**************************************************************************
1214 * widStart [internal]
1216 static DWORD widStart(WORD wDevID)
1218 int count = 1;
1219 int bytesRead;
1220 LPWAVEHDR *lpWaveHdr;
1221 LPBYTE lpData;
1223 TRACE(wave, "(%u);\n", wDevID);
1224 if (WInDev[wDevID].unixdev == 0) {
1225 WARN(wave, "can't start recording !\n");
1226 return MMSYSERR_NOTENABLED;
1229 lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
1230 TRACE(wave, "lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
1232 if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
1233 TRACE(wave, "never been prepared !\n");
1234 return WAVERR_UNPREPARED;
1237 while (*lpWaveHdr != NULL) {
1239 lpData = ((DWORD)*lpWaveHdr == (*lpWaveHdr)->reserved) ?
1240 (LPBYTE)(*lpWaveHdr)->lpData : (LPBYTE)PTR_SEG_TO_LIN((*lpWaveHdr)->lpData);
1242 TRACE(wave, "recording buf#%u=%p size=%lu \n",
1243 count, lpData, (*lpWaveHdr)->dwBufferLength);
1245 bytesRead = read(WInDev[wDevID].unixdev, lpData, (*lpWaveHdr)->dwBufferLength);
1247 if (bytesRead == -1)
1248 perror("read from audio device");
1250 TRACE(wave, "bytesread=%d (%ld)\n", bytesRead, (*lpWaveHdr)->dwBufferLength);
1251 (*lpWaveHdr)->dwBytesRecorded = bytesRead;
1252 WInDev[wDevID].dwTotalRecorded += (*lpWaveHdr)->dwBytesRecorded;
1253 (*lpWaveHdr)->dwFlags &= ~WHDR_INQUEUE;
1254 (*lpWaveHdr)->dwFlags |= WHDR_DONE;
1256 if (WAVE_NotifyClient(wDevID, WIM_DATA, (*lpWaveHdr)->reserved, (*lpWaveHdr)->dwBytesRecorded) != MMSYSERR_NOERROR) {
1257 WARN(wave, "can't notify client !\n");
1258 return MMSYSERR_INVALPARAM;
1260 /* removes the current block from the queue */
1261 *lpWaveHdr = (*lpWaveHdr)->lpNext;
1262 count++;
1264 TRACE(wave, "end of recording !\n");
1265 return MMSYSERR_NOERROR;
1268 /**************************************************************************
1269 * widStop [internal]
1271 static DWORD widStop(WORD wDevID)
1273 TRACE(wave, "(%u);\n", wDevID);
1274 if (WInDev[wDevID].unixdev == 0) {
1275 WARN(wave, "can't stop !\n");
1276 return MMSYSERR_NOTENABLED;
1278 return MMSYSERR_NOERROR;
1281 /**************************************************************************
1282 * widReset [internal]
1284 static DWORD widReset(WORD wDevID)
1286 TRACE(wave, "(%u);\n", wDevID);
1287 if (WInDev[wDevID].unixdev == 0) {
1288 WARN(wave, "can't reset !\n");
1289 return MMSYSERR_NOTENABLED;
1291 return MMSYSERR_NOERROR;
1294 /**************************************************************************
1295 * widGetPosition [internal]
1297 static DWORD widGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
1299 int time;
1301 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1302 if (WInDev[wDevID].unixdev == 0) {
1303 WARN(wave, "can't get pos !\n");
1304 return MMSYSERR_NOTENABLED;
1306 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1307 TRACE(wave, "wType=%04X !\n", lpTime->wType);
1308 TRACE(wave, "wBitsPerSample=%u\n", WInDev[wDevID].format.wBitsPerSample);
1309 TRACE(wave, "nSamplesPerSec=%lu\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1310 TRACE(wave, "nChannels=%u\n", WInDev[wDevID].format.wf.nChannels);
1311 TRACE(wave, "nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1312 switch (lpTime->wType) {
1313 case TIME_BYTES:
1314 lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
1315 TRACE(wave, "TIME_BYTES=%lu\n", lpTime->u.cb);
1316 break;
1317 case TIME_SAMPLES:
1318 lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
1319 WInDev[wDevID].format.wBitsPerSample;
1320 TRACE(wave, "TIME_SAMPLES=%lu\n", lpTime->u.sample);
1321 break;
1322 case TIME_SMPTE:
1323 time = WInDev[wDevID].dwTotalRecorded /
1324 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1325 lpTime->u.smpte.hour = time / 108000;
1326 time -= lpTime->u.smpte.hour * 108000;
1327 lpTime->u.smpte.min = time / 1800;
1328 time -= lpTime->u.smpte.min * 1800;
1329 lpTime->u.smpte.sec = time / 30;
1330 time -= lpTime->u.smpte.sec * 30;
1331 lpTime->u.smpte.frame = time;
1332 lpTime->u.smpte.fps = 30;
1333 TRACE(wave, "TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1334 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1335 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1336 break;
1337 case TIME_MS:
1338 lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1339 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1340 TRACE(wave, "TIME_MS=%lu\n", lpTime->u.ms);
1341 break;
1342 default:
1343 FIXME(wave, "format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1344 lpTime->wType = TIME_MS;
1346 return MMSYSERR_NOERROR;
1349 /**************************************************************************
1350 * widMessage [sample driver]
1352 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1353 DWORD dwParam1, DWORD dwParam2)
1355 TRACE(wave, "(%u, %04X, %08lX, %08lX, %08lX);\n",
1356 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1358 switch (wMsg) {
1359 case WIDM_OPEN: return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1360 case WIDM_CLOSE: return widClose(wDevID);
1361 case WIDM_ADDBUFFER: return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1362 case WIDM_PREPARE: return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1363 case WIDM_UNPREPARE: return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1364 case WIDM_GETDEVCAPS: return widGetDevCaps(wDevID, (LPWAVEINCAPS16)dwParam1,dwParam2);
1365 case WIDM_GETNUMDEVS: return wodGetNumDevs(); /* same number of devices in output as in input */
1366 case WIDM_GETPOS: return widGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
1367 case WIDM_RESET: return widReset(wDevID);
1368 case WIDM_START: return widStart(wDevID);
1369 case WIDM_PAUSE: return widStop(wDevID);
1370 case WIDM_STOP: return widStop(wDevID);
1371 default:
1372 FIXME(wave, "unknown message %u!\n", wMsg);
1374 return MMSYSERR_NOTSUPPORTED;
1377 /*======================================================================*
1378 * Low level WAVE implemantation - DriverProc *
1379 *======================================================================*/
1381 /**************************************************************************
1382 * WAVE_DriverProc [sample driver]
1384 LONG WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1385 DWORD dwParam1, DWORD dwParam2)
1387 TRACE(wave, "(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1388 switch (wMsg) {
1389 case DRV_LOAD: return 1;
1390 case DRV_FREE: return 1;
1391 case DRV_OPEN: return 1;
1392 case DRV_CLOSE: return 1;
1393 case DRV_ENABLE: return 1;
1394 case DRV_DISABLE: return 1;
1395 case DRV_QUERYCONFIGURE: return 1;
1396 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
1397 case DRV_INSTALL: return DRVCNF_RESTART;
1398 case DRV_REMOVE: return DRVCNF_RESTART;
1399 default:
1400 FIXME(wave, "is probably wrong msg=0x%04lx\n", wMsg);
1401 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1403 return MMSYSERR_NOTENABLED;
1406 #else /* !HAVE_OSS */
1408 /**************************************************************************
1409 * wodMessage [sample driver]
1411 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1412 DWORD dwParam1, DWORD dwParam2)
1414 FIXME(wave, "(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1415 return MMSYSERR_NOTENABLED;
1418 /**************************************************************************
1419 * widMessage [sample driver]
1421 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1422 DWORD dwParam1, DWORD dwParam2)
1424 FIXME(wave, "(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1425 return MMSYSERR_NOTENABLED;
1428 /**************************************************************************
1429 * WAVE_DriverProc [sample driver]
1431 LONG WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1432 DWORD dwParam1, DWORD dwParam2)
1434 return MMSYSERR_NOTENABLED;
1436 #endif /* HAVE_OSS */