moved to new trace interface
[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 "multimedia.h"
25 #include "heap.h"
26 #include "ldt.h"
27 #include "debugtools.h"
28
29 DEFAULT_DEBUG_CHANNEL(wave)
30
31 #ifdef HAVE_OSS
32
33 #define SOUND_DEV "/dev/dsp"
34 #define MIXER_DEV "/dev/mixer"
35
36 #define MAX_WAVEOUTDRV  (1)
37 #define MAX_WAVEINDRV   (1)
38
39 /* state diagram for waveOut writing:
40  *
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  * +---------+-------------+---------------+---------------------------------+
54  */
55
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
61
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)
68
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 */
78
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 */
82
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  */
86
87     /* synchronization stuff */
88     HANDLE                      hThread;
89     DWORD                       dwThreadID;
90     HANDLE                      hEvent;
91
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;
95
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;
106
107 static WINE_WAVEOUT     WOutDev   [MAX_WAVEOUTDRV];
108 static WINE_WAVEIN      WInDev    [MAX_WAVEOUTDRV];
109
110 /*======================================================================*
111  *                  Low level WAVE implemantation                       *
112  *======================================================================*/
113
114 /**************************************************************************
115  *                      WAVE_NotifyClient                       [internal]
116  */
117 static DWORD WAVE_NotifyClient(UINT16 wDevID, WORD wMsg, 
118                                DWORD dwParam1, DWORD dwParam2)
119 {
120     TRACE("wDevID = %04X wMsg = %d dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2);
121     
122     switch (wMsg) {
123     case WOM_OPEN:
124     case WOM_CLOSE:
125     case WOM_DONE:
126         if (wDevID > MAX_WAVEOUTDRV) return MCIERR_INTERNAL;
127         
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;
138         }
139         break;
140         
141     case WIM_OPEN:
142     case WIM_CLOSE:
143     case WIM_DATA:
144         if (wDevID > MAX_WAVEINDRV) return MCIERR_INTERNAL;
145         
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;
156         }
157         break;
158     default:
159         FIXME("Unknown CB message %u\n", wMsg);
160         break;
161     }
162     return 0;
163 }
164
165 /*======================================================================*
166  *                  Low level WAVE OUT implemantation                   *
167  *======================================================================*/
168
169 /**************************************************************************
170  *                              wodPlayer_WriteFragments        [internal]
171  *
172  * wodPlayer helper. Writes as many fragments it can to unixdev.
173  * Returns TRUE in case of buffer underrun.
174  */
175 static  BOOL    wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
176 {
177     LPWAVEHDR           lpWaveHdr;
178     LPBYTE              lpData;
179     int                 count;
180
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;
185
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 */
194             }
195             return FALSE;       /* wait a bit */
196         }
197         
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);
202         }
203         
204         lpData = ((DWORD)lpWaveHdr == lpWaveHdr->reserved) ?
205             (LPBYTE)lpWaveHdr->lpData : (LPBYTE)PTR_SEG_TO_LIN(lpWaveHdr->lpData);
206         
207         /* finish current wave hdr ? */
208         if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) { 
209             DWORD       toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
210             
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);
214             
215             if (count > 0) {
216                 LPWAVEHDR*      wh;
217                 
218                 /* move lpWaveHdr to the end of notify list */
219                 for (wh = &(wwo->lpNotifyHdr); *wh; wh = &((*wh)->lpNext));
220                     *wh = lpWaveHdr;
221
222                 wwo->lpQueueHdr = lpWaveHdr->lpNext;
223                 lpWaveHdr->lpNext = 0;
224                 
225                 wwo->dwOffCurrHdr = 0;
226                 if ((wwo->dwRemain -= count) == 0) {
227                     wwo->dwRemain = wwo->dwFragmentSize;
228                     wwo->wFragsUsedInQueue++;
229                 }
230             }
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++;
239             }
240         }
241     }
242 }
243
244 /**************************************************************************
245  *                              wodPlayer_WriteFragments        [internal]
246  *
247  * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
248  * have been played (actually to speaker, not to unixdev fd).
249  */
250 static  void    wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
251 {
252     LPWAVEHDR           lpWaveHdr;
253     count_info          cinfo;
254     
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);
262         }
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;
266     }
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;
272             
273             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
274             lpWaveHdr->dwFlags |= WHDR_DONE;
275             if (!force)
276                 wwo->dwNotifiedBytes += lpWaveHdr->dwBufferLength;
277             wwo->lpNotifyHdr = lpWaveHdr->lpNext;
278             
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");
282             }
283         }
284     }
285 }
286
287 /**************************************************************************
288  *                              wodPlayer_Reset                 [internal]
289  *
290  * wodPlayer helper. Resets current output stream.
291  */
292 static  void    wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
293 {
294     LPWAVEHDR           lpWaveHdr;
295
296     /* updates current notify list */
297     wodPlayer_Notify(wwo, uDevID, FALSE);
298
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);
305     }
306
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");
313         }
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;
318             
319             if (WAVE_NotifyClient(uDevID, WOM_DONE, lpWaveHdr->reserved, 0) != MMSYSERR_NOERROR) {
320                 WARN("can't notify client !\n");
321             }
322         }
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;
333         }
334         wwo->state = WINE_WS_PAUSED;
335         wwo->dwPlayedTotal += wwo->dwPlayed;
336     }
337     wwo->dwNotifiedBytes = wwo->dwPlayed = 0;
338     wwo->wFragsUsedInQueue = 0;
339 }
340
341 /**************************************************************************
342  *                              wodPlayer                       [internal]
343  */
344 static  DWORD   CALLBACK        wodPlayer(LPVOID pmt)
345 {
346     WORD                uDevID = (DWORD)pmt;
347     WINE_WAVEOUT*       wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
348     WAVEHDR*            lpWaveHdr;
349     DWORD               dwSleepTime;
350     MSG                 msg;
351
352     PeekMessageA(&msg, 0, 0, 0, 0);
353     wwo->state = WINE_WS_STOPPED;
354
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;
363
364     TRACE("imhere[0]\n");
365     SetEvent(wwo->hEvent);
366
367     /* make sleep time to be # of ms to output a fragment */
368     dwSleepTime = (wwo->dwFragmentSize * 1000) / wwo->format.wf.nAvgBytesPerSec;
369
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
374          */
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;
396                 
397                 lpWaveHdr->dwFlags &= ~WHDR_DONE;
398                 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
399                 lpWaveHdr->lpNext = 0;
400                 
401                 /* insert buffer at the end of queue */
402                 {
403                     LPWAVEHDR*  wh;
404                     for (wh = &(wwo->lpQueueHdr); *wh; wh = &((*wh)->lpNext));
405                     *wh = lpWaveHdr;
406                 }
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");
418                 }
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;
427             }
428         }
429         if (wwo->state == WINE_WS_PLAYING) {
430             wodPlayer_WriteFragments(wwo);
431         }
432         wodPlayer_Notify(wwo, uDevID, FALSE);
433     }
434     ExitThread(0);
435     /* just for not generating compilation warnings... should never be executed */
436     return 0; 
437 }
438
439 /**************************************************************************
440  *                      wodGetDevCaps                           [internal]
441  */
442 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPS16 lpCaps, DWORD dwSize)
443 {
444     int         audio;
445     int         smplrate;
446     int         samplesize = 16;
447     int         dsp_stereo = 1;
448     int         bytespersmpl;
449     int         caps;
450     int         mask;
451     
452     TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
453     
454     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
455     if (access(SOUND_DEV,0) != 0) return MMSYSERR_NOTENABLED;
456     
457     if (wDevID >= MAX_WAVEOUTDRV) {
458         TRACE("MAX_WAVOUTDRV reached !\n");
459         return MMSYSERR_BADDEVICEID;
460     }
461     
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;
467     }
468
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)
472      */
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;
486     
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);
492
493     /* First bytespersampl, then stereo */
494     bytespersmpl = (IOCTL(audio, SNDCTL_DSP_SAMPLESIZE, samplesize) != 0) ? 1 : 2;
495     
496     lpCaps->wChannels = (IOCTL(audio, SNDCTL_DSP_STEREO, dsp_stereo) != 0) ? 1 : 2;
497     if (lpCaps->wChannels > 1) lpCaps->dwSupport |= WAVECAPS_LRVOLUME;
498     
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;
508         }
509     }
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;
519         }
520     }
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;
530         }
531     }
532     if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) {
533         if ((caps & DSP_CAP_REALTIME) && !(caps && DSP_CAP_BATCH))
534             lpCaps->dwFormats |= WAVECAPS_SAMPLEACCURATE;
535     }
536     if (WOutDev[wDevID].unixdev == 0) {
537         close(audio);
538     }
539     TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
540     return MMSYSERR_NOERROR;
541 }
542
543 /**************************************************************************
544  *                              wodOpen                         [internal]
545  */
546 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
547 {
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;
555
556     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
557     if (lpDesc == NULL) {
558         WARN("Invalid Parameter !\n");
559         return MMSYSERR_INVALPARAM;
560     }
561     if (wDevID >= MAX_WAVEOUTDRV) {
562         TRACE("MAX_WAVOUTDRV reached !\n");
563         return MMSYSERR_BADDEVICEID;
564     }
565     wodGetDevCaps(wDevID, &woc, sizeof(woc));
566
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;
575     }
576
577     if (dwFlags & WAVE_FORMAT_QUERY)
578         return MMSYSERR_NOERROR;
579
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 ;
587     }
588
589     WOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
590     
591     memcpy(&WOutDev[wDevID].waveDesc, lpDesc,           sizeof(WAVEOPENDESC));
592     memcpy(&WOutDev[wDevID].format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
593     
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;
599     }
600     
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
603      */
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;
609
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);
615
616     /* even if we set fragment size above, read it again, just in case */
617     IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
618
619     WOutDev[wDevID].unixdev = audio;
620     WOutDev[wDevID].dwFragmentSize = fragment_size;
621     WOutDev[wDevID].wMaxFragments = HIWORD(audio_fragment) + 1;
622
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);
626
627     TRACE("fd=%d fragmentSize=%ld\n", 
628           WOutDev[wDevID].unixdev, WOutDev[wDevID].dwFragmentSize);
629
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);
633     
634     if (WAVE_NotifyClient(wDevID, WOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) {
635         WARN("can't notify client !\n");
636         return MMSYSERR_INVALPARAM;
637     }
638     return MMSYSERR_NOERROR;
639 }
640
641 /**************************************************************************
642  *                              wodClose                        [internal]
643  */
644 static DWORD wodClose(WORD wDevID)
645 {
646     DWORD       ret = MMSYSERR_NOERROR;
647     
648     TRACE("(%u);\n", wDevID);
649     
650     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
651         WARN("bad device ID !\n");
652         return MMSYSERR_BADDEVICEID;
653     }
654     
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);
663
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;
670         }
671     }
672     return ret;
673 }
674
675 /**************************************************************************
676  *                              wodWrite                        [internal]
677  * 
678  */
679 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
680 {
681     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
682     
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;
687     }
688     
689     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED)) 
690         return WAVERR_UNPREPARED;
691     
692     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) 
693         return WAVERR_STILLPLAYING;
694
695     TRACE("imhere[3-HEADER]\n");
696     PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_HEADER, 0, (DWORD)lpWaveHdr);
697
698     return MMSYSERR_NOERROR;
699 }
700
701 /**************************************************************************
702  *                              wodPrepare                      [internal]
703  */
704 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
705 {
706     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
707     
708     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
709         WARN("bad device ID !\n");
710         return MMSYSERR_BADDEVICEID;
711     }
712     
713     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
714         return WAVERR_STILLPLAYING;
715     
716     lpWaveHdr->dwFlags |= WHDR_PREPARED;
717     lpWaveHdr->dwFlags &= ~WHDR_DONE;
718     return MMSYSERR_NOERROR;
719 }
720
721 /**************************************************************************
722  *                              wodUnprepare                    [internal]
723  */
724 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
725 {
726     TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
727     
728     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
729         WARN("bad device ID !\n");
730         return MMSYSERR_BADDEVICEID;
731     }
732     
733     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
734         return WAVERR_STILLPLAYING;
735     
736     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
737     lpWaveHdr->dwFlags |= WHDR_DONE;
738     
739     return MMSYSERR_NOERROR;
740 }
741
742 /**************************************************************************
743  *                      wodPause                                [internal]
744  */
745 static DWORD wodPause(WORD wDevID)
746 {
747     TRACE("(%u);!\n", wDevID);
748     
749     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
750         WARN("bad device ID !\n");
751         return MMSYSERR_BADDEVICEID;
752     }
753     
754     TRACE("imhere[3-PAUSING]\n");
755     PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_PAUSING, 0, 0);
756     WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
757     
758     return MMSYSERR_NOERROR;
759 }
760
761 /**************************************************************************
762  *                      wodRestart                              [internal]
763  */
764 static DWORD wodRestart(WORD wDevID)
765 {
766     TRACE("(%u);\n", wDevID);
767     
768     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
769         WARN("bad device ID !\n");
770         return MMSYSERR_BADDEVICEID;
771     }
772     
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);
777     }
778     
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;
784        }
785     */
786     
787     return MMSYSERR_NOERROR;
788 }
789
790 /**************************************************************************
791  *                      wodReset                                [internal]
792  */
793 static DWORD wodReset(WORD wDevID)
794 {
795     TRACE("(%u);\n", wDevID);
796     
797     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
798         WARN("bad device ID !\n");
799         return MMSYSERR_BADDEVICEID;
800     }
801     
802     TRACE("imhere[3-RESET]\n");
803     PostThreadMessageA(WOutDev[wDevID].dwThreadID, WINE_WM_RESETTING, 0, 0);
804     WaitForSingleObject(WOutDev[wDevID].hEvent, INFINITE);
805     
806     return MMSYSERR_NOERROR;
807 }
808
809
810 /**************************************************************************
811  *                              wodGetPosition                  [internal]
812  */
813 static DWORD wodGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
814 {
815     int         time;
816     DWORD       val;
817
818     TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
819     
820     if (wDevID > MAX_WAVEOUTDRV || WOutDev[wDevID].unixdev == 0) {
821         WARN("bad device ID !\n");
822         return MMSYSERR_BADDEVICEID;
823     }
824     
825     if (lpTime == NULL) return MMSYSERR_INVALPARAM;
826
827     val = WOutDev[wDevID].dwPlayedTotal + WOutDev[wDevID].dwPlayed;
828
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);
834     
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;
865     }
866     return MMSYSERR_NOERROR;
867 }
868
869 /**************************************************************************
870  *                              wodGetVolume                    [internal]
871  */
872 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
873 {
874     int         mixer;
875     int         volume;
876     DWORD       left, right;
877     
878     TRACE("(%u, %p);\n", wDevID, lpdwVol);
879     
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;
885     }
886     if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
887         WARN("unable read mixer !\n");
888         return MMSYSERR_NOTENABLED;
889     }
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;
896 }
897
898
899 /**************************************************************************
900  *                              wodSetVolume                    [internal]
901  */
902 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
903 {
904     int         mixer;
905     int         volume;
906     DWORD       left, right;
907
908     TRACE("(%u, %08lX);\n", wDevID, dwParam);
909
910     left  = (LOWORD(dwParam) * 100) / 0xFFFFl;
911     right = (HIWORD(dwParam) * 100) / 0xFFFFl;
912     volume = left + (right << 8);
913     
914     if ((mixer = open(MIXER_DEV, O_WRONLY)) < 0) {
915         WARN("mixer device not available !\n");
916         return MMSYSERR_NOTENABLED;
917     }
918     if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
919         WARN("unable set mixer !\n");
920         return MMSYSERR_NOTENABLED;
921     }
922     close(mixer);
923     return MMSYSERR_NOERROR;
924 }
925
926 /**************************************************************************
927  *                              wodGetNumDevs                   [internal]
928  */
929 static  DWORD   wodGetNumDevs(void)
930 {
931     DWORD       ret = 1;
932     
933     /* FIXME: For now, only one sound device (SOUND_DEV) is allowed */
934     int audio = open(SOUND_DEV, O_WRONLY, 0);
935     
936     if (audio == -1) {
937         if (errno != EBUSY)
938             ret = 0;
939     } else {
940         close(audio);
941     }
942     return ret;
943 }
944
945 /**************************************************************************
946  *                              wodMessage                      [sample driver]
947  */
948 DWORD WINAPI wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
949                         DWORD dwParam1, DWORD dwParam2)
950 {
951     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
952           wDevID, wMsg, dwUser, dwParam1, dwParam2);
953     
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);
976     }
977     return MMSYSERR_NOTSUPPORTED;
978 }
979
980 /*======================================================================*
981  *                  Low level WAVE IN implemantation                    *
982  *======================================================================*/
983
984 /**************************************************************************
985  *                      widGetDevCaps                           [internal]
986  */
987 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPS16 lpCaps, DWORD dwSize)
988 {
989     int         audio,smplrate,samplesize=16,dsp_stereo=1,bytespersmpl;
990     
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;
1017         }
1018     }
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;
1028         }
1029     }
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;
1039         }
1040     }
1041     close(audio);
1042     TRACE("dwFormats = %08lX\n", lpCaps->dwFormats);
1043     return MMSYSERR_NOERROR;
1044 }
1045
1046 /**************************************************************************
1047  *                              widOpen                         [internal]
1048  */
1049 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1050 {
1051     int                 audio,abuf_size,smplrate,samplesize,dsp_stereo;
1052     LPWAVEFORMAT        lpFormat;
1053     
1054     TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1055     if (lpDesc == NULL) {
1056         WARN("Invalid Parameter !\n");
1057         return MMSYSERR_INVALPARAM;
1058     }
1059     if (wDevID >= MAX_WAVEINDRV) {
1060         TRACE("MAX_WAVINDRV reached !\n");
1061         return MMSYSERR_ALLOCATED;
1062     }
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;
1069     }
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;
1077     }
1078     WInDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1079
1080     if (WInDev[wDevID].lpQueueHdr) {
1081         HeapFree(GetProcessHeap(), 0, WInDev[wDevID].lpQueueHdr);
1082         WInDev[wDevID].lpQueueHdr = NULL;
1083     }
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;
1093     }
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;
1103     }
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;
1117     }
1118     return MMSYSERR_NOERROR;
1119 }
1120
1121 /**************************************************************************
1122  *                              widClose                        [internal]
1123  */
1124 static DWORD widClose(WORD wDevID)
1125 {
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;
1131     }
1132     if (WInDev[wDevID].lpQueueHdr != NULL) {
1133         WARN("still buffers open !\n");
1134         return WAVERR_STILLPLAYING;
1135     }
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;
1142     }
1143     return MMSYSERR_NOERROR;
1144 }
1145
1146 /**************************************************************************
1147  *                              widAddBuffer            [internal]
1148  */
1149 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1150 {
1151     int         count   = 1;
1152     LPWAVEHDR   lpWIHdr;
1153     
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;
1158     }
1159     if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1160         TRACE("never been prepared !\n");
1161         return WAVERR_UNPREPARED;
1162     }
1163     if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1164         TRACE("header already in use !\n");
1165         return WAVERR_STILLPLAYING;
1166     }
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++;
1178         }
1179         lpWIHdr->lpNext = lpWaveHdr;
1180         lpWaveHdr->lpNext = NULL;
1181         count++;
1182     }
1183     TRACE("buffer added ! (now %u in queue)\n", count);
1184     return MMSYSERR_NOERROR;
1185 }
1186
1187 /**************************************************************************
1188  *                              widPrepare                      [internal]
1189  */
1190 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1191 {
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;
1196     }
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;
1205 }
1206
1207 /**************************************************************************
1208  *                              widUnprepare                    [internal]
1209  */
1210 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1211 {
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;
1216     }
1217     lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1218     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1219     lpWaveHdr->dwFlags |= WHDR_DONE;
1220     
1221     return MMSYSERR_NOERROR;
1222 }
1223
1224 /**************************************************************************
1225  *                      widStart                                [internal]
1226  */
1227 static DWORD widStart(WORD wDevID)
1228 {
1229     int         count   = 1;
1230     int         bytesRead;
1231     LPWAVEHDR   *lpWaveHdr;
1232     LPBYTE      lpData;
1233
1234     TRACE("(%u);\n", wDevID);
1235     if (WInDev[wDevID].unixdev == 0) {
1236         WARN("can't start recording !\n");
1237         return MMSYSERR_NOTENABLED;
1238     }
1239     
1240     lpWaveHdr = &(WInDev[wDevID].lpQueueHdr);
1241     TRACE("lpWaveHdr = %08lx\n",(DWORD)lpWaveHdr);
1242
1243     if (!*lpWaveHdr || !(*lpWaveHdr)->lpData) {
1244         TRACE("never been prepared !\n");
1245         return WAVERR_UNPREPARED;
1246     }
1247     
1248     while (*lpWaveHdr != NULL) {
1249
1250         lpData = ((DWORD)*lpWaveHdr == (*lpWaveHdr)->reserved) ?
1251             (LPBYTE)(*lpWaveHdr)->lpData : (LPBYTE)PTR_SEG_TO_LIN((*lpWaveHdr)->lpData);
1252
1253         TRACE("recording buf#%u=%p size=%lu \n",
1254               count, lpData, (*lpWaveHdr)->dwBufferLength);
1255
1256         bytesRead = read(WInDev[wDevID].unixdev, lpData, (*lpWaveHdr)->dwBufferLength);
1257
1258         if (bytesRead == -1)
1259             perror("read from audio device");
1260
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;
1266         
1267         if (WAVE_NotifyClient(wDevID, WIM_DATA, (*lpWaveHdr)->reserved, (*lpWaveHdr)->dwBytesRecorded) != MMSYSERR_NOERROR) {
1268             WARN("can't notify client !\n");
1269             return MMSYSERR_INVALPARAM;
1270         }
1271         /* removes the current block from the queue */
1272         *lpWaveHdr = (*lpWaveHdr)->lpNext;
1273         count++;
1274     }
1275     TRACE("end of recording !\n");
1276     return MMSYSERR_NOERROR;
1277 }
1278
1279 /**************************************************************************
1280  *                      widStop                                 [internal]
1281  */
1282 static DWORD widStop(WORD wDevID)
1283 {
1284     TRACE("(%u);\n", wDevID);
1285     if (WInDev[wDevID].unixdev == 0) {
1286         WARN("can't stop !\n");
1287         return MMSYSERR_NOTENABLED;
1288     }
1289     return MMSYSERR_NOERROR;
1290 }
1291
1292 /**************************************************************************
1293  *                      widReset                                [internal]
1294  */
1295 static DWORD widReset(WORD wDevID)
1296 {
1297     TRACE("(%u);\n", wDevID);
1298     if (WInDev[wDevID].unixdev == 0) {
1299         WARN("can't reset !\n");
1300         return MMSYSERR_NOTENABLED;
1301     }
1302     return MMSYSERR_NOERROR;
1303 }
1304
1305 /**************************************************************************
1306  *                              widGetPosition                  [internal]
1307  */
1308 static DWORD widGetPosition(WORD wDevID, LPMMTIME16 lpTime, DWORD uSize)
1309 {
1310     int         time;
1311     
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;
1316     }
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;
1356     }
1357     return MMSYSERR_NOERROR;
1358 }
1359
1360 /**************************************************************************
1361  *                              widMessage                      [sample driver]
1362  */
1363 DWORD WINAPI widMessage(WORD wDevID, WORD wMsg, DWORD dwUser, 
1364                         DWORD dwParam1, DWORD dwParam2)
1365 {
1366     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1367           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1368
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);
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 */