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