Bugfix: Declare CALL32_CBClient[Ex] without WINAPI.
[wine] / multimedia / audio.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*                                 
3  * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
4  *
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
12  */
13
14 /*#define EMULATE_SB16*/
15
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 "mmddk.h"
25 #include "oss.h"
26 #include "heap.h"
27 #include "ldt.h"
28 #include "debugtools.h"
29
30 DEFAULT_DEBUG_CHANNEL(wave)
31
32 #ifdef HAVE_OSS
33
34 #define SOUND_DEV "/dev/dsp"
35 #define MIXER_DEV "/dev/mixer"
36
37 #define MAX_WAVEOUTDRV  (1)
38 #define MAX_WAVEINDRV   (1)
39
40 /* state diagram for waveOut writing:
41  *
42  * +---------+-------------+---------------+---------------------------------+
43  * |  state  |  function   |     event     |            new state            |
44  * +---------+-------------+---------------+---------------------------------+
45  * |         | open()      |               | STOPPED                         |
46  * | PAUSED  | write()     |               | PAUSED                          |
47  * | STOPPED | write()     | <thrd create> | PLAYING                         |
48  * | PLAYING | write()     | HEADER        | PLAYING                         |
49  * | (other) | write()     | <error>       |                                 |
50  * | (any)   | pause()     | PAUSING       | PAUSED                          |
51  * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd => STOPPED) |
52  * | (any)   | reset()     | RESETTING     | STOPPED                         |
53  * | (any)   | close()     | CLOSING       | CLOSED                          |
54  * +---------+-------------+---------------+---------------------------------+
55  */
56
57 /* states of the playing device */
58 #define WINE_WS_PLAYING         0
59 #define WINE_WS_PAUSED          1
60 #define WINE_WS_STOPPED         2
61 #define WINE_WS_CLOSED          3
62
63 /* events to be send to device */
64 #define WINE_WM_PAUSING         (WM_USER + 1)
65 #define WINE_WM_RESTARTING      (WM_USER + 2)
66 #define WINE_WM_RESETTING       (WM_USER + 3)
67 #define WINE_WM_CLOSING         (WM_USER + 4)
68 #define WINE_WM_HEADER          (WM_USER + 5)
69
70 typedef struct {
71     int                         unixdev;
72     volatile int                state;                  /* one of the WINE_WS_ manifest constants */
73     DWORD                       dwFragmentSize;         /* size of OSS buffer fragment */
74     WAVEOPENDESC                waveDesc;
75     WORD                        wFlags;
76     PCMWAVEFORMAT               format;
77     LPWAVEHDR                   lpQueueHdr;             /* pending buffers for playing */
78     LPWAVEHDR                   lpNotifyHdr;            /* list of wavehdr for which write() has been called, pending for notification */
79
80     DWORD                       dwPlayedTotal;          /* number of bytes played since opening */
81     DWORD                       dwPlayed;               /* number of bytes played since last DSP_RESET */
82     DWORD                       dwNotifiedBytes;        /* number of bytes for which wavehdr notification has been done */
83
84     /* info on current lpQueueHdr->lpWaveHdr */
85     DWORD                       dwOffCurrHdr;           /* offset in lpQueueHdr->lpWaveHdr->lpData for fragments */
86     DWORD                       dwRemain;               /* number of bytes to write to end the current fragment  */
87
88     /* synchronization stuff */
89     HANDLE                      hThread;
90     DWORD                       dwThreadID;
91     HANDLE                      hEvent;
92
93     WORD                        wMaxFragments;          /* max number of fragments that can be written to dsp */
94     WORD                        wFragsUsedInQueue;      /* current number of used fragments inside dsp queue */
95 } WINE_WAVEOUT;
96
97 typedef struct {
98     int                         unixdev;
99     volatile int                state;
100     DWORD                       dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */
101     WAVEOPENDESC                waveDesc;
102     WORD                        wFlags;
103     PCMWAVEFORMAT               format;
104     LPWAVEHDR                   lpQueueHdr;
105     DWORD                       dwTotalRecorded;
106 } WINE_WAVEIN;
107
108 static WINE_WAVEOUT     WOutDev   [MAX_WAVEOUTDRV];
109 static WINE_WAVEIN      WInDev    [MAX_WAVEOUTDRV];
110
111 /*======================================================================*
112  *                  Low level WAVE implemantation                       *
113  *======================================================================*/
114
115 /**************************************************************************
116  *                      WAVE_NotifyClient                       [internal]
117  */
118 static DWORD WAVE_NotifyClient(UINT16 wDevID, WORD wMsg, 
119                                DWORD dwParam1, DWORD dwParam2)
120 {
121     TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
122     
123     switch (wMsg) {
124     case WOM_OPEN:
125     case WOM_CLOSE:
126     case WOM_DONE:
127         if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
128         
129         if (WOutDev[wDevID].wFlags != DCB_NULL && 
130             !DriverCallback16(WOutDev[wDevID].waveDesc.dwCallBack, 
131                               WOutDev[wDevID].wFlags, 
132                               WOutDev[wDevID].waveDesc.hWave, 
133                               wMsg, 
134                               WOutDev[wDevID].waveDesc.dwInstance, 
135                               dwParam1, 
136                               dwParam2)) {
137             WARN("can't notify client !\n");
138             return MMSYSERR_NOERROR;
139         }
140         break;
141         
142     case WIM_OPEN:
143     case WIM_CLOSE:
144     case WIM_DATA:
145         if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
146         
147         if (WInDev[wDevID].wFlags != DCB_NULL && 
148             !DriverCallback16(WInDev[wDevID].waveDesc.dwCallBack, 
149                               WInDev[wDevID].wFlags, 
150                               WInDev[wDevID].waveDesc.hWave, 
151                               wMsg, 
152                               WInDev[wDevID].waveDesc.dwInstance, 
153                               dwParam1, 
154                               dwParam2)) {
155             WARN("can't notify client !\n");
156             return MMSYSERR_NOERROR;
157         }
158         break;
159     default:
160         FIXME("Unknown CB message %u\n", wMsg);
161         break;
162     }
163     return 0;
164 }
165
166 /*======================================================================*
167  *                  Low level WAVE OUT implemantation                   *
168  *======================================================================*/
169
170 /**************************************************************************
171  *                              wodPlayer_WriteFragments        [internal]
172  *
173  * wodPlayer helper. Writes as many fragments it can to unixdev.
174  * Returns TRUE in case of buffer underrun.
175  */
176 static  BOOL    wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
177 {
178     LPWAVEHDR           lpWaveHdr;
179     LPBYTE              lpData;
180     int                 count;
181
182     for (;;) {
183         TRACE("Fragments: %d used on fd %d\n", wwo->wFragsUsedInQueue, wwo->unixdev);
184         if (wwo->wFragsUsedInQueue == wwo->wMaxFragments)       /* output queue is full, wait a bit */
185             return FALSE;
186
187         lpWaveHdr = wwo->lpQueueHdr;
188         if (!lpWaveHdr) {
189             if (wwo->dwRemain > 0 &&                            /* still data to send to complete current fragment */
190                 wwo->dwNotifiedBytes >= wwo->dwFragmentSize &&  /* first fragment has been played */
191                 wwo->wFragsUsedInQueue < 2) {                   /* done with all waveOutWrite()' fragments */
192                 /* FIXME: should do better handling here */
193                 TRACE("Oooch, buffer underrun !\n");
194                 return TRUE; /* force resetting of waveOut device */
195             }
196             return FALSE;       /* wait a bit */
197         }
198         
199         if (wwo->dwOffCurrHdr == 0) {
200             TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
201             if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)
202                 FIXME("NIY: loops (%lu) in wavehdr\n", lpWaveHdr->dwLoops);
203         }
204         
205         lpData = ((DWORD)lpWaveHdr == lpWaveHdr->reserved) ?
206             (LPBYTE)lpWaveHdr->lpData : (LPBYTE)PTR_SEG_TO_LIN(lpWaveHdr->lpData);
207         
208         /* finish current wave hdr ? */
209         if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) { 
210             DWORD       toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
211             
212             /* write end of current wave hdr */
213             count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
214             TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
215             
216             if (count > 0) {
217                 LPWAVEHDR*      wh;
218                 
219                 /* move lpWaveHdr to the end of notify list */
220                 for (wh = &(wwo->lpNotifyHdr); *wh; wh = &((*wh)->lpNext));
221                     *wh = lpWaveHdr;
222
223                 wwo->lpQueueHdr = lpWaveHdr->lpNext;
224                 lpWaveHdr->lpNext = 0;
225                 
226                 wwo->dwOffCurrHdr = 0;
227                 if ((wwo->dwRemain -= count) == 0) {
228                     wwo->dwRemain = wwo->dwFragmentSize;
229                     wwo->wFragsUsedInQueue++;
230                 }
231             }
232             continue; /* try to go to use next wavehdr */
233         }  else {
234             count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
235             TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
236             if (count > 0) {
237                 wwo->dwOffCurrHdr += count;
238                 wwo->dwRemain = wwo->dwFragmentSize;
239                 wwo->wFragsUsedInQueue++;
240             }
241         }
242     }
243 }
244
245 /**************************************************************************
246  *                              wodPlayer_WriteFragments        [internal]
247  *
248  * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
249  * have been played (actually to speaker, not to unixdev fd).
250  */
251 static  void    wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
252 {
253     LPWAVEHDR           lpWaveHdr;
254     count_info          cinfo;
255     
256     /* get effective number of written bytes */
257     if (!force) {
258         if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOPTR, &cinfo) == -1) {
259             perror("ioctl SNDCTL_DSP_GETOPTR");
260             wwo->hThread = 0;
261             wwo->state = WINE_WS_STOPPED;
262             ExitThread(-1);
263         }
264         TRACE("Played %d bytes (played=%ld) on fd %d\n", cinfo.bytes, wwo->dwPlayed, wwo->unixdev);
265         wwo->wFragsUsedInQueue -= cinfo.bytes / wwo->dwFragmentSize - wwo->dwPlayed / wwo->dwFragmentSize;
266         wwo->dwPlayed = cinfo.bytes;
267     }
268     if (force || cinfo.bytes > wwo->dwNotifiedBytes) {
269         /* remove all wavehdr which can be notified */
270         while (wwo->lpNotifyHdr && 
271                (force || (cinfo.bytes >= wwo->dwNotifiedBytes + wwo->lpNotifyHdr->dwBufferLength))) {
272             lpWaveHdr = wwo->lpNotifyHdr;
273             
274             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
275             lpWaveHdr->dwFlags |= WHDR_DONE;
276             if (!force)
277                 wwo->dwNotifiedBytes += lpWaveHdr->dwBufferLength;
278             wwo->lpNotifyHdr = lpWaveHdr->lpNext;
279             
280             TRACE("Notifying client with %p\n", lpWaveHdr);
281             if (WAVE_NotifyClient(uDevID, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
282                 WARN("can't notify client !\n");
283             }
284         }
285     }
286 }
287
288 /**************************************************************************
289  *                              wodPlayer_Reset                 [internal]
290  *
291  * wodPlayer helper. Resets current output stream.
292  */
293 static  void    wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
294 {
295     LPWAVEHDR           lpWaveHdr;
296
297     /* updates current notify list */
298     wodPlayer_Notify(wwo, uDevID, FALSE);
299
300     /* flush all possible output */
301     if (ioctl(wwo->unixdev, SNDCTL_DSP_RESET, 0) == -1) {
302         perror("ioctl SNDCTL_DSP_RESET");
303         wwo->hThread = 0;
304         wwo->state = WINE_WS_STOPPED;
305         ExitThread(-1);
306     }
307
308     wwo->dwOffCurrHdr = 0;
309     if (reset) {
310         /* empty notify list */
311         wodPlayer_Notify(wwo, uDevID, TRUE);
312         if (wwo->lpNotifyHdr) {
313             ERR("out of sync\n");
314         }
315         /* get rid also of all the current queue */
316         for (lpWaveHdr = wwo->lpQueueHdr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
317             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
318             lpWaveHdr->dwFlags |= WHDR_DONE;
319             
320             if (WAVE_NotifyClient(uDevID, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
321                 WARN("can't notify client !\n");
322             }
323         }
324         wwo->lpQueueHdr = 0;
325         wwo->state = WINE_WS_STOPPED;
326         wwo->dwPlayedTotal = 0;
327     } else {
328         /* move notify list to begining of lpQueueHdr list */
329         while (wwo->lpNotifyHdr) {
330             lpWaveHdr = wwo->lpNotifyHdr;
331             wwo->lpNotifyHdr = lpWaveHdr->lpNext;
332             lpWaveHdr->lpNext = wwo->lpQueueHdr;
333             wwo->lpQueueHdr = lpWaveHdr;
334         }
335         wwo->state = WINE_WS_PAUSED;
336         wwo->dwPlayedTotal += wwo->dwPlayed;
337     }
338     wwo->dwNotifiedBytes = wwo->dwPlayed = 0;
339     wwo->wFragsUsedInQueue = 0;
340 }
341
342 /**************************************************************************
343  *                              wodPlayer                       [internal]
344  */
345 static  DWORD   CALLBACK        wodPlayer(LPVOID pmt)
346 {
347     WORD                uDevID = (DWORD)pmt;
348     WINE_WAVEOUT*       wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
349     WAVEHDR*            lpWaveHdr;
350     DWORD               dwSleepTime;
351     MSG                 msg;
352
353     PeekMessageA(&msg, 0, 0, 0, 0);
354     wwo->state = WINE_WS_STOPPED;
355
356     wwo->dwNotifiedBytes = 0;
357     wwo->dwOffCurrHdr = 0;
358     wwo->dwRemain = wwo->dwFragmentSize;
359     wwo->lpQueueHdr = NULL;
360     wwo->lpNotifyHdr = NULL;
361     wwo->wFragsUsedInQueue = 0;
362     wwo->dwPlayedTotal = 0;
363     wwo->dwPlayed = 0;
364
365     TRACE("imhere[0]\n");
366     SetEvent(wwo->hEvent);
367
368     /* make sleep time to be # of ms to output a fragment */
369     dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
370
371     for (;;) {
372         /* wait for dwSleepTime or an event in thread's queue */
373         /* FIXME: could improve wait time depending on queue state,
374          * ie, number of queued fragments
375          */
376         TRACE("imhere[1]\n");
377         MsgWaitForMultipleObjects(0, NULL, FALSE, 
378                                   (wwo->state == WINE_WS_PLAYING) ? 
379                                      (MAX(wwo->wFragsUsedInQueue, 4) - 2) * dwSleepTime : 
380                                      /*INFINITE*/100, 
381                                   QS_POSTMESSAGE);
382         TRACE("imhere[2]\n");
383         wodPlayer_Notify(wwo, uDevID, FALSE);
384         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
385             switch (msg.message) {
386             case WINE_WM_PAUSING:
387                 wodPlayer_Reset(wwo, uDevID, FALSE);
388                 wwo->state = WINE_WS_PAUSED;
389                 SetEvent(wwo->hEvent);
390                 break;
391             case WINE_WM_RESTARTING:
392                 wwo->state = WINE_WS_PLAYING;
393                 SetEvent(wwo->hEvent);
394                 break;
395             case WINE_WM_HEADER:
396                 lpWaveHdr = (LPWAVEHDR)msg.lParam;
397                 
398                 lpWaveHdr->dwFlags &= ~WHDR_DONE;
399                 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
400                 lpWaveHdr->lpNext = 0;
401                 
402                 /* insert buffer at the end of queue */
403                 {
404                     LPWAVEHDR*  wh;
405                     for (wh = &(wwo->lpQueueHdr); *wh; wh = &((*wh)->lpNext));
406                     *wh = lpWaveHdr;
407                 }
408                 if (wwo->state == WINE_WS_STOPPED)
409                     wwo->state = WINE_WS_PLAYING;
410                 break;
411             case WINE_WM_RESETTING:
412                 wodPlayer_Reset(wwo, uDevID, TRUE);
413                 SetEvent(wwo->hEvent);
414                 break;
415             case WINE_WM_CLOSING:
416                 /* sanity check: this should not happen since the device must have been reset before */
417                 if (wwo->lpNotifyHdr || wwo->lpQueueHdr) {
418                     ERR("out of sync\n");
419                 }
420                 wwo->hThread = 0;
421                 wwo->state = WINE_WS_CLOSED;
422                 SetEvent(wwo->hEvent);
423                 ExitThread(0);
424                 /* shouldn't go here */
425             default:
426                 FIXME("unknown message %d\n", msg.message);
427                 break;
428             }
429         }
430         if (wwo->state == WINE_WS_PLAYING) {
431             wodPlayer_WriteFragments(wwo);
432         }
433         wodPlayer_Notify(wwo, uDevID, FALSE);
434     }
435     ExitThread(0);
436     /* just for not generating compilation warnings... should never be executed */
437     return 0; 
438 }
439
440 /**************************************************************************
441  *                      wodGetDevCaps                           [internal]
442  */
443 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS16 lpCaps, DWORD dwSize)
444 {
445     int         audio;
446     int         smplrate;
447     int         samplesize = 16;
448     int         dsp_stereo = 1;
449     int         bytespersmpl;
450     int         caps;
451     int         mask;
452     
453     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
454     
455     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
456     if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
457     
458     if (wDevID >= MAX_WAVEOUTDRV) {
459         TRACE("MAX_WAVOUTDRV reached !\n");
460         return MMSYSERR_BADDEVICEID;
461     }
462     
463     if (WOutDev[wDevID].unixdev == 0) {
464         audio = open(SOUND_DEV, O_WRONLY, 0);
465         if (audio == -1) return MMSYSERR_ALLOCATED;
466     } else {
467         audio = WOutDev[wDevID].unixdev;
468     }
469
470     /* FIXME: some programs compare this string against the content of the registry
471      * for MM drivers. The name have to match in order the program to work 
472      * (e.g. MS win9x mplayer.exe)
473      */
474 #ifdef EMULATE_SB16
475     lpCaps->wMid = 0x0002;
476     lpCaps->wPid = 0x0104;
477     strcpy(lpCaps->szPname, "SB16 Wave Out");
478 #else
479     lpCaps->wMid = 0x00FF;      /* Manufac ID */
480     lpCaps->wPid = 0x0001;      /* Product ID */
481     /*    strcpy(lpCaps->szPname, "OpenSoundSystem WAVOUT Driver");*/
482     strcpy(lpCaps->szPname, "CS4236/37/38");
483 #endif
484     lpCaps->vDriverVersion = 0x0100;
485     lpCaps->dwFormats = 0x00000000;
486     lpCaps->dwSupport = WAVECAPS_VOLUME;
487     
488     IOCTL(audio, SNDCTL_DSP_GETFMTS, mask);
489     TRACE("OSS dsp mask=%08x\n", mask);
490     mask = AFMT_QUERY;
491     IOCTL(audio, SNDCTL_DSP_SETFMT, mask);
492     TRACE("OSS dsp current=%08x\n", mask);
493
494     /* First bytespersampl, then stereo */
495     bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
496     
497     lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
498     if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
499     
500     smplrate = 44100;
501     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
502         lpCaps->dwFormats |= WAVE_FORMAT_4M08;
503         if (lpCaps->wChannels > 1)
504             lpCaps->dwFormats |= WAVE_FORMAT_4S08;
505         if (bytespersmpl > 1) {
506             lpCaps->dwFormats |= WAVE_FORMAT_4M16;
507             if (lpCaps->wChannels > 1)
508                 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
509         }
510     }
511     smplrate = 22050;
512     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
513         lpCaps->dwFormats |= WAVE_FORMAT_2M08;
514         if (lpCaps->wChannels > 1)
515             lpCaps->dwFormats |= WAVE_FORMAT_2S08;
516         if (bytespersmpl > 1) {
517             lpCaps->dwFormats |= WAVE_FORMAT_2M16;
518             if (lpCaps->wChannels > 1)
519                 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
520         }
521     }
522     smplrate = 11025;
523     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
524         lpCaps->dwFormats |= WAVE_FORMAT_1M08;
525         if (lpCaps->wChannels > 1)
526             lpCaps->dwFormats |= WAVE_FORMAT_1S08;
527         if (bytespersmpl > 1) {
528             lpCaps->dwFormats |= WAVE_FORMAT_1M16;
529             if (lpCaps->wChannels > 1)
530                 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
531         }
532     }
533     if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
534         if ((caps & DSP_CAP_REALTIME) && !(caps && DSP_CAP_BATCH))
535             lpCaps->dwFormats |= WAVECAPS_SAMPLEACCURATE;
536     }
537     if (WOutDev[wDevID].unixdev == 0) {
538         close(audio);
539     }
540     TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
541     return MMSYSERR_NOERROR;
542 }
543
544 /**************************************************************************
545  *                              wodOpen                         [internal]
546  */
547 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
548 {
549     int                 audio;
550     int                 sample_rate;
551     int                 sample_size;
552     int                 dsp_stereo;
553     int                 audio_fragment;
554     int                 fragment_size;
555     WAVEOUTCAPS16       woc;
556
557     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
558     if (lpDesc == NULL) {
559         WARN("Invalid Parameter !\n");
560         return MMSYSERR_INVALPARAM;
561     }
562     if (wDevID >= MAX_WAVEOUTDRV) {
563         TRACE("MAX_WAVOUTDRV reached !\n");
564         return MMSYSERR_BADDEVICEID;
565     }
566     wodGetDevCaps(wDevID, &woc, sizeof(woc));
567
568     /* only PCM format is supported so far... */
569     if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
570         lpDesc->lpFormat->nChannels == 0 ||
571         lpDesc->lpFormat->nSamplesPerSec == 0) {
572         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n", 
573              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
574              lpDesc->lpFormat->nSamplesPerSec);
575         return WAVERR_BADFORMAT;
576     }
577
578     if (dwFlags & WAVE_FORMAT_QUERY)
579         return MMSYSERR_NOERROR;
580
581     WOutDev[wDevID].unixdev = 0;
582     if (access(SOUND_DEV, 0) != 0) 
583         return MMSYSERR_NOTENABLED;
584     audio = open(SOUND_DEV, O_WRONLY, 0);
585     if (audio == -1) {
586         WARN("can't open !\n");
587         return MMSYSERR_ALLOCATED ;
588     }
589
590     WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
591     
592     memcpy(&WOutDev[wDevID].waveDesc, lpDesc,           sizeof(WAVEOPENDESC));
593     memcpy(&WOutDev[wDevID].format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
594     
595     if (WOutDev[wDevID].format.wBitsPerSample == 0) {
596         WOutDev[wDevID].format.wBitsPerSample = 8 *
597             (WOutDev[wDevID].format.wf.nAvgBytesPerSec /
598              WOutDev[wDevID].format.wf.nSamplesPerSec) /
599             WOutDev[wDevID].format.wf.nChannels;
600     }
601     
602     /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
603      * thus leading to 46ms per fragment, and a turnaround time of 185ms
604      */
605     /* 2^10=1024 bytes per fragment, 16 fragments max */
606     audio_fragment = 0x000F000A;
607     sample_size = WOutDev[wDevID].format.wBitsPerSample;
608     sample_rate = WOutDev[wDevID].format.wf.nSamplesPerSec;
609     dsp_stereo = (WOutDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
610
611     IOCTL(audio, SNDCTL_DSP_SETFRAGMENT, audio_fragment);
612     /* First size and stereo then samplerate */
613     IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, sample_size);
614     IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
615     IOCTL(audio, SNDCTL_DSP_SPEED, sample_rate);
616
617     /* even if we set fragment size above, read it again, just in case */
618     IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
619
620     WOutDev[wDevID].unixdev = audio;
621     WOutDev[wDevID].dwFragmentSize = fragment_size;
622     WOutDev[wDevID].wMaxFragments = HIWORD(audio_fragment) + 1;
623
624     WOutDev[wDevID].hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
625     WOutDev[wDevID].hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(WOutDev[wDevID].dwThreadID));
626     WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
627
628     TRACE("fd=%d fragmentSize=%ld\n", 
629           WOutDev[wDevID].unixdev, WOutDev[wDevID].dwFragmentSize);
630
631     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u !\n", 
632           WOutDev[wDevID].format.wBitsPerSample, WOutDev[wDevID].format.wf.nAvgBytesPerSec, 
633           WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels);
634     
635     if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
636         WARN("can't notify client !\n");
637         return MMSYSERR_INVALPARAM;
638     }
639     return MMSYSERR_NOERROR;
640 }
641
642 /**************************************************************************
643  *                              wodClose                        [internal]
644  */
645 static DWORD wodClose(WORD wDevID)
646 {
647     DWORD       ret = MMSYSERR_NOERROR;
648     
649     TRACE("(%u);\n", wDevID);
650     
651     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
652         WARN("bad device ID !\n");
653         return MMSYSERR_BADDEVICEID;
654     }
655     
656     if (WOutDev[wDevID].lpQueueHdr != NULL || WOutDev[wDevID].lpNotifyHdr != NULL) {
657         WARN("buffers still playing !\n");
658         ret = WAVERR_STILLPLAYING;
659     } else {
660         TRACE("imhere[3-close]\n");
661         PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_CLOSING, 0, 0);
662         WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
663         CloseHandle(WOutDev[wDevID].hEvent);
664
665         close(WOutDev[wDevID].unixdev);
666         WOutDev[wDevID].unixdev = 0;
667         WOutDev[wDevID].dwFragmentSize = 0;
668         if (WAVE_NotifyClient(wDevID, WOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
669             WARN("can't notify client !\n");
670             ret = MMSYSERR_INVALPARAM;
671         }
672     }
673     return ret;
674 }
675
676 /**************************************************************************
677  *                              wodWrite                        [internal]
678  * 
679  */
680 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
681 {
682     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
683     
684     /* first, do the sanity checks... */
685     if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
686         WARN("bad dev ID !\n");
687         return MMSYSERR_BADDEVICEID;
688     }
689     
690     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED)) 
691         return WAVERR_UNPREPARED;
692     
693     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) 
694         return WAVERR_STILLPLAYING;
695
696     TRACE("imhere[3-HEADER]\n");
697     PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
698
699     return MMSYSERR_NOERROR;
700 }
701
702 /**************************************************************************
703  *                              wodPrepare                      [internal]
704  */
705 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
706 {
707     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
708     
709     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
710         WARN("bad device ID !\n");
711         return MMSYSERR_BADDEVICEID;
712     }
713     
714     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
715         return WAVERR_STILLPLAYING;
716     
717     lpWaveHdr->dwFlags |= WHDR_PREPARED;
718     lpWaveHdr->dwFlags &= ~WHDR_DONE;
719     return MMSYSERR_NOERROR;
720 }
721
722 /**************************************************************************
723  *                              wodUnprepare                    [internal]
724  */
725 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
726 {
727     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
728     
729     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
730         WARN("bad device ID !\n");
731         return MMSYSERR_BADDEVICEID;
732     }
733     
734     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
735         return WAVERR_STILLPLAYING;
736     
737     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
738     lpWaveHdr->dwFlags |= WHDR_DONE;
739     
740     return MMSYSERR_NOERROR;
741 }
742
743 /**************************************************************************
744  *                      wodPause                                [internal]
745  */
746 static DWORD wodPause(WORD wDevID)
747 {
748     TRACE("(%u);!\n", wDevID);
749     
750     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
751         WARN("bad device ID !\n");
752         return MMSYSERR_BADDEVICEID;
753     }
754     
755     TRACE("imhere[3-PAUSING]\n");
756     PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_PAUSING, 0, 0);
757     WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
758     
759     return MMSYSERR_NOERROR;
760 }
761
762 /**************************************************************************
763  *                      wodRestart                              [internal]
764  */
765 static DWORD wodRestart(WORD wDevID)
766 {
767     TRACE("(%u);\n", wDevID);
768     
769     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
770         WARN("bad device ID !\n");
771         return MMSYSERR_BADDEVICEID;
772     }
773     
774     if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
775         TRACE("imhere[3-RESTARTING]\n");
776         PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESTARTING, 0, 0);
777         WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
778     }
779     
780     /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
781     /* FIXME: Myst crashes with this ... hmm -MM
782        if (WAVE_NotifyClient(wDevID, WOM_DONE, 0L, 0L) != MMSYSERR_NOERROR) {
783        WARN("can't notify client !\n");
784        return MMSYSERR_INVALPARAM;
785        }
786     */
787     
788     return MMSYSERR_NOERROR;
789 }
790
791 /**************************************************************************
792  *                      wodReset                                [internal]
793  */
794 static DWORD wodReset(WORD wDevID)
795 {
796     TRACE("(%u);\n", wDevID);
797     
798     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
799         WARN("bad device ID !\n");
800         return MMSYSERR_BADDEVICEID;
801     }
802     
803     TRACE("imhere[3-RESET]\n");
804     PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
805     WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
806     
807     return MMSYSERR_NOERROR;
808 }
809
810
811 /**************************************************************************
812  *                              wodGetPosition                  [internal]
813  */
814 static DWORD wodGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
815 {
816     int         time;
817     DWORD       val;
818
819     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
820     
821     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
822         WARN("bad device ID !\n");
823         return MMSYSERR_BADDEVICEID;
824     }
825     
826     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
827
828     val = WOutDev[wDevID].dwPlayedTotal + WOutDev[wDevID].dwPlayed;
829
830     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n", 
831           lpTime->wType, WOutDev[wDevID].format.wBitsPerSample, 
832           WOutDev[wDevID].format.wf.nSamplesPerSec, WOutDev[wDevID].format.wf.nChannels, 
833           WOutDev[wDevID].format.wf.nAvgBytesPerSec); 
834     TRACE("dwTotalPlayed=%lu\n", val);
835     
836     switch (lpTime->wType) {
837     case TIME_BYTES:
838         lpTime->u.cb = val;
839         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
840         break;
841     case TIME_SAMPLES:
842         lpTime->u.sample = val * 8 / WOutDev[wDevID].format.wBitsPerSample;
843         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
844         break;
845     case TIME_SMPTE:
846         time = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
847         lpTime->u.smpte.hour = time / 108000;
848         time -= lpTime->u.smpte.hour * 108000;
849         lpTime->u.smpte.min = time / 1800;
850         time -= lpTime->u.smpte.min * 1800;
851         lpTime->u.smpte.sec = time / 30;
852         time -= lpTime->u.smpte.sec * 30;
853         lpTime->u.smpte.frame = time;
854         lpTime->u.smpte.fps = 30;
855         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
856               lpTime->u.smpte.hour, lpTime->u.smpte.min,
857               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
858         break;
859     default:
860         FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
861         lpTime->wType = TIME_MS;
862     case TIME_MS:
863         lpTime->u.ms = val / (WOutDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
864         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
865         break;
866     }
867     return MMSYSERR_NOERROR;
868 }
869
870 /**************************************************************************
871  *                              wodGetVolume                    [internal]
872  */
873 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
874 {
875     int         mixer;
876     int         volume;
877     DWORD       left, right;
878     
879     TRACE("(%u, %p);\n", wDevID, lpdwVol);
880     
881     if (lpdwVol == NULL) 
882         return MMSYSERR_NOTENABLED;
883     if ((mixer = open(MIXER_DEV, O_RDONLY)) < 0) {
884         WARN("mixer device not available !\n");
885         return MMSYSERR_NOTENABLED;
886     }
887     if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
888         WARN("unable read mixer !\n");
889         return MMSYSERR_NOTENABLED;
890     }
891     close(mixer);
892     left = LOBYTE(volume);
893     right = HIBYTE(volume);
894     TRACE("left=%ld right=%ld !\n", left, right);
895     *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
896     return MMSYSERR_NOERROR;
897 }
898
899
900 /**************************************************************************
901  *                              wodSetVolume                    [internal]
902  */
903 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
904 {
905     int         mixer;
906     int         volume;
907     DWORD       left, right;
908
909     TRACE("(%u, %08lX);\n", wDevID, dwParam);
910
911     left  = (LOWORD(dwParam) * 100) / 0xFFFFl;
912     right = (HIWORD(dwParam) * 100) / 0xFFFFl;
913     volume = left + (right << 8);
914     
915     if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) {
916         WARN("mixer device not available !\n");
917         return MMSYSERR_NOTENABLED;
918     }
919     if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
920         WARN("unable set mixer !\n");
921         return MMSYSERR_NOTENABLED;
922     }
923     close(mixer);
924     return MMSYSERR_NOERROR;
925 }
926
927 /**************************************************************************
928  *                              wodGetNumDevs                   [internal]
929  */
930 static  DWORD   wodGetNumDevs(void)
931 {
932     DWORD       ret = 1;
933     
934     /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
935     int audio = open(SOUND_DEV, O_WRONLY, 0);
936     
937     if (audio == -1) {
938         if (errno != EBUSY)
939             ret = 0;
940     } else {
941         close(audio);
942     }
943     return ret;
944 }
945
946 /**************************************************************************
947  *                              wodMessage                      [sample driver]
948  */
949 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
950                         DWORD dwParam1, DWORD dwParam2)
951 {
952     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
953           wDevID, wMsg, dwUser, dwParam1, dwParam2);
954     
955     switch (wMsg) {
956     case WODM_OPEN:             return wodOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
957     case WODM_CLOSE:            return wodClose         (wDevID);
958     case WODM_WRITE:            return wodWrite         (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
959     case WODM_PAUSE:            return wodPause         (wDevID);
960         /*    case WODM_STOP:           */
961     case WODM_GETPOS:           return wodGetPosition   (wDevID, (LPMMTIME16)dwParam1,          dwParam2);
962     case WODM_BREAKLOOP:        return MMSYSERR_NOTSUPPORTED;
963     case WODM_PREPARE:          return wodPrepare       (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
964     case WODM_UNPREPARE:        return wodUnprepare     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
965     case WODM_GETDEVCAPS:       return wodGetDevCaps    (wDevID, (LPWAVEOUTCAPS16)dwParam1,     dwParam2);
966     case WODM_GETNUMDEVS:       return wodGetNumDevs    ();
967     case WODM_GETPITCH:         return MMSYSERR_NOTSUPPORTED;
968     case WODM_SETPITCH:         return MMSYSERR_NOTSUPPORTED;
969     case WODM_GETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
970     case WODM_SETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
971     case WODM_GETVOLUME:        return wodGetVolume     (wDevID, (LPDWORD)dwParam1);
972     case WODM_SETVOLUME:        return wodSetVolume     (wDevID, dwParam1);
973     case WODM_RESTART:          return wodRestart       (wDevID);
974     case WODM_RESET:            return wodReset         (wDevID);
975     default:
976         FIXME("unknown message %d!\n", wMsg);
977     }
978     return MMSYSERR_NOTSUPPORTED;
979 }
980
981 /*======================================================================*
982  *                  Low level WAVE IN implemantation                    *
983  *======================================================================*/
984
985 /**************************************************************************
986  *                      widGetDevCaps                           [internal]
987  */
988 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS16 lpCaps, DWORD dwSize)
989 {
990     int         audio,smplrate,samplesize=16,dsp_stereo=1,bytespersmpl;
991     
992     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
993     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
994     if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
995     audio = open(SOUND_DEV, O_RDONLY, 0);
996     if (audio == -1) return MMSYSERR_ALLOCATED ;
997 #ifdef EMULATE_SB16
998     lpCaps->wMid = 0x0002;
999     lpCaps->wPid = 0x0004;
1000     strcpy(lpCaps->szPname, "SB16 Wave In");
1001 #else
1002     lpCaps->wMid = 0x00FF;      /* Manufac ID */
1003     lpCaps->wPid = 0x0001;      /* Product ID */
1004     strcpy(lpCaps->szPname, "OpenSoundSystem WAVIN Driver");
1005 #endif
1006     lpCaps->dwFormats = 0x00000000;
1007     lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
1008     bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
1009     smplrate = 44100;
1010     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1011         lpCaps->dwFormats |= WAVE_FORMAT_4M08;
1012         if (lpCaps->wChannels > 1)
1013             lpCaps->dwFormats |= WAVE_FORMAT_4S08;
1014         if (bytespersmpl > 1) {
1015             lpCaps->dwFormats |= WAVE_FORMAT_4M16;
1016             if (lpCaps->wChannels > 1)
1017                 lpCaps->dwFormats |= WAVE_FORMAT_4S16;
1018         }
1019     }
1020     smplrate = 22050;
1021     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1022         lpCaps->dwFormats |= WAVE_FORMAT_2M08;
1023         if (lpCaps->wChannels > 1)
1024             lpCaps->dwFormats |= WAVE_FORMAT_2S08;
1025         if (bytespersmpl > 1) {
1026             lpCaps->dwFormats |= WAVE_FORMAT_2M16;
1027             if (lpCaps->wChannels > 1)
1028                 lpCaps->dwFormats |= WAVE_FORMAT_2S16;
1029         }
1030     }
1031     smplrate = 11025;
1032     if (IOCTL(audio, SNDCTL_DSP_SPEED, smplrate) == 0) {
1033         lpCaps->dwFormats |= WAVE_FORMAT_1M08;
1034         if (lpCaps->wChannels > 1)
1035             lpCaps->dwFormats |= WAVE_FORMAT_1S08;
1036         if (bytespersmpl > 1) {
1037             lpCaps->dwFormats |= WAVE_FORMAT_1M16;
1038             if (lpCaps->wChannels > 1)
1039                 lpCaps->dwFormats |= WAVE_FORMAT_1S16;
1040         }
1041     }
1042     close(audio);
1043     TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
1044     return MMSYSERR_NOERROR;
1045 }
1046
1047 /**************************************************************************
1048  *                              widOpen                         [internal]
1049  */
1050 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1051 {
1052     int                 audio,abuf_size,smplrate,samplesize,dsp_stereo;
1053     LPWAVEFORMAT        lpFormat;
1054     
1055     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1056     if (lpDesc == NULL) {
1057         WARN("Invalid Parameter !\n");
1058         return MMSYSERR_INVALPARAM;
1059     }
1060     if (wDevID >= MAX_WAVEINDRV) {
1061         TRACE("MAX_WAVINDRV reached !\n");
1062         return MMSYSERR_ALLOCATED;
1063     }
1064     WInDev[wDevID].unixdev = 0;
1065     if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
1066     audio = open(SOUND_DEV, O_RDONLY, 0);
1067     if (audio == -1) {
1068         WARN("can't open !\n");
1069         return MMSYSERR_ALLOCATED;
1070     }
1071     IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, abuf_size);
1072     if (abuf_size < 1024 || abuf_size > 65536) {
1073         if (abuf_size == -1)
1074             WARN("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
1075         else
1076             WARN("SNDCTL_DSP_GETBLKSIZE Invalid dwFragmentSize !\n");
1077         return MMSYSERR_NOTENABLED;
1078     }
1079     WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1080
1081     if (WInDev[wDevID].lpQueueHdr) {
1082         HeapFree(GetProcessHeap(), 0, WInDev[wDevID].lpQueueHdr);
1083         WInDev[wDevID].lpQueueHdr = NULL;
1084     }
1085     WInDev[wDevID].unixdev = audio;
1086     WInDev[wDevID].dwFragmentSize = abuf_size;
1087     WInDev[wDevID].dwTotalRecorded = 0;
1088     memcpy(&WInDev[wDevID].waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1089     lpFormat = (LPWAVEFORMAT) lpDesc->lpFormat; 
1090     if (lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
1091         WARN("Bad format %04X !\n",
1092              lpFormat->wFormatTag);
1093         return WAVERR_BADFORMAT;
1094     }
1095     memcpy(&WInDev[wDevID].format, lpFormat, sizeof(PCMWAVEFORMAT));
1096     WInDev[wDevID].format.wBitsPerSample = 8; /* <-------------- */
1097     if (WInDev[wDevID].format.wf.nChannels == 0) return WAVERR_BADFORMAT;
1098     if (WInDev[wDevID].format.wf.nSamplesPerSec == 0) return WAVERR_BADFORMAT;
1099     if (WInDev[wDevID].format.wBitsPerSample == 0) {
1100         WInDev[wDevID].format.wBitsPerSample = 8 *
1101             (WInDev[wDevID].format.wf.nAvgBytesPerSec /
1102              WInDev[wDevID].format.wf.nSamplesPerSec) /
1103             WInDev[wDevID].format.wf.nChannels;
1104     }
1105     samplesize = WInDev[wDevID].format.wBitsPerSample;
1106     smplrate = WInDev[wDevID].format.wf.nSamplesPerSec;
1107     dsp_stereo = (WInDev[wDevID].format.wf.nChannels > 1) ? TRUE : FALSE;
1108     IOCTL(audio, SNDCTL_DSP_SPEED, smplrate);
1109     IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize);
1110     IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo);
1111     TRACE("wBitsPerSample=%u !\n", WInDev[wDevID].format.wBitsPerSample);
1112     TRACE("nSamplesPerSec=%lu !\n", WInDev[wDevID].format.wf.nSamplesPerSec);
1113     TRACE("nChannels=%u !\n", WInDev[wDevID].format.wf.nChannels);
1114     TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec); 
1115     if (WAVE_NotifyClient(wDevID, WIM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
1116         WARN("can't notify client !\n");
1117         return MMSYSERR_INVALPARAM;
1118     }
1119     return MMSYSERR_NOERROR;
1120 }
1121
1122 /**************************************************************************
1123  *                              widClose                        [internal]
1124  */
1125 static DWORD widClose(WORD wDevID)
1126 {
1127     TRACE("(%u);\n", wDevID);
1128     if (wDevID > MAX_WAVEINDRV) return MMSYSERR_INVALPARAM;
1129     if (WInDev[wDevID].unixdev == 0) {
1130         WARN("can't close !\n");
1131         return MMSYSERR_NOTENABLED;
1132     }
1133     if (WInDev[wDevID].lpQueueHdr != NULL) {
1134         WARN("still buffers open !\n");
1135         return WAVERR_STILLPLAYING;
1136     }
1137     close(WInDev[wDevID].unixdev);
1138     WInDev[wDevID].unixdev = 0;
1139     WInDev[wDevID].dwFragmentSize = 0;
1140     if (WAVE_NotifyClient(wDevID, WIM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) {
1141         WARN("can't notify client !\n");
1142         return MMSYSERR_INVALPARAM;
1143     }
1144     return MMSYSERR_NOERROR;
1145 }
1146
1147 /**************************************************************************
1148  *                              widAddBuffer            [internal]
1149  */
1150 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1151 {
1152     int         count   = 1;
1153     LPWAVEHDR   lpWIHdr;
1154     
1155     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1156     if (WInDev[wDevID].unixdev == 0) {
1157         WARN("can't do it !\n");
1158         return MMSYSERR_NOTENABLED;
1159     }
1160     if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1161         TRACE("never been prepared !\n");
1162         return WAVERR_UNPREPARED;
1163     }
1164     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1165         TRACE("header already in use !\n");
1166         return WAVERR_STILLPLAYING;
1167     }
1168     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1169     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1170     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1171     lpWaveHdr->dwBytesRecorded = 0;
1172     if (WInDev[wDevID].lpQueueHdr == NULL) {
1173         WInDev[wDevID].lpQueueHdr = lpWaveHdr;
1174     } else {
1175         lpWIHdr = WInDev[wDevID].lpQueueHdr;
1176         while (lpWIHdr->lpNext != NULL) {
1177             lpWIHdr = lpWIHdr->lpNext;
1178             count++;
1179         }
1180         lpWIHdr->lpNext = lpWaveHdr;
1181         lpWaveHdr->lpNext = NULL;
1182         count++;
1183     }
1184     TRACE("buffer added ! (now %u in queue)\n", count);
1185     return MMSYSERR_NOERROR;
1186 }
1187
1188 /**************************************************************************
1189  *                              widPrepare                      [internal]
1190  */
1191 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1192 {
1193     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1194     if (WInDev[wDevID].unixdev == 0) {
1195         WARN("can't prepare !\n");
1196         return MMSYSERR_NOTENABLED;
1197     }
1198     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1199         return WAVERR_STILLPLAYING;
1200     lpWaveHdr->dwFlags |= WHDR_PREPARED;
1201     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1202     lpWaveHdr->dwFlags &= ~WHDR_DONE;
1203     lpWaveHdr->dwBytesRecorded = 0;
1204     TRACE("header prepared !\n");
1205     return MMSYSERR_NOERROR;
1206 }
1207
1208 /**************************************************************************
1209  *                              widUnprepare                    [internal]
1210  */
1211 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1212 {
1213     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1214     if (WInDev[wDevID].unixdev == 0) {
1215         WARN("can't unprepare !\n");
1216         return MMSYSERR_NOTENABLED;
1217     }
1218     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1219     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1220     lpWaveHdr->dwFlags |= WHDR_DONE;
1221     
1222     return MMSYSERR_NOERROR;
1223 }
1224
1225 /**************************************************************************
1226  *                      widStart                                [internal]
1227  */
1228 static DWORD widStart(WORD wDevID)
1229 {
1230     int         count   = 1;
1231     int         bytesRead;
1232     LPWAVEHDR   *lpWaveHdr;
1233     LPBYTE      lpData;
1234
1235     TRACE("(%u);\n", wDevID);
1236     if (WInDev[wDevID].unixdev == 0) {
1237         WARN("can't start recording !\n");
1238         return MMSYSERR_NOTENABLED;
1239     }
1240     
1241     lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
1242     TRACE("lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
1243
1244     if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
1245         TRACE("never been prepared !\n");
1246         return WAVERR_UNPREPARED;
1247     }
1248     
1249     while (*lpWaveHdr != NULL) {
1250
1251         lpData = ((DWORD)*lpWaveHdr == (*lpWaveHdr)->reserved) ?
1252             (LPBYTE)(*lpWaveHdr)->lpData : (LPBYTE)PTR_SEG_TO_LIN((*lpWaveHdr)->lpData);
1253
1254         TRACE("recording buf#%u=%p size=%lu \n",
1255               count, lpData, (*lpWaveHdr)->dwBufferLength);
1256
1257         bytesRead = read(WInDev[wDevID].unixdev, lpData, (*lpWaveHdr)->dwBufferLength);
1258
1259         if (bytesRead == -1)
1260             perror("read from audio device");
1261
1262         TRACE("bytesread=%d (%ld)\n", bytesRead, (*lpWaveHdr)->dwBufferLength);
1263         (*lpWaveHdr)->dwBytesRecorded = bytesRead;
1264         WInDev[wDevID].dwTotalRecorded += (*lpWaveHdr)->dwBytesRecorded;
1265         (*lpWaveHdr)->dwFlags &= ~WHDR_INQUEUE;
1266         (*lpWaveHdr)->dwFlags |= WHDR_DONE;
1267         
1268         if (WAVE_NotifyClient(wDevID, WIM_DATA, (*lpWaveHdr)->reserved, (*lpWaveHdr)->dwBytesRecorded) != MMSYSERR_NOERROR) {
1269             WARN("can't notify client !\n");
1270             return MMSYSERR_INVALPARAM;
1271         }
1272         /* removes the current block from the queue */
1273         *lpWaveHdr = (*lpWaveHdr)->lpNext;
1274         count++;
1275     }
1276     TRACE("end of recording !\n");
1277     return MMSYSERR_NOERROR;
1278 }
1279
1280 /**************************************************************************
1281  *                      widStop                                 [internal]
1282  */
1283 static DWORD widStop(WORD wDevID)
1284 {
1285     TRACE("(%u);\n", wDevID);
1286     if (WInDev[wDevID].unixdev == 0) {
1287         WARN("can't stop !\n");
1288         return MMSYSERR_NOTENABLED;
1289     }
1290     return MMSYSERR_NOERROR;
1291 }
1292
1293 /**************************************************************************
1294  *                      widReset                                [internal]
1295  */
1296 static DWORD widReset(WORD wDevID)
1297 {
1298     TRACE("(%u);\n", wDevID);
1299     if (WInDev[wDevID].unixdev == 0) {
1300         WARN("can't reset !\n");
1301         return MMSYSERR_NOTENABLED;
1302     }
1303     return MMSYSERR_NOERROR;
1304 }
1305
1306 /**************************************************************************
1307  *                              widGetPosition                  [internal]
1308  */
1309 static DWORD widGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
1310 {
1311     int         time;
1312     
1313     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1314     if (WInDev[wDevID].unixdev == 0) {
1315         WARN("can't get pos !\n");
1316         return MMSYSERR_NOTENABLED;
1317     }
1318     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1319     TRACE("wType=%04X !\n", lpTime->wType);
1320     TRACE("wBitsPerSample=%u\n", WInDev[wDevID].format.wBitsPerSample); 
1321     TRACE("nSamplesPerSec=%lu\n", WInDev[wDevID].format.wf.nSamplesPerSec); 
1322     TRACE("nChannels=%u\n", WInDev[wDevID].format.wf.nChannels); 
1323     TRACE("nAvgBytesPerSec=%lu\n", WInDev[wDevID].format.wf.nAvgBytesPerSec); 
1324     switch (lpTime->wType) {
1325     case TIME_BYTES:
1326         lpTime->u.cb = WInDev[wDevID].dwTotalRecorded;
1327         TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1328         break;
1329     case TIME_SAMPLES:
1330         lpTime->u.sample = WInDev[wDevID].dwTotalRecorded * 8 /
1331             WInDev[wDevID].format.wBitsPerSample;
1332         TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1333         break;
1334     case TIME_SMPTE:
1335         time = WInDev[wDevID].dwTotalRecorded /
1336             (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1337         lpTime->u.smpte.hour = time / 108000;
1338         time -= lpTime->u.smpte.hour * 108000;
1339         lpTime->u.smpte.min = time / 1800;
1340         time -= lpTime->u.smpte.min * 1800;
1341         lpTime->u.smpte.sec = time / 30;
1342         time -= lpTime->u.smpte.sec * 30;
1343         lpTime->u.smpte.frame = time;
1344         lpTime->u.smpte.fps = 30;
1345         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1346               lpTime->u.smpte.hour, lpTime->u.smpte.min,
1347               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1348         break;
1349     case TIME_MS:
1350         lpTime->u.ms = WInDev[wDevID].dwTotalRecorded /
1351             (WInDev[wDevID].format.wf.nAvgBytesPerSec / 1000);
1352         TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1353         break;
1354     default:
1355         FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
1356         lpTime->wType = TIME_MS;
1357     }
1358     return MMSYSERR_NOERROR;
1359 }
1360
1361 /**************************************************************************
1362  *                              widMessage                      [sample driver]
1363  */
1364 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1365                         DWORD dwParam1, DWORD dwParam2)
1366 {
1367     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1368           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1369
1370     switch (wMsg) {
1371     case WIDM_OPEN:             return widOpen(wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1372     case WIDM_CLOSE:            return widClose(wDevID);
1373     case WIDM_ADDBUFFER:        return widAddBuffer(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1374     case WIDM_PREPARE:          return widPrepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1375     case WIDM_UNPREPARE:        return widUnprepare(wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1376     case WIDM_GETDEVCAPS:       return widGetDevCaps(wDevID, (LPWAVEINCAPS16)dwParam1,dwParam2);
1377     case WIDM_GETNUMDEVS:       return wodGetNumDevs(); /* same number of devices in output as in input */
1378     case WIDM_GETPOS:           return widGetPosition(wDevID, (LPMMTIME16)dwParam1, dwParam2);
1379     case WIDM_RESET:            return widReset(wDevID);
1380     case WIDM_START:            return widStart(wDevID);
1381     case WIDM_STOP:             return widStop(wDevID);
1382     default:
1383         FIXME("unknown message %u!\n", wMsg);
1384     }
1385     return MMSYSERR_NOTSUPPORTED;
1386 }
1387
1388 /*======================================================================*
1389  *              Low level WAVE implemantation - DriverProc              *
1390  *======================================================================*/
1391
1392 /**************************************************************************
1393  *                              WAVE_DriverProc                 [sample driver]
1394  */
1395 LONG CALLBACK   WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg, 
1396                                 DWORD dwParam1, DWORD dwParam2)
1397 {
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);
1413     }
1414     return MMSYSERR_NOTENABLED;
1415 }
1416
1417 #else /* !HAVE_OSS */
1418
1419 /**************************************************************************
1420  *                              wodMessage                      [sample driver]
1421  */
1422 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1423                         DWORD dwParam1, DWORD dwParam2)
1424 {
1425     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1426     return MMSYSERR_NOTENABLED;
1427 }
1428
1429 /**************************************************************************
1430  *                              widMessage                      [sample driver]
1431  */
1432 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1433                         DWORD dwParam1, DWORD dwParam2)
1434 {
1435     FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1436     return MMSYSERR_NOTENABLED;
1437 }
1438
1439 /**************************************************************************
1440  *                              WAVE_DriverProc                 [sample driver]
1441  */
1442 LONG CALLBACK   WAVE_DriverProc(DWORD dwDevID, HDRVR16 hDriv, DWORD wMsg, 
1443                                 DWORD dwParam1, DWORD dwParam2)
1444 {
1445     return MMSYSERR_NOTENABLED;
1446 }
1447 #endif /* HAVE_OSS */