mmdevapi/tests: Add tests for IAudioClient::GetCurrentPadding.
[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 WINE_WAVEDEV    *WInDev;
63 DWORD            ALSA_WidNumMallocedDevs;
64 DWORD            ALSA_WidNumDevs;
65
66 /**************************************************************************
67 *                       widNotifyClient                 [internal]
68 */
69 static void widNotifyClient(WINE_WAVEDEV* wwi, WORD wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
70 {
71    TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
72
73    switch (wMsg) {
74    case WIM_OPEN:
75    case WIM_CLOSE:
76    case WIM_DATA:
77        DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags, (HDRVR)wwi->waveDesc.hWave,
78                       wMsg, wwi->waveDesc.dwInstance, dwParam1, dwParam2);
79        break;
80    default:
81        FIXME("Unknown callback message %u\n", wMsg);
82    }
83 }
84
85 /**************************************************************************
86  *                      widGetDevCaps                           [internal]
87  */
88 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSW lpCaps, DWORD dwSize)
89 {
90     TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
91
92     if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
93
94     if (wDevID >= ALSA_WidNumDevs) {
95         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
96         return MMSYSERR_BADDEVICEID;
97     }
98
99     memcpy(lpCaps, &WInDev[wDevID].incaps, min(dwSize, sizeof(*lpCaps)));
100     return MMSYSERR_NOERROR;
101 }
102
103 /**************************************************************************
104  *                              widRecorder_ReadHeaders         [internal]
105  */
106 static void widRecorder_ReadHeaders(WINE_WAVEDEV * wwi)
107 {
108     enum win_wm_message tmp_msg;
109     DWORD_PTR           tmp_param;
110     HANDLE              tmp_ev;
111     WAVEHDR*            lpWaveHdr;
112
113     while (ALSA_RetrieveRingMessage(&wwi->msgRing, &tmp_msg, &tmp_param, &tmp_ev)) {
114         if (tmp_msg == WINE_WM_HEADER) {
115             LPWAVEHDR*  wh;
116             lpWaveHdr = (LPWAVEHDR)tmp_param;
117             lpWaveHdr->lpNext = 0;
118
119             if (wwi->lpQueuePtr == 0)
120                 wwi->lpQueuePtr = lpWaveHdr;
121             else {
122                 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
123                 *wh = lpWaveHdr;
124             }
125         } else {
126             ERR("should only have headers left\n");
127         }
128     }
129 }
130
131 /**************************************************************************
132  *                              widRecorder                     [internal]
133  */
134 static  DWORD   CALLBACK        widRecorder(LPVOID pmt)
135 {
136     WORD                uDevID = (DWORD_PTR)pmt;
137     WINE_WAVEDEV*       wwi = &WInDev[uDevID];
138     WAVEHDR*            lpWaveHdr;
139     DWORD               dwSleepTime;
140     DWORD               bytesRead;
141     enum win_wm_message msg;
142     DWORD_PTR           param;
143     HANDLE              ev;
144
145     wwi->state = WINE_WS_STOPPED;
146     InterlockedExchange((LONG*)&wwi->dwTotalRecorded, 0);
147     wwi->lpQueuePtr = NULL;
148
149     SetEvent(wwi->hStartUpEvent);
150
151     /* make sleep time to be # of ms to output a period */
152     dwSleepTime = (wwi->dwPeriodSize * 1000) / wwi->format.Format.nAvgBytesPerSec;
153     TRACE("sleeptime=%d ms, total buffer length=%d ms (%d bytes)\n", dwSleepTime, wwi->dwBufferSize * 1000 / wwi->format.Format.nAvgBytesPerSec, wwi->dwBufferSize);
154
155     for (;;) {
156         /* wait for dwSleepTime or an event in thread's queue */
157         if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING)
158         {
159             DWORD frames;
160             DWORD bytes;
161             DWORD read;
162
163             lpWaveHdr = wwi->lpQueuePtr;
164             /* read all the fragments accumulated so far */
165             frames = snd_pcm_avail_update(wwi->pcm);
166             bytes = snd_pcm_frames_to_bytes(wwi->pcm, frames);
167
168             TRACE("frames = %d  bytes = %d state=%d\n", frames, bytes, snd_pcm_state(wwi->pcm));
169             if (snd_pcm_state(wwi->pcm) == SND_PCM_STATE_XRUN)
170             {
171                 FIXME("Recovering from XRUN!\n");
172                 snd_pcm_prepare(wwi->pcm);
173                 frames = snd_pcm_avail_update(wwi->pcm);
174                 bytes = snd_pcm_frames_to_bytes(wwi->pcm, frames);
175                 snd_pcm_start(wwi->pcm);
176                 snd_pcm_forward(wwi->pcm, frames - snd_pcm_bytes_to_frames(wwi->pcm, wwi->dwPeriodSize));
177                 continue;
178             }
179             while (frames > 0 && wwi->lpQueuePtr)
180             {
181                 TRACE("bytes = %d\n", bytes);
182                 if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded < bytes)
183                 {
184                     bytes = lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded;
185                     frames = snd_pcm_bytes_to_frames(wwi->pcm, bytes);
186                 }
187                 /* directly read fragment in wavehdr */
188                 read = wwi->read(wwi->pcm, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, frames);
189                 bytesRead = snd_pcm_frames_to_bytes(wwi->pcm, read);
190
191                 TRACE("bytesRead=(%d(%d)/(%d)) -> (%d/%d)\n", bytesRead, read, frames, lpWaveHdr->dwBufferLength, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
192                 if (read != (DWORD) -1)
193                 {
194                     /* update number of bytes recorded in current buffer and by this device */
195                     lpWaveHdr->dwBytesRecorded += bytesRead;
196                     InterlockedExchangeAdd((LONG*)&wwi->dwTotalRecorded, bytesRead);
197                     frames -= read;
198                     bytes -= bytesRead;
199
200                     /* buffer is full. notify client */
201                     if (!snd_pcm_bytes_to_frames(wwi->pcm, lpWaveHdr->dwBytesRecorded - lpWaveHdr->dwBufferLength))
202                     {
203                         /* must copy the value of next waveHdr, because we have no idea of what
204                          * will be done with the content of lpWaveHdr in callback
205                          */
206                         LPWAVEHDR       lpNext = lpWaveHdr->lpNext;
207
208                         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
209                         lpWaveHdr->dwFlags |=  WHDR_DONE;
210
211                         wwi->lpQueuePtr = lpNext;
212                         widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0);
213                         lpWaveHdr = lpNext;
214                     }
215                 } else {
216                     WARN("read(%s, %p, %d) failed (%d/%s)\n", wwi->pcmname,
217                         lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
218                         frames, frames, snd_strerror(read));
219                 }
220             }
221         }
222
223         ALSA_WaitRingMessage(&wwi->msgRing, dwSleepTime);
224
225         while (ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev))
226         {
227             TRACE("msg=%s param=0x%lx\n", ALSA_getCmdString(msg), param);
228             switch (msg) {
229             case WINE_WM_PAUSING:
230                 wwi->state = WINE_WS_PAUSED;
231                 /*FIXME("Device should stop recording\n");*/
232                 SetEvent(ev);
233                 break;
234             case WINE_WM_STARTING:
235                 wwi->state = WINE_WS_PLAYING;
236                 snd_pcm_start(wwi->pcm);
237                 SetEvent(ev);
238                 break;
239             case WINE_WM_HEADER:
240                 lpWaveHdr = (LPWAVEHDR)param;
241                 lpWaveHdr->lpNext = 0;
242
243                 /* insert buffer at the end of queue */
244                 {
245                     LPWAVEHDR*  wh;
246                     for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
247                     *wh = lpWaveHdr;
248                 }
249                 break;
250             case WINE_WM_STOPPING:
251                 if (wwi->state != WINE_WS_STOPPED)
252                 {
253                     snd_pcm_drain(wwi->pcm);
254
255                     /* read any headers in queue */
256                     widRecorder_ReadHeaders(wwi);
257
258                     /* return current buffer to app */
259                     lpWaveHdr = wwi->lpQueuePtr;
260                     if (lpWaveHdr)
261                     {
262                         LPWAVEHDR       lpNext = lpWaveHdr->lpNext;
263                         TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
264                         lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
265                         lpWaveHdr->dwFlags |= WHDR_DONE;
266                         wwi->lpQueuePtr = lpNext;
267                         widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0);
268                     }
269                 }
270                 wwi->state = WINE_WS_STOPPED;
271                 SetEvent(ev);
272                 break;
273             case WINE_WM_RESETTING:
274                 if (wwi->state != WINE_WS_STOPPED)
275                 {
276                     snd_pcm_drain(wwi->pcm);
277                 }
278                 wwi->state = WINE_WS_STOPPED;
279                 wwi->dwTotalRecorded = 0;
280
281                 /* read any headers in queue */
282                 widRecorder_ReadHeaders(wwi);
283
284                 /* return all buffers to the app */
285                 while (wwi->lpQueuePtr) {
286                     lpWaveHdr = wwi->lpQueuePtr;
287                     TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
288                     wwi->lpQueuePtr = lpWaveHdr->lpNext;
289                     lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
290                     lpWaveHdr->dwFlags |= WHDR_DONE;
291                     widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0);
292                 }
293
294                 SetEvent(ev);
295                 break;
296             case WINE_WM_CLOSING:
297                 wwi->hThread = 0;
298                 wwi->state = WINE_WS_CLOSED;
299                 SetEvent(ev);
300                 ExitThread(0);
301                 /* shouldn't go here */
302             default:
303                 FIXME("unknown message %d\n", msg);
304                 break;
305             }
306         }
307     }
308     ExitThread(0);
309     /* just for not generating compilation warnings... should never be executed */
310     return 0;
311 }
312
313 /**************************************************************************
314  *                              widOpen                         [internal]
315  */
316 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
317 {
318     WINE_WAVEDEV*               wwi;
319     snd_pcm_hw_params_t *       hw_params;
320     snd_pcm_sw_params_t *       sw_params;
321     snd_pcm_access_t            access;
322     snd_pcm_format_t            format;
323     unsigned int                rate;
324     unsigned int                buffer_time = 500000;
325     unsigned int                period_time = 10000;
326     snd_pcm_uframes_t           buffer_size;
327     snd_pcm_uframes_t           period_size;
328     int                         flags;
329     snd_pcm_t *                 pcm;
330     int                         err;
331     int                         dir;
332     DWORD                       ret;
333
334     /* JPW TODO - review this code */
335     TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
336     if (lpDesc == NULL) {
337         WARN("Invalid Parameter !\n");
338         return MMSYSERR_INVALPARAM;
339     }
340     if (wDevID >= ALSA_WidNumDevs) {
341         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
342         return MMSYSERR_BADDEVICEID;
343     }
344
345     /* only PCM format is supported so far... */
346     if (!ALSA_supportedFormat(lpDesc->lpFormat)) {
347         WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
348              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
349              lpDesc->lpFormat->nSamplesPerSec);
350         return WAVERR_BADFORMAT;
351     }
352
353     if (dwFlags & WAVE_FORMAT_QUERY) {
354         TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
355              lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
356              lpDesc->lpFormat->nSamplesPerSec);
357         return MMSYSERR_NOERROR;
358     }
359
360     wwi = &WInDev[wDevID];
361
362     if (wwi->pcm != NULL) {
363         WARN("already allocated\n");
364         return MMSYSERR_ALLOCATED;
365     }
366
367     flags = SND_PCM_NONBLOCK;
368
369     if ( (err=snd_pcm_open(&pcm, wwi->pcmname, SND_PCM_STREAM_CAPTURE, flags)) < 0 )
370     {
371         ERR("Error open: %s\n", snd_strerror(err));
372         return MMSYSERR_NOTENABLED;
373     }
374
375     wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
376
377     wwi->waveDesc = *lpDesc;
378     ALSA_copyFormat(lpDesc->lpFormat, &wwi->format);
379
380     if (wwi->format.Format.wBitsPerSample == 0) {
381         WARN("Resetting zeroed wBitsPerSample\n");
382         wwi->format.Format.wBitsPerSample = 8 *
383             (wwi->format.Format.nAvgBytesPerSec /
384              wwi->format.Format.nSamplesPerSec) /
385             wwi->format.Format.nChannels;
386     }
387
388     hw_params = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_hw_params_sizeof() );
389     sw_params = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof() );
390     if (!hw_params || !sw_params)
391     {
392         ret = MMSYSERR_NOMEM;
393         goto error;
394     }
395     snd_pcm_hw_params_any(pcm, hw_params);
396
397 #define EXIT_ON_ERROR(f,e,txt) do \
398 { \
399     int err; \
400     if ( (err = (f) ) < 0) \
401     { \
402         WARN(txt ": %s\n", snd_strerror(err)); \
403         ret = (e); \
404         goto error; \
405     } \
406 } while(0)
407
408     access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
409     if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {
410         WARN("mmap not available. switching to standard write.\n");
411         access = SND_PCM_ACCESS_RW_INTERLEAVED;
412         EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");
413         wwi->read = snd_pcm_readi;
414     }
415     else
416         wwi->read = snd_pcm_mmap_readi;
417
418     EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwi->format.Format.nChannels), WAVERR_BADFORMAT, "unable to set required channels");
419
420     if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_PCM) ||
421         ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
422         IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) {
423         format = (wwi->format.Format.wBitsPerSample == 8) ? SND_PCM_FORMAT_U8 :
424                  (wwi->format.Format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE :
425                  (wwi->format.Format.wBitsPerSample == 24) ? SND_PCM_FORMAT_S24_3LE :
426                  (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_S32_LE : -1;
427     } else if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
428         IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)){
429         format = (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_FLOAT_LE : -1;
430     } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_MULAW) {
431         FIXME("unimplemented format: WAVE_FORMAT_MULAW\n");
432         ret = WAVERR_BADFORMAT;
433         goto error;
434     } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ALAW) {
435         FIXME("unimplemented format: WAVE_FORMAT_ALAW\n");
436         ret = WAVERR_BADFORMAT;
437         goto error;
438     } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ADPCM) {
439         FIXME("unimplemented format: WAVE_FORMAT_ADPCM\n");
440         ret = WAVERR_BADFORMAT;
441         goto error;
442     } else {
443         ERR("invalid format: %0x04x\n", wwi->format.Format.wFormatTag);
444         ret = WAVERR_BADFORMAT;
445         goto error;
446     }
447
448     EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), WAVERR_BADFORMAT, "unable to set required format");
449
450     rate = wwi->format.Format.nSamplesPerSec;
451     dir = 0;
452     err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir);
453     if (err < 0) {
454         WARN("Rate %d Hz not available for playback: %s\n", wwi->format.Format.nSamplesPerSec, snd_strerror(rate));
455         ret = WAVERR_BADFORMAT;
456         goto error;
457     }
458     if (!ALSA_NearMatch(rate, wwi->format.Format.nSamplesPerSec)) {
459         WARN("Rate doesn't match (requested %d Hz, got %d Hz)\n", wwi->format.Format.nSamplesPerSec, rate);
460         ret = WAVERR_BADFORMAT;
461         goto error;
462     }
463
464     dir=0;
465     EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time");
466     dir=0;
467     EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time");
468
469     EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");
470
471     dir=0;
472     err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
473     err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
474
475     snd_pcm_sw_params_current(pcm, sw_params);
476     EXIT_ON_ERROR( snd_pcm_sw_params_set_start_threshold(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set start threshold");
477     EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");
478     EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min");
479     EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold");
480     EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback");
481 #undef EXIT_ON_ERROR
482
483     snd_pcm_prepare(pcm);
484
485     if (TRACE_ON(wave))
486         ALSA_TraceParameters(hw_params, sw_params, FALSE);
487
488     /* now, we can save all required data for later use... */
489
490     wwi->dwBufferSize = snd_pcm_frames_to_bytes(pcm, buffer_size);
491     wwi->lpQueuePtr = wwi->lpPlayPtr = wwi->lpLoopPtr = NULL;
492
493     ALSA_InitRingMessage(&wwi->msgRing);
494
495     wwi->dwPeriodSize = snd_pcm_frames_to_bytes(pcm, period_size);
496     TRACE("dwPeriodSize=%u\n", wwi->dwPeriodSize);
497     TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%u, nSamplesPerSec=%u, nChannels=%u nBlockAlign=%u!\n",
498           wwi->format.Format.wBitsPerSample, wwi->format.Format.nAvgBytesPerSec,
499           wwi->format.Format.nSamplesPerSec, wwi->format.Format.nChannels,
500           wwi->format.Format.nBlockAlign);
501
502     wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
503     wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD_PTR)wDevID, 0, &(wwi->dwThreadID));
504     if (!wwi->hThread) {
505         ERR("Thread creation for the widRecorder failed!\n");
506         CloseHandle(wwi->hStartUpEvent);
507         ret = MMSYSERR_NOMEM;
508         goto error;
509     }
510     SetThreadPriority(wwi->hThread, THREAD_PRIORITY_TIME_CRITICAL);
511     WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
512     CloseHandle(wwi->hStartUpEvent);
513     wwi->hStartUpEvent = NULL;
514
515     HeapFree( GetProcessHeap(), 0, sw_params );
516     wwi->hw_params = hw_params;
517     wwi->pcm = pcm;
518
519     widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
520     return MMSYSERR_NOERROR;
521
522 error:
523     snd_pcm_close(pcm);
524     HeapFree( GetProcessHeap(), 0, hw_params );
525     HeapFree( GetProcessHeap(), 0, sw_params );
526     if (wwi->msgRing.ring_buffer_size > 0)
527         ALSA_DestroyRingMessage(&wwi->msgRing);
528     return ret;
529 }
530
531
532 /**************************************************************************
533  *                              widClose                        [internal]
534  */
535 static DWORD widClose(WORD wDevID)
536 {
537     WINE_WAVEDEV*       wwi;
538
539     TRACE("(%u);\n", wDevID);
540
541     if (wDevID >= ALSA_WidNumDevs) {
542         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
543         return MMSYSERR_BADDEVICEID;
544     }
545
546     wwi = &WInDev[wDevID];
547     if (wwi->pcm == NULL) {
548         WARN("Requested to close already closed device %d!\n", wDevID);
549         return MMSYSERR_BADDEVICEID;
550     }
551
552     if (wwi->lpQueuePtr) {
553         WARN("buffers still playing !\n");
554         return WAVERR_STILLPLAYING;
555     } else {
556         if (wwi->hThread) {
557             ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
558         }
559         ALSA_DestroyRingMessage(&wwi->msgRing);
560
561         HeapFree( GetProcessHeap(), 0, wwi->hw_params );
562         wwi->hw_params = NULL;
563
564         snd_pcm_close(wwi->pcm);
565         wwi->pcm = NULL;
566
567         widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
568     }
569
570     return MMSYSERR_NOERROR;
571 }
572
573 /**************************************************************************
574  *                              widAddBuffer                    [internal]
575  *
576  */
577 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
578 {
579     TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
580
581     /* first, do the sanity checks... */
582     if (wDevID >= ALSA_WidNumDevs) {
583         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
584         return MMSYSERR_BADDEVICEID;
585     }
586
587     if (WInDev[wDevID].pcm == NULL) {
588         WARN("Requested to add buffer to already closed device %d!\n", wDevID);
589         return MMSYSERR_BADDEVICEID;
590     }
591
592     if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
593         return WAVERR_UNPREPARED;
594
595     if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
596         return WAVERR_STILLPLAYING;
597
598     lpWaveHdr->dwFlags &= ~WHDR_DONE;
599     lpWaveHdr->dwFlags |= WHDR_INQUEUE;
600     lpWaveHdr->dwBytesRecorded = 0;
601     lpWaveHdr->lpNext = 0;
602
603     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD_PTR)lpWaveHdr, FALSE);
604
605     return MMSYSERR_NOERROR;
606 }
607
608 /**************************************************************************
609  *                              widStart                        [internal]
610  *
611  */
612 static DWORD widStart(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
613 {
614     TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
615
616     /* first, do the sanity checks... */
617     if (wDevID >= ALSA_WidNumDevs) {
618         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
619         return MMSYSERR_BADDEVICEID;
620     }
621
622     if (WInDev[wDevID].pcm == NULL) {
623         WARN("Requested to start closed device %d!\n", wDevID);
624         return MMSYSERR_BADDEVICEID;
625     }
626
627     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
628
629     return MMSYSERR_NOERROR;
630 }
631
632 /**************************************************************************
633  *                              widStop                 [internal]
634  *
635  */
636 static DWORD widStop(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
637 {
638     TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
639
640     /* first, do the sanity checks... */
641     if (wDevID >= ALSA_WidNumDevs) {
642         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
643         return MMSYSERR_BADDEVICEID;
644     }
645
646     if (WInDev[wDevID].pcm == NULL) {
647         WARN("Requested to stop closed device %d!\n", wDevID);
648         return MMSYSERR_BADDEVICEID;
649     }
650
651     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
652
653     return MMSYSERR_NOERROR;
654 }
655
656 /**************************************************************************
657  *                      widReset                                [internal]
658  */
659 static DWORD widReset(WORD wDevID)
660 {
661     TRACE("(%u);\n", wDevID);
662     if (wDevID >= ALSA_WidNumDevs) {
663         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
664         return MMSYSERR_BADDEVICEID;
665     }
666
667     if (WInDev[wDevID].pcm == NULL) {
668         WARN("Requested to reset closed device %d!\n", wDevID);
669         return MMSYSERR_BADDEVICEID;
670     }
671
672     ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
673     return MMSYSERR_NOERROR;
674 }
675
676 /**************************************************************************
677  *                              widGetPosition                  [internal]
678  */
679 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
680 {
681     WINE_WAVEDEV*       wwi;
682
683     TRACE("(%u, %p, %u);\n", wDevID, lpTime, uSize);
684
685     if (wDevID >= ALSA_WidNumDevs) {
686         TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
687         return MMSYSERR_BADDEVICEID;
688     }
689
690     if (WInDev[wDevID].state == WINE_WS_CLOSED) {
691         WARN("Requested position of closed device %d!\n", wDevID);
692         return MMSYSERR_BADDEVICEID;
693     }
694
695     if (lpTime == NULL) {
696         WARN("invalid parameter: lpTime = NULL\n");
697         return MMSYSERR_INVALPARAM;
698     }
699
700     wwi = &WInDev[wDevID];
701     return ALSA_bytes_to_mmtime(lpTime, wwi->dwTotalRecorded, &wwi->format);
702 }
703
704 /**************************************************************************
705  *                              widGetNumDevs                   [internal]
706  */
707 static  DWORD   widGetNumDevs(void)
708 {
709     return ALSA_WidNumDevs;
710 }
711
712 /**************************************************************************
713  *                              widDevInterfaceSize             [internal]
714  */
715 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
716 {
717     TRACE("(%u, %p)\n", wDevID, dwParam1);
718
719     *dwParam1 = MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -1,
720                                     NULL, 0 ) * sizeof(WCHAR);
721     return MMSYSERR_NOERROR;
722 }
723
724 /**************************************************************************
725  *                              widDevInterface                 [internal]
726  */
727 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
728 {
729     if (dwParam2 >= MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -1,
730                                         NULL, 0 ) * sizeof(WCHAR))
731     {
732         MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -1,
733                             dwParam1, dwParam2 / sizeof(WCHAR));
734         return MMSYSERR_NOERROR;
735     }
736     return MMSYSERR_INVALPARAM;
737 }
738
739 /**************************************************************************
740  *                              widMessage (WINEALSA.@)
741  */
742 DWORD WINAPI ALSA_widMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
743                              DWORD_PTR dwParam1, DWORD_PTR dwParam2)
744 {
745     TRACE("(%u, %s, %08lX, %08lX, %08lX);\n",
746           wDevID, ALSA_getMessage(wMsg), dwUser, dwParam1, dwParam2);
747
748     switch (wMsg) {
749     case DRVM_INIT:
750         ALSA_WaveInit();
751     case DRVM_EXIT:
752     case DRVM_ENABLE:
753     case DRVM_DISABLE:
754         /* FIXME: Pretend this is supported */
755         return 0;
756     case WIDM_OPEN:             return widOpen          (wDevID, (LPWAVEOPENDESC)dwParam1,      dwParam2);
757     case WIDM_CLOSE:            return widClose         (wDevID);
758     case WIDM_ADDBUFFER:        return widAddBuffer     (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
759     case WIDM_PREPARE:          return MMSYSERR_NOTSUPPORTED;
760     case WIDM_UNPREPARE:        return MMSYSERR_NOTSUPPORTED;
761     case WIDM_GETDEVCAPS:       return widGetDevCaps    (wDevID, (LPWAVEINCAPSW)dwParam1,       dwParam2);
762     case WIDM_GETNUMDEVS:       return widGetNumDevs    ();
763     case WIDM_GETPOS:           return widGetPosition   (wDevID, (LPMMTIME)dwParam1,            dwParam2);
764     case WIDM_RESET:            return widReset         (wDevID);
765     case WIDM_START:            return widStart (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
766     case WIDM_STOP:             return widStop  (wDevID, (LPWAVEHDR)dwParam1,           dwParam2);
767     case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize       (wDevID, (LPDWORD)dwParam1);
768     case DRV_QUERYDEVICEINTERFACE:     return widDevInterface           (wDevID, (PWCHAR)dwParam1, dwParam2);
769     case DRV_QUERYDSOUNDIFACE:  return widDsCreate   (wDevID, (PIDSCDRIVER*)dwParam1);
770     case DRV_QUERYDSOUNDDESC:   return widDsDesc     (wDevID, (PDSDRIVERDESC)dwParam1);
771     default:
772         FIXME("unknown message %d!\n", wMsg);
773     }
774     return MMSYSERR_NOTSUPPORTED;
775 }