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