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