po: Add message contexts (and French translations) for some polysemic English transla...
[wine] / dlls / winealsa.drv / alsa.c
1 /*
2  * Wine Driver for ALSA
3  *
4  * Copyright    2002 Eric Pouech
5  * Copyright    2006 Jaroslav Kysela
6  * Copyright    2007 Maarten Lankhorst
7  *
8  * This file has a few shared generic subroutines shared among the alsa
9  * implementation.
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25
26 #include "config.h"
27
28 #include <stdarg.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "wingdi.h"
33 #include "winuser.h"
34 #include "mmddk.h"
35
36 #include "ks.h"
37 #include "guiddef.h"
38 #include "ksmedia.h"
39
40 #include "alsa.h"
41
42 #include "wine/library.h"
43 #include "wine/unicode.h"
44 #include "wine/debug.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(alsa);
47 /* unless someone makes a wineserver kernel module, Unix pipes are faster than win32 events */
48 #define USE_PIPE_SYNC
49
50 #ifdef USE_PIPE_SYNC
51 #define INIT_OMR(omr) do { if (pipe(omr->msg_pipe) < 0) { omr->msg_pipe[0] = omr->msg_pipe[1] = -1; } } while (0)
52 #define CLOSE_OMR(omr) do { close(omr->msg_pipe[0]); close(omr->msg_pipe[1]); } while (0)
53 #define SIGNAL_OMR(omr) do { int x = 0; write((omr)->msg_pipe[1], &x, sizeof(x)); } while (0)
54 #define CLEAR_OMR(omr) do { int x = 0; read((omr)->msg_pipe[0], &x, sizeof(x)); } while (0)
55 #define RESET_OMR(omr) do { } while (0)
56 #define WAIT_OMR(omr, sleep) \
57   do { struct pollfd pfd; pfd.fd = (omr)->msg_pipe[0]; \
58        pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)
59 #else
60 #define INIT_OMR(omr) do { omr->msg_event = CreateEventW(NULL, FALSE, FALSE, NULL); } while (0)
61 #define CLOSE_OMR(omr) do { CloseHandle(omr->msg_event); } while (0)
62 #define SIGNAL_OMR(omr) do { SetEvent((omr)->msg_event); } while (0)
63 #define CLEAR_OMR(omr) do { } while (0)
64 #define RESET_OMR(omr) do { ResetEvent((omr)->msg_event); } while (0)
65 #define WAIT_OMR(omr, sleep) \
66   do { WaitForSingleObject((omr)->msg_event, sleep); } while (0)
67 #endif
68
69 #define ALSA_RING_BUFFER_INCREMENT      64
70
71 /******************************************************************
72  *              ALSA_InitRingMessage
73  *
74  * Initialize the ring of messages for passing between driver's caller and playback/record
75  * thread
76  */
77 int ALSA_InitRingMessage(ALSA_MSG_RING* omr)
78 {
79     omr->msg_toget = 0;
80     omr->msg_tosave = 0;
81     INIT_OMR(omr);
82     omr->ring_buffer_size = ALSA_RING_BUFFER_INCREMENT;
83     omr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,omr->ring_buffer_size * sizeof(ALSA_MSG));
84
85     InitializeCriticalSection(&omr->msg_crst);
86     omr->msg_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ALSA_MSG_RING.msg_crst");
87     return 0;
88 }
89
90 /******************************************************************
91  *              ALSA_DestroyRingMessage
92  *
93  */
94 int ALSA_DestroyRingMessage(ALSA_MSG_RING* omr)
95 {
96     CLOSE_OMR(omr);
97     HeapFree(GetProcessHeap(),0,omr->messages);
98     omr->ring_buffer_size = 0;
99     omr->msg_crst.DebugInfo->Spare[0] = 0;
100     DeleteCriticalSection(&omr->msg_crst);
101     return 0;
102 }
103 /******************************************************************
104  *              ALSA_ResetRingMessage
105  *
106  */
107 void ALSA_ResetRingMessage(ALSA_MSG_RING* omr)
108 {
109     RESET_OMR(omr);
110 }
111
112 /******************************************************************
113  *              ALSA_WaitRingMessage
114  *
115  */
116 void ALSA_WaitRingMessage(ALSA_MSG_RING* omr, DWORD sleep)
117 {
118     WAIT_OMR(omr, sleep);
119 }
120
121 /******************************************************************
122  *              ALSA_AddRingMessage
123  *
124  * Inserts a new message into the ring (should be called from DriverProc derived routines)
125  */
126 int ALSA_AddRingMessage(ALSA_MSG_RING* omr, enum win_wm_message msg, DWORD_PTR param, BOOL wait)
127 {
128     HANDLE      hEvent = NULL;
129
130     EnterCriticalSection(&omr->msg_crst);
131     if (omr->msg_toget == ((omr->msg_tosave + 1) % omr->ring_buffer_size))
132     {
133         int old_ring_buffer_size = omr->ring_buffer_size;
134         omr->ring_buffer_size += ALSA_RING_BUFFER_INCREMENT;
135         omr->messages = HeapReAlloc(GetProcessHeap(),0,omr->messages, omr->ring_buffer_size * sizeof(ALSA_MSG));
136         /* Now we need to rearrange the ring buffer so that the new
137            buffers just allocated are in between omr->msg_tosave and
138            omr->msg_toget.
139         */
140         if (omr->msg_tosave < omr->msg_toget)
141         {
142             memmove(&(omr->messages[omr->msg_toget + ALSA_RING_BUFFER_INCREMENT]),
143                     &(omr->messages[omr->msg_toget]),
144                     sizeof(ALSA_MSG)*(old_ring_buffer_size - omr->msg_toget)
145                     );
146             omr->msg_toget += ALSA_RING_BUFFER_INCREMENT;
147         }
148     }
149     if (wait)
150     {
151         hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
152         if (!hEvent)
153         {
154             ERR("can't create event !?\n");
155             LeaveCriticalSection(&omr->msg_crst);
156             return 0;
157         }
158         if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER)
159             FIXME("two fast messages in the queue!!!! toget = %d(%s), tosave=%d(%s)\n",
160                   omr->msg_toget,ALSA_getCmdString(omr->messages[omr->msg_toget].msg),
161                   omr->msg_tosave,ALSA_getCmdString(omr->messages[omr->msg_tosave].msg));
162
163         /* fast messages have to be added at the start of the queue */
164         omr->msg_toget = (omr->msg_toget + omr->ring_buffer_size - 1) % omr->ring_buffer_size;
165
166         omr->messages[omr->msg_toget].msg = msg;
167         omr->messages[omr->msg_toget].param = param;
168         omr->messages[omr->msg_toget].hEvent = hEvent;
169     }
170     else
171     {
172         omr->messages[omr->msg_tosave].msg = msg;
173         omr->messages[omr->msg_tosave].param = param;
174         omr->messages[omr->msg_tosave].hEvent = NULL;
175         omr->msg_tosave = (omr->msg_tosave + 1) % omr->ring_buffer_size;
176     }
177     LeaveCriticalSection(&omr->msg_crst);
178     /* signal a new message */
179     SIGNAL_OMR(omr);
180     if (wait)
181     {
182         /* wait for playback/record thread to have processed the message */
183         WaitForSingleObject(hEvent, INFINITE);
184         CloseHandle(hEvent);
185     }
186     return 1;
187 }
188
189 /******************************************************************
190  *              ALSA_RetrieveRingMessage
191  *
192  * Get a message from the ring. Should be called by the playback/record thread.
193  */
194 int ALSA_RetrieveRingMessage(ALSA_MSG_RING* omr, enum win_wm_message *msg,
195                              DWORD_PTR *param, HANDLE *hEvent)
196 {
197     EnterCriticalSection(&omr->msg_crst);
198
199     if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
200     {
201         LeaveCriticalSection(&omr->msg_crst);
202         return 0;
203     }
204
205     *msg = omr->messages[omr->msg_toget].msg;
206     omr->messages[omr->msg_toget].msg = 0;
207     *param = omr->messages[omr->msg_toget].param;
208     *hEvent = omr->messages[omr->msg_toget].hEvent;
209     omr->msg_toget = (omr->msg_toget + 1) % omr->ring_buffer_size;
210     CLEAR_OMR(omr);
211     LeaveCriticalSection(&omr->msg_crst);
212     return 1;
213 }
214
215 /*======================================================================*
216  *                  Utility functions                                   *
217  *======================================================================*/
218
219 /* These strings used only for tracing */
220 const char * ALSA_getCmdString(enum win_wm_message msg)
221 {
222 #define MSG_TO_STR(x) case x: return #x
223     switch(msg) {
224     MSG_TO_STR(WINE_WM_PAUSING);
225     MSG_TO_STR(WINE_WM_RESTARTING);
226     MSG_TO_STR(WINE_WM_RESETTING);
227     MSG_TO_STR(WINE_WM_HEADER);
228     MSG_TO_STR(WINE_WM_UPDATE);
229     MSG_TO_STR(WINE_WM_BREAKLOOP);
230     MSG_TO_STR(WINE_WM_CLOSING);
231     MSG_TO_STR(WINE_WM_STARTING);
232     MSG_TO_STR(WINE_WM_STOPPING);
233     }
234 #undef MSG_TO_STR
235     return wine_dbg_sprintf("UNKNOWN(0x%08x)", msg);
236 }
237
238 const char * ALSA_getMessage(UINT msg)
239 {
240 #define MSG_TO_STR(x) case x: return #x
241     switch(msg) {
242     MSG_TO_STR(DRVM_INIT);
243     MSG_TO_STR(DRVM_EXIT);
244     MSG_TO_STR(DRVM_ENABLE);
245     MSG_TO_STR(DRVM_DISABLE);
246     MSG_TO_STR(WIDM_OPEN);
247     MSG_TO_STR(WIDM_CLOSE);
248     MSG_TO_STR(WIDM_ADDBUFFER);
249     MSG_TO_STR(WIDM_PREPARE);
250     MSG_TO_STR(WIDM_UNPREPARE);
251     MSG_TO_STR(WIDM_GETDEVCAPS);
252     MSG_TO_STR(WIDM_GETNUMDEVS);
253     MSG_TO_STR(WIDM_GETPOS);
254     MSG_TO_STR(WIDM_RESET);
255     MSG_TO_STR(WIDM_START);
256     MSG_TO_STR(WIDM_STOP);
257     MSG_TO_STR(WODM_OPEN);
258     MSG_TO_STR(WODM_CLOSE);
259     MSG_TO_STR(WODM_WRITE);
260     MSG_TO_STR(WODM_PAUSE);
261     MSG_TO_STR(WODM_GETPOS);
262     MSG_TO_STR(WODM_BREAKLOOP);
263     MSG_TO_STR(WODM_PREPARE);
264     MSG_TO_STR(WODM_UNPREPARE);
265     MSG_TO_STR(WODM_GETDEVCAPS);
266     MSG_TO_STR(WODM_GETNUMDEVS);
267     MSG_TO_STR(WODM_GETPITCH);
268     MSG_TO_STR(WODM_SETPITCH);
269     MSG_TO_STR(WODM_GETPLAYBACKRATE);
270     MSG_TO_STR(WODM_SETPLAYBACKRATE);
271     MSG_TO_STR(WODM_GETVOLUME);
272     MSG_TO_STR(WODM_SETVOLUME);
273     MSG_TO_STR(WODM_RESTART);
274     MSG_TO_STR(WODM_RESET);
275     MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE);
276     MSG_TO_STR(DRV_QUERYDEVICEINTERFACE);
277     MSG_TO_STR(DRV_QUERYDSOUNDIFACE);
278     MSG_TO_STR(DRV_QUERYDSOUNDDESC);
279     }
280 #undef MSG_TO_STR
281     return wine_dbg_sprintf("UNKNOWN(0x%04x)", msg);
282 }
283
284 const char * ALSA_getFormat(WORD wFormatTag)
285 {
286 #define FMT_TO_STR(x) case x: return #x
287     switch(wFormatTag) {
288     FMT_TO_STR(WAVE_FORMAT_PCM);
289     FMT_TO_STR(WAVE_FORMAT_EXTENSIBLE);
290     FMT_TO_STR(WAVE_FORMAT_MULAW);
291     FMT_TO_STR(WAVE_FORMAT_ALAW);
292     FMT_TO_STR(WAVE_FORMAT_ADPCM);
293     }
294 #undef FMT_TO_STR
295     return wine_dbg_sprintf("UNKNOWN(0x%04x)", wFormatTag);
296 }
297
298 /* Allow 1% deviation for sample rates (some ES137x cards) */
299 BOOL ALSA_NearMatch(int rate1, int rate2)
300 {
301     return (((100 * (rate1 - rate2)) / rate1) == 0);
302 }
303
304 DWORD ALSA_bytes_to_mmtime(LPMMTIME lpTime, DWORD position, WAVEFORMATPCMEX* format)
305 {
306     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%u nChannels=%u nAvgBytesPerSec=%u\n",
307           lpTime->wType, format->Format.wBitsPerSample, format->Format.nSamplesPerSec,
308           format->Format.nChannels, format->Format.nAvgBytesPerSec);
309     TRACE("Position in bytes=%u\n", position);
310
311     switch (lpTime->wType) {
312     case TIME_SAMPLES:
313         lpTime->u.sample = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
314         TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample);
315         break;
316     case TIME_MS:
317         lpTime->u.ms = 1000.0 * position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels * format->Format.nSamplesPerSec);
318         TRACE("TIME_MS=%u\n", lpTime->u.ms);
319         break;
320     case TIME_SMPTE:
321         lpTime->u.smpte.fps = 30;
322         position = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
323         position += (format->Format.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
324         lpTime->u.smpte.sec = position / format->Format.nSamplesPerSec;
325         position -= lpTime->u.smpte.sec * format->Format.nSamplesPerSec;
326         lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
327         lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
328         lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
329         lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
330         lpTime->u.smpte.fps = 30;
331         lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->Format.nSamplesPerSec;
332         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
333               lpTime->u.smpte.hour, lpTime->u.smpte.min,
334               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
335         break;
336     default:
337         WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
338         lpTime->wType = TIME_BYTES;
339         /* fall through */
340     case TIME_BYTES:
341         lpTime->u.cb = position;
342         TRACE("TIME_BYTES=%u\n", lpTime->u.cb);
343         break;
344     }
345     return MMSYSERR_NOERROR;
346 }
347
348 void ALSA_copyFormat(LPWAVEFORMATEX wf1, LPWAVEFORMATPCMEX wf2)
349 {
350     unsigned int iLength;
351
352     ZeroMemory(wf2, sizeof(*wf2));
353     if (wf1->wFormatTag == WAVE_FORMAT_PCM)
354         iLength = sizeof(PCMWAVEFORMAT);
355     else if (wf1->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
356         iLength = sizeof(WAVEFORMATPCMEX);
357     else
358         iLength = sizeof(WAVEFORMATEX) + wf1->cbSize;
359     memcpy(wf2, wf1, iLength);
360 }
361
362 BOOL ALSA_supportedFormat(LPWAVEFORMATEX wf)
363 {
364     TRACE("(%p)\n",wf);
365
366     if (wf->nSamplesPerSec<DSBFREQUENCY_MIN||wf->nSamplesPerSec>DSBFREQUENCY_MAX)
367         return FALSE;
368
369     if (wf->wFormatTag == WAVE_FORMAT_PCM) {
370         if (wf->nChannels==1||wf->nChannels==2) {
371             if (wf->wBitsPerSample==8||wf->wBitsPerSample==16)
372                 return TRUE;
373         }
374     } else if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
375         WAVEFORMATEXTENSIBLE    * wfex = (WAVEFORMATEXTENSIBLE *)wf;
376
377         if (wf->cbSize == 22 &&
378             (IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) ||
379              IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) {
380             if (wf->nChannels>=1 && wf->nChannels<=6) {
381                 if (wf->wBitsPerSample==wfex->Samples.wValidBitsPerSample) {
382                     if (wf->wBitsPerSample==8||wf->wBitsPerSample==16||
383                         wf->wBitsPerSample==24||wf->wBitsPerSample==32) {
384                         return TRUE;
385                     }
386                 } else
387                     WARN("wBitsPerSample != wValidBitsPerSample not supported yet\n");
388             }
389         } else
390             WARN("only KSDATAFORMAT_SUBTYPE_PCM and KSDATAFORMAT_SUBTYPE_IEEE_FLOAT "
391                  "supported\n");
392     } else
393         WARN("only WAVE_FORMAT_PCM and WAVE_FORMAT_EXTENSIBLE supported\n");
394
395     return FALSE;
396 }
397
398 /*======================================================================*
399  *                  Low level WAVE implementation                       *
400  *======================================================================*/
401
402 /**************************************************************************
403  *                      ALSA_CheckSetVolume             [internal]
404  *
405  *  Helper function for Alsa volume queries.  This tries to simplify
406  * the process of managing the volume.  All parameters are optional
407  * (pass NULL to ignore or not use).
408  *  Return values are MMSYSERR_NOERROR on success, or !0 on failure;
409  * error codes are normalized into the possible documented return
410  * values from waveOutGetVolume.
411  */
412 int ALSA_CheckSetVolume(snd_hctl_t *hctl, int *out_left, int *out_right,
413             int *out_min, int *out_max, int *out_step,
414             int *new_left, int *new_right)
415 {
416     int rc = MMSYSERR_NOERROR;
417     int value_count = 0;
418     snd_hctl_elem_t *           elem = NULL;
419     snd_ctl_elem_info_t *       eleminfop = NULL;
420     snd_ctl_elem_value_t *      elemvaluep = NULL;
421     snd_ctl_elem_id_t *         elemidp = NULL;
422     const char *names[] = {"PCM Playback Volume", "Line Playback Volume", NULL};
423     const char **name;
424
425
426 #define EXIT_ON_ERROR(f,txt,exitcode) do \
427 { \
428     int err; \
429     if ( (err = (f) ) < 0) \
430     { \
431         ERR(txt " failed: %s\n", snd_strerror(err)); \
432         rc = exitcode; \
433         goto out; \
434     } \
435 } while(0)
436
437     if (! hctl)
438         return MMSYSERR_NOTSUPPORTED;
439
440     /* Allocate areas to return information about the volume */
441     EXIT_ON_ERROR(snd_ctl_elem_id_malloc(&elemidp), "snd_ctl_elem_id_malloc", MMSYSERR_NOMEM);
442     EXIT_ON_ERROR(snd_ctl_elem_value_malloc (&elemvaluep), "snd_ctl_elem_value_malloc", MMSYSERR_NOMEM);
443     EXIT_ON_ERROR(snd_ctl_elem_info_malloc (&eleminfop), "snd_ctl_elem_info_malloc", MMSYSERR_NOMEM);
444     snd_ctl_elem_id_clear(elemidp);
445     snd_ctl_elem_value_clear(elemvaluep);
446     snd_ctl_elem_info_clear(eleminfop);
447
448     /* Setup and find an element id that exactly matches the characteristic we want
449     ** FIXME:  It is probably short sighted to hard code and fixate on PCM Playback Volume */
450
451     for( name = names; *name; name++ )
452     {
453         snd_ctl_elem_id_set_name(elemidp, *name);
454         snd_ctl_elem_id_set_interface(elemidp, SND_CTL_ELEM_IFACE_MIXER);
455         elem = snd_hctl_find_elem(hctl, elemidp);
456         if (elem)
457         {
458             /* Read and return volume information */
459             EXIT_ON_ERROR(snd_hctl_elem_info(elem, eleminfop), "snd_hctl_elem_info", MMSYSERR_NOTSUPPORTED);
460             value_count = snd_ctl_elem_info_get_count(eleminfop);
461             if (out_min || out_max || out_step)
462             {
463                 if (!snd_ctl_elem_info_is_readable(eleminfop))
464                 {
465                     ERR("snd_ctl_elem_info_is_readable returned false; cannot return info\n");
466                     rc = MMSYSERR_NOTSUPPORTED;
467                     goto out;
468                 }
469
470                 if (out_min)
471                     *out_min = snd_ctl_elem_info_get_min(eleminfop);
472
473                 if (out_max)
474                     *out_max = snd_ctl_elem_info_get_max(eleminfop);
475
476                 if (out_step)
477                     *out_step = snd_ctl_elem_info_get_step(eleminfop);
478             }
479
480             if (out_left || out_right)
481             {
482                 EXIT_ON_ERROR(snd_hctl_elem_read(elem, elemvaluep), "snd_hctl_elem_read", MMSYSERR_NOTSUPPORTED);
483
484                 if (out_left)
485                     *out_left = snd_ctl_elem_value_get_integer(elemvaluep, 0);
486
487                 if (out_right)
488                 {
489                     if (value_count == 1)
490                         *out_right = snd_ctl_elem_value_get_integer(elemvaluep, 0);
491                     else if (value_count == 2)
492                         *out_right = snd_ctl_elem_value_get_integer(elemvaluep, 1);
493                     else
494                     {
495                         ERR("Unexpected value count %d from snd_ctl_elem_info_get_count while getting volume info\n", value_count);
496                         rc = -1;
497                         goto out;
498                     }
499                 }
500             }
501
502             /* Set the volume */
503             if (new_left || new_right)
504             {
505                 EXIT_ON_ERROR(snd_hctl_elem_read(elem, elemvaluep), "snd_hctl_elem_read", MMSYSERR_NOTSUPPORTED);
506                 if (new_left)
507                     snd_ctl_elem_value_set_integer(elemvaluep, 0, *new_left);
508                 if (new_right)
509                 {
510                     if (value_count == 1)
511                         snd_ctl_elem_value_set_integer(elemvaluep, 0, *new_right);
512                     else if (value_count == 2)
513                         snd_ctl_elem_value_set_integer(elemvaluep, 1, *new_right);
514                     else
515                     {
516                         ERR("Unexpected value count %d from snd_ctl_elem_info_get_count while setting volume info\n", value_count);
517                         rc = -1;
518                         goto out;
519                     }
520                 }
521
522                 EXIT_ON_ERROR(snd_hctl_elem_write(elem, elemvaluep), "snd_hctl_elem_write", MMSYSERR_NOTSUPPORTED);
523             }
524
525             break;
526         }
527     }
528
529     if( !*name )
530     {
531         ERR("Could not find '{PCM,Line} Playback Volume' element\n");
532         rc = MMSYSERR_NOTSUPPORTED;
533     }
534
535
536 #undef EXIT_ON_ERROR
537
538 out:
539
540     if (elemvaluep)
541         snd_ctl_elem_value_free(elemvaluep);
542     if (eleminfop)
543         snd_ctl_elem_info_free(eleminfop);
544     if (elemidp)
545         snd_ctl_elem_id_free(elemidp);
546
547     return rc;
548 }
549
550
551 /**************************************************************************
552  *                      wine_snd_pcm_recover            [internal]
553  *
554  * Code slightly modified from alsa-lib v1.0.23 snd_pcm_recover implementation.
555  * used to recover from XRUN errors (buffer underflow/overflow)
556  */
557 int wine_snd_pcm_recover(snd_pcm_t *pcm, int err, int silent)
558 {
559     if (err > 0)
560         err = -err;
561     if (err == -EINTR)  /* nothing to do, continue */
562         return 0;
563     if (err == -EPIPE) {
564         const char *s;
565         if (snd_pcm_stream(pcm) == SND_PCM_STREAM_PLAYBACK)
566             s = "underrun";
567         else
568             s = "overrun";
569         if (!silent)
570             ERR("%s occurred\n", s);
571         err = snd_pcm_prepare(pcm);
572         if (err < 0) {
573             ERR("cannot recover from %s, prepare failed: %s\n", s, snd_strerror(err));
574             return err;
575         }
576         return 0;
577     }
578     if (err == -ESTRPIPE) {
579         while ((err = snd_pcm_resume(pcm)) == -EAGAIN)
580             /* wait until suspend flag is released */
581             poll(NULL, 0, 1000);
582         if (err < 0) {
583             err = snd_pcm_prepare(pcm);
584             if (err < 0) {
585                 ERR("cannot recover from suspend, prepare failed: %s\n", snd_strerror(err));
586                 return err;
587             }
588         }
589         return 0;
590     }
591     return err;
592 }
593
594 /**************************************************************************
595  *                      ALSA_TraceParameters            [internal]
596  *
597  * used to trace format changes, hw and sw parameters
598  */
599 void ALSA_TraceParameters(snd_pcm_hw_params_t * hw_params, snd_pcm_sw_params_t * sw, int full)
600 {
601     int err;
602     snd_pcm_format_t   format;
603     snd_pcm_access_t   access;
604
605 #define X(x) ((x)? "true" : "false")
606     if (full)
607         TRACE("FLAGS: sampleres=%s overrng=%s pause=%s resume=%s syncstart=%s batch=%s block=%s double=%s "
608               "halfd=%s joint=%s\n",
609               X(snd_pcm_hw_params_can_mmap_sample_resolution(hw_params)),
610               X(snd_pcm_hw_params_can_overrange(hw_params)),
611               X(snd_pcm_hw_params_can_pause(hw_params)),
612               X(snd_pcm_hw_params_can_resume(hw_params)),
613               X(snd_pcm_hw_params_can_sync_start(hw_params)),
614               X(snd_pcm_hw_params_is_batch(hw_params)),
615               X(snd_pcm_hw_params_is_block_transfer(hw_params)),
616               X(snd_pcm_hw_params_is_double(hw_params)),
617               X(snd_pcm_hw_params_is_half_duplex(hw_params)),
618               X(snd_pcm_hw_params_is_joint_duplex(hw_params)));
619 #undef X
620
621     err = snd_pcm_hw_params_get_access(hw_params, &access);
622     if (err >= 0)
623     {
624         TRACE("access=%s\n", snd_pcm_access_name(access));
625     }
626     else
627     {
628         snd_pcm_access_mask_t * acmask;
629
630         acmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_access_mask_sizeof());
631         snd_pcm_hw_params_get_access_mask(hw_params, acmask);
632         for ( access = SND_PCM_ACCESS_MMAP_INTERLEAVED; access <= SND_PCM_ACCESS_LAST; access++)
633             if (snd_pcm_access_mask_test(acmask, access))
634                 TRACE("access=%s\n", snd_pcm_access_name(access));
635         HeapFree( GetProcessHeap(), 0, acmask );
636     }
637
638     err = snd_pcm_hw_params_get_format(hw_params, &format);
639     if (err >= 0)
640     {
641         TRACE("format=%s\n", snd_pcm_format_name(format));
642
643     }
644     else
645     {
646         snd_pcm_format_mask_t *     fmask;
647
648         fmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
649         snd_pcm_hw_params_get_format_mask(hw_params, fmask);
650         for ( format = SND_PCM_FORMAT_S8; format <= SND_PCM_FORMAT_LAST ; format++)
651             if ( snd_pcm_format_mask_test(fmask, format) )
652                 TRACE("format=%s\n", snd_pcm_format_name(format));
653         HeapFree( GetProcessHeap(), 0, fmask );
654     }
655
656     do {
657       int err=0;
658       unsigned int val=0;
659       err = snd_pcm_hw_params_get_channels(hw_params, &val);
660       if (err<0) {
661         unsigned int min = 0;
662         unsigned int max = 0;
663         err = snd_pcm_hw_params_get_channels_min(hw_params, &min),
664         err = snd_pcm_hw_params_get_channels_max(hw_params, &max);
665         TRACE("channels_min=%u, channels_min_max=%u\n", min, max);
666       } else {
667         TRACE("channels=%d\n", val);
668       }
669     } while(0);
670     do {
671       int err=0;
672       snd_pcm_uframes_t val=0;
673       err = snd_pcm_hw_params_get_buffer_size(hw_params, &val);
674       if (err<0) {
675         snd_pcm_uframes_t min = 0;
676         snd_pcm_uframes_t max = 0;
677         err = snd_pcm_hw_params_get_buffer_size_min(hw_params, &min),
678         err = snd_pcm_hw_params_get_buffer_size_max(hw_params, &max);
679         TRACE("buffer_size_min=%lu, buffer_size_min_max=%lu\n", min, max);
680       } else {
681         TRACE("buffer_size=%lu\n", val);
682       }
683     } while(0);
684
685 #define X(x) do { \
686 int err=0; \
687 int dir=0; \
688 unsigned int val=0; \
689 err = snd_pcm_hw_params_get_##x(hw_params,&val, &dir); \
690 if (err<0) { \
691   unsigned int min = 0; \
692   unsigned int max = 0; \
693   err = snd_pcm_hw_params_get_##x##_min(hw_params, &min, &dir); \
694   err = snd_pcm_hw_params_get_##x##_max(hw_params, &max, &dir); \
695   TRACE(#x "_min=%u " #x "_max=%u\n", min, max); \
696 } else \
697     TRACE(#x "=%d\n", val); \
698 } while(0)
699
700     X(rate);
701     X(buffer_time);
702     X(periods);
703     do {
704       int err=0;
705       int dir=0;
706       snd_pcm_uframes_t val=0;
707       err = snd_pcm_hw_params_get_period_size(hw_params, &val, &dir);
708       if (err<0) {
709         snd_pcm_uframes_t min = 0;
710         snd_pcm_uframes_t max = 0;
711         err = snd_pcm_hw_params_get_period_size_min(hw_params, &min, &dir),
712         err = snd_pcm_hw_params_get_period_size_max(hw_params, &max, &dir);
713         TRACE("period_size_min=%lu, period_size_min_max=%lu\n", min, max);
714       } else {
715         TRACE("period_size=%lu\n", val);
716       }
717     } while(0);
718
719     X(period_time);
720 #undef X
721
722     if (!sw)
723         return;
724 }
725
726 /**************************************************************************
727  *                              DriverProc (WINEALSA.@)
728  */
729 LRESULT CALLBACK ALSA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
730                                  LPARAM dwParam1, LPARAM dwParam2)
731 {
732 /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
733 /* EPP    dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
734
735     switch(wMsg) {
736     case DRV_LOAD:
737     case DRV_FREE:
738     case DRV_OPEN:
739     case DRV_CLOSE:
740     case DRV_ENABLE:
741     case DRV_DISABLE:
742     case DRV_QUERYCONFIGURE:
743         return 1;
744     case DRV_CONFIGURE:         MessageBoxA(0, "ALSA MultiMedia Driver !", "ALSA Driver", MB_OK);       return 1;
745     case DRV_INSTALL:
746     case DRV_REMOVE:
747         return DRV_SUCCESS;
748     default:
749         return 0;
750     }
751 }