wintrust: Add a helper function to initialize chain creation parameters.
[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, LPWAVEINCAPSW 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     InterlockedExchange((LONG*)&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                         InterlockedExchangeAdd((LONG*)&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                         InterlockedExchangeAdd((LONG*)&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             default:
389                 FIXME("unknown message %d\n", msg);
390                 break;
391             }
392         }
393     }
394     ExitThread(0);
395     /* just for not generating compilation warnings... should never be executed */
396     return 0;
397 }
398
399 /**************************************************************************
400  *                              widOpen                         [internal]
401  */
402 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
403 {
404     WINE_WAVEDEV*               wwi;
405     snd_pcm_hw_params_t *       hw_params;
406     snd_pcm_sw_params_t *       sw_params;
407     snd_pcm_access_t            access;
408     snd_pcm_format_t            format;
409     unsigned int                rate;
410     unsigned int                buffer_time = 500000;
411     unsigned int                period_time = 10000;
412     snd_pcm_uframes_t           buffer_size;
413     snd_pcm_uframes_t           period_size;
414     int                         flags;
415     snd_pcm_t *                 pcm;
416     int                         err;
417     int                         dir;
418
419     snd_pcm_hw_params_alloca(&hw_params);
420     snd_pcm_sw_params_alloca(&sw_params);
421
422     /* JPW TODO - review this code */
423     TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
424     if (lpDesc == NULL) {
425         WARN("Invalid Parameter !\n");
426         return MMSYSERR_INVALPARAM;
427     }
428     if (wDevID >= ALSA_WidNumDevs) {
429         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
430         return MMSYSERR_BADDEVICEID;
431     }
432
433     /* only PCM format is supported so far... */
434     if (!ALSA_supportedFormat(lpDesc->lpFormat)) {
435         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
436              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
437              lpDesc->lpFormat->nSamplesPerSec);
438         return WAVERR_BADFORMAT;
439     }
440
441     if (dwFlags & WAVE_FORMAT_QUERY) {
442         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
443              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
444              lpDesc->lpFormat->nSamplesPerSec);
445         return MMSYSERR_NOERROR;
446     }
447
448     wwi = &WInDev[wDevID];
449
450     if (wwi->pcm != NULL) {
451         WARN("already allocated\n");
452         return MMSYSERR_ALLOCATED;
453     }
454
455     wwi->pcm = 0;
456     flags = SND_PCM_NONBLOCK;
457
458     if ( (err=snd_pcm_open(&pcm, wwi->pcmname, SND_PCM_STREAM_CAPTURE, flags)) < 0 )
459     {
460         ERR("Error open: %s\n", snd_strerror(err));
461         return MMSYSERR_NOTENABLED;
462     }
463
464     wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
465
466     memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
467     ALSA_copyFormat(lpDesc->lpFormat, &wwi->format);
468
469     if (wwi->format.Format.wBitsPerSample == 0) {
470         WARN("Resetting zeroed wBitsPerSample\n");
471         wwi->format.Format.wBitsPerSample = 8 *
472             (wwi->format.Format.nAvgBytesPerSec /
473              wwi->format.Format.nSamplesPerSec) /
474             wwi->format.Format.nChannels;
475     }
476
477     snd_pcm_hw_params_any(pcm, hw_params);
478
479 #define EXIT_ON_ERROR(f,e,txt) do \
480 { \
481     int err; \
482     if ( (err = (f) ) < 0) \
483     { \
484         WARN(txt ": %s\n", snd_strerror(err)); \
485         snd_pcm_close(pcm); \
486         return e; \
487     } \
488 } while(0)
489
490     access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
491     if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {
492         WARN("mmap not available. switching to standard write.\n");
493         access = SND_PCM_ACCESS_RW_INTERLEAVED;
494         EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");
495         wwi->read = snd_pcm_readi;
496     }
497     else
498         wwi->read = snd_pcm_mmap_readi;
499
500     EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwi->format.Format.nChannels), WAVERR_BADFORMAT, "unable to set required channels");
501
502     if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_PCM) ||
503         ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
504         IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) {
505         format = (wwi->format.Format.wBitsPerSample == 8) ? SND_PCM_FORMAT_U8 :
506                  (wwi->format.Format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE :
507                  (wwi->format.Format.wBitsPerSample == 24) ? SND_PCM_FORMAT_S24_LE :
508                  (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_S32_LE : -1;
509     } else if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
510         IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)){
511         format = (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_FLOAT_LE : -1;
512     } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_MULAW) {
513         FIXME("unimplemented format: WAVE_FORMAT_MULAW\n");
514         snd_pcm_close(pcm);
515         return WAVERR_BADFORMAT;
516     } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ALAW) {
517         FIXME("unimplemented format: WAVE_FORMAT_ALAW\n");
518         snd_pcm_close(pcm);
519         return WAVERR_BADFORMAT;
520     } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ADPCM) {
521         FIXME("unimplemented format: WAVE_FORMAT_ADPCM\n");
522         snd_pcm_close(pcm);
523         return WAVERR_BADFORMAT;
524     } else {
525         ERR("invalid format: %0x04x\n", wwi->format.Format.wFormatTag);
526         snd_pcm_close(pcm);
527         return WAVERR_BADFORMAT;
528     }
529
530     EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), WAVERR_BADFORMAT, "unable to set required format");
531
532     rate = wwi->format.Format.nSamplesPerSec;
533     dir = 0;
534     err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir);
535     if (err < 0) {
536         WARN("Rate %d Hz not available for playback: %s\n", wwi->format.Format.nSamplesPerSec, snd_strerror(rate));
537         snd_pcm_close(pcm);
538         return WAVERR_BADFORMAT;
539     }
540     if (!ALSA_NearMatch(rate, wwi->format.Format.nSamplesPerSec)) {
541         WARN("Rate doesn't match (requested %d Hz, got %d Hz)\n", wwi->format.Format.nSamplesPerSec, rate);
542         snd_pcm_close(pcm);
543         return WAVERR_BADFORMAT;
544     }
545
546     dir=0;
547     EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time");
548     dir=0;
549     EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time");
550
551     EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");
552
553     dir=0;
554     err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
555     err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
556
557     snd_pcm_sw_params_current(pcm, sw_params);
558     EXIT_ON_ERROR( snd_pcm_sw_params_set_start_threshold(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set start threshold");
559     EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");
560     EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min");
561     EXIT_ON_ERROR( snd_pcm_sw_params_set_xfer_align(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set xfer align");
562     EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold");
563     EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback");
564 #undef EXIT_ON_ERROR
565
566     snd_pcm_prepare(pcm);
567
568     if (TRACE_ON(wave))
569         ALSA_TraceParameters(hw_params, sw_params, FALSE);
570
571     /* now, we can save all required data for later use... */
572     if ( wwi->hw_params )
573         snd_pcm_hw_params_free(wwi->hw_params);
574     snd_pcm_hw_params_malloc(&(wwi->hw_params));
575     snd_pcm_hw_params_copy(wwi->hw_params, hw_params);
576
577     wwi->dwBufferSize = snd_pcm_frames_to_bytes(pcm, buffer_size);
578     wwi->lpQueuePtr = wwi->lpPlayPtr = wwi->lpLoopPtr = NULL;
579     wwi->pcm = pcm;
580
581     ALSA_InitRingMessage(&wwi->msgRing);
582
583     wwi->dwPeriodSize = period_size;
584     /*if (wwi->dwFragmentSize % wwi->format.Format.nBlockAlign)
585         ERR("Fragment doesn't contain an integral number of data blocks\n");
586     */
587     TRACE("dwPeriodSize=%u\n", wwi->dwPeriodSize);
588     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%u, nSamplesPerSec=%u, nChannels=%u nBlockAlign=%u!\n",
589           wwi->format.Format.wBitsPerSample, wwi->format.Format.nAvgBytesPerSec,
590           wwi->format.Format.nSamplesPerSec, wwi->format.Format.nChannels,
591           wwi->format.Format.nBlockAlign);
592
593     wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
594     wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
595     if (wwi->hThread)
596         SetThreadPriority(wwi->hThread, THREAD_PRIORITY_TIME_CRITICAL);
597     WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
598     CloseHandle(wwi->hStartUpEvent);
599     wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
600
601     return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
602 }
603
604
605 /**************************************************************************
606  *                              widClose                        [internal]
607  */
608 static DWORD widClose(WORD wDevID)
609 {
610     DWORD               ret = MMSYSERR_NOERROR;
611     WINE_WAVEDEV*       wwi;
612
613     TRACE("(%u);\n", wDevID);
614
615     if (wDevID >= ALSA_WidNumDevs) {
616         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
617         return MMSYSERR_BADDEVICEID;
618     }
619
620     if (WInDev[wDevID].pcm == NULL) {
621         WARN("Requested to close already closed device %d!\n", wDevID);
622         return MMSYSERR_BADDEVICEID;
623     }
624
625     wwi = &WInDev[wDevID];
626     if (wwi->lpQueuePtr) {
627         WARN("buffers still playing !\n");
628         ret = WAVERR_STILLPLAYING;
629     } else {
630         if (wwi->hThread != INVALID_HANDLE_VALUE) {
631             ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
632         }
633         ALSA_DestroyRingMessage(&wwi->msgRing);
634
635         snd_pcm_hw_params_free(wwi->hw_params);
636         wwi->hw_params = NULL;
637
638         snd_pcm_close(wwi->pcm);
639         wwi->pcm = NULL;
640
641         ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
642     }
643
644     return ret;
645 }
646
647 /**************************************************************************
648  *                              widAddBuffer                    [internal]
649  *
650  */
651 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
652 {
653     TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
654
655     /* first, do the sanity checks... */
656     if (wDevID >= ALSA_WidNumDevs) {
657         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
658         return MMSYSERR_BADDEVICEID;
659     }
660
661     if (WInDev[wDevID].pcm == NULL) {
662         WARN("Requested to add buffer to already closed device %d!\n", wDevID);
663         return MMSYSERR_BADDEVICEID;
664     }
665
666     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
667         return WAVERR_UNPREPARED;
668
669     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
670         return WAVERR_STILLPLAYING;
671
672     lpWaveHdr->dwFlags &= ~WHDR_DONE;
673     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
674     lpWaveHdr->lpNext = 0;
675
676     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
677
678     return MMSYSERR_NOERROR;
679 }
680
681 /**************************************************************************
682  *                              widStart                        [internal]
683  *
684  */
685 static DWORD widStart(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
686 {
687     TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
688
689     /* first, do the sanity checks... */
690     if (wDevID >= ALSA_WidNumDevs) {
691         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
692         return MMSYSERR_BADDEVICEID;
693     }
694
695     if (WInDev[wDevID].pcm == NULL) {
696         WARN("Requested to start closed device %d!\n", wDevID);
697         return MMSYSERR_BADDEVICEID;
698     }
699
700     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
701
702     return MMSYSERR_NOERROR;
703 }
704
705 /**************************************************************************
706  *                              widStop                 [internal]
707  *
708  */
709 static DWORD widStop(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
710 {
711     TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
712
713     /* first, do the sanity checks... */
714     if (wDevID >= ALSA_WidNumDevs) {
715         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
716         return MMSYSERR_BADDEVICEID;
717     }
718
719     if (WInDev[wDevID].pcm == NULL) {
720         WARN("Requested to stop closed device %d!\n", wDevID);
721         return MMSYSERR_BADDEVICEID;
722     }
723
724     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
725
726     return MMSYSERR_NOERROR;
727 }
728
729 /**************************************************************************
730  *                      widReset                                [internal]
731  */
732 static DWORD widReset(WORD wDevID)
733 {
734     TRACE("(%u);\n", wDevID);
735     if (wDevID >= ALSA_WidNumDevs) {
736         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
737         return MMSYSERR_BADDEVICEID;
738     }
739
740     if (WInDev[wDevID].pcm == NULL) {
741         WARN("Requested to reset closed device %d!\n", wDevID);
742         return MMSYSERR_BADDEVICEID;
743     }
744
745     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
746     return MMSYSERR_NOERROR;
747 }
748
749 /**************************************************************************
750  *                              widGetPosition                  [internal]
751  */
752 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
753 {
754     WINE_WAVEDEV*       wwi;
755
756     TRACE("(%u, %p, %u);\n", wDevID, lpTime, uSize);
757
758     if (wDevID >= ALSA_WidNumDevs) {
759         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
760         return MMSYSERR_BADDEVICEID;
761     }
762
763     if (WInDev[wDevID].state == WINE_WS_CLOSED) {
764         WARN("Requested position of closed device %d!\n", wDevID);
765         return MMSYSERR_BADDEVICEID;
766     }
767
768     if (lpTime == NULL) {
769         WARN("invalid parameter: lpTime = NULL\n");
770         return MMSYSERR_INVALPARAM;
771     }
772
773     wwi = &WInDev[wDevID];
774     return ALSA_bytes_to_mmtime(lpTime, wwi->dwTotalRecorded, &wwi->format);
775 }
776
777 /**************************************************************************
778  *                              widGetNumDevs                   [internal]
779  */
780 static  DWORD   widGetNumDevs(void)
781 {
782     return ALSA_WidNumDevs;
783 }
784
785 /**************************************************************************
786  *                              widDevInterfaceSize             [internal]
787  */
788 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
789 {
790     TRACE("(%u, %p)\n", wDevID, dwParam1);
791
792     *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
793                                     NULL, 0 ) * sizeof(WCHAR);
794     return MMSYSERR_NOERROR;
795 }
796
797 /**************************************************************************
798  *                              widDevInterface                 [internal]
799  */
800 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
801 {
802     if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
803                                         NULL, 0 ) * sizeof(WCHAR))
804     {
805         MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
806                             dwParam1, dwParam2 / sizeof(WCHAR));
807         return MMSYSERR_NOERROR;
808     }
809     return MMSYSERR_INVALPARAM;
810 }
811
812 /**************************************************************************
813  *                              widMessage (WINEALSA.@)
814  */
815 DWORD WINAPI ALSA_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
816                              DWORD dwParam1, DWORD dwParam2)
817 {
818     TRACE("(%u, %s, %08X, %08X, %08X);\n",
819           wDevID, ALSA_getMessage(wMsg), dwUser, dwParam1, dwParam2);
820
821     switch (wMsg) {
822     case DRVM_INIT:
823     case DRVM_EXIT:
824     case DRVM_ENABLE:
825     case DRVM_DISABLE:
826         /* FIXME: Pretend this is supported */
827         return 0;
828     case WIDM_OPEN:             return widOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
829     case WIDM_CLOSE:            return widClose         (wDevID);
830     case WIDM_ADDBUFFER:        return widAddBuffer     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
831     case WIDM_PREPARE:          return MMSYSERR_NOTSUPPORTED;
832     case WIDM_UNPREPARE:        return MMSYSERR_NOTSUPPORTED;
833     case WIDM_GETDEVCAPS:       return widGetDevCaps    (wDevID, (LPWAVEINCAPSW)dwParam1,       dwParam2);
834     case WIDM_GETNUMDEVS:       return widGetNumDevs    ();
835     case WIDM_GETPOS:           return widGetPosition   (wDevID, (LPMMTIME)dwParam1,            dwParam2);
836     case WIDM_RESET:            return widReset         (wDevID);
837     case WIDM_START:            return widStart (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
838     case WIDM_STOP:             return widStop  (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
839     case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize       (wDevID, (LPDWORD)dwParam1);
840     case DRV_QUERYDEVICEINTERFACE:     return widDevInterface           (wDevID, (PWCHAR)dwParam1, dwParam2);
841     case DRV_QUERYDSOUNDIFACE:  return widDsCreate   (wDevID, (PIDSCDRIVER*)dwParam1);
842     case DRV_QUERYDSOUNDDESC:   return widDsDesc     (wDevID, (PDSDRIVERDESC)dwParam1);
843     default:
844         FIXME("unknown message %d!\n", wMsg);
845     }
846     return MMSYSERR_NOTSUPPORTED;
847 }
848
849 #else /* HAVE_ALSA */
850
851 /**************************************************************************
852  *                              widMessage (WINEALSA.@)
853  */
854 DWORD WINAPI ALSA_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
855                              DWORD dwParam1, DWORD dwParam2)
856 {
857     FIXME("(%u, %04X, %08X, %08X, %08X):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
858     return MMSYSERR_NOTENABLED;
859 }
860
861 #endif /* HAVE_ALSA */