shdocvw, winemenubuilder: Generate fd.o entries for .url files.
[wine/hacks.git] / dlls / winealsa.drv / alsa.c
blob0623244fdfd4e3942788d59cc32e911133c5cf3b
1 /*
2 * Wine Driver for ALSA
4 * Copyright 2002 Eric Pouech
5 * Copyright 2007 Maarten Lankhorst
7 * This file has a few shared generic subroutines shared among the alsa
8 * implementation.
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.
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.
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
25 #include "config.h"
27 #include <stdarg.h>
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "mmddk.h"
35 #include "ks.h"
36 #include "guiddef.h"
37 #include "ksmedia.h"
39 #include "alsa.h"
41 #ifdef HAVE_ALSA
43 #include "wine/library.h"
44 #include "wine/unicode.h"
45 #include "wine/debug.h"
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
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
70 #define ALSA_RING_BUFFER_INCREMENT 64
72 /******************************************************************
73 * ALSA_InitRingMessage
75 * Initialize the ring of messages for passing between driver's caller and playback/record
76 * thread
78 int ALSA_InitRingMessage(ALSA_MSG_RING* omr)
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));
86 InitializeCriticalSection(&omr->msg_crst);
87 omr->msg_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": ALSA_MSG_RING.msg_crst");
88 return 0;
91 /******************************************************************
92 * ALSA_DestroyRingMessage
95 int ALSA_DestroyRingMessage(ALSA_MSG_RING* omr)
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;
104 /******************************************************************
105 * ALSA_ResetRingMessage
108 void ALSA_ResetRingMessage(ALSA_MSG_RING* omr)
110 RESET_OMR(omr);
113 /******************************************************************
114 * ALSA_WaitRingMessage
117 void ALSA_WaitRingMessage(ALSA_MSG_RING* omr, DWORD sleep)
119 WAIT_OMR(omr, sleep);
122 /******************************************************************
123 * ALSA_AddRingMessage
125 * Inserts a new message into the ring (should be called from DriverProc derived routines)
127 int ALSA_AddRingMessage(ALSA_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait)
129 HANDLE hEvent = INVALID_HANDLE_VALUE;
131 EnterCriticalSection(&omr->msg_crst);
132 if ((omr->msg_toget == ((omr->msg_tosave + 1) % omr->ring_buffer_size)))
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.
141 if (omr->msg_tosave < omr->msg_toget)
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)
147 omr->msg_toget += ALSA_RING_BUFFER_INCREMENT;
150 if (wait)
152 hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
153 if (hEvent == INVALID_HANDLE_VALUE)
155 ERR("can't create event !?\n");
156 LeaveCriticalSection(&omr->msg_crst);
157 return 0;
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));
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;
167 omr->messages[omr->msg_toget].msg = msg;
168 omr->messages[omr->msg_toget].param = param;
169 omr->messages[omr->msg_toget].hEvent = hEvent;
171 else
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;
178 LeaveCriticalSection(&omr->msg_crst);
179 /* signal a new message */
180 SIGNAL_OMR(omr);
181 if (wait)
183 /* wait for playback/record thread to have processed the message */
184 WaitForSingleObject(hEvent, INFINITE);
185 CloseHandle(hEvent);
187 return 1;
190 /******************************************************************
191 * ALSA_RetrieveRingMessage
193 * Get a message from the ring. Should be called by the playback/record thread.
195 int ALSA_RetrieveRingMessage(ALSA_MSG_RING* omr,
196 enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
198 EnterCriticalSection(&omr->msg_crst);
200 if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
202 LeaveCriticalSection(&omr->msg_crst);
203 return 0;
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;
216 /******************************************************************
217 * ALSA_PeekRingMessage
219 * Peek at a message from the ring but do not remove it.
220 * Should be called by the playback/record thread.
222 int ALSA_PeekRingMessage(ALSA_MSG_RING* omr,
223 enum win_wm_message *msg,
224 DWORD *param, HANDLE *hEvent)
226 EnterCriticalSection(&omr->msg_crst);
228 if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
230 LeaveCriticalSection(&omr->msg_crst);
231 return 0;
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;
241 /*======================================================================*
242 * Utility functions *
243 *======================================================================*/
245 /* These strings used only for tracing */
246 const char * ALSA_getCmdString(enum win_wm_message msg)
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);
261 #undef MSG_TO_STR
262 sprintf(unknown, "UNKNOWN(0x%08x)", msg);
263 return unknown;
266 const char * ALSA_getMessage(UINT msg)
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);
309 #undef MSG_TO_STR
310 sprintf(unknown, "UNKNOWN(0x%04x)", msg);
311 return unknown;
314 const char * ALSA_getFormat(WORD wFormatTag)
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);
325 #undef FMT_TO_STR
326 sprintf(unknown, "UNKNOWN(0x%04x)", wFormatTag);
327 return unknown;
330 /* Allow 1% deviation for sample rates (some ES137x cards) */
331 BOOL ALSA_NearMatch(int rate1, int rate2)
333 return (((100 * (rate1 - rate2)) / rate1) == 0);
336 DWORD ALSA_bytes_to_mmtime(LPMMTIME lpTime, DWORD position, WAVEFORMATPCMEX* format)
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);
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;
377 return MMSYSERR_NOERROR;
380 void ALSA_copyFormat(LPWAVEFORMATEX wf1, LPWAVEFORMATPCMEX wf2)
382 unsigned int iLength;
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);
395 memcpy(wf2, wf1, iLength);
398 BOOL ALSA_supportedFormat(LPWAVEFORMATEX wf)
400 TRACE("(%p)\n",wf);
402 if (wf->nSamplesPerSec<DSBFREQUENCY_MIN||wf->nSamplesPerSec>DSBFREQUENCY_MAX)
403 return FALSE;
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;
410 } else if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
411 WAVEFORMATEXTENSIBLE * wfex = (WAVEFORMATEXTENSIBLE *)wf;
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;
422 } else
423 WARN("wBitsPerSample != wValidBitsPerSample not supported yet\n");
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");
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");
442 return FALSE;
445 /*======================================================================*
446 * Low level WAVE implementation *
447 *======================================================================*/
449 /**************************************************************************
450 * ALSA_CheckSetVolume [internal]
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.
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)
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;
471 #define EXIT_ON_ERROR(f,txt,exitcode) do \
473 int err; \
474 if ( (err = (f) ) < 0) \
476 ERR(txt " failed: %s\n", snd_strerror(err)); \
477 rc = exitcode; \
478 goto out; \
480 } while(0)
482 if (! hctl)
483 return MMSYSERR_NOTSUPPORTED;
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);
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)
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)
505 if (!snd_ctl_elem_info_is_readable(eleminfop))
507 ERR("snd_ctl_elem_info_is_readable returned false; cannot return info\n");
508 rc = MMSYSERR_NOTSUPPORTED;
509 goto out;
512 if (out_min)
513 *out_min = snd_ctl_elem_info_get_min(eleminfop);
515 if (out_max)
516 *out_max = snd_ctl_elem_info_get_max(eleminfop);
518 if (out_step)
519 *out_step = snd_ctl_elem_info_get_step(eleminfop);
522 if (out_left || out_right)
524 EXIT_ON_ERROR(snd_hctl_elem_read(elem, elemvaluep), "snd_hctl_elem_read", MMSYSERR_NOTSUPPORTED);
526 if (out_left)
527 *out_left = snd_ctl_elem_value_get_integer(elemvaluep, 0);
529 if (out_right)
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
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;
544 /* Set the volume */
545 if (new_left || new_right)
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)
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
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;
564 EXIT_ON_ERROR(snd_hctl_elem_write(elem, elemvaluep), "snd_hctl_elem_write", MMSYSERR_NOTSUPPORTED);
567 else
569 ERR("Could not find 'PCM Playback Volume' element\n");
570 rc = MMSYSERR_NOTSUPPORTED;
574 #undef EXIT_ON_ERROR
576 out:
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);
585 return rc;
589 /**************************************************************************
590 * ALSA_XRUNRecovery [internal]
592 * used to recovery from XRUN errors (buffer underflow/overflow)
594 int ALSA_XRUNRecovery(WINE_WAVEDEV * wwo, int err)
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));
609 return 0;
611 return err;
614 /**************************************************************************
615 * ALSA_TraceParameters [internal]
617 * used to trace format changes, hw and sw parameters
619 void ALSA_TraceParameters(snd_pcm_hw_params_t * hw_params, snd_pcm_sw_params_t * sw, int full)
621 int err;
622 snd_pcm_format_t format;
623 snd_pcm_access_t access;
625 #define X(x) ((x)? "true" : "false")
626 if (full)
627 TRACE("FLAGS: sampleres=%s overrng=%s pause=%s resume=%s syncstart=%s batch=%s block=%s double=%s "
628 "halfd=%s joint=%s\n",
629 X(snd_pcm_hw_params_can_mmap_sample_resolution(hw_params)),
630 X(snd_pcm_hw_params_can_overrange(hw_params)),
631 X(snd_pcm_hw_params_can_pause(hw_params)),
632 X(snd_pcm_hw_params_can_resume(hw_params)),
633 X(snd_pcm_hw_params_can_sync_start(hw_params)),
634 X(snd_pcm_hw_params_is_batch(hw_params)),
635 X(snd_pcm_hw_params_is_block_transfer(hw_params)),
636 X(snd_pcm_hw_params_is_double(hw_params)),
637 X(snd_pcm_hw_params_is_half_duplex(hw_params)),
638 X(snd_pcm_hw_params_is_joint_duplex(hw_params)));
639 #undef X
641 err = snd_pcm_hw_params_get_access(hw_params, &access);
642 if (err >= 0)
644 TRACE("access=%s\n", snd_pcm_access_name(access));
646 else
648 snd_pcm_access_mask_t * acmask;
650 acmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_access_mask_sizeof());
651 snd_pcm_hw_params_get_access_mask(hw_params, acmask);
652 for ( access = SND_PCM_ACCESS_MMAP_INTERLEAVED; access <= SND_PCM_ACCESS_LAST; access++)
653 if (snd_pcm_access_mask_test(acmask, access))
654 TRACE("access=%s\n", snd_pcm_access_name(access));
655 HeapFree( GetProcessHeap(), 0, acmask );
658 err = snd_pcm_hw_params_get_format(hw_params, &format);
659 if (err >= 0)
661 TRACE("format=%s\n", snd_pcm_format_name(format));
664 else
666 snd_pcm_format_mask_t * fmask;
668 fmask = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
669 snd_pcm_hw_params_get_format_mask(hw_params, fmask);
670 for ( format = SND_PCM_FORMAT_S8; format <= SND_PCM_FORMAT_LAST ; format++)
671 if ( snd_pcm_format_mask_test(fmask, format) )
672 TRACE("format=%s\n", snd_pcm_format_name(format));
673 HeapFree( GetProcessHeap(), 0, fmask );
676 do {
677 int err=0;
678 unsigned int val=0;
679 err = snd_pcm_hw_params_get_channels(hw_params, &val);
680 if (err<0) {
681 unsigned int min = 0;
682 unsigned int max = 0;
683 err = snd_pcm_hw_params_get_channels_min(hw_params, &min),
684 err = snd_pcm_hw_params_get_channels_max(hw_params, &max);
685 TRACE("channels_min=%u, channels_min_max=%u\n", min, max);
686 } else {
687 TRACE("channels=%d\n", val);
689 } while(0);
690 do {
691 int err=0;
692 snd_pcm_uframes_t val=0;
693 err = snd_pcm_hw_params_get_buffer_size(hw_params, &val);
694 if (err<0) {
695 snd_pcm_uframes_t min = 0;
696 snd_pcm_uframes_t max = 0;
697 err = snd_pcm_hw_params_get_buffer_size_min(hw_params, &min),
698 err = snd_pcm_hw_params_get_buffer_size_max(hw_params, &max);
699 TRACE("buffer_size_min=%lu, buffer_size_min_max=%lu\n", min, max);
700 } else {
701 TRACE("buffer_size=%lu\n", val);
703 } while(0);
705 #define X(x) do { \
706 int err=0; \
707 int dir=0; \
708 unsigned int val=0; \
709 err = snd_pcm_hw_params_get_##x(hw_params,&val, &dir); \
710 if (err<0) { \
711 unsigned int min = 0; \
712 unsigned int max = 0; \
713 err = snd_pcm_hw_params_get_##x##_min(hw_params, &min, &dir); \
714 err = snd_pcm_hw_params_get_##x##_max(hw_params, &max, &dir); \
715 TRACE(#x "_min=%u " #x "_max=%u\n", min, max); \
716 } else \
717 TRACE(#x "=%d\n", val); \
718 } while(0)
720 X(rate);
721 X(buffer_time);
722 X(periods);
723 do {
724 int err=0;
725 int dir=0;
726 snd_pcm_uframes_t val=0;
727 err = snd_pcm_hw_params_get_period_size(hw_params, &val, &dir);
728 if (err<0) {
729 snd_pcm_uframes_t min = 0;
730 snd_pcm_uframes_t max = 0;
731 err = snd_pcm_hw_params_get_period_size_min(hw_params, &min, &dir),
732 err = snd_pcm_hw_params_get_period_size_max(hw_params, &max, &dir);
733 TRACE("period_size_min=%lu, period_size_min_max=%lu\n", min, max);
734 } else {
735 TRACE("period_size=%lu\n", val);
737 } while(0);
739 X(period_time);
740 #undef X
742 if (!sw)
743 return;
746 #endif
748 /**************************************************************************
749 * DriverProc (WINEALSA.@)
751 LRESULT CALLBACK ALSA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
752 LPARAM dwParam1, LPARAM dwParam2)
754 /* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
755 /* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
757 switch(wMsg) {
758 #ifdef HAVE_ALSA
759 case DRV_LOAD: ALSA_WaveInit();
760 ALSA_MidiInit();
761 return 1;
762 case DRV_FREE: return 1;
763 case DRV_OPEN: return 1;
764 case DRV_CLOSE: return 1;
765 case DRV_ENABLE: return 1;
766 case DRV_DISABLE: return 1;
767 case DRV_QUERYCONFIGURE: return 1;
768 case DRV_CONFIGURE: MessageBoxA(0, "ALSA MultiMedia Driver !", "ALSA Driver", MB_OK); return 1;
769 case DRV_INSTALL: return DRVCNF_RESTART;
770 case DRV_REMOVE: return DRVCNF_RESTART;
771 #endif
772 default:
773 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);