ntdll: Added parsing of the external proxy element in manifests.
[wine] / dlls / winealsa.drv / wavein.c
1 /*
2  * Sample Wine Driver for Advanced Linux Sound System (ALSA)
3  *      Based on version <final> of the ALSA API
4  *
5  * Copyright    2002 Eric Pouech
6  *              2002 Marco Pietrobono
7  *              2003 Christian Costa : WaveIn support
8  *              2006-2007 Maarten Lankhorst
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24
25 /*======================================================================*
26  *                  Low level WAVE IN implementation                    *
27  *======================================================================*/
28
29 #include "config.h"
30 #include "wine/port.h"
31
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <string.h>
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif
39 #include <errno.h>
40 #include <limits.h>
41 #include <fcntl.h>
42 #ifdef HAVE_SYS_IOCTL_H
43 # include <sys/ioctl.h>
44 #endif
45 #ifdef HAVE_SYS_MMAN_H
46 # include <sys/mman.h>
47 #endif
48 #include "windef.h"
49 #include "winbase.h"
50 #include "wingdi.h"
51 #include "winuser.h"
52 #include "winnls.h"
53 #include "mmddk.h"
54
55 #include "alsa.h"
56 #include "wine/library.h"
57 #include "wine/unicode.h"
58 #include "wine/debug.h"
59
60 WINE_DEFAULT_DEBUG_CHANNEL(wave);
61
62 #ifdef HAVE_ALSA
63
64 WINE_WAVEDEV    *WInDev;
65 DWORD            ALSA_WidNumMallocedDevs;
66 DWORD            ALSA_WidNumDevs;
67
68 /**************************************************************************
69 *                       widNotifyClient                 [internal]
70 */
71 static DWORD widNotifyClient(WINE_WAVEDEV* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
72 {
73    TRACE("wMsg = 0x%04x dwParm1 = %04X dwParam2 = %04X\n", wMsg, dwParam1, dwParam2);
74
75    switch (wMsg) {
76    case WIM_OPEN:
77    case WIM_CLOSE:
78    case WIM_DATA:
79        if (wwi->wFlags != DCB_NULL &&
80            !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags, (HDRVR)wwi->waveDesc.hWave,
81                            wMsg, wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
82            WARN("can't notify client !\n");
83            return MMSYSERR_ERROR;
84        }
85        break;
86    default:
87        FIXME("Unknown callback message %u\n", wMsg);
88        return MMSYSERR_INVALPARAM;
89    }
90    return MMSYSERR_NOERROR;
91 }
92
93 /**************************************************************************
94  *                      widGetDevCaps                           [internal]
95  */
96 static DWORD widGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
97 {
98     TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
99
100     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
101
102     if (wDevID >= ALSA_WidNumDevs) {
103         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
104         return MMSYSERR_BADDEVICEID;
105     }
106
107     memcpy(lpCaps, &WInDev[wDevID].incaps, min(dwSize, sizeof(*lpCaps)));
108     return MMSYSERR_NOERROR;
109 }
110
111 /**************************************************************************
112  *                              widRecorder_ReadHeaders         [internal]
113  */
114 static void widRecorder_ReadHeaders(WINE_WAVEDEV * wwi)
115 {
116     enum win_wm_message tmp_msg;
117     DWORD               tmp_param;
118     HANDLE              tmp_ev;
119     WAVEHDR*            lpWaveHdr;
120
121     while (ALSA_RetrieveRingMessage(&wwi->msgRing, &tmp_msg, &tmp_param, &tmp_ev)) {
122         if (tmp_msg == WINE_WM_HEADER) {
123             LPWAVEHDR*  wh;
124             lpWaveHdr = (LPWAVEHDR)tmp_param;
125             lpWaveHdr->lpNext = 0;
126
127             if (wwi->lpQueuePtr == 0)
128                 wwi->lpQueuePtr = lpWaveHdr;
129             else {
130                 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
131                 *wh = lpWaveHdr;
132             }
133         } else {
134             ERR("should only have headers left\n");
135         }
136     }
137 }
138
139 /**************************************************************************
140  *                              widRecorder                     [internal]
141  */
142 static  DWORD   CALLBACK        widRecorder(LPVOID pmt)
143 {
144     WORD                uDevID = (DWORD)pmt;
145     WINE_WAVEDEV*       wwi = (WINE_WAVEDEV*)&WInDev[uDevID];
146     WAVEHDR*            lpWaveHdr;
147     DWORD               dwSleepTime;
148     DWORD               bytesRead;
149     LPVOID              buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, wwi->dwPeriodSize);
150     char               *pOffset = buffer;
151     enum win_wm_message msg;
152     DWORD               param;
153     HANDLE              ev;
154     DWORD               frames_per_period;
155
156     wwi->state = WINE_WS_STOPPED;
157     wwi->dwTotalRecorded = 0;
158     wwi->lpQueuePtr = NULL;
159
160     SetEvent(wwi->hStartUpEvent);
161
162     /* make sleep time to be # of ms to output a period */
163     dwSleepTime = (1024/*wwi-dwPeriodSize => overrun!*/ * 1000) / wwi->format.Format.nAvgBytesPerSec;
164     frames_per_period = snd_pcm_bytes_to_frames(wwi->pcm, wwi->dwPeriodSize);
165     TRACE("sleeptime=%d ms\n", dwSleepTime);
166
167     for (;;) {
168         /* wait for dwSleepTime or an event in thread's queue */
169         /* FIXME: could improve wait time depending on queue state,
170          * ie, number of queued fragments
171          */
172         if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING)
173         {
174             int periods;
175             DWORD frames;
176             DWORD bytes;
177             DWORD read;
178
179             lpWaveHdr = wwi->lpQueuePtr;
180             /* read all the fragments accumulated so far */
181             frames = snd_pcm_avail_update(wwi->pcm);
182             bytes = snd_pcm_frames_to_bytes(wwi->pcm, frames);
183             TRACE("frames = %d  bytes = %d\n", frames, bytes);
184             periods = bytes / wwi->dwPeriodSize;
185             while ((periods > 0) && (wwi->lpQueuePtr))
186             {
187                 periods--;
188                 bytes = wwi->dwPeriodSize;
189                 TRACE("bytes = %d\n",bytes);
190                 if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded >= wwi->dwPeriodSize)
191                 {
192                     /* directly read fragment in wavehdr */
193                     read = wwi->read(wwi->pcm, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, frames_per_period);
194                     bytesRead = snd_pcm_frames_to_bytes(wwi->pcm, read);
195
196                     TRACE("bytesRead=%d (direct)\n", bytesRead);
197                     if (bytesRead != (DWORD) -1)
198                     {
199                         /* update number of bytes recorded in current buffer and by this device */
200                         lpWaveHdr->dwBytesRecorded += bytesRead;
201                         wwi->dwTotalRecorded       += bytesRead;
202
203                         /* buffer is full. notify client */
204                         if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
205                         {
206                             /* must copy the value of next waveHdr, because we have no idea of what
207                              * will be done with the content of lpWaveHdr in callback
208                              */
209                             LPWAVEHDR   lpNext = lpWaveHdr->lpNext;
210
211                             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
212                             lpWaveHdr->dwFlags |=  WHDR_DONE;
213
214                             wwi->lpQueuePtr = lpNext;
215                             widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
216                             lpWaveHdr = lpNext;
217                         }
218                     } else {
219                         TRACE("read(%s, %p, %d) failed (%s)\n", wwi->pcmname,
220                             lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
221                             frames_per_period, strerror(errno));
222                     }
223                 }
224                 else
225                 {
226                     /* read the fragment in a local buffer */
227                     read = wwi->read(wwi->pcm, buffer, frames_per_period);
228                     bytesRead = snd_pcm_frames_to_bytes(wwi->pcm, read);
229                     pOffset = buffer;
230
231                     TRACE("bytesRead=%d (local)\n", bytesRead);
232
233                     if (bytesRead == (DWORD) -1) {
234                         TRACE("read(%s, %p, %d) failed (%s)\n", wwi->pcmname,
235                               buffer, frames_per_period, strerror(errno));
236                         continue;
237                     }
238
239                     /* copy data in client buffers */
240                     while (bytesRead != (DWORD) -1 && bytesRead > 0)
241                     {
242                         DWORD dwToCopy = min (bytesRead, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
243
244                         memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
245                                pOffset,
246                                dwToCopy);
247
248                         /* update number of bytes recorded in current buffer and by this device */
249                         lpWaveHdr->dwBytesRecorded += dwToCopy;
250                         wwi->dwTotalRecorded += dwToCopy;
251                         bytesRead -= dwToCopy;
252                         pOffset   += dwToCopy;
253
254                         /* client buffer is full. notify client */
255                         if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
256                         {
257                             /* must copy the value of next waveHdr, because we have no idea of what
258                              * will be done with the content of lpWaveHdr in callback
259                              */
260                             LPWAVEHDR   lpNext = lpWaveHdr->lpNext;
261                             TRACE("lpNext=%p\n", lpNext);
262
263                             lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
264                             lpWaveHdr->dwFlags |=  WHDR_DONE;
265
266                             wwi->lpQueuePtr = lpNext;
267                             widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
268
269                             lpWaveHdr = lpNext;
270                             if (!lpNext && bytesRead) {
271                                 /* before we give up, check for more header messages */
272                                 while (ALSA_PeekRingMessage(&wwi->msgRing, &msg, &param, &ev))
273                                 {
274                                     if (msg == WINE_WM_HEADER) {
275                                         LPWAVEHDR hdr;
276                                         ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev);
277                                         hdr = ((LPWAVEHDR)param);
278                                         TRACE("msg = %s, hdr = %p, ev = %p\n", ALSA_getCmdString(msg), hdr, ev);
279                                         hdr->lpNext = 0;
280                                         if (lpWaveHdr == 0) {
281                                             /* new head of queue */
282                                             wwi->lpQueuePtr = lpWaveHdr = hdr;
283                                         } else {
284                                             /* insert buffer at the end of queue */
285                                             LPWAVEHDR*  wh;
286                                             for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
287                                             *wh = hdr;
288                                         }
289                                     } else
290                                         break;
291                                 }
292
293                                 if (lpWaveHdr == 0) {
294                                     /* no more buffer to copy data to, but we did read more.
295                                      * what hasn't been copied will be dropped
296                                      */
297                                     WARN("buffer under run! %u bytes dropped.\n", bytesRead);
298                                     wwi->lpQueuePtr = NULL;
299                                     break;
300                                 }
301                             }
302                         }
303                     }
304                 }
305             }
306         }
307
308         ALSA_WaitRingMessage(&wwi->msgRing, dwSleepTime);
309
310         while (ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev))
311         {
312             TRACE("msg=%s param=0x%x\n", ALSA_getCmdString(msg), param);
313             switch (msg) {
314             case WINE_WM_PAUSING:
315                 wwi->state = WINE_WS_PAUSED;
316                 /*FIXME("Device should stop recording\n");*/
317                 SetEvent(ev);
318                 break;
319             case WINE_WM_STARTING:
320                 wwi->state = WINE_WS_PLAYING;
321                 snd_pcm_start(wwi->pcm);
322                 SetEvent(ev);
323                 break;
324             case WINE_WM_HEADER:
325                 lpWaveHdr = (LPWAVEHDR)param;
326                 lpWaveHdr->lpNext = 0;
327
328                 /* insert buffer at the end of queue */
329                 {
330                     LPWAVEHDR*  wh;
331                     for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
332                     *wh = lpWaveHdr;
333                 }
334                 break;
335             case WINE_WM_STOPPING:
336                 if (wwi->state != WINE_WS_STOPPED)
337                 {
338                     snd_pcm_drain(wwi->pcm);
339
340                     /* read any headers in queue */
341                     widRecorder_ReadHeaders(wwi);
342
343                     /* return current buffer to app */
344                     lpWaveHdr = wwi->lpQueuePtr;
345                     if (lpWaveHdr)
346                     {
347                         LPWAVEHDR       lpNext = lpWaveHdr->lpNext;
348                         TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
349                         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
350                         lpWaveHdr->dwFlags |= WHDR_DONE;
351                         wwi->lpQueuePtr = lpNext;
352                         widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
353                     }
354                 }
355                 wwi->state = WINE_WS_STOPPED;
356                 SetEvent(ev);
357                 break;
358             case WINE_WM_RESETTING:
359                 if (wwi->state != WINE_WS_STOPPED)
360                 {
361                     snd_pcm_drain(wwi->pcm);
362                 }
363                 wwi->state = WINE_WS_STOPPED;
364                 wwi->dwTotalRecorded = 0;
365
366                 /* read any headers in queue */
367                 widRecorder_ReadHeaders(wwi);
368
369                 /* return all buffers to the app */
370                 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
371                     TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
372                     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
373                     lpWaveHdr->dwFlags |= WHDR_DONE;
374                     wwi->lpQueuePtr = lpWaveHdr->lpNext;
375                     widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
376                 }
377
378                 wwi->lpQueuePtr = NULL;
379                 SetEvent(ev);
380                 break;
381             case WINE_WM_CLOSING:
382                 wwi->hThread = 0;
383                 wwi->state = WINE_WS_CLOSED;
384                 SetEvent(ev);
385                 HeapFree(GetProcessHeap(), 0, buffer);
386                 ExitThread(0);
387                 /* shouldn't go here */
388             case WINE_WM_UPDATE:
389                 SetEvent(ev);
390                 break;
391
392             default:
393                 FIXME("unknown message %d\n", msg);
394                 break;
395             }
396         }
397     }
398     ExitThread(0);
399     /* just for not generating compilation warnings... should never be executed */
400     return 0;
401 }
402
403 /**************************************************************************
404  *                              widOpen                         [internal]
405  */
406 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
407 {
408     WINE_WAVEDEV*               wwi;
409     snd_pcm_hw_params_t *       hw_params;
410     snd_pcm_sw_params_t *       sw_params;
411     snd_pcm_access_t            access;
412     snd_pcm_format_t            format;
413     unsigned int                rate;
414     unsigned int                buffer_time = 500000;
415     unsigned int                period_time = 10000;
416     snd_pcm_uframes_t           buffer_size;
417     snd_pcm_uframes_t           period_size;
418     int                         flags;
419     snd_pcm_t *                 pcm;
420     int                         err;
421     int                         dir;
422
423     snd_pcm_hw_params_alloca(&hw_params);
424     snd_pcm_sw_params_alloca(&sw_params);
425
426     /* JPW TODO - review this code */
427     TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
428     if (lpDesc == NULL) {
429         WARN("Invalid Parameter !\n");
430         return MMSYSERR_INVALPARAM;
431     }
432     if (wDevID >= ALSA_WidNumDevs) {
433         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
434         return MMSYSERR_BADDEVICEID;
435     }
436
437     /* only PCM format is supported so far... */
438     if (!ALSA_supportedFormat(lpDesc->lpFormat)) {
439         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
440              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
441              lpDesc->lpFormat->nSamplesPerSec);
442         return WAVERR_BADFORMAT;
443     }
444
445     if (dwFlags & WAVE_FORMAT_QUERY) {
446         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
447              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
448              lpDesc->lpFormat->nSamplesPerSec);
449         return MMSYSERR_NOERROR;
450     }
451
452     wwi = &WInDev[wDevID];
453
454     if (wwi->pcm != NULL) {
455         WARN("already allocated\n");
456         return MMSYSERR_ALLOCATED;
457     }
458
459     if ((dwFlags & WAVE_DIRECTSOUND) && !(wwi->dwSupport & WAVECAPS_DIRECTSOUND))
460         /* not supported, ignore it */
461         dwFlags &= ~WAVE_DIRECTSOUND;
462
463     wwi->pcm = 0;
464     flags = SND_PCM_NONBLOCK;
465
466     if ( (err=snd_pcm_open(&pcm, wwi->pcmname, SND_PCM_STREAM_CAPTURE, flags)) < 0 )
467     {
468         ERR("Error open: %s\n", snd_strerror(err));
469         return MMSYSERR_NOTENABLED;
470     }
471
472     wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
473
474     memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
475     ALSA_copyFormat(lpDesc->lpFormat, &wwi->format);
476
477     if (wwi->format.Format.wBitsPerSample == 0) {
478         WARN("Resetting zeroed wBitsPerSample\n");
479         wwi->format.Format.wBitsPerSample = 8 *
480             (wwi->format.Format.nAvgBytesPerSec /
481              wwi->format.Format.nSamplesPerSec) /
482             wwi->format.Format.nChannels;
483     }
484
485     snd_pcm_hw_params_any(pcm, hw_params);
486
487 #define EXIT_ON_ERROR(f,e,txt) do \
488 { \
489     int err; \
490     if ( (err = (f) ) < 0) \
491     { \
492         WARN(txt ": %s\n", snd_strerror(err)); \
493         snd_pcm_close(pcm); \
494         return e; \
495     } \
496 } while(0)
497
498     access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
499     if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {
500         WARN("mmap not available. switching to standard write.\n");
501         access = SND_PCM_ACCESS_RW_INTERLEAVED;
502         EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");
503         wwi->read = snd_pcm_readi;
504     }
505     else
506         wwi->read = snd_pcm_mmap_readi;
507
508     EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwi->format.Format.nChannels), WAVERR_BADFORMAT, "unable to set required channels");
509
510     if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_PCM) ||
511         ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
512         IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) {
513         format = (wwi->format.Format.wBitsPerSample == 8) ? SND_PCM_FORMAT_U8 :
514                  (wwi->format.Format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE :
515                  (wwi->format.Format.wBitsPerSample == 24) ? SND_PCM_FORMAT_S24_LE :
516                  (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_S32_LE : -1;
517     } else if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
518         IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)){
519         format = (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_FLOAT_LE : -1;
520     } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_MULAW) {
521         FIXME("unimplemented format: WAVE_FORMAT_MULAW\n");
522         snd_pcm_close(pcm);
523         return WAVERR_BADFORMAT;
524     } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ALAW) {
525         FIXME("unimplemented format: WAVE_FORMAT_ALAW\n");
526         snd_pcm_close(pcm);
527         return WAVERR_BADFORMAT;
528     } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ADPCM) {
529         FIXME("unimplemented format: WAVE_FORMAT_ADPCM\n");
530         snd_pcm_close(pcm);
531         return WAVERR_BADFORMAT;
532     } else {
533         ERR("invalid format: %0x04x\n", wwi->format.Format.wFormatTag);
534         snd_pcm_close(pcm);
535         return WAVERR_BADFORMAT;
536     }
537
538     EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), WAVERR_BADFORMAT, "unable to set required format");
539
540     rate = wwi->format.Format.nSamplesPerSec;
541     dir = 0;
542     err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir);
543     if (err < 0) {
544         WARN("Rate %d Hz not available for playback: %s\n", wwi->format.Format.nSamplesPerSec, snd_strerror(rate));
545         snd_pcm_close(pcm);
546         return WAVERR_BADFORMAT;
547     }
548     if (!ALSA_NearMatch(rate, wwi->format.Format.nSamplesPerSec)) {
549         WARN("Rate doesn't match (requested %d Hz, got %d Hz)\n", wwi->format.Format.nSamplesPerSec, rate);
550         snd_pcm_close(pcm);
551         return WAVERR_BADFORMAT;
552     }
553
554     dir=0;
555     EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time");
556     dir=0;
557     EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time");
558
559     EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");
560
561     dir=0;
562     err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
563     err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
564
565     snd_pcm_sw_params_current(pcm, sw_params);
566     EXIT_ON_ERROR( snd_pcm_sw_params_set_start_threshold(pcm, sw_params, dwFlags & WAVE_DIRECTSOUND ? INT_MAX : 1 ), MMSYSERR_ERROR, "unable to set start threshold");
567     EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");
568     EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min");
569     EXIT_ON_ERROR( snd_pcm_sw_params_set_xfer_align(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set xfer align");
570     EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold");
571     EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback");
572 #undef EXIT_ON_ERROR
573
574     snd_pcm_prepare(pcm);
575
576     if (TRACE_ON(wave))
577         ALSA_TraceParameters(hw_params, sw_params, FALSE);
578
579     /* now, we can save all required data for later use... */
580     if ( wwi->hw_params )
581         snd_pcm_hw_params_free(wwi->hw_params);
582     snd_pcm_hw_params_malloc(&(wwi->hw_params));
583     snd_pcm_hw_params_copy(wwi->hw_params, hw_params);
584
585     wwi->dwBufferSize = snd_pcm_frames_to_bytes(pcm, buffer_size);
586     wwi->lpQueuePtr = wwi->lpPlayPtr = wwi->lpLoopPtr = NULL;
587     wwi->pcm = pcm;
588
589     ALSA_InitRingMessage(&wwi->msgRing);
590
591     wwi->dwPeriodSize = period_size;
592     /*if (wwi->dwFragmentSize % wwi->format.Format.nBlockAlign)
593         ERR("Fragment doesn't contain an integral number of data blocks\n");
594     */
595     TRACE("dwPeriodSize=%u\n", wwi->dwPeriodSize);
596     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%u, nSamplesPerSec=%u, nChannels=%u nBlockAlign=%u!\n",
597           wwi->format.Format.wBitsPerSample, wwi->format.Format.nAvgBytesPerSec,
598           wwi->format.Format.nSamplesPerSec, wwi->format.Format.nChannels,
599           wwi->format.Format.nBlockAlign);
600
601     if (!(dwFlags & WAVE_DIRECTSOUND)) {
602         wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
603         wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
604         if (wwi->hThread)
605             SetThreadPriority(wwi->hThread, THREAD_PRIORITY_TIME_CRITICAL);
606         WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
607         CloseHandle(wwi->hStartUpEvent);
608     } else {
609         wwi->hThread = INVALID_HANDLE_VALUE;
610         wwi->dwThreadID = 0;
611     }
612     wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
613
614     return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
615 }
616
617
618 /**************************************************************************
619  *                              widClose                        [internal]
620  */
621 static DWORD widClose(WORD wDevID)
622 {
623     DWORD               ret = MMSYSERR_NOERROR;
624     WINE_WAVEDEV*       wwi;
625
626     TRACE("(%u);\n", wDevID);
627
628     if (wDevID >= ALSA_WidNumDevs) {
629         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
630         return MMSYSERR_BADDEVICEID;
631     }
632
633     if (WInDev[wDevID].pcm == NULL) {
634         WARN("Requested to close already closed device %d!\n", wDevID);
635         return MMSYSERR_BADDEVICEID;
636     }
637
638     wwi = &WInDev[wDevID];
639     if (wwi->lpQueuePtr) {
640         WARN("buffers still playing !\n");
641         ret = WAVERR_STILLPLAYING;
642     } else {
643         if (wwi->hThread != INVALID_HANDLE_VALUE) {
644             ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
645         }
646         ALSA_DestroyRingMessage(&wwi->msgRing);
647
648         snd_pcm_hw_params_free(wwi->hw_params);
649         wwi->hw_params = NULL;
650
651         snd_pcm_close(wwi->pcm);
652         wwi->pcm = NULL;
653
654         ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
655     }
656
657     return ret;
658 }
659
660 /**************************************************************************
661  *                              widAddBuffer                    [internal]
662  *
663  */
664 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
665 {
666     TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
667
668     /* first, do the sanity checks... */
669     if (wDevID >= ALSA_WidNumDevs) {
670         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
671         return MMSYSERR_BADDEVICEID;
672     }
673
674     if (WInDev[wDevID].pcm == NULL) {
675         WARN("Requested to add buffer to already closed device %d!\n", wDevID);
676         return MMSYSERR_BADDEVICEID;
677     }
678
679     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
680         return WAVERR_UNPREPARED;
681
682     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
683         return WAVERR_STILLPLAYING;
684
685     lpWaveHdr->dwFlags &= ~WHDR_DONE;
686     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
687     lpWaveHdr->lpNext = 0;
688
689     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
690
691     return MMSYSERR_NOERROR;
692 }
693
694 /**************************************************************************
695  *                              widStart                        [internal]
696  *
697  */
698 static DWORD widStart(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
699 {
700     TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
701
702     /* first, do the sanity checks... */
703     if (wDevID >= ALSA_WidNumDevs) {
704         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
705         return MMSYSERR_BADDEVICEID;
706     }
707
708     if (WInDev[wDevID].pcm == NULL) {
709         WARN("Requested to start closed device %d!\n", wDevID);
710         return MMSYSERR_BADDEVICEID;
711     }
712
713     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
714
715     return MMSYSERR_NOERROR;
716 }
717
718 /**************************************************************************
719  *                              widStop                 [internal]
720  *
721  */
722 static DWORD widStop(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
723 {
724     TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
725
726     /* first, do the sanity checks... */
727     if (wDevID >= ALSA_WidNumDevs) {
728         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
729         return MMSYSERR_BADDEVICEID;
730     }
731
732     if (WInDev[wDevID].pcm == NULL) {
733         WARN("Requested to stop closed device %d!\n", wDevID);
734         return MMSYSERR_BADDEVICEID;
735     }
736
737     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
738
739     return MMSYSERR_NOERROR;
740 }
741
742 /**************************************************************************
743  *                      widReset                                [internal]
744  */
745 static DWORD widReset(WORD wDevID)
746 {
747     TRACE("(%u);\n", wDevID);
748     if (wDevID >= ALSA_WidNumDevs) {
749         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
750         return MMSYSERR_BADDEVICEID;
751     }
752
753     if (WInDev[wDevID].pcm == NULL) {
754         WARN("Requested to reset closed device %d!\n", wDevID);
755         return MMSYSERR_BADDEVICEID;
756     }
757
758     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
759     return MMSYSERR_NOERROR;
760 }
761
762 /**************************************************************************
763  *                              widGetPosition                  [internal]
764  */
765 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
766 {
767     WINE_WAVEDEV*       wwi;
768
769     TRACE("(%u, %p, %u);\n", wDevID, lpTime, uSize);
770
771     if (wDevID >= ALSA_WidNumDevs) {
772         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
773         return MMSYSERR_BADDEVICEID;
774     }
775
776     if (WInDev[wDevID].state == WINE_WS_CLOSED) {
777         WARN("Requested position of closed device %d!\n", wDevID);
778         return MMSYSERR_BADDEVICEID;
779     }
780
781     if (lpTime == NULL) {
782         WARN("invalid parameter: lpTime = NULL\n");
783         return MMSYSERR_INVALPARAM;
784     }
785
786     wwi = &WInDev[wDevID];
787     ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_UPDATE, 0, TRUE);
788
789     return ALSA_bytes_to_mmtime(lpTime, wwi->dwTotalRecorded, &wwi->format);
790 }
791
792 /**************************************************************************
793  *                              widGetNumDevs                   [internal]
794  */
795 static  DWORD   widGetNumDevs(void)
796 {
797     return ALSA_WidNumDevs;
798 }
799
800 /**************************************************************************
801  *                              widDevInterfaceSize             [internal]
802  */
803 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
804 {
805     TRACE("(%u, %p)\n", wDevID, dwParam1);
806
807     *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
808                                     NULL, 0 ) * sizeof(WCHAR);
809     return MMSYSERR_NOERROR;
810 }
811
812 /**************************************************************************
813  *                              widDevInterface                 [internal]
814  */
815 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
816 {
817     if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
818                                         NULL, 0 ) * sizeof(WCHAR))
819     {
820         MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
821                             dwParam1, dwParam2 / sizeof(WCHAR));
822         return MMSYSERR_NOERROR;
823     }
824     return MMSYSERR_INVALPARAM;
825 }
826
827 /**************************************************************************
828  *                              widDsCreate                     [internal]
829  */
830 static DWORD widDsCreate(UINT wDevID, PIDSCDRIVER* drv)
831 {
832     TRACE("(%d,%p)\n",wDevID,drv);
833
834     /* the HAL isn't much better than the HEL if we can't do mmap() */
835     FIXME("DirectSoundCapture not implemented\n");
836     FIXME("The (slower) DirectSound HEL mode will be used instead.\n");
837     return MMSYSERR_NOTSUPPORTED;
838 }
839
840 /**************************************************************************
841  *                              widDsDesc                       [internal]
842  */
843 static DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc)
844 {
845     memcpy(desc, &(WInDev[wDevID].ds_desc), sizeof(DSDRIVERDESC));
846     return MMSYSERR_NOERROR;
847 }
848
849 /**************************************************************************
850  *                              widMessage (WINEALSA.@)
851  */
852 DWORD WINAPI ALSA_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
853                              DWORD dwParam1, DWORD dwParam2)
854 {
855     TRACE("(%u, %s, %08X, %08X, %08X);\n",
856           wDevID, ALSA_getMessage(wMsg), dwUser, dwParam1, dwParam2);
857
858     switch (wMsg) {
859     case DRVM_INIT:
860     case DRVM_EXIT:
861     case DRVM_ENABLE:
862     case DRVM_DISABLE:
863         /* FIXME: Pretend this is supported */
864         return 0;
865     case WIDM_OPEN:             return widOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
866     case WIDM_CLOSE:            return widClose         (wDevID);
867     case WIDM_ADDBUFFER:        return widAddBuffer     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
868     case WIDM_PREPARE:          return MMSYSERR_NOTSUPPORTED;
869     case WIDM_UNPREPARE:        return MMSYSERR_NOTSUPPORTED;
870     case WIDM_GETDEVCAPS:       return widGetDevCaps    (wDevID, (LPWAVEOUTCAPSW)dwParam1,      dwParam2);
871     case WIDM_GETNUMDEVS:       return widGetNumDevs    ();
872     case WIDM_GETPOS:           return widGetPosition   (wDevID, (LPMMTIME)dwParam1,            dwParam2);
873     case WIDM_RESET:            return widReset         (wDevID);
874     case WIDM_START:            return widStart (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
875     case WIDM_STOP:             return widStop  (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
876     case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize       (wDevID, (LPDWORD)dwParam1);
877     case DRV_QUERYDEVICEINTERFACE:     return widDevInterface           (wDevID, (PWCHAR)dwParam1, dwParam2);
878     case DRV_QUERYDSOUNDIFACE:  return widDsCreate   (wDevID, (PIDSCDRIVER*)dwParam1);
879     case DRV_QUERYDSOUNDDESC:   return widDsDesc     (wDevID, (PDSDRIVERDESC)dwParam1);
880     default:
881         FIXME("unknown message %d!\n", wMsg);
882     }
883     return MMSYSERR_NOTSUPPORTED;
884 }
885
886 #else /* HAVE_ALSA */
887
888 /**************************************************************************
889  *                              widMessage (WINEALSA.@)
890  */
891 DWORD WINAPI ALSA_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
892                              DWORD dwParam1, DWORD dwParam2)
893 {
894     FIXME("(%u, %04X, %08X, %08X, %08X):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
895     return MMSYSERR_NOTENABLED;
896 }
897
898 #endif /* HAVE_ALSA */