msdaps: Make use of the row server in ICreateRow_CreateRow.
[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_PTR 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, enum win_wm_message *msg,
196                              DWORD_PTR *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  *                  Utility functions                                   *
218  *======================================================================*/
219
220 /* These strings used only for tracing */
221 const char * ALSA_getCmdString(enum win_wm_message msg)
222 {
223 #define MSG_TO_STR(x) case x: return #x
224     switch(msg) {
225     MSG_TO_STR(WINE_WM_PAUSING);
226     MSG_TO_STR(WINE_WM_RESTARTING);
227     MSG_TO_STR(WINE_WM_RESETTING);
228     MSG_TO_STR(WINE_WM_HEADER);
229     MSG_TO_STR(WINE_WM_UPDATE);
230     MSG_TO_STR(WINE_WM_BREAKLOOP);
231     MSG_TO_STR(WINE_WM_CLOSING);
232     MSG_TO_STR(WINE_WM_STARTING);
233     MSG_TO_STR(WINE_WM_STOPPING);
234     }
235 #undef MSG_TO_STR
236     return wine_dbg_sprintf("UNKNOWN(0x%08x)", msg);
237 }
238
239 const char * ALSA_getMessage(UINT msg)
240 {
241 #define MSG_TO_STR(x) case x: return #x
242     switch(msg) {
243     MSG_TO_STR(DRVM_INIT);
244     MSG_TO_STR(DRVM_EXIT);
245     MSG_TO_STR(DRVM_ENABLE);
246     MSG_TO_STR(DRVM_DISABLE);
247     MSG_TO_STR(WIDM_OPEN);
248     MSG_TO_STR(WIDM_CLOSE);
249     MSG_TO_STR(WIDM_ADDBUFFER);
250     MSG_TO_STR(WIDM_PREPARE);
251     MSG_TO_STR(WIDM_UNPREPARE);
252     MSG_TO_STR(WIDM_GETDEVCAPS);
253     MSG_TO_STR(WIDM_GETNUMDEVS);
254     MSG_TO_STR(WIDM_GETPOS);
255     MSG_TO_STR(WIDM_RESET);
256     MSG_TO_STR(WIDM_START);
257     MSG_TO_STR(WIDM_STOP);
258     MSG_TO_STR(WODM_OPEN);
259     MSG_TO_STR(WODM_CLOSE);
260     MSG_TO_STR(WODM_WRITE);
261     MSG_TO_STR(WODM_PAUSE);
262     MSG_TO_STR(WODM_GETPOS);
263     MSG_TO_STR(WODM_BREAKLOOP);
264     MSG_TO_STR(WODM_PREPARE);
265     MSG_TO_STR(WODM_UNPREPARE);
266     MSG_TO_STR(WODM_GETDEVCAPS);
267     MSG_TO_STR(WODM_GETNUMDEVS);
268     MSG_TO_STR(WODM_GETPITCH);
269     MSG_TO_STR(WODM_SETPITCH);
270     MSG_TO_STR(WODM_GETPLAYBACKRATE);
271     MSG_TO_STR(WODM_SETPLAYBACKRATE);
272     MSG_TO_STR(WODM_GETVOLUME);
273     MSG_TO_STR(WODM_SETVOLUME);
274     MSG_TO_STR(WODM_RESTART);
275     MSG_TO_STR(WODM_RESET);
276     MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE);
277     MSG_TO_STR(DRV_QUERYDEVICEINTERFACE);
278     MSG_TO_STR(DRV_QUERYDSOUNDIFACE);
279     MSG_TO_STR(DRV_QUERYDSOUNDDESC);
280     }
281 #undef MSG_TO_STR
282     return wine_dbg_sprintf("UNKNOWN(0x%04x)", msg);
283 }
284
285 const char * ALSA_getFormat(WORD wFormatTag)
286 {
287 #define FMT_TO_STR(x) case x: return #x
288     switch(wFormatTag) {
289     FMT_TO_STR(WAVE_FORMAT_PCM);
290     FMT_TO_STR(WAVE_FORMAT_EXTENSIBLE);
291     FMT_TO_STR(WAVE_FORMAT_MULAW);
292     FMT_TO_STR(WAVE_FORMAT_ALAW);
293     FMT_TO_STR(WAVE_FORMAT_ADPCM);
294     }
295 #undef FMT_TO_STR
296     return wine_dbg_sprintf("UNKNOWN(0x%04x)", wFormatTag);
297 }
298
299 /* Allow 1% deviation for sample rates (some ES137x cards) */
300 BOOL ALSA_NearMatch(int rate1, int rate2)
301 {
302     return (((100 * (rate1 - rate2)) / rate1) == 0);
303 }
304
305 DWORD ALSA_bytes_to_mmtime(LPMMTIME lpTime, DWORD position, WAVEFORMATPCMEX* format)
306 {
307     TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%u nChannels=%u nAvgBytesPerSec=%u\n",
308           lpTime->wType, format->Format.wBitsPerSample, format->Format.nSamplesPerSec,
309           format->Format.nChannels, format->Format.nAvgBytesPerSec);
310     TRACE("Position in bytes=%u\n", position);
311
312     switch (lpTime->wType) {
313     case TIME_SAMPLES:
314         lpTime->u.sample = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
315         TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample);
316         break;
317     case TIME_MS:
318         lpTime->u.ms = 1000.0 * position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels * format->Format.nSamplesPerSec);
319         TRACE("TIME_MS=%u\n", lpTime->u.ms);
320         break;
321     case TIME_SMPTE:
322         lpTime->u.smpte.fps = 30;
323         position = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
324         position += (format->Format.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
325         lpTime->u.smpte.sec = position / format->Format.nSamplesPerSec;
326         position -= lpTime->u.smpte.sec * format->Format.nSamplesPerSec;
327         lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
328         lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
329         lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
330         lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
331         lpTime->u.smpte.fps = 30;
332         lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->Format.nSamplesPerSec;
333         TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
334               lpTime->u.smpte.hour, lpTime->u.smpte.min,
335               lpTime->u.smpte.sec, lpTime->u.smpte.frame);
336         break;
337     default:
338         WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
339         lpTime->wType = TIME_BYTES;
340         /* fall through */
341     case TIME_BYTES:
342         lpTime->u.cb = position;
343         TRACE("TIME_BYTES=%u\n", lpTime->u.cb);
344         break;
345     }
346     return MMSYSERR_NOERROR;
347 }
348
349 void ALSA_copyFormat(LPWAVEFORMATEX wf1, LPWAVEFORMATPCMEX wf2)
350 {
351     unsigned int iLength;
352
353     ZeroMemory(wf2, sizeof(wf2));
354     if (wf1->wFormatTag == WAVE_FORMAT_PCM)
355         iLength = sizeof(PCMWAVEFORMAT);
356     else if (wf1->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
357         iLength = sizeof(WAVEFORMATPCMEX);
358     else
359         iLength = sizeof(WAVEFORMATEX) + wf1->cbSize;
360     if (iLength > sizeof(WAVEFORMATPCMEX)) {
361         ERR("calculated %u bytes, capping\n", iLength);
362         iLength = sizeof(WAVEFORMATPCMEX);
363     }
364     memcpy(wf2, wf1, iLength);
365 }
366
367 BOOL ALSA_supportedFormat(LPWAVEFORMATEX wf)
368 {
369     TRACE("(%p)\n",wf);
370
371     if (wf->nSamplesPerSec<DSBFREQUENCY_MIN||wf->nSamplesPerSec>DSBFREQUENCY_MAX)
372         return FALSE;
373
374     if (wf->wFormatTag == WAVE_FORMAT_PCM) {
375         if (wf->nChannels==1||wf->nChannels==2) {
376             if (wf->wBitsPerSample==8||wf->wBitsPerSample==16)
377                 return TRUE;
378         }
379     } else if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
380         WAVEFORMATEXTENSIBLE    * wfex = (WAVEFORMATEXTENSIBLE *)wf;
381
382         if (wf->cbSize == 22 &&
383             (IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) ||
384              IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) {
385             if (wf->nChannels>=1 && wf->nChannels<=6) {
386                 if (wf->wBitsPerSample==wfex->Samples.wValidBitsPerSample) {
387                     if (wf->wBitsPerSample==8||wf->wBitsPerSample==16||
388                         wf->wBitsPerSample==24||wf->wBitsPerSample==32) {
389                         return TRUE;
390                     }
391                 } else
392                     WARN("wBitsPerSample != wValidBitsPerSample not supported yet\n");
393             }
394         } else
395             WARN("only KSDATAFORMAT_SUBTYPE_PCM and KSDATAFORMAT_SUBTYPE_IEEE_FLOAT "
396                  "supported\n");
397     } else if (wf->wFormatTag == WAVE_FORMAT_MULAW || wf->wFormatTag == WAVE_FORMAT_ALAW) {
398         if (wf->wBitsPerSample==8)
399             return TRUE;
400         else
401             ERR("WAVE_FORMAT_MULAW and WAVE_FORMAT_ALAW wBitsPerSample must = 8\n");
402
403     } else if (wf->wFormatTag == WAVE_FORMAT_ADPCM) {
404         if (wf->wBitsPerSample==4)
405             return TRUE;
406         else
407             ERR("WAVE_FORMAT_ADPCM wBitsPerSample must = 4\n");
408     } else
409         WARN("only WAVE_FORMAT_PCM and WAVE_FORMAT_EXTENSIBLE supported\n");
410
411     return FALSE;
412 }
413
414 /*======================================================================*
415  *                  Low level WAVE implementation                       *
416  *======================================================================*/
417
418 /**************************************************************************
419  *                      ALSA_CheckSetVolume             [internal]
420  *
421  *  Helper function for Alsa volume queries.  This tries to simplify
422  * the process of managing the volume.  All parameters are optional
423  * (pass NULL to ignore or not use).
424  *  Return values are MMSYSERR_NOERROR on success, or !0 on failure;
425  * error codes are normalized into the possible documented return
426  * values from waveOutGetVolume.
427  */
428 int ALSA_CheckSetVolume(snd_hctl_t *hctl, int *out_left, int *out_right,
429             int *out_min, int *out_max, int *out_step,
430             int *new_left, int *new_right)
431 {
432     int rc = MMSYSERR_NOERROR;
433     int value_count = 0;
434     snd_hctl_elem_t *           elem = NULL;
435     snd_ctl_elem_info_t *       eleminfop = NULL;
436     snd_ctl_elem_value_t *      elemvaluep = NULL;
437     snd_ctl_elem_id_t *         elemidp = NULL;
438
439
440 #define EXIT_ON_ERROR(f,txt,exitcode) do \
441 { \
442     int err; \
443     if ( (err = (f) ) < 0) \
444     { \
445         ERR(txt " failed: %s\n", snd_strerror(err)); \
446         rc = exitcode; \
447         goto out; \
448     } \
449 } while(0)
450
451     if (! hctl)
452         return MMSYSERR_NOTSUPPORTED;
453
454     /* Allocate areas to return information about the volume */
455     EXIT_ON_ERROR(snd_ctl_elem_id_malloc(&elemidp), "snd_ctl_elem_id_malloc", MMSYSERR_NOMEM);
456     EXIT_ON_ERROR(snd_ctl_elem_value_malloc (&elemvaluep), "snd_ctl_elem_value_malloc", MMSYSERR_NOMEM);
457     EXIT_ON_ERROR(snd_ctl_elem_info_malloc (&eleminfop), "snd_ctl_elem_info_malloc", MMSYSERR_NOMEM);
458     snd_ctl_elem_id_clear(elemidp);
459     snd_ctl_elem_value_clear(elemvaluep);
460     snd_ctl_elem_info_clear(eleminfop);
461
462     /* Setup and find an element id that exactly matches the characteristic we want
463     ** FIXME:  It is probably short sighted to hard code and fixate on PCM Playback Volume */
464     snd_ctl_elem_id_set_name(elemidp, "PCM Playback Volume");
465     snd_ctl_elem_id_set_interface(elemidp, SND_CTL_ELEM_IFACE_MIXER);
466     elem = snd_hctl_find_elem(hctl, elemidp);
467     if (elem)
468     {
469         /* Read and return volume information */
470         EXIT_ON_ERROR(snd_hctl_elem_info(elem, eleminfop), "snd_hctl_elem_info", MMSYSERR_NOTSUPPORTED);
471         value_count = snd_ctl_elem_info_get_count(eleminfop);
472         if (out_min || out_max || out_step)
473         {
474             if (!snd_ctl_elem_info_is_readable(eleminfop))
475             {
476                 ERR("snd_ctl_elem_info_is_readable returned false; cannot return info\n");
477                 rc = MMSYSERR_NOTSUPPORTED;
478                 goto out;
479             }
480
481             if (out_min)
482                 *out_min = snd_ctl_elem_info_get_min(eleminfop);
483
484             if (out_max)
485                 *out_max = snd_ctl_elem_info_get_max(eleminfop);
486
487             if (out_step)
488                 *out_step = snd_ctl_elem_info_get_step(eleminfop);
489         }
490
491         if (out_left || out_right)
492         {
493             EXIT_ON_ERROR(snd_hctl_elem_read(elem, elemvaluep), "snd_hctl_elem_read", MMSYSERR_NOTSUPPORTED);
494
495             if (out_left)
496                 *out_left = snd_ctl_elem_value_get_integer(elemvaluep, 0);
497
498             if (out_right)
499             {
500                 if (value_count == 1)
501                     *out_right = snd_ctl_elem_value_get_integer(elemvaluep, 0);
502                 else if (value_count == 2)
503                     *out_right = snd_ctl_elem_value_get_integer(elemvaluep, 1);
504                 else
505                 {
506                     ERR("Unexpected value count %d from snd_ctl_elem_info_get_count while getting volume info\n", value_count);
507                     rc = -1;
508                     goto out;
509                 }
510             }
511         }
512
513         /* Set the volume */
514         if (new_left || new_right)
515         {
516             EXIT_ON_ERROR(snd_hctl_elem_read(elem, elemvaluep), "snd_hctl_elem_read", MMSYSERR_NOTSUPPORTED);
517             if (new_left)
518                 snd_ctl_elem_value_set_integer(elemvaluep, 0, *new_left);
519             if (new_right)
520             {
521                 if (value_count == 1)
522                     snd_ctl_elem_value_set_integer(elemvaluep, 0, *new_right);
523                 else if (value_count == 2)
524                     snd_ctl_elem_value_set_integer(elemvaluep, 1, *new_right);
525                 else
526                 {
527                     ERR("Unexpected value count %d from snd_ctl_elem_info_get_count while setting volume info\n", value_count);
528                     rc = -1;
529                     goto out;
530                 }
531             }
532
533             EXIT_ON_ERROR(snd_hctl_elem_write(elem, elemvaluep), "snd_hctl_elem_write", MMSYSERR_NOTSUPPORTED);
534         }
535     }
536     else
537     {
538         ERR("Could not find 'PCM Playback Volume' element\n");
539         rc = MMSYSERR_NOTSUPPORTED;
540     }
541
542
543 #undef EXIT_ON_ERROR
544
545 out:
546
547     if (elemvaluep)
548         snd_ctl_elem_value_free(elemvaluep);
549     if (eleminfop)
550         snd_ctl_elem_info_free(eleminfop);
551     if (elemidp)
552         snd_ctl_elem_id_free(elemidp);
553
554     return rc;
555 }
556
557
558 /**************************************************************************
559  *                      ALSA_XRUNRecovery               [internal]
560  *
561  * used to recovery from XRUN errors (buffer underflow/overflow)
562  */
563 int ALSA_XRUNRecovery(WINE_WAVEDEV * wwo, int err)
564 {
565     if (err == -EPIPE) {    /* under-run */
566         err = snd_pcm_prepare(wwo->pcm);
567         if (err < 0)
568              ERR( "underrun recovery failed. prepare failed: %s\n", snd_strerror(err));
569         return 0;
570     } else if (err == -ESTRPIPE) {
571         while ((err = snd_pcm_resume(wwo->pcm)) == -EAGAIN)
572             sleep(1);       /* wait until the suspend flag is released */
573         if (err < 0) {
574             err = snd_pcm_prepare(wwo->pcm);
575             if (err < 0)
576                 ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err));
577         }
578         return 0;
579     }
580     return err;
581 }
582
583 /**************************************************************************
584  *                      ALSA_TraceParameters            [internal]
585  *
586  * used to trace format changes, hw and sw parameters
587  */
588 void ALSA_TraceParameters(snd_pcm_hw_params_t * hw_params, snd_pcm_sw_params_t * sw, int full)
589 {
590     int err;
591     snd_pcm_format_t   format;
592     snd_pcm_access_t   access;
593
594 #define X(x) ((x)? "true" : "false")
595     if (full)
596         TRACE("FLAGS: sampleres=%s overrng=%s pause=%s resume=%s syncstart=%s batch=%s block=%s double=%s "
597               "halfd=%s joint=%s\n",
598               X(snd_pcm_hw_params_can_mmap_sample_resolution(hw_params)),
599               X(snd_pcm_hw_params_can_overrange(hw_params)),
600               X(snd_pcm_hw_params_can_pause(hw_params)),
601               X(snd_pcm_hw_params_can_resume(hw_params)),
602               X(snd_pcm_hw_params_can_sync_start(hw_params)),
603               X(snd_pcm_hw_params_is_batch(hw_params)),
604               X(snd_pcm_hw_params_is_block_transfer(hw_params)),
605               X(snd_pcm_hw_params_is_double(hw_params)),
606               X(snd_pcm_hw_params_is_half_duplex(hw_params)),
607               X(snd_pcm_hw_params_is_joint_duplex(hw_params)));
608 #undef X
609
610     err = snd_pcm_hw_params_get_access(hw_params, &access);
611     if (err >= 0)
612     {
613         TRACE("access=%s\n", snd_pcm_access_name(access));
614     }
615     else
616     {
617         snd_pcm_access_mask_t * acmask;
618
619         acmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_access_mask_sizeof());
620         snd_pcm_hw_params_get_access_mask(hw_params, acmask);
621         for ( access = SND_PCM_ACCESS_MMAP_INTERLEAVED; access <= SND_PCM_ACCESS_LAST; access++)
622             if (snd_pcm_access_mask_test(acmask, access))
623                 TRACE("access=%s\n", snd_pcm_access_name(access));
624         HeapFree( GetProcessHeap(), 0, acmask );
625     }
626
627     err = snd_pcm_hw_params_get_format(hw_params, &format);
628     if (err >= 0)
629     {
630         TRACE("format=%s\n", snd_pcm_format_name(format));
631
632     }
633     else
634     {
635         snd_pcm_format_mask_t *     fmask;
636
637         fmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
638         snd_pcm_hw_params_get_format_mask(hw_params, fmask);
639         for ( format = SND_PCM_FORMAT_S8; format <= SND_PCM_FORMAT_LAST ; format++)
640             if ( snd_pcm_format_mask_test(fmask, format) )
641                 TRACE("format=%s\n", snd_pcm_format_name(format));
642         HeapFree( GetProcessHeap(), 0, fmask );
643     }
644
645     do {
646       int err=0;
647       unsigned int val=0;
648       err = snd_pcm_hw_params_get_channels(hw_params, &val);
649       if (err<0) {
650         unsigned int min = 0;
651         unsigned int max = 0;
652         err = snd_pcm_hw_params_get_channels_min(hw_params, &min),
653         err = snd_pcm_hw_params_get_channels_max(hw_params, &max);
654         TRACE("channels_min=%u, channels_min_max=%u\n", min, max);
655       } else {
656         TRACE("channels=%d\n", val);
657       }
658     } while(0);
659     do {
660       int err=0;
661       snd_pcm_uframes_t val=0;
662       err = snd_pcm_hw_params_get_buffer_size(hw_params, &val);
663       if (err<0) {
664         snd_pcm_uframes_t min = 0;
665         snd_pcm_uframes_t max = 0;
666         err = snd_pcm_hw_params_get_buffer_size_min(hw_params, &min),
667         err = snd_pcm_hw_params_get_buffer_size_max(hw_params, &max);
668         TRACE("buffer_size_min=%lu, buffer_size_min_max=%lu\n", min, max);
669       } else {
670         TRACE("buffer_size=%lu\n", val);
671       }
672     } while(0);
673
674 #define X(x) do { \
675 int err=0; \
676 int dir=0; \
677 unsigned int val=0; \
678 err = snd_pcm_hw_params_get_##x(hw_params,&val, &dir); \
679 if (err<0) { \
680   unsigned int min = 0; \
681   unsigned int max = 0; \
682   err = snd_pcm_hw_params_get_##x##_min(hw_params, &min, &dir); \
683   err = snd_pcm_hw_params_get_##x##_max(hw_params, &max, &dir); \
684   TRACE(#x "_min=%u " #x "_max=%u\n", min, max); \
685 } else \
686     TRACE(#x "=%d\n", val); \
687 } while(0)
688
689     X(rate);
690     X(buffer_time);
691     X(periods);
692     do {
693       int err=0;
694       int dir=0;
695       snd_pcm_uframes_t val=0;
696       err = snd_pcm_hw_params_get_period_size(hw_params, &val, &dir);
697       if (err<0) {
698         snd_pcm_uframes_t min = 0;
699         snd_pcm_uframes_t max = 0;
700         err = snd_pcm_hw_params_get_period_size_min(hw_params, &min, &dir),
701         err = snd_pcm_hw_params_get_period_size_max(hw_params, &max, &dir);
702         TRACE("period_size_min=%lu, period_size_min_max=%lu\n", min, max);
703       } else {
704         TRACE("period_size=%lu\n", val);
705       }
706     } while(0);
707
708     X(period_time);
709 #undef X
710
711     if (!sw)
712         return;
713 }
714
715 #endif
716
717 /**************************************************************************
718  *                              DriverProc (WINEALSA.@)
719  */
720 LRESULT CALLBACK ALSA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
721                                  LPARAM dwParam1, LPARAM dwParam2)
722 {
723 /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
724 /* EPP    dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
725
726     switch(wMsg) {
727 #ifdef HAVE_ALSA
728     case DRV_LOAD:              ALSA_WaveInit();
729                                 ALSA_MidiInit();
730                                 return 1;
731     case DRV_FREE:              return 1;
732     case DRV_OPEN:              return 1;
733     case DRV_CLOSE:             return 1;
734     case DRV_ENABLE:            return 1;
735     case DRV_DISABLE:           return 1;
736     case DRV_QUERYCONFIGURE:    return 1;
737     case DRV_CONFIGURE:         MessageBoxA(0, "ALSA MultiMedia Driver !", "ALSA Driver", MB_OK);       return 1;
738     case DRV_INSTALL:           return DRVCNF_RESTART;
739     case DRV_REMOVE:            return DRVCNF_RESTART;
740 #endif
741     default:
742         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
743     }
744 }