Initialize the output buffer parameters to 0 in RegQueryValue*
[wine.git] / multimedia / audio.c
blobf613fce3d44d9d1a60f489df2bd71ecf15e093fd
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;
145 return 0;
148 /*======================================================================*
149 * Low level WAVE OUT implemantation *
150 *======================================================================*/
152 /**************************************************************************
153 * wodPlayer_WriteFragments [internal]
155 * wodPlayer helper. Writes as many fragments it can to unixdev.
156 * Returns TRUE in case of buffer underrun.
158 static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
160 LPWAVEHDR lpWaveHdr;
161 LPWAVEHDR wh;
162 LPBYTE lpData;
163 int count;
164 audio_buf_info abinfo;
166 for (;;) {
167 /* get number of writable fragments */
168 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &abinfo) == -1) {
169 perror("ioctl SNDCTL_DSP_GETOSPACE");
170 wwo->hThread = 0;
171 LeaveCriticalSection(&wwo->critSect);
172 ExitThread(-1);
174 TRACE(wave, "Can write %d fragments on fd %d\n", abinfo.fragments, wwo->unixdev);
176 if (abinfo.fragments == 0) /* output queue is full, wait a bit */
177 return FALSE;
179 lpWaveHdr = wwo->lpQueueHdr;
180 if (!lpWaveHdr) {
181 if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */
182 wwo->dwNotifiedBytes >= wwo->dwFragmentSize && /* first fragment has been played */
183 abinfo.fragstotal - abinfo.fragments < 2) { /* done with all waveOutWrite()' fragments */
185 /* FIXME: should do better handling here */
186 TRACE(wave, "Oooch, buffer underrun !\n");
187 return TRUE; /* force resetting of waveOut device */
189 return FALSE; /* wait a bit */
192 if (wwo->dwOffCurrHdr == 0) {
193 TRACE(wave, "Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
194 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
195 FIXME(wave, "NIY: loops (%lu) in wavehdr\n", lpWaveHdr->dwLoops);
198 lpData = ((DWORD)lpWaveHdr == lpWaveHdr->reserved) ?
199 (LPBYTE)lpWaveHdr->lpData : (LPBYTE)PTR_SEG_TO_LIN(lpWaveHdr->lpData);
201 /* finish current wave hdr ? */
202 if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) {
203 DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
205 /* write end of current wave hdr */
206 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
207 TRACE(wave, "write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
209 /* move lpWaveHdr to notify list */
210 if (wwo->lpNotifyHdr) {
211 for (wh = wwo->lpNotifyHdr; wh->lpNext != NULL; wh = wh->lpNext);
212 wh->lpNext = lpWaveHdr;
213 } else {
214 wwo->lpNotifyHdr = lpWaveHdr;
216 wwo->lpQueueHdr = lpWaveHdr->lpNext;
217 lpWaveHdr->lpNext = 0;
219 wwo->dwOffCurrHdr = 0;
220 if ((wwo->dwRemain -= toWrite) == 0)
221 wwo->dwRemain = wwo->dwFragmentSize;
222 continue; /* try to go to use next wavehdr */
223 } else {
224 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
225 TRACE(wave, "write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
226 wwo->dwOffCurrHdr += count;
227 wwo->dwRemain = wwo->dwFragmentSize;
232 /**************************************************************************
233 * wodPlayer_WriteFragments [internal]
235 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
236 * have been played (actually to speaker, not to unixdev fd).
238 static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
240 LPWAVEHDR lpWaveHdr;
241 count_info cinfo;
243 /* get effective number of written bytes */
244 if (!force) {
245 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOPTR, &cinfo) == -1) {
246 perror("ioctl SNDCTL_DSP_GETOPTR");
247 wwo->hThread = 0;
248 LeaveCriticalSection(&wwo->critSect);
249 ExitThread(-1);
251 TRACE(wave, "Played %d bytes on fd %d\n", cinfo.bytes, wwo->unixdev);
252 /* FIXME: this will not work if a RESET or SYNC occurs somewhere */
253 wwo->dwTotalPlayed = cinfo.bytes;
255 if (force || cinfo.bytes > wwo->dwNotifiedBytes) {
256 /* remove all wavehdr which can be notified */
257 while (wwo->lpNotifyHdr &&
258 (force || (cinfo.bytes >= wwo->dwNotifiedBytes + wwo->lpNotifyHdr->dwBufferLength))) {
259 lpWaveHdr = wwo->lpNotifyHdr;
261 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
262 lpWaveHdr->dwFlags |= WHDR_DONE;
263 if (!force)
264 wwo->dwNotifiedBytes += lpWaveHdr->dwBufferLength;
265 wwo->lpNotifyHdr = lpWaveHdr->lpNext;
267 /* FIXME: should we keep the wwo->critsect locked during callback ? */
268 /* normally yes, only a limited set of functions can be called...
269 * the ones which can be called inside an i386 interruption
270 * so this should be fine
272 LeaveCriticalSection(&wwo->critSect);
273 TRACE(wave, "Notifying client with %p\n", lpWaveHdr);
274 if (WAVE_NotifyClient(uDevID, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
275 WARN(wave, "can't notify client !\n");
277 EnterCriticalSection(&wwo->critSect);
282 /**************************************************************************
283 * wodPlayer [internal]
285 static DWORD WINAPI wodPlayer(LPVOID pmt)
287 WORD uDevID = (DWORD)pmt;
288 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
289 int laststate = -1;
290 WAVEHDR* lpWaveHdr;
291 DWORD dwSleepTime;
293 wwo->dwNotifiedBytes = 0;
294 wwo->dwOffCurrHdr = 0;
295 wwo->dwRemain = wwo->dwFragmentSize;
296 wwo->lpNotifyHdr = NULL;
298 /* make sleep time to be # of ms to output a fragment */
299 dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
301 for (;;) {
303 EnterCriticalSection(&wwo->critSect);
305 switch (wwo->state) {
306 case WINE_WS_PAUSED:
307 wodPlayer_Notify(wwo, uDevID, FALSE);
308 if (laststate != WINE_WS_PAUSED) {
309 if (ioctl(wwo->unixdev, SNDCTL_DSP_POST, 0) == -1) {
310 perror("ioctl SNDCTL_DSP_POST");
311 wwo->hThread = 0;
312 LeaveCriticalSection(&wwo->critSect);
313 ExitThread(-1);
316 break;
317 case WINE_WS_STOPPED:
318 /* should we kill the Thread here and to restart it later when needed ? */
319 break;
320 case WINE_WS_PLAYING:
321 if (!wodPlayer_WriteFragments(wwo)) {
322 wodPlayer_Notify(wwo, uDevID, FALSE);
323 break;
325 /* fall thru */
326 case WINE_WS_RESETTING:
327 /* updates current notify list */
328 wodPlayer_Notify(wwo, uDevID, FALSE);
329 /* flush all possible output */
330 if (ioctl(wwo->unixdev, SNDCTL_DSP_SYNC, 0) == -1) {
331 perror("ioctl SNDCTL_DSP_SYNC");
332 wwo->hThread = 0;
333 LeaveCriticalSection(&wwo->critSect);
334 ExitThread(-1);
336 /* DSP_SYNC resets the number of send bytes */
337 wwo->dwNotifiedBytes = 0;
338 /* empty notify list */
339 wodPlayer_Notify(wwo, uDevID, TRUE);
340 if (wwo->lpNotifyHdr) {
341 ERR(wave, "out of sync\n");
343 wwo->dwOffCurrHdr = 0;
344 /* get rid also of all the current queue */
345 for (lpWaveHdr = wwo->lpQueueHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
346 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
347 lpWaveHdr->dwFlags |= WHDR_DONE;
349 if (WAVE_NotifyClient((DWORD)pmt, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
350 WARN(wave, "can't notify client !\n");
353 wwo->lpQueueHdr = 0;
354 wwo->state = WINE_WS_STOPPED;
355 break;
356 case WINE_WS_CLOSING:
357 /* this should not happen since the device must have been reset before */
358 if (wwo->lpNotifyHdr || wwo->lpQueueHdr) {
359 ERR(wave, "out of sync\n");
361 wwo->hThread = 0;
362 wwo->state = WINE_WS_CLOSED;
363 LeaveCriticalSection(&wwo->critSect);
364 DeleteCriticalSection(&wwo->critSect);
365 ExitThread(0);
366 /* shouldn't go here */
367 default:
368 FIXME(wave, "unknown state %d\n", wwo->state);
369 break;
371 laststate = wwo->state;
372 LeaveCriticalSection(&wwo->critSect);
373 /* sleep for one fragment to be sent */
374 Sleep(dwSleepTime);
376 ExitThread(0);
379 /**************************************************************************
380 * wodGetDevCaps [internal]
382 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS16 lpCaps, DWORD dwSize)
384 int audio;
385 int smplrate;
386 int samplesize = 16;
387 int dsp_stereo = 1;
388 int bytespersmpl;
389 int caps;
391 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
393 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
394 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
396 if (wDevID >= MAX_WAVEOUTDRV) {
397 TRACE(wave, "MAX_WAVOUTDRV reached !\n");
398 return MMSYSERR_BADDEVICEID;
401 if (WOutDev[wDevID].unixdev == 0) {
402 audio = open(SOUND_DEV, O_WRONLY, 0);
403 if (audio == -1) return MMSYSERR_ALLOCATED;
404 } else {
405 audio = WOutDev[wDevID].unixdev;
408 /* FIXME: some programs compare this string against the content of the registry
409 * for MM drivers. The name have to match in order the program to work
410 * (e.g. MS win9x mplayer.exe)
412 #ifdef EMULATE_SB16
413 lpCaps->wMid = 0x0002;
414 lpCaps->wPid = 0x0104;
415 strcpy(lpCaps->szPname, "SB16 Wave Out");
416 #else
417 lpCaps->wMid = 0x00FF; /* Manufac ID */
418 lpCaps->wPid = 0x0001; /* Product ID */
419 /* strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");*/
420 strcpy(lpCaps->szPname, "CS4236/37/38");
421 #endif
422 lpCaps->vDriverVersion = 0x0100;
423 lpCaps->dwFormats = 0x00000000;
424 lpCaps->dwSupport = WAVECAPS_VOLUME;
426 /* First bytespersampl, then stereo */
427 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
429 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
430 if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
432 smplrate = 44100;
433 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
434 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
435 if (lpCaps->wChannels > 1)
436 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
437 if (bytespersmpl > 1) {
438 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
439 if (lpCaps->wChannels > 1)
440 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
443 smplrate = 22050;
444 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
445 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
446 if (lpCaps->wChannels > 1)
447 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
448 if (bytespersmpl > 1) {
449 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
450 if (lpCaps->wChannels > 1)
451 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
454 smplrate = 11025;
455 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
456 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
457 if (lpCaps->wChannels > 1)
458 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
459 if (bytespersmpl > 1) {
460 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
461 if (lpCaps->wChannels > 1)
462 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
465 if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
466 if ((caps & DSP_CAP_REALTIME) && !(caps && DSP_CAP_BATCH))
467 lpCaps->dwFormats |= WAVECAPS_SAMPLEACCURATE;
469 if (WOutDev[wDevID].unixdev == 0) {
470 close(audio);
472 TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
473 return MMSYSERR_NOERROR;
477 /**************************************************************************
478 * wodOpen [internal]
480 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
482 int audio;
483 int sample_rate;
484 int sample_size;
485 int dsp_stereo;
486 int audio_fragment;
487 int fragment_size;
489 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
490 if (lpDesc == NULL) {
491 WARN(wave, "Invalid Parameter !\n");
492 return MMSYSERR_INVALPARAM;
494 if (wDevID >= MAX_WAVEOUTDRV) {
495 TRACE(wave, "MAX_WAVOUTDRV reached !\n");
496 return MMSYSERR_BADDEVICEID;
498 /* only PCM format is supported so far... */
499 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
500 lpDesc->lpFormat->nChannels == 0 ||
501 lpDesc->lpFormat->nSamplesPerSec == 0) {
502 WARN(wave, "Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
503 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
504 lpDesc->lpFormat->nSamplesPerSec);
505 return WAVERR_BADFORMAT;
508 if (dwFlags & WAVE_FORMAT_QUERY)
509 return MMSYSERR_NOERROR;
511 WOutDev[wDevID].unixdev = 0;
512 if (access(SOUND_DEV, 0) != 0)
513 return MMSYSERR_NOTENABLED;
514 audio = open(SOUND_DEV, O_WRONLY, 0);
515 if (audio == -1) {
516 WARN(wave, "can't open !\n");
517 return MMSYSERR_ALLOCATED ;
520 WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
522 memcpy(&WOutDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
523 memcpy(&WOutDev[wDevID].format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
525 if (WOutDev[wDevID].format.wBitsPerSample == 0) {
526 WOutDev[wDevID].format.wBitsPerSample = 8 *
527 (WOutDev[wDevID].format.wf.nAvgBytesPerSec /
528 WOutDev[wDevID].format.wf.nSamplesPerSec) /
529 WOutDev[wDevID].format.wf.nChannels;
532 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
533 * thus leading to 46ms per fragment, and a turnaround time of 185ms
535 /* 2^10=1024 bytes per fragment, 16 fragments max */
536 audio_fragment = 0x000F000A;
537 sample_size = WOutDev[wDevID].format.wBitsPerSample;
538 sample_rate = WOutDev[wDevID].format.wf.nSamplesPerSec;
539 dsp_stereo = (WOutDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
541 IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
542 /* First size and stereo then samplerate */
543 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, sample_size);
544 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
545 IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
547 /* even if we set fragment size above, read it again, just in case */
548 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
550 WOutDev[wDevID].lpQueueHdr = NULL;
551 WOutDev[wDevID].unixdev = audio;
552 WOutDev[wDevID].dwTotalPlayed = 0;
553 WOutDev[wDevID].dwFragmentSize = fragment_size;
554 WOutDev[wDevID].state = WINE_WS_STOPPED;
555 WOutDev[wDevID].hThread = 0;
557 /* FIXME: do we need to make critsect global ? */
558 InitializeCriticalSection(&WOutDev[wDevID].critSect);
559 MakeCriticalSectionGlobal(&WOutDev[wDevID].critSect);
561 TRACE(wave, "fd=%d fragmentSize=%ld\n",
562 WOutDev[wDevID].unixdev, WOutDev[wDevID].dwFragmentSize);
564 TRACE(wave, "wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u !\n",
565 WOutDev[wDevID].format.wBitsPerSample, WOutDev[wDevID].format.wf.nAvgBytesPerSec,
566 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels);
568 if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
569 WARN(wave, "can't notify client !\n");
570 return MMSYSERR_INVALPARAM;
572 return MMSYSERR_NOERROR;
575 /**************************************************************************
576 * wodClose [internal]
578 static DWORD wodClose(WORD wDevID)
580 DWORD ret = MMSYSERR_NOERROR;
582 TRACE(wave, "(%u);\n", wDevID);
584 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
585 WARN(wave, "bad device ID !\n");
586 return MMSYSERR_BADDEVICEID;
589 if (WOutDev[wDevID].lpQueueHdr != NULL || WOutDev[wDevID].lpNotifyHdr != NULL) {
590 WARN(wave, "buffers still playing !\n");
591 ret = WAVERR_STILLPLAYING;
592 } else {
593 EnterCriticalSection(&WOutDev[wDevID].critSect);
594 WOutDev[wDevID].state = WINE_WS_CLOSING;
595 LeaveCriticalSection(&WOutDev[wDevID].critSect);
597 while (WOutDev[wDevID].state != WINE_WS_CLOSED)
598 Sleep(10);
600 close(WOutDev[wDevID].unixdev);
601 WOutDev[wDevID].unixdev = 0;
602 WOutDev[wDevID].dwFragmentSize = 0;
603 if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
604 WARN(wave, "can't notify client !\n");
605 ret = MMSYSERR_INVALPARAM;
608 return ret;
611 /**************************************************************************
612 * wodWrite [internal]
615 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
617 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
619 /* first, do the sanity checks... */
620 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
621 WARN(wave, "bad dev ID !\n");
622 return MMSYSERR_BADDEVICEID;
625 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
626 return WAVERR_UNPREPARED;
628 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
629 return WAVERR_STILLPLAYING;
631 lpWaveHdr->dwFlags &= ~WHDR_DONE;
632 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
633 lpWaveHdr->lpNext = 0;
635 EnterCriticalSection(&WOutDev[wDevID].critSect);
636 /* insert buffer in queue */
637 if (WOutDev[wDevID].lpQueueHdr) {
638 WAVEHDR* wh;
639 for (wh = WOutDev[wDevID].lpQueueHdr; wh->lpNext; wh = wh->lpNext);
640 wh->lpNext = lpWaveHdr;
641 } else {
642 WOutDev[wDevID].lpQueueHdr = lpWaveHdr;
644 /* starts thread if wod is not paused */
645 switch (WOutDev[wDevID].state) {
646 case WINE_WS_PAUSED:
647 case WINE_WS_PLAYING:
648 /* nothing to do */
649 break;
650 case WINE_WS_STOPPED:
651 WOutDev[wDevID].state = WINE_WS_PLAYING;
652 if (WOutDev[wDevID].hThread == 0) {
653 WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, NULL);
655 break;
656 default:
657 FIXME(wave, "Unsupported state %d\n", WOutDev[wDevID].state);
659 LeaveCriticalSection(&WOutDev[wDevID].critSect);
661 return MMSYSERR_NOERROR;
664 /**************************************************************************
665 * wodPrepare [internal]
667 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
669 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
671 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
672 WARN(wave, "bad device ID !\n");
673 return MMSYSERR_BADDEVICEID;
676 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
677 return WAVERR_STILLPLAYING;
679 lpWaveHdr->dwFlags |= WHDR_PREPARED;
680 lpWaveHdr->dwFlags &= ~WHDR_DONE;
681 return MMSYSERR_NOERROR;
684 /**************************************************************************
685 * wodUnprepare [internal]
687 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
689 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
691 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
692 WARN(wave, "bad device ID !\n");
693 return MMSYSERR_BADDEVICEID;
696 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
697 return WAVERR_STILLPLAYING;
699 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
700 lpWaveHdr->dwFlags |= WHDR_DONE;
702 return MMSYSERR_NOERROR;
705 /**************************************************************************
706 * wodPause [internal]
708 static DWORD wodPause(WORD wDevID)
710 TRACE(wave, "(%u);!\n", wDevID);
712 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
713 WARN(wave, "bad device ID !\n");
714 return MMSYSERR_BADDEVICEID;
717 EnterCriticalSection(&WOutDev[wDevID].critSect);
718 WOutDev[wDevID].state = WINE_WS_PAUSED;
719 LeaveCriticalSection(&WOutDev[wDevID].critSect);
721 return MMSYSERR_NOERROR;
724 /**************************************************************************
725 * wodStop [internal]
727 static DWORD wodStop(WORD wDevID)
729 FIXME(wave, "(%u); NIY!\n", wDevID);
731 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
732 WARN(wave, "bad device ID !\n");
733 return MMSYSERR_BADDEVICEID;
736 return MMSYSERR_NOERROR;
739 /**************************************************************************
740 * wodRestart [internal]
742 static DWORD wodRestart(WORD wDevID)
744 TRACE(wave, "(%u);\n", wDevID);
746 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
747 WARN(wave, "bad device ID !\n");
748 return MMSYSERR_BADDEVICEID;
751 EnterCriticalSection(&WOutDev[wDevID].critSect);
752 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
753 if (WOutDev[wDevID].lpQueueHdr != NULL) {
754 WOutDev[wDevID].state = WINE_WS_PLAYING;
755 if (WOutDev[wDevID].hThread == 0)
756 WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, NULL);
757 } else {
758 WOutDev[wDevID].state = WINE_WS_STOPPED;
761 LeaveCriticalSection(&WOutDev[wDevID].critSect);
763 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
764 /* FIXME: Myst crashes with this ... hmm -MM
765 if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
766 WARN(wave, "can't notify client !\n");
767 return MMSYSERR_INVALPARAM;
771 return MMSYSERR_NOERROR;
774 /**************************************************************************
775 * wodReset [internal]
777 static DWORD wodReset(WORD wDevID)
779 TRACE(wave, "(%u);\n", wDevID);
781 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
782 WARN(wave, "bad device ID !\n");
783 return MMSYSERR_BADDEVICEID;
786 EnterCriticalSection(&WOutDev[wDevID].critSect);
787 if (WOutDev[wDevID].hThread == 0) {
788 WOutDev[wDevID].state = WINE_WS_STOPPED;
789 } else {
790 WOutDev[wDevID].state = WINE_WS_RESETTING;
792 LeaveCriticalSection(&WOutDev[wDevID].critSect);
794 while (WOutDev[wDevID].state != WINE_WS_STOPPED)
795 Sleep(10);
797 return MMSYSERR_NOERROR;
801 /**************************************************************************
802 * wodGetPosition [internal]
804 static DWORD wodGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
806 int time;
808 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpTime, uSize);
810 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
811 WARN(wave, "bad device ID !\n");
812 return MMSYSERR_BADDEVICEID;
815 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
816 TRACE(wave, "wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
817 lpTime->wType, WOutDev[wDevID].format.wBitsPerSample,
818 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels,
819 WOutDev[wDevID].format.wf.nAvgBytesPerSec);
820 TRACE(wave, "dwTotalPlayed=%lu\n", WOutDev[wDevID].dwTotalPlayed);
822 switch (lpTime->wType) {
823 case TIME_BYTES:
824 lpTime->u.cb = WOutDev[wDevID].dwTotalPlayed;
825 TRACE(wave, "TIME_BYTES=%lu\n", lpTime->u.cb);
826 break;
827 case TIME_SAMPLES:
828 lpTime->u.sample = WOutDev[wDevID].dwTotalPlayed * 8 /
829 WOutDev[wDevID].format.wBitsPerSample;
830 TRACE(wave, "TIME_SAMPLES=%lu\n", lpTime->u.sample);
831 break;
832 case TIME_SMPTE:
833 time = WOutDev[wDevID].dwTotalPlayed /
834 (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
835 lpTime->u.smpte.hour = time / 108000;
836 time -= lpTime->u.smpte.hour * 108000;
837 lpTime->u.smpte.min = time / 1800;
838 time -= lpTime->u.smpte.min * 1800;
839 lpTime->u.smpte.sec = time / 30;
840 time -= lpTime->u.smpte.sec * 30;
841 lpTime->u.smpte.frame = time;
842 lpTime->u.smpte.fps = 30;
843 TRACE(wave, "TIME_SMPTE=%02u:%02u:%02u:%02u\n",
844 lpTime->u.smpte.hour, lpTime->u.smpte.min,
845 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
846 break;
847 default:
848 FIXME(wave, "Format %d not supported ! use TIME_MS !\n", lpTime->wType);
849 lpTime->wType = TIME_MS;
850 case TIME_MS:
851 lpTime->u.ms = WOutDev[wDevID].dwTotalPlayed /
852 (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
853 TRACE(wave, "TIME_MS=%lu\n", lpTime->u.ms);
854 break;
856 return MMSYSERR_NOERROR;
859 /**************************************************************************
860 * wodGetVolume [internal]
862 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
864 int mixer;
865 int volume, left, right;
867 TRACE(wave, "(%u, %p);\n", wDevID, lpdwVol);
869 if (lpdwVol == NULL)
870 return MMSYSERR_NOTENABLED;
871 if ((mixer = open(MIXER_DEV, O_RDONLY)) < 0) {
872 WARN(wave, "mixer device not available !\n");
873 return MMSYSERR_NOTENABLED;
875 if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
876 WARN(wave, "unable read mixer !\n");
877 return MMSYSERR_NOTENABLED;
879 close(mixer);
880 left = volume & 0x7F;
881 right = (volume >> 8) & 0x7F;
882 TRACE(wave, "left=%d right=%d !\n", left, right);
883 *lpdwVol = MAKELONG(left << 9, right << 9);
884 return MMSYSERR_NOERROR;
888 /**************************************************************************
889 * wodSetVolume [internal]
891 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
893 int mixer;
894 int volume;
895 TRACE(wave, "(%u, %08lX);\n", wDevID, dwParam);
896 volume = (LOWORD(dwParam) >> 9 & 0x7F) +
897 ((HIWORD(dwParam) >> 9 & 0x7F) << 8);
898 if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) {
899 WARN(wave, "mixer device not available !\n");
900 return MMSYSERR_NOTENABLED;
902 if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
903 WARN(wave, "unable set mixer !\n");
904 return MMSYSERR_NOTENABLED;
906 close(mixer);
907 return MMSYSERR_NOERROR;
910 /**************************************************************************
911 * wodGetNumDevs [internal]
913 static DWORD wodGetNumDevs(void)
915 DWORD ret = 1;
917 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
918 int audio = open(SOUND_DEV, O_WRONLY, 0);
920 if (audio == -1) {
921 if (errno != EBUSY)
922 ret = 0;
923 } else {
924 close(audio);
926 return ret;
929 /**************************************************************************
930 * wodMessage [sample driver]
932 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
933 DWORD dwParam1, DWORD dwParam2)
935 TRACE(wave, "(%u, %04X, %08lX, %08lX, %08lX);\n",
936 wDevID, wMsg, dwUser, dwParam1, dwParam2);
938 switch (wMsg) {
939 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
940 case WODM_CLOSE: return wodClose (wDevID);
941 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
942 case WODM_PAUSE: return wodPause (wDevID);
943 case WODM_STOP: return wodStop (wDevID);
944 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME16)dwParam1, dwParam2);
945 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
946 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
947 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
948 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPS16)dwParam1, dwParam2);
949 case WODM_GETNUMDEVS: return wodGetNumDevs ();
950 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
951 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
952 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
953 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
954 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
955 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
956 case WODM_RESTART: return wodRestart (wDevID);
957 case WODM_RESET: return wodReset (wDevID);
958 default:
959 FIXME(wave, "unknown message %d!\n", wMsg);
961 return MMSYSERR_NOTSUPPORTED;
964 /*======================================================================*
965 * Low level WAVE IN implemantation *
966 *======================================================================*/
968 /**************************************************************************
969 * widGetDevCaps [internal]
971 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS16 lpCaps, DWORD dwSize)
973 int audio,smplrate,samplesize=16,dsp_stereo=1,bytespersmpl;
975 TRACE(wave, "(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
976 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
977 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
978 audio = open(SOUND_DEV, O_RDONLY, 0);
979 if (audio == -1) return MMSYSERR_ALLOCATED ;
980 #ifdef EMULATE_SB16
981 lpCaps->wMid = 0x0002;
982 lpCaps->wPid = 0x0004;
983 strcpy(lpCaps->szPname, "SB16 Wave In");
984 #else
985 lpCaps->wMid = 0x00FF; /* Manufac ID */
986 lpCaps->wPid = 0x0001; /* Product ID */
987 strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
988 #endif
989 lpCaps->dwFormats = 0x00000000;
990 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
991 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
992 smplrate = 44100;
993 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
994 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
995 if (lpCaps->wChannels > 1)
996 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
997 if (bytespersmpl > 1) {
998 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
999 if (lpCaps->wChannels > 1)
1000 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
1003 smplrate = 22050;
1004 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1005 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
1006 if (lpCaps->wChannels > 1)
1007 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
1008 if (bytespersmpl > 1) {
1009 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
1010 if (lpCaps->wChannels > 1)
1011 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
1014 smplrate = 11025;
1015 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1016 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
1017 if (lpCaps->wChannels > 1)
1018 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
1019 if (bytespersmpl > 1) {
1020 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
1021 if (lpCaps->wChannels > 1)
1022 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
1025 close(audio);
1026 TRACE(wave, "dwFormats = %08lX\n", lpCaps->dwFormats);
1027 return MMSYSERR_NOERROR;
1030 /**************************************************************************
1031 * widOpen [internal]
1033 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1035 int audio,abuf_size,smplrate,samplesize,dsp_stereo;
1036 LPWAVEFORMAT lpFormat;
1038 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1039 if (lpDesc == NULL) {
1040 WARN(wave, "Invalid Parameter !\n");
1041 return MMSYSERR_INVALPARAM;
1043 if (wDevID >= MAX_WAVEINDRV) {
1044 TRACE(wave, "MAX_WAVINDRV reached !\n");
1045 return MMSYSERR_ALLOCATED;
1047 WInDev[wDevID].unixdev = 0;
1048 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1049 audio = open(SOUND_DEV, O_RDONLY, 0);
1050 if (audio == -1) {
1051 WARN(wave, "can't open !\n");
1052 return MMSYSERR_ALLOCATED;
1054 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
1055 if (abuf_size < 1024 || abuf_size > 65536) {
1056 if (abuf_size == -1)
1057 WARN(wave, "IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1058 else
1059 WARN(wave, "SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize !\n");
1060 return MMSYSERR_NOTENABLED;
1062 WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1064 if (WInDev[wDevID].lpQueueHdr) {
1065 HeapFree(GetProcessHeap(), 0, WInDev[wDevID].lpQueueHdr);
1066 WInDev[wDevID].lpQueueHdr = NULL;
1068 WInDev[wDevID].unixdev = audio;
1069 WInDev[wDevID].dwFragmentSize = abuf_size;
1070 WInDev[wDevID].dwTotalRecorded = 0;
1071 memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1072 lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat;
1073 if (lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
1074 WARN(wave, "Bad format %04X !\n",
1075 lpFormat->wFormatTag);
1076 return WAVERR_BADFORMAT;
1078 memcpy(&WInDev[wDevID].format, lpFormat, sizeof(PCMWAVEFORMAT));
1079 WInDev[wDevID].format.wBitsPerSample = 8; /* <-------------- */
1080 if (WInDev[wDevID].format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1081 if (WInDev[wDevID].format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1082 if (WInDev[wDevID].format.wBitsPerSample == 0) {
1083 WInDev[wDevID].format.wBitsPerSample = 8 *
1084 (WInDev[wDevID].format.wf.nAvgBytesPerSec /
1085 WInDev[wDevID].format.wf.nSamplesPerSec) /
1086 WInDev[wDevID].format.wf.nChannels;
1088 samplesize = WInDev[wDevID].format.wBitsPerSample;
1089 smplrate = WInDev[wDevID].format.wf.nSamplesPerSec;
1090 dsp_stereo = (WInDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
1091 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1092 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1093 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1094 TRACE(wave, "wBitsPerSample=%u !\n", WInDev[wDevID].format.wBitsPerSample);
1095 TRACE(wave, "nSamplesPerSec=%lu !\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1096 TRACE(wave, "nChannels=%u !\n", WInDev[wDevID].format.wf.nChannels);
1097 TRACE(wave, "nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1098 if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1099 WARN(wave, "can't notify client !\n");
1100 return MMSYSERR_INVALPARAM;
1102 return MMSYSERR_NOERROR;
1105 /**************************************************************************
1106 * widClose [internal]
1108 static DWORD widClose(WORD wDevID)
1110 TRACE(wave, "(%u);\n", wDevID);
1111 if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
1112 if (WInDev[wDevID].unixdev == 0) {
1113 WARN(wave, "can't close !\n");
1114 return MMSYSERR_NOTENABLED;
1116 if (WInDev[wDevID].lpQueueHdr != NULL) {
1117 WARN(wave, "still buffers open !\n");
1118 return WAVERR_STILLPLAYING;
1120 close(WInDev[wDevID].unixdev);
1121 WInDev[wDevID].unixdev = 0;
1122 WInDev[wDevID].dwFragmentSize = 0;
1123 if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1124 WARN(wave, "can't notify client !\n");
1125 return MMSYSERR_INVALPARAM;
1127 return MMSYSERR_NOERROR;
1130 /**************************************************************************
1131 * widAddBuffer [internal]
1133 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1135 int count = 1;
1136 LPWAVEHDR lpWIHdr;
1138 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1139 if (WInDev[wDevID].unixdev == 0) {
1140 WARN(wave, "can't do it !\n");
1141 return MMSYSERR_NOTENABLED;
1143 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1144 TRACE(wave, "never been prepared !\n");
1145 return WAVERR_UNPREPARED;
1147 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1148 TRACE(wave, "header already in use !\n");
1149 return WAVERR_STILLPLAYING;
1151 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1152 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1153 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1154 lpWaveHdr->dwBytesRecorded = 0;
1155 if (WInDev[wDevID].lpQueueHdr == NULL) {
1156 WInDev[wDevID].lpQueueHdr = lpWaveHdr;
1157 } else {
1158 lpWIHdr = WInDev[wDevID].lpQueueHdr;
1159 while (lpWIHdr->lpNext != NULL) {
1160 lpWIHdr = lpWIHdr->lpNext;
1161 count++;
1163 lpWIHdr->lpNext = lpWaveHdr;
1164 lpWaveHdr->lpNext = NULL;
1165 count++;
1167 TRACE(wave, "buffer added ! (now %u in queue)\n", count);
1168 return MMSYSERR_NOERROR;
1171 /**************************************************************************
1172 * widPrepare [internal]
1174 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1176 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1177 if (WInDev[wDevID].unixdev == 0) {
1178 WARN(wave, "can't prepare !\n");
1179 return MMSYSERR_NOTENABLED;
1181 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1182 return WAVERR_STILLPLAYING;
1183 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1184 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1185 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1186 lpWaveHdr->dwBytesRecorded = 0;
1187 TRACE(wave, "header prepared !\n");
1188 return MMSYSERR_NOERROR;
1191 /**************************************************************************
1192 * widUnprepare [internal]
1194 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1196 TRACE(wave, "(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1197 if (WInDev[wDevID].unixdev == 0) {
1198 WARN(wave, "can't unprepare !\n");
1199 return MMSYSERR_NOTENABLED;
1201 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1202 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1203 lpWaveHdr->dwFlags |= WHDR_DONE;
1205 return MMSYSERR_NOERROR;
1208 /**************************************************************************
1209 * widStart [internal]
1211 static DWORD widStart(WORD wDevID)
1213 int count = 1;
1214 int bytesRead;
1215 LPWAVEHDR *lpWaveHdr;
1216 LPBYTE lpData;
1218 TRACE(wave, "(%u);\n", wDevID);
1219 if (WInDev[wDevID].unixdev == 0) {
1220 WARN(wave, "can't start recording !\n");
1221 return MMSYSERR_NOTENABLED;
1224 lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
1225 TRACE(wave, "lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
1227 if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
1228 TRACE(wave, "never been prepared !\n");
1229 return WAVERR_UNPREPARED;
1232 while (*lpWaveHdr != NULL) {
1234 lpData = ((DWORD)*lpWaveHdr == (*lpWaveHdr)->reserved) ?
1235 (LPBYTE)(*lpWaveHdr)->lpData : (LPBYTE)PTR_SEG_TO_LIN((*lpWaveHdr)->lpData);
1237 TRACE(wave, "recording buf#%u=%p size=%lu \n",
1238 count, lpData, (*lpWaveHdr)->dwBufferLength);
1240 bytesRead = read(WInDev[wDevID].unixdev, lpData, (*lpWaveHdr)->dwBufferLength);
1242 if (bytesRead == -1)
1243 perror("read from audio device");
1245 TRACE(wave, "bytesread=%d (%ld)\n", bytesRead, (*lpWaveHdr)->dwBufferLength);
1246 (*lpWaveHdr)->dwBytesRecorded = bytesRead;
1247 WInDev[wDevID].dwTotalRecorded += (*lpWaveHdr)->dwBytesRecorded;
1248 (*lpWaveHdr)->dwFlags &= ~WHDR_INQUEUE;
1249 (*lpWaveHdr)->dwFlags |= WHDR_DONE;
1251 if (WAVE_NotifyClient(wDevID, WIM_DATA, (*lpWaveHdr)->reserved, (*lpWaveHdr)->dwBytesRecorded) != MMSYSERR_NOERROR) {
1252 WARN(wave, "can't notify client !\n");
1253 return MMSYSERR_INVALPARAM;
1255 /* removes the current block from the queue */
1256 *lpWaveHdr = (*lpWaveHdr)->lpNext;
1257 count++;
1259 TRACE(wave, "end of recording !\n");
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 switch (lpTime->wType) {
1308 case TIME_BYTES:
1309 lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
1310 TRACE(wave, "TIME_BYTES=%lu\n", lpTime->u.cb);
1311 break;
1312 case TIME_SAMPLES:
1313 lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
1314 WInDev[wDevID].format.wBitsPerSample;
1315 TRACE(wave, "TIME_SAMPLES=%lu\n", lpTime->u.sample);
1316 break;
1317 case TIME_SMPTE:
1318 time = WInDev[wDevID].dwTotalRecorded /
1319 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1320 lpTime->u.smpte.hour = time / 108000;
1321 time -= lpTime->u.smpte.hour * 108000;
1322 lpTime->u.smpte.min = time / 1800;
1323 time -= lpTime->u.smpte.min * 1800;
1324 lpTime->u.smpte.sec = time / 30;
1325 time -= lpTime->u.smpte.sec * 30;
1326 lpTime->u.smpte.frame = time;
1327 lpTime->u.smpte.fps = 30;
1328 TRACE(wave, "TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1329 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1330 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1331 break;
1332 case TIME_MS:
1333 lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1334 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1335 TRACE(wave, "TIME_MS=%lu\n", lpTime->u.ms);
1336 break;
1337 default:
1338 FIXME(wave, "format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1339 lpTime->wType = TIME_MS;
1341 return MMSYSERR_NOERROR;
1344 /**************************************************************************
1345 * widMessage [sample driver]
1347 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1348 DWORD dwParam1, DWORD dwParam2)
1350 TRACE(wave, "(%u, %04X, %08lX, %08lX, %08lX);\n",
1351 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1353 switch (wMsg) {
1354 case WIDM_OPEN: return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1355 case WIDM_CLOSE: return widClose(wDevID);
1356 case WIDM_ADDBUFFER: return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1357 case WIDM_PREPARE: return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1358 case WIDM_UNPREPARE: return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1359 case WIDM_GETDEVCAPS: return widGetDevCaps(wDevID, (LPWAVEINCAPS16)dwParam1,dwParam2);
1360 case WIDM_GETNUMDEVS: return wodGetNumDevs(); /* same number of devices in output as in input */
1361 case WIDM_GETPOS: return widGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
1362 case WIDM_RESET: return widReset(wDevID);
1363 case WIDM_START: return widStart(wDevID);
1364 case WIDM_PAUSE: return widStop(wDevID);
1365 case WIDM_STOP: return widStop(wDevID);
1366 default:
1367 FIXME(wave, "unknown message %u!\n", wMsg);
1369 return MMSYSERR_NOTSUPPORTED;
1372 /*======================================================================*
1373 * Low level WAVE implemantation - DriverProc *
1374 *======================================================================*/
1376 /**************************************************************************
1377 * WAVE_DriverProc [sample driver]
1379 LONG WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1380 DWORD dwParam1, DWORD dwParam2)
1382 TRACE(wave, "(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1383 switch (wMsg) {
1384 case DRV_LOAD: return 1;
1385 case DRV_FREE: return 1;
1386 case DRV_OPEN: return 1;
1387 case DRV_CLOSE: return 1;
1388 case DRV_ENABLE: return 1;
1389 case DRV_DISABLE: return 1;
1390 case DRV_QUERYCONFIGURE: return 1;
1391 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
1392 case DRV_INSTALL: return DRVCNF_RESTART;
1393 case DRV_REMOVE: return DRVCNF_RESTART;
1394 default:
1395 FIXME(wave, "is probably wrong msg=0x%04lx\n", wMsg);
1396 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1398 return MMSYSERR_NOTENABLED;
1401 #else /* !HAVE_OSS */
1403 /**************************************************************************
1404 * wodMessage [sample driver]
1406 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1407 DWORD dwParam1, DWORD dwParam2)
1409 FIXME(wave, "(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1410 return MMSYSERR_NOTENABLED;
1413 /**************************************************************************
1414 * widMessage [sample driver]
1416 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1417 DWORD dwParam1, DWORD dwParam2)
1419 FIXME(wave, "(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1420 return MMSYSERR_NOTENABLED;
1423 /**************************************************************************
1424 * WAVE_DriverProc [sample driver]
1426 LONG WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1427 DWORD dwParam1, DWORD dwParam2)
1429 return MMSYSERR_NOTENABLED;
1431 #endif /* HAVE_OSS */