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