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