Rearrange winver detection code and cache the winver value we
[wine.git] / multimedia / audio.c
blobff0459f1b5e31f17a5cf9fb5850840f3c126555e
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 "debugtools.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:
41 * +---------+-------------+---------------+---------------------------------+
42 * | state | function | event | new state |
43 * +---------+-------------+---------------+---------------------------------+
44 * | | open() | | STOPPED |
45 * | PAUSED | write() | | PAUSED |
46 * | STOPPED | write() | <thrd create> | PLAYING |
47 * | PLAYING | write() | HEADER | PLAYING |
48 * | (other) | write() | <error> | |
49 * | (any) | pause() | PAUSING | PAUSED |
50 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
51 * | (any) | reset() | RESETTING | STOPPED |
52 * | (any) | close() | CLOSING | CLOSED |
53 * +---------+-------------+---------------+---------------------------------+
56 /* states of the playing device */
57 #define WINE_WS_PLAYING 0
58 #define WINE_WS_PAUSED 1
59 #define WINE_WS_STOPPED 2
60 #define WINE_WS_CLOSED 3
62 /* events to be send to device */
63 #define WINE_WM_PAUSING (WM_USER + 1)
64 #define WINE_WM_RESTARTING (WM_USER + 2)
65 #define WINE_WM_RESETTING (WM_USER + 3)
66 #define WINE_WM_CLOSING (WM_USER + 4)
67 #define WINE_WM_HEADER (WM_USER + 5)
69 typedef struct {
70 int unixdev;
71 volatile int state; /* one of the WINE_WS_ manifest constants */
72 DWORD dwFragmentSize; /* size of OSS buffer fragment */
73 WAVEOPENDESC waveDesc;
74 WORD wFlags;
75 PCMWAVEFORMAT format;
76 LPWAVEHDR lpQueueHdr; /* pending buffers for playing */
77 LPWAVEHDR lpNotifyHdr; /* list of wavehdr for which write() has been called, pending for notification */
79 DWORD dwPlayedTotal; /* number of bytes played since opening */
80 DWORD dwPlayed; /* number of bytes played since last DSP_RESET */
81 DWORD dwNotifiedBytes; /* number of bytes for which wavehdr notification has been done */
83 /* info on current lpQueueHdr->lpWaveHdr */
84 DWORD dwOffCurrHdr; /* offset in lpQueueHdr->lpWaveHdr->lpData for fragments */
85 DWORD dwRemain; /* number of bytes to write to end the current fragment */
87 /* synchronization stuff */
88 HANDLE hThread;
89 DWORD dwThreadID;
90 HANDLE hEvent;
92 WORD wMaxFragments; /* max number of fragments that can be written to dsp */
93 WORD wFragsUsedInQueue; /* current number of used fragments inside dsp queue */
94 } WINE_WAVEOUT;
96 typedef struct {
97 int unixdev;
98 volatile int state;
99 DWORD dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */
100 WAVEOPENDESC waveDesc;
101 WORD wFlags;
102 PCMWAVEFORMAT format;
103 LPWAVEHDR lpQueueHdr;
104 DWORD dwTotalRecorded;
105 } WINE_WAVEIN;
107 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
108 static WINE_WAVEIN WInDev [MAX_WAVEOUTDRV];
110 /*======================================================================*
111 * Low level WAVE implemantation *
112 *======================================================================*/
114 /**************************************************************************
115 * WAVE_NotifyClient [internal]
117 static DWORD WAVE_NotifyClient(UINT16 wDevID, WORD wMsg,
118 DWORD dwParam1, DWORD dwParam2)
120 TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
122 switch (wMsg) {
123 case WOM_OPEN:
124 case WOM_CLOSE:
125 case WOM_DONE:
126 if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
128 if (WOutDev[wDevID].wFlags != DCB_NULL &&
129 !DriverCallback16(WOutDev[wDevID].waveDesc.dwCallBack,
130 WOutDev[wDevID].wFlags,
131 WOutDev[wDevID].waveDesc.hWave,
132 wMsg,
133 WOutDev[wDevID].waveDesc.dwInstance,
134 dwParam1,
135 dwParam2)) {
136 WARN("can't notify client !\n");
137 return MMSYSERR_NOERROR;
139 break;
141 case WIM_OPEN:
142 case WIM_CLOSE:
143 case WIM_DATA:
144 if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
146 if (WInDev[wDevID].wFlags != DCB_NULL &&
147 !DriverCallback16(WInDev[wDevID].waveDesc.dwCallBack,
148 WInDev[wDevID].wFlags,
149 WInDev[wDevID].waveDesc.hWave,
150 wMsg,
151 WInDev[wDevID].waveDesc.dwInstance,
152 dwParam1,
153 dwParam2)) {
154 WARN("can't notify client !\n");
155 return MMSYSERR_NOERROR;
157 break;
158 default:
159 FIXME("Unknown CB message %u\n", wMsg);
160 break;
162 return 0;
165 /*======================================================================*
166 * Low level WAVE OUT implemantation *
167 *======================================================================*/
169 /**************************************************************************
170 * wodPlayer_WriteFragments [internal]
172 * wodPlayer helper. Writes as many fragments it can to unixdev.
173 * Returns TRUE in case of buffer underrun.
175 static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
177 LPWAVEHDR lpWaveHdr;
178 LPBYTE lpData;
179 int count;
181 for (;;) {
182 TRACE("Fragments: %d used on fd %d\n", wwo->wFragsUsedInQueue, wwo->unixdev);
183 if (wwo->wFragsUsedInQueue == wwo->wMaxFragments) /* output queue is full, wait a bit */
184 return FALSE;
186 lpWaveHdr = wwo->lpQueueHdr;
187 if (!lpWaveHdr) {
188 if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */
189 wwo->dwNotifiedBytes >= wwo->dwFragmentSize && /* first fragment has been played */
190 wwo->wFragsUsedInQueue < 2) { /* done with all waveOutWrite()' fragments */
191 /* FIXME: should do better handling here */
192 TRACE("Oooch, buffer underrun !\n");
193 return TRUE; /* force resetting of waveOut device */
195 return FALSE; /* wait a bit */
198 if (wwo->dwOffCurrHdr == 0) {
199 TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
200 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
201 FIXME("NIY: loops (%lu) in wavehdr\n", lpWaveHdr->dwLoops);
204 lpData = ((DWORD)lpWaveHdr == lpWaveHdr->reserved) ?
205 (LPBYTE)lpWaveHdr->lpData : (LPBYTE)PTR_SEG_TO_LIN(lpWaveHdr->lpData);
207 /* finish current wave hdr ? */
208 if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) {
209 DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
211 /* write end of current wave hdr */
212 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
213 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
215 if (count > 0) {
216 LPWAVEHDR* wh;
218 /* move lpWaveHdr to the end of notify list */
219 for (wh = &(wwo->lpNotifyHdr); *wh; wh = &((*wh)->lpNext));
220 *wh = lpWaveHdr;
222 wwo->lpQueueHdr = lpWaveHdr->lpNext;
223 lpWaveHdr->lpNext = 0;
225 wwo->dwOffCurrHdr = 0;
226 if ((wwo->dwRemain -= count) == 0) {
227 wwo->dwRemain = wwo->dwFragmentSize;
228 wwo->wFragsUsedInQueue++;
231 continue; /* try to go to use next wavehdr */
232 } else {
233 count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
234 TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
235 if (count > 0) {
236 wwo->dwOffCurrHdr += count;
237 wwo->dwRemain = wwo->dwFragmentSize;
238 wwo->wFragsUsedInQueue++;
244 /**************************************************************************
245 * wodPlayer_WriteFragments [internal]
247 * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
248 * have been played (actually to speaker, not to unixdev fd).
250 static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
252 LPWAVEHDR lpWaveHdr;
253 count_info cinfo;
255 /* get effective number of written bytes */
256 if (!force) {
257 if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOPTR, &cinfo) == -1) {
258 perror("ioctl SNDCTL_DSP_GETOPTR");
259 wwo->hThread = 0;
260 wwo->state = WINE_WS_STOPPED;
261 ExitThread(-1);
263 TRACE("Played %d bytes (played=%ld) on fd %d\n", cinfo.bytes, wwo->dwPlayed, wwo->unixdev);
264 wwo->wFragsUsedInQueue -= cinfo.bytes / wwo->dwFragmentSize - wwo->dwPlayed / wwo->dwFragmentSize;
265 wwo->dwPlayed = cinfo.bytes;
267 if (force || cinfo.bytes > wwo->dwNotifiedBytes) {
268 /* remove all wavehdr which can be notified */
269 while (wwo->lpNotifyHdr &&
270 (force || (cinfo.bytes >= wwo->dwNotifiedBytes + wwo->lpNotifyHdr->dwBufferLength))) {
271 lpWaveHdr = wwo->lpNotifyHdr;
273 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
274 lpWaveHdr->dwFlags |= WHDR_DONE;
275 if (!force)
276 wwo->dwNotifiedBytes += lpWaveHdr->dwBufferLength;
277 wwo->lpNotifyHdr = lpWaveHdr->lpNext;
279 TRACE("Notifying client with %p\n", lpWaveHdr);
280 if (WAVE_NotifyClient(uDevID, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
281 WARN("can't notify client !\n");
287 /**************************************************************************
288 * wodPlayer_Reset [internal]
290 * wodPlayer helper. Resets current output stream.
292 static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
294 LPWAVEHDR lpWaveHdr;
296 /* updates current notify list */
297 wodPlayer_Notify(wwo, uDevID, FALSE);
299 /* flush all possible output */
300 if (ioctl(wwo->unixdev, SNDCTL_DSP_RESET, 0) == -1) {
301 perror("ioctl SNDCTL_DSP_RESET");
302 wwo->hThread = 0;
303 wwo->state = WINE_WS_STOPPED;
304 ExitThread(-1);
307 wwo->dwOffCurrHdr = 0;
308 if (reset) {
309 /* empty notify list */
310 wodPlayer_Notify(wwo, uDevID, TRUE);
311 if (wwo->lpNotifyHdr) {
312 ERR("out of sync\n");
314 /* get rid also of all the current queue */
315 for (lpWaveHdr = wwo->lpQueueHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
316 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
317 lpWaveHdr->dwFlags |= WHDR_DONE;
319 if (WAVE_NotifyClient(uDevID, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
320 WARN("can't notify client !\n");
323 wwo->lpQueueHdr = 0;
324 wwo->state = WINE_WS_STOPPED;
325 wwo->dwPlayedTotal = 0;
326 } else {
327 /* move notify list to begining of lpQueueHdr list */
328 while (wwo->lpNotifyHdr) {
329 lpWaveHdr = wwo->lpNotifyHdr;
330 wwo->lpNotifyHdr = lpWaveHdr->lpNext;
331 lpWaveHdr->lpNext = wwo->lpQueueHdr;
332 wwo->lpQueueHdr = lpWaveHdr;
334 wwo->state = WINE_WS_PAUSED;
335 wwo->dwPlayedTotal += wwo->dwPlayed;
337 wwo->dwNotifiedBytes = wwo->dwPlayed = 0;
338 wwo->wFragsUsedInQueue = 0;
341 /**************************************************************************
342 * wodPlayer [internal]
344 static DWORD CALLBACK wodPlayer(LPVOID pmt)
346 WORD uDevID = (DWORD)pmt;
347 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
348 WAVEHDR* lpWaveHdr;
349 DWORD dwSleepTime;
350 MSG msg;
352 PeekMessageA(&msg, 0, 0, 0, 0);
353 wwo->state = WINE_WS_STOPPED;
355 wwo->dwNotifiedBytes = 0;
356 wwo->dwOffCurrHdr = 0;
357 wwo->dwRemain = wwo->dwFragmentSize;
358 wwo->lpQueueHdr = NULL;
359 wwo->lpNotifyHdr = NULL;
360 wwo->wFragsUsedInQueue = 0;
361 wwo->dwPlayedTotal = 0;
362 wwo->dwPlayed = 0;
364 TRACE("imhere[0]\n");
365 SetEvent(wwo->hEvent);
367 /* make sleep time to be # of ms to output a fragment */
368 dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
370 for (;;) {
371 /* wait for dwSleepTime or an event in thread's queue */
372 /* FIXME: could improve wait time depending on queue state,
373 * ie, number of queued fragments
375 TRACE("imhere[1]\n");
376 MsgWaitForMultipleObjects(0, NULL, FALSE,
377 (wwo->state == WINE_WS_PLAYING) ?
378 (MAX(wwo->wFragsUsedInQueue, 4) - 2) * dwSleepTime :
379 /*INFINITE*/100,
380 QS_POSTMESSAGE);
381 TRACE("imhere[2]\n");
382 wodPlayer_Notify(wwo, uDevID, FALSE);
383 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
384 switch (msg.message) {
385 case WINE_WM_PAUSING:
386 wodPlayer_Reset(wwo, uDevID, FALSE);
387 wwo->state = WINE_WS_PAUSED;
388 SetEvent(wwo->hEvent);
389 break;
390 case WINE_WM_RESTARTING:
391 wwo->state = WINE_WS_PLAYING;
392 SetEvent(wwo->hEvent);
393 break;
394 case WINE_WM_HEADER:
395 lpWaveHdr = (LPWAVEHDR)msg.lParam;
397 lpWaveHdr->dwFlags &= ~WHDR_DONE;
398 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
399 lpWaveHdr->lpNext = 0;
401 /* insert buffer at the end of queue */
403 LPWAVEHDR* wh;
404 for (wh = &(wwo->lpQueueHdr); *wh; wh = &((*wh)->lpNext));
405 *wh = lpWaveHdr;
407 if (wwo->state == WINE_WS_STOPPED)
408 wwo->state = WINE_WS_PLAYING;
409 break;
410 case WINE_WM_RESETTING:
411 wodPlayer_Reset(wwo, uDevID, TRUE);
412 SetEvent(wwo->hEvent);
413 break;
414 case WINE_WM_CLOSING:
415 /* sanity check: this should not happen since the device must have been reset before */
416 if (wwo->lpNotifyHdr || wwo->lpQueueHdr) {
417 ERR("out of sync\n");
419 wwo->hThread = 0;
420 wwo->state = WINE_WS_CLOSED;
421 SetEvent(wwo->hEvent);
422 ExitThread(0);
423 /* shouldn't go here */
424 default:
425 FIXME("unknown message %d\n", msg.message);
426 break;
429 if (wwo->state == WINE_WS_PLAYING) {
430 wodPlayer_WriteFragments(wwo);
432 wodPlayer_Notify(wwo, uDevID, FALSE);
434 ExitThread(0);
435 /* just for not generating compilation warnings... should never be executed */
436 return 0;
439 /**************************************************************************
440 * wodGetDevCaps [internal]
442 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS16 lpCaps, DWORD dwSize)
444 int audio;
445 int smplrate;
446 int samplesize = 16;
447 int dsp_stereo = 1;
448 int bytespersmpl;
449 int caps;
450 int mask;
452 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
454 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
455 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
457 if (wDevID >= MAX_WAVEOUTDRV) {
458 TRACE("MAX_WAVOUTDRV reached !\n");
459 return MMSYSERR_BADDEVICEID;
462 if (WOutDev[wDevID].unixdev == 0) {
463 audio = open(SOUND_DEV, O_WRONLY, 0);
464 if (audio == -1) return MMSYSERR_ALLOCATED;
465 } else {
466 audio = WOutDev[wDevID].unixdev;
469 /* FIXME: some programs compare this string against the content of the registry
470 * for MM drivers. The name have to match in order the program to work
471 * (e.g. MS win9x mplayer.exe)
473 #ifdef EMULATE_SB16
474 lpCaps->wMid = 0x0002;
475 lpCaps->wPid = 0x0104;
476 strcpy(lpCaps->szPname, "SB16 Wave Out");
477 #else
478 lpCaps->wMid = 0x00FF; /* Manufac ID */
479 lpCaps->wPid = 0x0001; /* Product ID */
480 /* strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");*/
481 strcpy(lpCaps->szPname, "CS4236/37/38");
482 #endif
483 lpCaps->vDriverVersion = 0x0100;
484 lpCaps->dwFormats = 0x00000000;
485 lpCaps->dwSupport = WAVECAPS_VOLUME;
487 IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
488 TRACE("OSS dsp mask=%08x\n", mask);
489 mask = AFMT_QUERY;
490 IOCTL(audio, SNDCTL_DSP_SETFMT, mask);
491 TRACE("OSS dsp current=%08x\n", mask);
493 /* First bytespersampl, then stereo */
494 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
496 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
497 if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
499 smplrate = 44100;
500 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
501 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
502 if (lpCaps->wChannels > 1)
503 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
504 if (bytespersmpl > 1) {
505 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
506 if (lpCaps->wChannels > 1)
507 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
510 smplrate = 22050;
511 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
512 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
513 if (lpCaps->wChannels > 1)
514 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
515 if (bytespersmpl > 1) {
516 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
517 if (lpCaps->wChannels > 1)
518 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
521 smplrate = 11025;
522 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
523 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
524 if (lpCaps->wChannels > 1)
525 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
526 if (bytespersmpl > 1) {
527 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
528 if (lpCaps->wChannels > 1)
529 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
532 if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
533 if ((caps & DSP_CAP_REALTIME) && !(caps && DSP_CAP_BATCH))
534 lpCaps->dwFormats |= WAVECAPS_SAMPLEACCURATE;
536 if (WOutDev[wDevID].unixdev == 0) {
537 close(audio);
539 TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
540 return MMSYSERR_NOERROR;
543 /**************************************************************************
544 * wodOpen [internal]
546 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
548 int audio;
549 int sample_rate;
550 int sample_size;
551 int dsp_stereo;
552 int audio_fragment;
553 int fragment_size;
554 WAVEOUTCAPS16 woc;
556 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
557 if (lpDesc == NULL) {
558 WARN("Invalid Parameter !\n");
559 return MMSYSERR_INVALPARAM;
561 if (wDevID >= MAX_WAVEOUTDRV) {
562 TRACE("MAX_WAVOUTDRV reached !\n");
563 return MMSYSERR_BADDEVICEID;
565 wodGetDevCaps(wDevID, &woc, sizeof(woc));
567 /* only PCM format is supported so far... */
568 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
569 lpDesc->lpFormat->nChannels == 0 ||
570 lpDesc->lpFormat->nSamplesPerSec == 0) {
571 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
572 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
573 lpDesc->lpFormat->nSamplesPerSec);
574 return WAVERR_BADFORMAT;
577 if (dwFlags & WAVE_FORMAT_QUERY)
578 return MMSYSERR_NOERROR;
580 WOutDev[wDevID].unixdev = 0;
581 if (access(SOUND_DEV, 0) != 0)
582 return MMSYSERR_NOTENABLED;
583 audio = open(SOUND_DEV, O_WRONLY, 0);
584 if (audio == -1) {
585 WARN("can't open !\n");
586 return MMSYSERR_ALLOCATED ;
589 WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
591 memcpy(&WOutDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
592 memcpy(&WOutDev[wDevID].format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
594 if (WOutDev[wDevID].format.wBitsPerSample == 0) {
595 WOutDev[wDevID].format.wBitsPerSample = 8 *
596 (WOutDev[wDevID].format.wf.nAvgBytesPerSec /
597 WOutDev[wDevID].format.wf.nSamplesPerSec) /
598 WOutDev[wDevID].format.wf.nChannels;
601 /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
602 * thus leading to 46ms per fragment, and a turnaround time of 185ms
604 /* 2^10=1024 bytes per fragment, 16 fragments max */
605 audio_fragment = 0x000F000A;
606 sample_size = WOutDev[wDevID].format.wBitsPerSample;
607 sample_rate = WOutDev[wDevID].format.wf.nSamplesPerSec;
608 dsp_stereo = (WOutDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
610 IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
611 /* First size and stereo then samplerate */
612 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, sample_size);
613 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
614 IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
616 /* even if we set fragment size above, read it again, just in case */
617 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
619 WOutDev[wDevID].unixdev = audio;
620 WOutDev[wDevID].dwFragmentSize = fragment_size;
621 WOutDev[wDevID].wMaxFragments = HIWORD(audio_fragment) + 1;
623 WOutDev[wDevID].hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
624 WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(WOutDev[wDevID].dwThreadID));
625 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
627 TRACE("fd=%d fragmentSize=%ld\n",
628 WOutDev[wDevID].unixdev, WOutDev[wDevID].dwFragmentSize);
630 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u !\n",
631 WOutDev[wDevID].format.wBitsPerSample, WOutDev[wDevID].format.wf.nAvgBytesPerSec,
632 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels);
634 if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
635 WARN("can't notify client !\n");
636 return MMSYSERR_INVALPARAM;
638 return MMSYSERR_NOERROR;
641 /**************************************************************************
642 * wodClose [internal]
644 static DWORD wodClose(WORD wDevID)
646 DWORD ret = MMSYSERR_NOERROR;
648 TRACE("(%u);\n", wDevID);
650 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
651 WARN("bad device ID !\n");
652 return MMSYSERR_BADDEVICEID;
655 if (WOutDev[wDevID].lpQueueHdr != NULL || WOutDev[wDevID].lpNotifyHdr != NULL) {
656 WARN("buffers still playing !\n");
657 ret = WAVERR_STILLPLAYING;
658 } else {
659 TRACE("imhere[3-close]\n");
660 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_CLOSING, 0, 0);
661 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
662 CloseHandle(WOutDev[wDevID].hEvent);
664 close(WOutDev[wDevID].unixdev);
665 WOutDev[wDevID].unixdev = 0;
666 WOutDev[wDevID].dwFragmentSize = 0;
667 if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
668 WARN("can't notify client !\n");
669 ret = MMSYSERR_INVALPARAM;
672 return ret;
675 /**************************************************************************
676 * wodWrite [internal]
679 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
681 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
683 /* first, do the sanity checks... */
684 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
685 WARN("bad dev ID !\n");
686 return MMSYSERR_BADDEVICEID;
689 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
690 return WAVERR_UNPREPARED;
692 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
693 return WAVERR_STILLPLAYING;
695 TRACE("imhere[3-HEADER]\n");
696 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
698 return MMSYSERR_NOERROR;
701 /**************************************************************************
702 * wodPrepare [internal]
704 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
706 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
708 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
709 WARN("bad device ID !\n");
710 return MMSYSERR_BADDEVICEID;
713 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
714 return WAVERR_STILLPLAYING;
716 lpWaveHdr->dwFlags |= WHDR_PREPARED;
717 lpWaveHdr->dwFlags &= ~WHDR_DONE;
718 return MMSYSERR_NOERROR;
721 /**************************************************************************
722 * wodUnprepare [internal]
724 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
726 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
728 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
729 WARN("bad device ID !\n");
730 return MMSYSERR_BADDEVICEID;
733 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
734 return WAVERR_STILLPLAYING;
736 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
737 lpWaveHdr->dwFlags |= WHDR_DONE;
739 return MMSYSERR_NOERROR;
742 /**************************************************************************
743 * wodPause [internal]
745 static DWORD wodPause(WORD wDevID)
747 TRACE("(%u);!\n", wDevID);
749 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
750 WARN("bad device ID !\n");
751 return MMSYSERR_BADDEVICEID;
754 TRACE("imhere[3-PAUSING]\n");
755 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_PAUSING, 0, 0);
756 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
758 return MMSYSERR_NOERROR;
761 /**************************************************************************
762 * wodRestart [internal]
764 static DWORD wodRestart(WORD wDevID)
766 TRACE("(%u);\n", wDevID);
768 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
769 WARN("bad device ID !\n");
770 return MMSYSERR_BADDEVICEID;
773 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
774 TRACE("imhere[3-RESTARTING]\n");
775 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
776 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
779 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
780 /* FIXME: Myst crashes with this ... hmm -MM
781 if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
782 WARN("can't notify client !\n");
783 return MMSYSERR_INVALPARAM;
787 return MMSYSERR_NOERROR;
790 /**************************************************************************
791 * wodReset [internal]
793 static DWORD wodReset(WORD wDevID)
795 TRACE("(%u);\n", wDevID);
797 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
798 WARN("bad device ID !\n");
799 return MMSYSERR_BADDEVICEID;
802 TRACE("imhere[3-RESET]\n");
803 PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
804 WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
806 return MMSYSERR_NOERROR;
810 /**************************************************************************
811 * wodGetPosition [internal]
813 static DWORD wodGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
815 int time;
816 DWORD val;
818 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
820 if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
821 WARN("bad device ID !\n");
822 return MMSYSERR_BADDEVICEID;
825 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
827 val = WOutDev[wDevID].dwPlayedTotal + WOutDev[wDevID].dwPlayed;
829 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
830 lpTime->wType, WOutDev[wDevID].format.wBitsPerSample,
831 WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels,
832 WOutDev[wDevID].format.wf.nAvgBytesPerSec);
833 TRACE("dwTotalPlayed=%lu\n", val);
835 switch (lpTime->wType) {
836 case TIME_BYTES:
837 lpTime->u.cb = val;
838 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
839 break;
840 case TIME_SAMPLES:
841 lpTime->u.sample = val * 8 / WOutDev[wDevID].format.wBitsPerSample;
842 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
843 break;
844 case TIME_SMPTE:
845 time = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
846 lpTime->u.smpte.hour = time / 108000;
847 time -= lpTime->u.smpte.hour * 108000;
848 lpTime->u.smpte.min = time / 1800;
849 time -= lpTime->u.smpte.min * 1800;
850 lpTime->u.smpte.sec = time / 30;
851 time -= lpTime->u.smpte.sec * 30;
852 lpTime->u.smpte.frame = time;
853 lpTime->u.smpte.fps = 30;
854 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
855 lpTime->u.smpte.hour, lpTime->u.smpte.min,
856 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
857 break;
858 default:
859 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
860 lpTime->wType = TIME_MS;
861 case TIME_MS:
862 lpTime->u.ms = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
863 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
864 break;
866 return MMSYSERR_NOERROR;
869 /**************************************************************************
870 * wodGetVolume [internal]
872 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
874 int mixer;
875 int volume;
876 DWORD left, right;
878 TRACE("(%u, %p);\n", wDevID, lpdwVol);
880 if (lpdwVol == NULL)
881 return MMSYSERR_NOTENABLED;
882 if ((mixer = open(MIXER_DEV, O_RDONLY)) < 0) {
883 WARN("mixer device not available !\n");
884 return MMSYSERR_NOTENABLED;
886 if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
887 WARN("unable read mixer !\n");
888 return MMSYSERR_NOTENABLED;
890 close(mixer);
891 left = LOBYTE(volume);
892 right = HIBYTE(volume);
893 TRACE("left=%ld right=%ld !\n", left, right);
894 *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
895 return MMSYSERR_NOERROR;
899 /**************************************************************************
900 * wodSetVolume [internal]
902 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
904 int mixer;
905 int volume;
906 DWORD left, right;
908 TRACE("(%u, %08lX);\n", wDevID, dwParam);
910 left = (LOWORD(dwParam) * 100) / 0xFFFFl;
911 right = (HIWORD(dwParam) * 100) / 0xFFFFl;
912 volume = left + (right << 8);
914 if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) {
915 WARN("mixer device not available !\n");
916 return MMSYSERR_NOTENABLED;
918 if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
919 WARN("unable set mixer !\n");
920 return MMSYSERR_NOTENABLED;
922 close(mixer);
923 return MMSYSERR_NOERROR;
926 /**************************************************************************
927 * wodGetNumDevs [internal]
929 static DWORD wodGetNumDevs(void)
931 DWORD ret = 1;
933 /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
934 int audio = open(SOUND_DEV, O_WRONLY, 0);
936 if (audio == -1) {
937 if (errno != EBUSY)
938 ret = 0;
939 } else {
940 close(audio);
942 return ret;
945 /**************************************************************************
946 * wodMessage [sample driver]
948 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
949 DWORD dwParam1, DWORD dwParam2)
951 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
952 wDevID, wMsg, dwUser, dwParam1, dwParam2);
954 switch (wMsg) {
955 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
956 case WODM_CLOSE: return wodClose (wDevID);
957 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
958 case WODM_PAUSE: return wodPause (wDevID);
959 /* case WODM_STOP: */
960 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME16)dwParam1, dwParam2);
961 case WODM_BREAKLOOP: return MMSYSERR_NOTSUPPORTED;
962 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
963 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
964 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPS16)dwParam1, dwParam2);
965 case WODM_GETNUMDEVS: return wodGetNumDevs ();
966 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
967 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
968 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
969 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
970 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
971 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
972 case WODM_RESTART: return wodRestart (wDevID);
973 case WODM_RESET: return wodReset (wDevID);
974 default:
975 FIXME("unknown message %d!\n", wMsg);
977 return MMSYSERR_NOTSUPPORTED;
980 /*======================================================================*
981 * Low level WAVE IN implemantation *
982 *======================================================================*/
984 /**************************************************************************
985 * widGetDevCaps [internal]
987 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS16 lpCaps, DWORD dwSize)
989 int audio,smplrate,samplesize=16,dsp_stereo=1,bytespersmpl;
991 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
992 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
993 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
994 audio = open(SOUND_DEV, O_RDONLY, 0);
995 if (audio == -1) return MMSYSERR_ALLOCATED ;
996 #ifdef EMULATE_SB16
997 lpCaps->wMid = 0x0002;
998 lpCaps->wPid = 0x0004;
999 strcpy(lpCaps->szPname, "SB16 Wave In");
1000 #else
1001 lpCaps->wMid = 0x00FF; /* Manufac ID */
1002 lpCaps->wPid = 0x0001; /* Product ID */
1003 strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
1004 #endif
1005 lpCaps->dwFormats = 0x00000000;
1006 lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
1007 bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
1008 smplrate = 44100;
1009 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1010 lpCaps->dwFormats |= WAVE_FORMAT_4M08;
1011 if (lpCaps->wChannels > 1)
1012 lpCaps->dwFormats |= WAVE_FORMAT_4S08;
1013 if (bytespersmpl > 1) {
1014 lpCaps->dwFormats |= WAVE_FORMAT_4M16;
1015 if (lpCaps->wChannels > 1)
1016 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
1019 smplrate = 22050;
1020 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1021 lpCaps->dwFormats |= WAVE_FORMAT_2M08;
1022 if (lpCaps->wChannels > 1)
1023 lpCaps->dwFormats |= WAVE_FORMAT_2S08;
1024 if (bytespersmpl > 1) {
1025 lpCaps->dwFormats |= WAVE_FORMAT_2M16;
1026 if (lpCaps->wChannels > 1)
1027 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
1030 smplrate = 11025;
1031 if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1032 lpCaps->dwFormats |= WAVE_FORMAT_1M08;
1033 if (lpCaps->wChannels > 1)
1034 lpCaps->dwFormats |= WAVE_FORMAT_1S08;
1035 if (bytespersmpl > 1) {
1036 lpCaps->dwFormats |= WAVE_FORMAT_1M16;
1037 if (lpCaps->wChannels > 1)
1038 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
1041 close(audio);
1042 TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
1043 return MMSYSERR_NOERROR;
1046 /**************************************************************************
1047 * widOpen [internal]
1049 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1051 int audio,abuf_size,smplrate,samplesize,dsp_stereo;
1052 LPWAVEFORMAT lpFormat;
1054 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1055 if (lpDesc == NULL) {
1056 WARN("Invalid Parameter !\n");
1057 return MMSYSERR_INVALPARAM;
1059 if (wDevID >= MAX_WAVEINDRV) {
1060 TRACE("MAX_WAVINDRV reached !\n");
1061 return MMSYSERR_ALLOCATED;
1063 WInDev[wDevID].unixdev = 0;
1064 if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1065 audio = open(SOUND_DEV, O_RDONLY, 0);
1066 if (audio == -1) {
1067 WARN("can't open !\n");
1068 return MMSYSERR_ALLOCATED;
1070 IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
1071 if (abuf_size < 1024 || abuf_size > 65536) {
1072 if (abuf_size == -1)
1073 WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1074 else
1075 WARN("SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize !\n");
1076 return MMSYSERR_NOTENABLED;
1078 WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1080 if (WInDev[wDevID].lpQueueHdr) {
1081 HeapFree(GetProcessHeap(), 0, WInDev[wDevID].lpQueueHdr);
1082 WInDev[wDevID].lpQueueHdr = NULL;
1084 WInDev[wDevID].unixdev = audio;
1085 WInDev[wDevID].dwFragmentSize = abuf_size;
1086 WInDev[wDevID].dwTotalRecorded = 0;
1087 memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1088 lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat;
1089 if (lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
1090 WARN("Bad format %04X !\n",
1091 lpFormat->wFormatTag);
1092 return WAVERR_BADFORMAT;
1094 memcpy(&WInDev[wDevID].format, lpFormat, sizeof(PCMWAVEFORMAT));
1095 WInDev[wDevID].format.wBitsPerSample = 8; /* <-------------- */
1096 if (WInDev[wDevID].format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1097 if (WInDev[wDevID].format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1098 if (WInDev[wDevID].format.wBitsPerSample == 0) {
1099 WInDev[wDevID].format.wBitsPerSample = 8 *
1100 (WInDev[wDevID].format.wf.nAvgBytesPerSec /
1101 WInDev[wDevID].format.wf.nSamplesPerSec) /
1102 WInDev[wDevID].format.wf.nChannels;
1104 samplesize = WInDev[wDevID].format.wBitsPerSample;
1105 smplrate = WInDev[wDevID].format.wf.nSamplesPerSec;
1106 dsp_stereo = (WInDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
1107 IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1108 IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1109 IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1110 TRACE("wBitsPerSample=%u !\n", WInDev[wDevID].format.wBitsPerSample);
1111 TRACE("nSamplesPerSec=%lu !\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1112 TRACE("nChannels=%u !\n", WInDev[wDevID].format.wf.nChannels);
1113 TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1114 if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1115 WARN("can't notify client !\n");
1116 return MMSYSERR_INVALPARAM;
1118 return MMSYSERR_NOERROR;
1121 /**************************************************************************
1122 * widClose [internal]
1124 static DWORD widClose(WORD wDevID)
1126 TRACE("(%u);\n", wDevID);
1127 if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
1128 if (WInDev[wDevID].unixdev == 0) {
1129 WARN("can't close !\n");
1130 return MMSYSERR_NOTENABLED;
1132 if (WInDev[wDevID].lpQueueHdr != NULL) {
1133 WARN("still buffers open !\n");
1134 return WAVERR_STILLPLAYING;
1136 close(WInDev[wDevID].unixdev);
1137 WInDev[wDevID].unixdev = 0;
1138 WInDev[wDevID].dwFragmentSize = 0;
1139 if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1140 WARN("can't notify client !\n");
1141 return MMSYSERR_INVALPARAM;
1143 return MMSYSERR_NOERROR;
1146 /**************************************************************************
1147 * widAddBuffer [internal]
1149 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1151 int count = 1;
1152 LPWAVEHDR lpWIHdr;
1154 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1155 if (WInDev[wDevID].unixdev == 0) {
1156 WARN("can't do it !\n");
1157 return MMSYSERR_NOTENABLED;
1159 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1160 TRACE("never been prepared !\n");
1161 return WAVERR_UNPREPARED;
1163 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1164 TRACE("header already in use !\n");
1165 return WAVERR_STILLPLAYING;
1167 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1168 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1169 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1170 lpWaveHdr->dwBytesRecorded = 0;
1171 if (WInDev[wDevID].lpQueueHdr == NULL) {
1172 WInDev[wDevID].lpQueueHdr = lpWaveHdr;
1173 } else {
1174 lpWIHdr = WInDev[wDevID].lpQueueHdr;
1175 while (lpWIHdr->lpNext != NULL) {
1176 lpWIHdr = lpWIHdr->lpNext;
1177 count++;
1179 lpWIHdr->lpNext = lpWaveHdr;
1180 lpWaveHdr->lpNext = NULL;
1181 count++;
1183 TRACE("buffer added ! (now %u in queue)\n", count);
1184 return MMSYSERR_NOERROR;
1187 /**************************************************************************
1188 * widPrepare [internal]
1190 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1192 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1193 if (WInDev[wDevID].unixdev == 0) {
1194 WARN("can't prepare !\n");
1195 return MMSYSERR_NOTENABLED;
1197 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1198 return WAVERR_STILLPLAYING;
1199 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1200 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1201 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1202 lpWaveHdr->dwBytesRecorded = 0;
1203 TRACE("header prepared !\n");
1204 return MMSYSERR_NOERROR;
1207 /**************************************************************************
1208 * widUnprepare [internal]
1210 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1212 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1213 if (WInDev[wDevID].unixdev == 0) {
1214 WARN("can't unprepare !\n");
1215 return MMSYSERR_NOTENABLED;
1217 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1218 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1219 lpWaveHdr->dwFlags |= WHDR_DONE;
1221 return MMSYSERR_NOERROR;
1224 /**************************************************************************
1225 * widStart [internal]
1227 static DWORD widStart(WORD wDevID)
1229 int count = 1;
1230 int bytesRead;
1231 LPWAVEHDR *lpWaveHdr;
1232 LPBYTE lpData;
1234 TRACE("(%u);\n", wDevID);
1235 if (WInDev[wDevID].unixdev == 0) {
1236 WARN("can't start recording !\n");
1237 return MMSYSERR_NOTENABLED;
1240 lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
1241 TRACE("lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
1243 if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
1244 TRACE("never been prepared !\n");
1245 return WAVERR_UNPREPARED;
1248 while (*lpWaveHdr != NULL) {
1250 lpData = ((DWORD)*lpWaveHdr == (*lpWaveHdr)->reserved) ?
1251 (LPBYTE)(*lpWaveHdr)->lpData : (LPBYTE)PTR_SEG_TO_LIN((*lpWaveHdr)->lpData);
1253 TRACE("recording buf#%u=%p size=%lu \n",
1254 count, lpData, (*lpWaveHdr)->dwBufferLength);
1256 bytesRead = read(WInDev[wDevID].unixdev, lpData, (*lpWaveHdr)->dwBufferLength);
1258 if (bytesRead == -1)
1259 perror("read from audio device");
1261 TRACE("bytesread=%d (%ld)\n", bytesRead, (*lpWaveHdr)->dwBufferLength);
1262 (*lpWaveHdr)->dwBytesRecorded = bytesRead;
1263 WInDev[wDevID].dwTotalRecorded += (*lpWaveHdr)->dwBytesRecorded;
1264 (*lpWaveHdr)->dwFlags &= ~WHDR_INQUEUE;
1265 (*lpWaveHdr)->dwFlags |= WHDR_DONE;
1267 if (WAVE_NotifyClient(wDevID, WIM_DATA, (*lpWaveHdr)->reserved, (*lpWaveHdr)->dwBytesRecorded) != MMSYSERR_NOERROR) {
1268 WARN("can't notify client !\n");
1269 return MMSYSERR_INVALPARAM;
1271 /* removes the current block from the queue */
1272 *lpWaveHdr = (*lpWaveHdr)->lpNext;
1273 count++;
1275 TRACE("end of recording !\n");
1276 return MMSYSERR_NOERROR;
1279 /**************************************************************************
1280 * widStop [internal]
1282 static DWORD widStop(WORD wDevID)
1284 TRACE("(%u);\n", wDevID);
1285 if (WInDev[wDevID].unixdev == 0) {
1286 WARN("can't stop !\n");
1287 return MMSYSERR_NOTENABLED;
1289 return MMSYSERR_NOERROR;
1292 /**************************************************************************
1293 * widReset [internal]
1295 static DWORD widReset(WORD wDevID)
1297 TRACE("(%u);\n", wDevID);
1298 if (WInDev[wDevID].unixdev == 0) {
1299 WARN("can't reset !\n");
1300 return MMSYSERR_NOTENABLED;
1302 return MMSYSERR_NOERROR;
1305 /**************************************************************************
1306 * widGetPosition [internal]
1308 static DWORD widGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
1310 int time;
1312 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1313 if (WInDev[wDevID].unixdev == 0) {
1314 WARN("can't get pos !\n");
1315 return MMSYSERR_NOTENABLED;
1317 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1318 TRACE("wType=%04X !\n", lpTime->wType);
1319 TRACE("wBitsPerSample=%u\n", WInDev[wDevID].format.wBitsPerSample);
1320 TRACE("nSamplesPerSec=%lu\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1321 TRACE("nChannels=%u\n", WInDev[wDevID].format.wf.nChannels);
1322 TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec);
1323 switch (lpTime->wType) {
1324 case TIME_BYTES:
1325 lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
1326 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1327 break;
1328 case TIME_SAMPLES:
1329 lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
1330 WInDev[wDevID].format.wBitsPerSample;
1331 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1332 break;
1333 case TIME_SMPTE:
1334 time = WInDev[wDevID].dwTotalRecorded /
1335 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1336 lpTime->u.smpte.hour = time / 108000;
1337 time -= lpTime->u.smpte.hour * 108000;
1338 lpTime->u.smpte.min = time / 1800;
1339 time -= lpTime->u.smpte.min * 1800;
1340 lpTime->u.smpte.sec = time / 30;
1341 time -= lpTime->u.smpte.sec * 30;
1342 lpTime->u.smpte.frame = time;
1343 lpTime->u.smpte.fps = 30;
1344 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1345 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1346 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1347 break;
1348 case TIME_MS:
1349 lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1350 (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1351 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1352 break;
1353 default:
1354 FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1355 lpTime->wType = TIME_MS;
1357 return MMSYSERR_NOERROR;
1360 /**************************************************************************
1361 * widMessage [sample driver]
1363 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1364 DWORD dwParam1, DWORD dwParam2)
1366 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1367 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1369 switch (wMsg) {
1370 case WIDM_OPEN: return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1371 case WIDM_CLOSE: return widClose(wDevID);
1372 case WIDM_ADDBUFFER: return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1373 case WIDM_PREPARE: return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1374 case WIDM_UNPREPARE: return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1375 case WIDM_GETDEVCAPS: return widGetDevCaps(wDevID, (LPWAVEINCAPS16)dwParam1,dwParam2);
1376 case WIDM_GETNUMDEVS: return wodGetNumDevs(); /* same number of devices in output as in input */
1377 case WIDM_GETPOS: return widGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
1378 case WIDM_RESET: return widReset(wDevID);
1379 case WIDM_START: return widStart(wDevID);
1380 case WIDM_PAUSE: return widStop(wDevID);
1381 case WIDM_STOP: return widStop(wDevID);
1382 default:
1383 FIXME("unknown message %u!\n", wMsg);
1385 return MMSYSERR_NOTSUPPORTED;
1388 /*======================================================================*
1389 * Low level WAVE implemantation - DriverProc *
1390 *======================================================================*/
1392 /**************************************************************************
1393 * WAVE_DriverProc [sample driver]
1395 LONG CALLBACK WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1396 DWORD dwParam1, DWORD dwParam2)
1398 TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1399 switch (wMsg) {
1400 case DRV_LOAD: return 1;
1401 case DRV_FREE: return 1;
1402 case DRV_OPEN: return 1;
1403 case DRV_CLOSE: return 1;
1404 case DRV_ENABLE: return 1;
1405 case DRV_DISABLE: return 1;
1406 case DRV_QUERYCONFIGURE: return 1;
1407 case DRV_CONFIGURE: MessageBoxA(0, "Sample MultiMedia Linux Driver !", "MMLinux Driver", MB_OK); return 1;
1408 case DRV_INSTALL: return DRVCNF_RESTART;
1409 case DRV_REMOVE: return DRVCNF_RESTART;
1410 default:
1411 FIXME("is probably wrong msg=0x%04lx\n", wMsg);
1412 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1414 return MMSYSERR_NOTENABLED;
1417 #else /* !HAVE_OSS */
1419 /**************************************************************************
1420 * wodMessage [sample driver]
1422 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1423 DWORD dwParam1, DWORD dwParam2)
1425 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1426 return MMSYSERR_NOTENABLED;
1429 /**************************************************************************
1430 * widMessage [sample driver]
1432 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1433 DWORD dwParam1, DWORD dwParam2)
1435 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1436 return MMSYSERR_NOTENABLED;
1439 /**************************************************************************
1440 * WAVE_DriverProc [sample driver]
1442 LONG CALLBACK WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg,
1443 DWORD dwParam1, DWORD dwParam2)
1445 return MMSYSERR_NOTENABLED;
1447 #endif /* HAVE_OSS */