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