-Implemented TCS_BUTTONS, TCS_FIXEDWIDTH, TCM_GETITEMRECT,
[wine/multimedia.git] / multimedia / audio.c
blob11af16907ce9dc179d7f6cb964f073e68bf8e216
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 #ifdef HAVE_OSS
31 #define SOUND_DEV "/dev/dsp"
32 #define MIXER_DEV "/dev/mixer"
34 #define MAX_WAVEOUTDRV (1)
35 #define MAX_WAVEINDRV (1)
37 /* state diagram for waveOut writing:
38 * FIXME: I'm not 100% pleased with the handling of RESETTING and CLOSING which are
39 * temporary states.
40 * Basically, they are events handled in waveOutXXXX functions (open, write, pause, restart),
41 * and the others (resetn close) which are handled by the player. We (I ?) use transient
42 * states (RESETTING and CLOSING) to send the event from the waveOutXXX functions to
43 * the player. Not very good.
45 * open() STOPPED
46 * PAUSED write() PAUSED
47 * STOPPED write() PLAYING
48 * (other) write() <error>
49 * (any) pause() PAUSED
50 * PAUSED restart() PLAYING if hdr, STOPPED otherwise
51 * reset() RESETTING => STOPPED
52 * close() CLOSING => CLOSED
55 #define WINE_WS_PLAYING 0
56 #define WINE_WS_PAUSED 1
57 #define WINE_WS_STOPPED 2
58 #define WINE_WS_RESETTING 3
59 #define WINE_WS_CLOSING 4
60 #define WINE_WS_CLOSED 5
62 typedef struct {
63 int unixdev;
64 volatile int state; /* one of the WINE_WS_ manifest constants */
65 DWORD dwFragmentSize;
66 WAVEOPENDESC waveDesc;
67 WORD wFlags;
68 PCMWAVEFORMAT format;
69 LPWAVEHDR lpQueueHdr;
70 DWORD dwTotalPlayed;
71 HANDLE hThread;
72 CRITICAL_SECTION critSect;
73 /* used only by wodPlayer */
74 DWORD dwOffCurrHdr; /* offset in lpQueueHdr->lpWaveHdr->lpData for fragments */
75 DWORD dwRemain; /* number of bytes to write to end the current fragment */
76 DWORD dwNotifiedBytes; /* number of bytes for which wavehdr notification has been done */
77 WAVEHDR* lpNotifyHdr; /* list of wavehdr for which write() has been called, pending for notification */
78 } WINE_WAVEOUT;
80 typedef struct {
81 int unixdev;
82 volatile int state;
83 DWORD dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */
84 WAVEOPENDESC waveDesc;
85 WORD wFlags;
86 PCMWAVEFORMAT format;
87 LPWAVEHDR lpQueueHdr;
88 DWORD dwTotalRecorded;
89 } WINE_WAVEIN;
91 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
92 static WINE_WAVEIN WInDev [MAX_WAVEOUTDRV];
94 /*======================================================================*
95 * Low level WAVE implemantation *
96 *======================================================================*/
98 /**************************************************************************
99 * WAVE_NotifyClient [internal]
101 static DWORD WAVE_NotifyClient(UINT16 wDevID, WORD wMsg,
102 DWORD dwParam1, DWORD dwParam2)
104 TRACE(wave, "wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
106 switch (wMsg) {
107 case WOM_OPEN:
108 case WOM_CLOSE:
109 case WOM_DONE:
110 if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
112 if (WOutDev[wDevID].wFlags != DCB_NULL &&
113 !DriverCallback16(WOutDev[wDevID].waveDesc.dwCallBack,
114 WOutDev[wDevID].wFlags,
115 WOutDev[wDevID].waveDesc.hWave,
116 wMsg,
117 WOutDev[wDevID].waveDesc.dwInstance,
118 dwParam1,
119 dwParam2)) {
120 WARN(wave, "can't notify client !\n");
121 return MMSYSERR_NOERROR;
123 break;
125 case WIM_OPEN:
126 case WIM_CLOSE:
127 case WIM_DATA:
128 if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
130 if (WInDev[wDevID].wFlags != DCB_NULL &&
131 !DriverCallback16(WInDev[wDevID].waveDesc.dwCallBack,
132 WInDev[wDevID].wFlags,
133 WInDev[wDevID].waveDesc.hWave,
134 wMsg,
135 WInDev[wDevID].waveDesc.dwInstance,
136 dwParam1,
137 dwParam2)) {
138 WARN(wave, "can't notify client !\n");
139 return MMSYSERR_NOERROR;
141 break;
143 return 0;
146 /*======================================================================*
147 * Low level WAVE OUT implemantation *
148 *======================================================================*/
150 /**************************************************************************
151 * wodPlayer_WriteFragments [internal]
153 * wodPlayer helper. Writes as many fragments it can to unixdev.
154 * Returns TRUE in case of buffer underrun.
156 static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
158 LPWAVEHDR lpWaveHdr;
159 LPWAVEHDR wh;
160 LPBYTE lpData;
161 int count;
162 audio_buf_info abinfo;
164 for (;;) {
165 /* get number of writable fragments */
166 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &abinfo) == -1) {
167 perror("ioctl SNDCTL_DSP_GETOSPACE");
168 wwo->hThread = 0;
169 LeaveCriticalSection(&wwo->critSect);
170 ExitThread(-1);
172 TRACE(wave, "Can write %d fragments on fd %d\n", abinfo.fragments, wwo->unixdev);
174 if (abinfo.fragments == 0) /* output queue is full, wait a bit */
175 return FALSE;
177 lpWaveHdr = wwo->lpQueueHdr;
178 if (!lpWaveHdr) {
179 if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */
180 wwo->dwNotifiedBytes >= wwo->dwFragmentSize && /* first fragment has been played */
181 abinfo.fragstotal - abinfo.fragments < 2) { /* done with all waveOutWrite()' fragments */
183 /* FIXME: should do better handling here */
184 TRACE(wave, "Oooch, buffer underrun !\n");
185 return TRUE; /* force resetting of waveOut device */
187 return FALSE; /* wait a bit */
190 if (wwo->dwOffCurrHdr == 0) {
191 TRACE(wave, "Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
192 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
193 FIXME(wave, "NIY: loops (%lu) in wavehdr\n", lpWaveHdr->dwLoops);
196 lpData = ((DWORD)lpWaveHdr == lpWaveHdr->reserved) ?
197 (LPBYTE)lpWaveHdr->lpData : (LPBYTE)PTR_SEG_TO_LIN(lpWaveHdr->lpData);
199 /* finish current wave hdr ? */
200 if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) {
201 DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
203 /* write end of current wave hdr */
204 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
205 TRACE(wave, "write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
207 /* move lpWaveHdr to notify list */
208 if (wwo->lpNotifyHdr) {
209 for (wh = wwo->lpNotifyHdr; wh->lpNext != NULL; wh = wh->lpNext);
210 wh->lpNext = lpWaveHdr;
211 } else {
212 wwo->lpNotifyHdr = lpWaveHdr;
214 wwo->lpQueueHdr = lpWaveHdr->lpNext;
215 lpWaveHdr->lpNext = 0;
217 wwo->dwOffCurrHdr = 0;
218 if ((wwo->dwRemain -= toWrite) == 0)
219 wwo->dwRemain = wwo->dwFragmentSize;
220 continue; /* try to go to use next wavehdr */
221 } else {
222 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
223 TRACE(wave, "write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
224 wwo->dwOffCurrHdr += count;
225 wwo->dwRemain = wwo->dwFragmentSize;
230 /**************************************************************************
231 * wodPlayer_WriteFragments [internal]
233 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
234 * have been played (actually to speaker, not to unixdev fd).
236 static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
238 LPWAVEHDR lpWaveHdr;
239 count_info cinfo;
241 /* get effective number of written bytes */
242 if (!force) {
243 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOPTR, &cinfo) == -1) {
244 perror("ioctl SNDCTL_DSP_GETOPTR");
245 wwo->hThread = 0;
246 LeaveCriticalSection(&wwo->critSect);
247 ExitThread(-1);
249 TRACE(wave, "Played %d bytes on fd %d\n", cinfo.bytes, wwo->unixdev);
250 /* FIXME: this will not work if a RESET or SYNC occurs somewhere */
251 wwo->dwTotalPlayed = cinfo.bytes;
253 if (force || cinfo.bytes > wwo->dwNotifiedBytes) {
254 /* remove all wavehdr which can be notified */
255 while (wwo->lpNotifyHdr &&
256 (force || (cinfo.bytes >= wwo->dwNotifiedBytes + wwo->lpNotifyHdr->dwBufferLength))) {
257 lpWaveHdr = wwo->lpNotifyHdr;
259 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
260 lpWaveHdr->dwFlags |= WHDR_DONE;
261 if (!force)
262 wwo->dwNotifiedBytes += lpWaveHdr->dwBufferLength;
263 wwo->lpNotifyHdr = lpWaveHdr->lpNext;
265 /* FIXME: should we keep the wwo->critsect locked during callback ? */
266 /* normally yes, only a limited set of functions can be called...
267 * the ones which can be called inside an i386 interruption
268 * so this should be fine
270 LeaveCriticalSection(&wwo->critSect);
271 TRACE(wave, "Notifying client with %p\n", lpWaveHdr);
272 if (WAVE_NotifyClient(uDevID, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
273 WARN(wave, "can't notify client !\n");
275 EnterCriticalSection(&wwo->critSect);
280 /**************************************************************************
281 * wodPlayer [internal]
283 static DWORD WINAPI wodPlayer(LPVOID pmt)
285 WORD uDevID = (DWORD)pmt;
286 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
287 int laststate = -1;
288 WAVEHDR* lpWaveHdr;
289 DWORD dwSleepTime;
291 wwo->dwNotifiedBytes = 0;
292 wwo->dwOffCurrHdr = 0;
293 wwo->dwRemain = wwo->dwFragmentSize;
294 wwo->lpNotifyHdr = NULL;
296 /* make sleep time to be # of ms to output a fragment */
297 dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
299 for (;;) {
301 EnterCriticalSection(&wwo->critSect);
303 switch (wwo->state) {
304 case WINE_WS_PAUSED:
305 wodPlayer_Notify(wwo, uDevID, FALSE);
306 if (laststate != WINE_WS_PAUSED) {
307 if (ioctl(wwo->unixdev, SNDCTL_DSP_POST, 0) == -1) {
308 perror("ioctl SNDCTL_DSP_POST");
309 wwo->hThread = 0;
310 LeaveCriticalSection(&wwo->critSect);
311 ExitThread(-1);
314 break;
315 case WINE_WS_STOPPED:
316 /* should we kill the Thread here and to restart it later when needed ? */
317 break;
318 case WINE_WS_PLAYING:
319 if (!wodPlayer_WriteFragments(wwo)) {
320 wodPlayer_Notify(wwo, uDevID, FALSE);
321 break;
323 /* fall thru */
324 case WINE_WS_RESETTING:
325 /* updates current notify list */
326 wodPlayer_Notify(wwo, uDevID, FALSE);
327 /* flush all possible output */
328 if (ioctl(wwo->unixdev, SNDCTL_DSP_SYNC, 0) == -1) {
329 perror("ioctl SNDCTL_DSP_SYNC");
330 wwo->hThread = 0;
331 LeaveCriticalSection(&wwo->critSect);
332 ExitThread(-1);
334 /* DSP_SYNC resets the number of send bytes */
335 wwo->dwNotifiedBytes = 0;
336 /* empty notify list */
337 wodPlayer_Notify(wwo, uDevID, TRUE);
338 if (wwo->lpNotifyHdr) {
339 ERR(wave, "out of sync\n");
341 wwo->dwOffCurrHdr = 0;
342 /* get rid also of all the current queue */
343 for (lpWaveHdr = wwo->lpQueueHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
344 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
345 lpWaveHdr->dwFlags |= WHDR_DONE;
347 if (WAVE_NotifyClient((DWORD)pmt, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
348 WARN(wave, "can't notify client !\n");
351 wwo->lpQueueHdr = 0;
352 wwo->state = WINE_WS_STOPPED;
353 break;
354 case WINE_WS_CLOSING:
355 /* this should not happen since the device must have been reset before */
356 if (wwo->lpNotifyHdr || wwo->lpQueueHdr) {
357 ERR(wave, "out of sync\n");
359 wwo->hThread = 0;
360 wwo->state = WINE_WS_CLOSED;
361 LeaveCriticalSection(&wwo->critSect);
362 DeleteCriticalSection(&wwo->critSect);
363 ExitThread(0);
364 /* shouldn't go here */
365 default:
366 FIXME(wave, "unknown state %d\n", wwo->state);
367 break;
369 laststate = wwo->state;
370 LeaveCriticalSection(&wwo->critSect);
371 /* sleep for one fragment to be sent */
372 Sleep(dwSleepTime);
374 ExitThread(0);
377 /**************************************************************************
378 * wodGetDevCaps [internal]
380 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS16 lpCaps, DWORD dwSize)
382 int audio;
383 int smplrate;
384 int samplesize = 16;
385 int dsp_stereo = 1;
386 int bytespersmpl;
387 int caps;
389 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
391 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
392 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
394 if (wDevID >= MAX_WAVEOUTDRV) {
395 TRACE(wave, "MAX_WAVOUTDRV reached !\n");
396 return MMSYSERR_BADDEVICEID;
399 if (WOutDev[wDevID].unixdev == 0) {
400 audio = open(SOUND_DEV, O_WRONLY, 0);
401 if (audio == -1) return MMSYSERR_ALLOCATED;
402 } else {
403 audio = WOutDev[wDevID].unixdev;
406 /* FIXME: some programs compare this string against the content of the registry
407 * for MM drivers. The name have to match in order the program to work
408 * (e.g. MS win9x mplayer.exe)
410 #ifdef EMULATE_SB16
411 lpCaps->wMid = 0x0002;
412 lpCaps->wPid = 0x0104;
413 strcpy(lpCaps->szPname, "SB16 Wave Out");
414 #else
415 lpCaps->wMid = 0x00FF; /* Manufac ID */
416 lpCaps->wPid = 0x0001; /* Product ID */
417 /* strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");*/
418 strcpy(lpCaps->szPname, "CS4236/37/38");
419 #endif
420 lpCaps->vDriverVersion = 0x0100;
421 lpCaps->dwFormats = 0x00000000;
422 lpCaps->dwSupport = WAVECAPS_VOLUME;
424 /* First bytespersampl, then stereo */
425 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
427 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
428 if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
430 smplrate = 44100;
431 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
432 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
433 if (lpCaps->wChannels > 1)
434 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
435 if (bytespersmpl > 1) {
436 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
437 if (lpCaps->wChannels > 1)
438 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
441 smplrate = 22050;
442 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
443 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
444 if (lpCaps->wChannels > 1)
445 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
446 if (bytespersmpl > 1) {
447 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
448 if (lpCaps->wChannels > 1)
449 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
452 smplrate = 11025;
453 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
454 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
455 if (lpCaps->wChannels > 1)
456 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
457 if (bytespersmpl > 1) {
458 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
459 if (lpCaps->wChannels > 1)
460 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
463 if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
464 if ((caps & DSP_CAP_REALTIME) && !(caps && DSP_CAP_BATCH))
465 lpCaps->dwFormats |= WAVECAPS_SAMPLEACCURATE;
467 if (WOutDev[wDevID].unixdev == 0) {
468 close(audio);
470 TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
471 return MMSYSERR_NOERROR;
475 /**************************************************************************
476 * wodOpen [internal]
478 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
480 int audio;
481 int sample_rate;
482 int sample_size;
483 int dsp_stereo;
484 int audio_fragment;
485 int fragment_size;
487 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
488 if (lpDesc == NULL) {
489 WARN(wave, "Invalid Parameter !\n");
490 return MMSYSERR_INVALPARAM;
492 if (wDevID >= MAX_WAVEOUTDRV) {
493 TRACE(wave, "MAX_WAVOUTDRV reached !\n");
494 return MMSYSERR_BADDEVICEID;
496 /* only PCM format is supported so far... */
497 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
498 lpDesc->lpFormat->nChannels == 0 ||
499 lpDesc->lpFormat->nSamplesPerSec == 0) {
500 WARN(wave, "Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
501 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
502 lpDesc->lpFormat->nSamplesPerSec);
503 return WAVERR_BADFORMAT;
506 if (dwFlags & WAVE_FORMAT_QUERY)
507 return MMSYSERR_NOERROR;
509 WOutDev[wDevID].unixdev = 0;
510 if (access(SOUND_DEV, 0) != 0)
511 return MMSYSERR_NOTENABLED;
512 audio = open(SOUND_DEV, O_WRONLY, 0);
513 if (audio == -1) {
514 WARN(wave, "can't open !\n");
515 return MMSYSERR_ALLOCATED ;
518 WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
520 memcpy(&WOutDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
521 memcpy(&WOutDev[wDevID].format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
523 if (WOutDev[wDevID].format.wBitsPerSample == 0) {
524 WOutDev[wDevID].format.wBitsPerSample = 8 *
525 (WOutDev[wDevID].format.wf.nAvgBytesPerSec /
526 WOutDev[wDevID].format.wf.nSamplesPerSec) /
527 WOutDev[wDevID].format.wf.nChannels;
530 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
531 * thus leading to 46ms per fragment, and a turnaround time of 185ms
533 /* 2^10=1024 bytes per fragment, 16 fragments max */
534 audio_fragment = 0x000F000A;
535 sample_size = WOutDev[wDevID].format.wBitsPerSample;
536 sample_rate = WOutDev[wDevID].format.wf.nSamplesPerSec;
537 dsp_stereo = (WOutDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
539 IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
540 /* First size and stereo then samplerate */
541 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, sample_size);
542 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
543 IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
545 /* even if we set fragment size above, read it again, just in case */
546 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
548 WOutDev[wDevID].lpQueueHdr = NULL;
549 WOutDev[wDevID].unixdev = audio;
550 WOutDev[wDevID].dwTotalPlayed = 0;
551 WOutDev[wDevID].dwFragmentSize = fragment_size;
552 WOutDev[wDevID].state = WINE_WS_STOPPED;
553 WOutDev[wDevID].hThread = 0;
555 /* FIXME: do we need to make critsect global ? */
556 InitializeCriticalSection(&WOutDev[wDevID].critSect);
557 MakeCriticalSectionGlobal(&WOutDev[wDevID].critSect);
559 TRACE(wave, "fd=%d fragmentSize=%ld\n",
560 WOutDev[wDevID].unixdev, WOutDev[wDevID].dwFragmentSize);
562 TRACE(wave, "wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u !\n",
563 WOutDev[wDevID].format.wBitsPerSample, WOutDev[wDevID].format.wf.nAvgBytesPerSec,
564 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels);
566 if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
567 WARN(wave, "can't notify client !\n");
568 return MMSYSERR_INVALPARAM;
570 return MMSYSERR_NOERROR;
573 /**************************************************************************
574 * wodClose [internal]
576 static DWORD wodClose(WORD wDevID)
578 DWORD ret = MMSYSERR_NOERROR;
580 TRACE(wave, "(%u);\n", wDevID);
582 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
583 WARN(wave, "bad device ID !\n");
584 return MMSYSERR_BADDEVICEID;
587 if (WOutDev[wDevID].lpQueueHdr != NULL || WOutDev[wDevID].lpNotifyHdr != NULL) {
588 WARN(wave, "buffers still playing !\n");
589 ret = WAVERR_STILLPLAYING;
590 } else {
591 EnterCriticalSection(&WOutDev[wDevID].critSect);
592 WOutDev[wDevID].state = WINE_WS_CLOSING;
593 LeaveCriticalSection(&WOutDev[wDevID].critSect);
595 while (WOutDev[wDevID].state != WINE_WS_CLOSED)
596 Sleep(10);
598 close(WOutDev[wDevID].unixdev);
599 WOutDev[wDevID].unixdev = 0;
600 WOutDev[wDevID].dwFragmentSize = 0;
601 if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
602 WARN(wave, "can't notify client !\n");
603 ret = MMSYSERR_INVALPARAM;
606 return ret;
609 /**************************************************************************
610 * wodWrite [internal]
613 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
615 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
617 /* first, do the sanity checks... */
618 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
619 WARN(wave, "bad dev ID !\n");
620 return MMSYSERR_BADDEVICEID;
623 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
624 return WAVERR_UNPREPARED;
626 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
627 return WAVERR_STILLPLAYING;
629 lpWaveHdr->dwFlags &= ~WHDR_DONE;
630 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
631 lpWaveHdr->lpNext = 0;
633 EnterCriticalSection(&WOutDev[wDevID].critSect);
634 /* insert buffer in queue */
635 if (WOutDev[wDevID].lpQueueHdr) {
636 WAVEHDR* wh;
637 for (wh = WOutDev[wDevID].lpQueueHdr; wh->lpNext; wh = wh->lpNext);
638 wh->lpNext = lpWaveHdr;
639 } else {
640 WOutDev[wDevID].lpQueueHdr = lpWaveHdr;
642 /* starts thread if wod is not paused */
643 switch (WOutDev[wDevID].state) {
644 case WINE_WS_PAUSED:
645 case WINE_WS_PLAYING:
646 /* nothing to do */
647 break;
648 case WINE_WS_STOPPED:
649 WOutDev[wDevID].state = WINE_WS_PLAYING;
650 if (WOutDev[wDevID].hThread == 0) {
651 WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, NULL);
653 break;
654 default:
655 FIXME(wave, "Unsupported state %d\n", WOutDev[wDevID].state);
657 LeaveCriticalSection(&WOutDev[wDevID].critSect);
659 return MMSYSERR_NOERROR;
662 /**************************************************************************
663 * wodPrepare [internal]
665 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
667 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
669 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
670 WARN(wave, "bad device ID !\n");
671 return MMSYSERR_BADDEVICEID;
674 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
675 return WAVERR_STILLPLAYING;
677 lpWaveHdr->dwFlags |= WHDR_PREPARED;
678 lpWaveHdr->dwFlags &= ~WHDR_DONE;
679 return MMSYSERR_NOERROR;
682 /**************************************************************************
683 * wodUnprepare [internal]
685 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
687 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
689 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
690 WARN(wave, "bad device ID !\n");
691 return MMSYSERR_BADDEVICEID;
694 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
695 return WAVERR_STILLPLAYING;
697 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
698 lpWaveHdr->dwFlags |= WHDR_DONE;
700 return MMSYSERR_NOERROR;
703 /**************************************************************************
704 * wodPause [internal]
706 static DWORD wodPause(WORD wDevID)
708 TRACE(wave, "(%u);!\n", wDevID);
710 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
711 WARN(wave, "bad device ID !\n");
712 return MMSYSERR_BADDEVICEID;
715 EnterCriticalSection(&WOutDev[wDevID].critSect);
716 WOutDev[wDevID].state = WINE_WS_PAUSED;
717 LeaveCriticalSection(&WOutDev[wDevID].critSect);
719 return MMSYSERR_NOERROR;
722 /**************************************************************************
723 * wodStop [internal]
725 static DWORD wodStop(WORD wDevID)
727 FIXME(wave, "(%u); NIY!\n", wDevID);
729 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
730 WARN(wave, "bad device ID !\n");
731 return MMSYSERR_BADDEVICEID;
734 return MMSYSERR_NOERROR;
737 /**************************************************************************
738 * wodRestart [internal]
740 static DWORD wodRestart(WORD wDevID)
742 TRACE(wave, "(%u);\n", wDevID);
744 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
745 WARN(wave, "bad device ID !\n");
746 return MMSYSERR_BADDEVICEID;
749 EnterCriticalSection(&WOutDev[wDevID].critSect);
750 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
751 if (WOutDev[wDevID].lpQueueHdr != NULL) {
752 WOutDev[wDevID].state = WINE_WS_PLAYING;
753 if (WOutDev[wDevID].hThread == 0)
754 WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, NULL);
755 } else {
756 WOutDev[wDevID].state = WINE_WS_STOPPED;
759 LeaveCriticalSection(&WOutDev[wDevID].critSect);
761 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
762 /* FIXME: Myst crashes with this ... hmm -MM
763 if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
764 WARN(wave, "can't notify client !\n");
765 return MMSYSERR_INVALPARAM;
769 return MMSYSERR_NOERROR;
772 /**************************************************************************
773 * wodReset [internal]
775 static DWORD wodReset(WORD wDevID)
777 TRACE(wave, "(%u);\n", wDevID);
779 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
780 WARN(wave, "bad device ID !\n");
781 return MMSYSERR_BADDEVICEID;
784 EnterCriticalSection(&WOutDev[wDevID].critSect);
785 if (WOutDev[wDevID].hThread == 0) {
786 WOutDev[wDevID].state = WINE_WS_STOPPED;
787 } else {
788 WOutDev[wDevID].state = WINE_WS_RESETTING;
790 LeaveCriticalSection(&WOutDev[wDevID].critSect);
792 while (WOutDev[wDevID].state != WINE_WS_STOPPED)
793 Sleep(10);
795 return MMSYSERR_NOERROR;
799 /**************************************************************************
800 * wodGetPosition [internal]
802 static DWORD wodGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
804 int time;
806 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpTime, uSize);
808 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
809 WARN(wave, "bad device ID !\n");
810 return MMSYSERR_BADDEVICEID;
813 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
814 TRACE(wave, "wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
815 lpTime->wType, WOutDev[wDevID].format.wBitsPerSample,
816 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels,
817 WOutDev[wDevID].format.wf.nAvgBytesPerSec);
818 TRACE(wave, "dwTotalPlayed=%lu\n", WOutDev[wDevID].dwTotalPlayed);
820 switch (lpTime->wType) {
821 case TIME_BYTES:
822 lpTime->u.cb = WOutDev[wDevID].dwTotalPlayed;
823 TRACE(wave, "TIME_BYTES=%lu\n", lpTime->u.cb);
824 break;
825 case TIME_SAMPLES:
826 lpTime->u.sample = WOutDev[wDevID].dwTotalPlayed * 8 /
827 WOutDev[wDevID].format.wBitsPerSample;
828 TRACE(wave, "TIME_SAMPLES=%lu\n", lpTime->u.sample);
829 break;
830 case TIME_SMPTE:
831 time = WOutDev[wDevID].dwTotalPlayed /
832 (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
833 lpTime->u.smpte.hour = time / 108000;
834 time -= lpTime->u.smpte.hour * 108000;
835 lpTime->u.smpte.min = time / 1800;
836 time -= lpTime->u.smpte.min * 1800;
837 lpTime->u.smpte.sec = time / 30;
838 time -= lpTime->u.smpte.sec * 30;
839 lpTime->u.smpte.frame = time;
840 lpTime->u.smpte.fps = 30;
841 TRACE(wave, "TIME_SMPTE=%02u:%02u:%02u:%02u\n",
842 lpTime->u.smpte.hour, lpTime->u.smpte.min,
843 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
844 break;
845 default:
846 FIXME(wave, "Format %d not supported ! use TIME_MS !\n", lpTime->wType);
847 lpTime->wType = TIME_MS;
848 case TIME_MS:
849 lpTime->u.ms = WOutDev[wDevID].dwTotalPlayed /
850 (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
851 TRACE(wave, "TIME_MS=%lu\n", lpTime->u.ms);
852 break;
854 return MMSYSERR_NOERROR;
857 /**************************************************************************
858 * wodGetVolume [internal]
860 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
862 int mixer;
863 int volume, left, right;
865 TRACE(wave, "(%u, %p);\n", wDevID, lpdwVol);
867 if (lpdwVol == NULL)
868 return MMSYSERR_NOTENABLED;
869 if ((mixer = open(MIXER_DEV, O_RDONLY)) < 0) {
870 WARN(wave, "mixer device not available !\n");
871 return MMSYSERR_NOTENABLED;
873 if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
874 WARN(wave, "unable read mixer !\n");
875 return MMSYSERR_NOTENABLED;
877 close(mixer);
878 left = volume & 0x7F;
879 right = (volume >> 8) & 0x7F;
880 TRACE(wave, "left=%d right=%d !\n", left, right);
881 *lpdwVol = MAKELONG(left << 9, right << 9);
882 return MMSYSERR_NOERROR;
886 /**************************************************************************
887 * wodSetVolume [internal]
889 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
891 int mixer;
892 int volume;
893 TRACE(wave, "(%u, %08lX);\n", wDevID, dwParam);
894 volume = (LOWORD(dwParam) >> 9 & 0x7F) +
895 ((HIWORD(dwParam) >> 9 & 0x7F) << 8);
896 if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) {
897 WARN(wave, "mixer device not available !\n");
898 return MMSYSERR_NOTENABLED;
900 if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
901 WARN(wave, "unable set mixer !\n");
902 return MMSYSERR_NOTENABLED;
904 close(mixer);
905 return MMSYSERR_NOERROR;
908 /**************************************************************************
909 * wodGetNumDevs [internal]
911 static DWORD wodGetNumDevs(void)
913 DWORD ret = 1;
915 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
916 int audio = open(SOUND_DEV, O_WRONLY, 0);
918 if (audio == -1) {
919 if (errno != EBUSY)
920 ret = 0;
921 } else {
922 close(audio);
924 return ret;
927 /**************************************************************************
928 * wodMessage [sample driver]
930 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
931 DWORD dwParam1, DWORD dwParam2)
933 TRACE(wave, "(%u, %04X, %08lX, %08lX, %08lX);\n",
934 wDevID, wMsg, dwUser, dwParam1, dwParam2);
936 switch (wMsg) {
937 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
938 case WODM_CLOSE: return wodClose (wDevID);
939 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
940 case WODM_PAUSE: return wodPause (wDevID);
941 case WODM_STOP: return wodStop (wDevID);
942 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME16)dwParam1, dwParam2);
943 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
944 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
945 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
946 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPS16)dwParam1, dwParam2);
947 case WODM_GETNUMDEVS: return wodGetNumDevs ();
948 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
949 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
950 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
951 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
952 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
953 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
954 case WODM_RESTART: return wodRestart (wDevID);
955 case WODM_RESET: return wodReset (wDevID);
956 default:
957 FIXME(wave, "unknown message %d!\n", wMsg);
959 return MMSYSERR_NOTSUPPORTED;
962 /*======================================================================*
963 * Low level WAVE IN implemantation *
964 *======================================================================*/
966 /**************************************************************************
967 * widGetDevCaps [internal]
969 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS16 lpCaps, DWORD dwSize)
971 int audio,smplrate,samplesize=16,dsp_stereo=1,bytespersmpl;
973 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
974 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
975 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
976 audio = open(SOUND_DEV, O_RDONLY, 0);
977 if (audio == -1) return MMSYSERR_ALLOCATED ;
978 #ifdef EMULATE_SB16
979 lpCaps->wMid = 0x0002;
980 lpCaps->wPid = 0x0004;
981 strcpy(lpCaps->szPname, "SB16 Wave In");
982 #else
983 lpCaps->wMid = 0x00FF; /* Manufac ID */
984 lpCaps->wPid = 0x0001; /* Product ID */
985 strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
986 #endif
987 lpCaps->dwFormats = 0x00000000;
988 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
989 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
990 smplrate = 44100;
991 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
992 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
993 if (lpCaps->wChannels > 1)
994 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
995 if (bytespersmpl > 1) {
996 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
997 if (lpCaps->wChannels > 1)
998 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
1001 smplrate = 22050;
1002 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1003 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
1004 if (lpCaps->wChannels > 1)
1005 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
1006 if (bytespersmpl > 1) {
1007 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
1008 if (lpCaps->wChannels > 1)
1009 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
1012 smplrate = 11025;
1013 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1014 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
1015 if (lpCaps->wChannels > 1)
1016 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
1017 if (bytespersmpl > 1) {
1018 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
1019 if (lpCaps->wChannels > 1)
1020 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
1023 close(audio);
1024 TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
1025 return MMSYSERR_NOERROR;
1028 /**************************************************************************
1029 * widOpen [internal]
1031 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1033 int audio,abuf_size,smplrate,samplesize,dsp_stereo;
1034 LPWAVEFORMAT lpFormat;
1036 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1037 if (lpDesc == NULL) {
1038 WARN(wave, "Invalid Parameter !\n");
1039 return MMSYSERR_INVALPARAM;
1041 if (wDevID >= MAX_WAVEINDRV) {
1042 TRACE(wave, "MAX_WAVINDRV reached !\n");
1043 return MMSYSERR_ALLOCATED;
1045 WInDev[wDevID].unixdev = 0;
1046 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1047 audio = open(SOUND_DEV, O_RDONLY, 0);
1048 if (audio == -1) {
1049 WARN(wave, "can't open !\n");
1050 return MMSYSERR_ALLOCATED;
1052 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
1053 if (abuf_size < 1024 || abuf_size > 65536) {
1054 if (abuf_size == -1)
1055 WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1056 else
1057 WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize !\n");
1058 return MMSYSERR_NOTENABLED;
1060 WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1062 if (WInDev[wDevID].lpQueueHdr) {
1063 HeapFree(GetProcessHeap(), 0, WInDev[wDevID].lpQueueHdr);
1064 WInDev[wDevID].lpQueueHdr = NULL;
1066 WInDev[wDevID].unixdev = audio;
1067 WInDev[wDevID].dwFragmentSize = abuf_size;
1068 WInDev[wDevID].dwTotalRecorded = 0;
1069 memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1070 lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat;
1071 if (lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
1072 WARN(wave, "Bad format %04X !\n",
1073 lpFormat->wFormatTag);
1074 return WAVERR_BADFORMAT;
1076 memcpy(&WInDev[wDevID].format, lpFormat, sizeof(PCMWAVEFORMAT));
1077 WInDev[wDevID].format.wBitsPerSample = 8; /* <-------------- */
1078 if (WInDev[wDevID].format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1079 if (WInDev[wDevID].format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1080 if (WInDev[wDevID].format.wBitsPerSample == 0) {
1081 WInDev[wDevID].format.wBitsPerSample = 8 *
1082 (WInDev[wDevID].format.wf.nAvgBytesPerSec /
1083 WInDev[wDevID].format.wf.nSamplesPerSec) /
1084 WInDev[wDevID].format.wf.nChannels;
1086 samplesize = WInDev[wDevID].format.wBitsPerSample;
1087 smplrate = WInDev[wDevID].format.wf.nSamplesPerSec;
1088 dsp_stereo = (WInDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
1089 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1090 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1091 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1092 TRACE(wave, "wBitsPerSample=%u !\n", WInDev[wDevID].format.wBitsPerSample);
1093 TRACE(wave, "nSamplesPerSec=%lu !\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1094 TRACE(wave, "nChannels=%u !\n", WInDev[wDevID].format.wf.nChannels);
1095 TRACE(wave, "nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1096 if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1097 WARN(wave, "can't notify client !\n");
1098 return MMSYSERR_INVALPARAM;
1100 return MMSYSERR_NOERROR;
1103 /**************************************************************************
1104 * widClose [internal]
1106 static DWORD widClose(WORD wDevID)
1108 TRACE(wave, "(%u);\n", wDevID);
1109 if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
1110 if (WInDev[wDevID].unixdev == 0) {
1111 WARN(wave, "can't close !\n");
1112 return MMSYSERR_NOTENABLED;
1114 if (WInDev[wDevID].lpQueueHdr != NULL) {
1115 WARN(wave, "still buffers open !\n");
1116 return WAVERR_STILLPLAYING;
1118 close(WInDev[wDevID].unixdev);
1119 WInDev[wDevID].unixdev = 0;
1120 WInDev[wDevID].dwFragmentSize = 0;
1121 if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1122 WARN(wave, "can't notify client !\n");
1123 return MMSYSERR_INVALPARAM;
1125 return MMSYSERR_NOERROR;
1128 /**************************************************************************
1129 * widAddBuffer [internal]
1131 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1133 int count = 1;
1134 LPWAVEHDR lpWIHdr;
1136 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1137 if (WInDev[wDevID].unixdev == 0) {
1138 WARN(wave, "can't do it !\n");
1139 return MMSYSERR_NOTENABLED;
1141 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1142 TRACE(wave, "never been prepared !\n");
1143 return WAVERR_UNPREPARED;
1145 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1146 TRACE(wave, "header already in use !\n");
1147 return WAVERR_STILLPLAYING;
1149 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1150 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1151 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1152 lpWaveHdr->dwBytesRecorded = 0;
1153 if (WInDev[wDevID].lpQueueHdr == NULL) {
1154 WInDev[wDevID].lpQueueHdr = lpWaveHdr;
1155 } else {
1156 lpWIHdr = WInDev[wDevID].lpQueueHdr;
1157 while (lpWIHdr->lpNext != NULL) {
1158 lpWIHdr = lpWIHdr->lpNext;
1159 count++;
1161 lpWIHdr->lpNext = lpWaveHdr;
1162 lpWaveHdr->lpNext = NULL;
1163 count++;
1165 TRACE(wave, "buffer added ! (now %u in queue)\n", count);
1166 return MMSYSERR_NOERROR;
1169 /**************************************************************************
1170 * widPrepare [internal]
1172 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1174 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1175 if (WInDev[wDevID].unixdev == 0) {
1176 WARN(wave, "can't prepare !\n");
1177 return MMSYSERR_NOTENABLED;
1179 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1180 return WAVERR_STILLPLAYING;
1181 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1182 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1183 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1184 lpWaveHdr->dwBytesRecorded = 0;
1185 TRACE(wave, "header prepared !\n");
1186 return MMSYSERR_NOERROR;
1189 /**************************************************************************
1190 * widUnprepare [internal]
1192 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1194 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1195 if (WInDev[wDevID].unixdev == 0) {
1196 WARN(wave, "can't unprepare !\n");
1197 return MMSYSERR_NOTENABLED;
1199 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1200 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1201 lpWaveHdr->dwFlags |= WHDR_DONE;
1203 return MMSYSERR_NOERROR;
1206 /**************************************************************************
1207 * widStart [internal]
1209 static DWORD widStart(WORD wDevID)
1211 int count = 1;
1212 int bytesRead;
1213 LPWAVEHDR *lpWaveHdr;
1214 LPBYTE lpData;
1216 TRACE(wave, "(%u);\n", wDevID);
1217 if (WInDev[wDevID].unixdev == 0) {
1218 WARN(wave, "can't start recording !\n");
1219 return MMSYSERR_NOTENABLED;
1222 lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
1223 TRACE(wave, "lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
1225 if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
1226 TRACE(wave, "never been prepared !\n");
1227 return WAVERR_UNPREPARED;
1230 while (*lpWaveHdr != NULL) {
1232 lpData = ((DWORD)*lpWaveHdr == (*lpWaveHdr)->reserved) ?
1233 (LPBYTE)(*lpWaveHdr)->lpData : (LPBYTE)PTR_SEG_TO_LIN((*lpWaveHdr)->lpData);
1235 TRACE(wave, "recording buf#%u=%p size=%lu \n",
1236 count, lpData, (*lpWaveHdr)->dwBufferLength);
1237 fflush(stddeb);
1239 bytesRead = read(WInDev[wDevID].unixdev, lpData, (*lpWaveHdr)->dwBufferLength);
1241 if (bytesRead == -1)
1242 perror("read from audio device");
1244 TRACE(wave, "bytesread=%d (%ld)\n", bytesRead, (*lpWaveHdr)->dwBufferLength);
1245 (*lpWaveHdr)->dwBytesRecorded = bytesRead;
1246 WInDev[wDevID].dwTotalRecorded += (*lpWaveHdr)->dwBytesRecorded;
1247 (*lpWaveHdr)->dwFlags &= ~WHDR_INQUEUE;
1248 (*lpWaveHdr)->dwFlags |= WHDR_DONE;
1250 if (WAVE_NotifyClient(wDevID, WIM_DATA, (*lpWaveHdr)->reserved, (*lpWaveHdr)->dwBytesRecorded) != MMSYSERR_NOERROR) {
1251 WARN(wave, "can't notify client !\n");
1252 return MMSYSERR_INVALPARAM;
1254 /* removes the current block from the queue */
1255 *lpWaveHdr = (*lpWaveHdr)->lpNext;
1256 count++;
1258 TRACE(wave, "end of recording !\n");
1259 fflush(stddeb);
1260 return MMSYSERR_NOERROR;
1263 /**************************************************************************
1264 * widStop [internal]
1266 static DWORD widStop(WORD wDevID)
1268 TRACE(wave, "(%u);\n", wDevID);
1269 if (WInDev[wDevID].unixdev == 0) {
1270 WARN(wave, "can't stop !\n");
1271 return MMSYSERR_NOTENABLED;
1273 return MMSYSERR_NOERROR;
1276 /**************************************************************************
1277 * widReset [internal]
1279 static DWORD widReset(WORD wDevID)
1281 TRACE(wave, "(%u);\n", wDevID);
1282 if (WInDev[wDevID].unixdev == 0) {
1283 WARN(wave, "can't reset !\n");
1284 return MMSYSERR_NOTENABLED;
1286 return MMSYSERR_NOERROR;
1289 /**************************************************************************
1290 * widGetPosition [internal]
1292 static DWORD widGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
1294 int time;
1296 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1297 if (WInDev[wDevID].unixdev == 0) {
1298 WARN(wave, "can't get pos !\n");
1299 return MMSYSERR_NOTENABLED;
1301 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1302 TRACE(wave, "wType=%04X !\n", lpTime->wType);
1303 TRACE(wave, "wBitsPerSample=%u\n", WInDev[wDevID].format.wBitsPerSample);
1304 TRACE(wave, "nSamplesPerSec=%lu\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1305 TRACE(wave, "nChannels=%u\n", WInDev[wDevID].format.wf.nChannels);
1306 TRACE(wave, "nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1307 fflush(stddeb);
1308 switch (lpTime->wType) {
1309 case TIME_BYTES:
1310 lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
1311 TRACE(wave, "TIME_BYTES=%lu\n", lpTime->u.cb);
1312 break;
1313 case TIME_SAMPLES:
1314 lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
1315 WInDev[wDevID].format.wBitsPerSample;
1316 TRACE(wave, "TIME_SAMPLES=%lu\n", lpTime->u.sample);
1317 break;
1318 case TIME_SMPTE:
1319 time = WInDev[wDevID].dwTotalRecorded /
1320 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1321 lpTime->u.smpte.hour = time / 108000;
1322 time -= lpTime->u.smpte.hour * 108000;
1323 lpTime->u.smpte.min = time / 1800;
1324 time -= lpTime->u.smpte.min * 1800;
1325 lpTime->u.smpte.sec = time / 30;
1326 time -= lpTime->u.smpte.sec * 30;
1327 lpTime->u.smpte.frame = time;
1328 lpTime->u.smpte.fps = 30;
1329 TRACE(wave, "TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1330 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1331 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1332 break;
1333 case TIME_MS:
1334 lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1335 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1336 TRACE(wave, "TIME_MS=%lu\n", lpTime->u.ms);
1337 break;
1338 default:
1339 FIXME(wave, "format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1340 lpTime->wType = TIME_MS;
1342 return MMSYSERR_NOERROR;
1345 /**************************************************************************
1346 * widMessage [sample driver]
1348 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1349 DWORD dwParam1, DWORD dwParam2)
1351 TRACE(wave, "(%u, %04X, %08lX, %08lX, %08lX);\n",
1352 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1354 switch (wMsg) {
1355 case WIDM_OPEN: return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1356 case WIDM_CLOSE: return widClose(wDevID);
1357 case WIDM_ADDBUFFER: return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1358 case WIDM_PREPARE: return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1359 case WIDM_UNPREPARE: return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1360 case WIDM_GETDEVCAPS: return widGetDevCaps(wDevID, (LPWAVEINCAPS16)dwParam1,dwParam2);
1361 case WIDM_GETNUMDEVS: return wodGetNumDevs(); /* same number of devices in output as in input */
1362 case WIDM_GETPOS: return widGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
1363 case WIDM_RESET: return widReset(wDevID);
1364 case WIDM_START: return widStart(wDevID);
1365 case WIDM_PAUSE: return widStop(wDevID);
1366 case WIDM_STOP: return widStop(wDevID);
1367 default:
1368 FIXME(wave, "unknown message %u!\n", wMsg);
1370 return MMSYSERR_NOTSUPPORTED;
1373 /*======================================================================*
1374 * Low level WAVE implemantation - DriverProc *
1375 *======================================================================*/
1377 /**************************************************************************
1378 * WAVE_DriverProc [sample driver]
1380 LONG WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1381 DWORD dwParam1, DWORD dwParam2)
1383 TRACE(wave, "(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1384 switch (wMsg) {
1385 case DRV_LOAD: return 1;
1386 case DRV_FREE: return 1;
1387 case DRV_OPEN: return 1;
1388 case DRV_CLOSE: return 1;
1389 case DRV_ENABLE: return 1;
1390 case DRV_DISABLE: return 1;
1391 case DRV_QUERYCONFIGURE: return 1;
1392 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
1393 case DRV_INSTALL: return DRVCNF_RESTART;
1394 case DRV_REMOVE: return DRVCNF_RESTART;
1395 default:
1396 FIXME(wave, "is probably wrong msg=0x%04lx\n", wMsg);
1397 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1399 return MMSYSERR_NOTENABLED;
1402 #else /* !HAVE_OSS */
1404 /**************************************************************************
1405 * wodMessage [sample driver]
1407 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1408 DWORD dwParam1, DWORD dwParam2)
1410 FIXME(wave, "(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1411 return MMSYSERR_NOTENABLED;
1414 /**************************************************************************
1415 * widMessage [sample driver]
1417 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1418 DWORD dwParam1, DWORD dwParam2)
1420 FIXME(wave, "(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1421 return MMSYSERR_NOTENABLED;
1424 /**************************************************************************
1425 * WAVE_DriverProc [sample driver]
1427 LONG WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1428 DWORD dwParam1, DWORD dwParam2)
1430 return MMSYSERR_NOTENABLED;
1432 #endif /* HAVE_OSS */