1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for Advanced Linux Sound System (ALSA)
4 * Based on version <final> of the ALSA API
6 * Copyright 2002 Eric Pouech
7 * 2002 Marco Pietrobono
8 * 2003 Christian Costa : WaveIn support
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 /* unless someone makes a wineserver kernel module, Unix pipes are faster than win32 events */
29 #include "wine/port.h"
41 #ifdef HAVE_SYS_IOCTL_H
42 # include <sys/ioctl.h>
44 #ifdef HAVE_SYS_MMAN_H
45 # include <sys/mman.h>
57 #define ALSA_PCM_NEW_HW_PARAMS_API
58 #define ALSA_PCM_NEW_SW_PARAMS_API
60 #include "wine/library.h"
61 #include "wine/debug.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(wave);
66 #if defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)
68 /* internal ALSALIB functions */
69 snd_pcm_uframes_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm);
72 #define MAX_WAVEOUTDRV (1)
73 #define MAX_WAVEINDRV (1)
75 /* state diagram for waveOut writing:
77 * +---------+-------------+---------------+---------------------------------+
78 * | state | function | event | new state |
79 * +---------+-------------+---------------+---------------------------------+
80 * | | open() | | STOPPED |
81 * | PAUSED | write() | | PAUSED |
82 * | STOPPED | write() | <thrd create> | PLAYING |
83 * | PLAYING | write() | HEADER | PLAYING |
84 * | (other) | write() | <error> | |
85 * | (any) | pause() | PAUSING | PAUSED |
86 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
87 * | (any) | reset() | RESETTING | STOPPED |
88 * | (any) | close() | CLOSING | CLOSED |
89 * +---------+-------------+---------------+---------------------------------+
92 /* states of the playing device */
93 #define WINE_WS_PLAYING 0
94 #define WINE_WS_PAUSED 1
95 #define WINE_WS_STOPPED 2
96 #define WINE_WS_CLOSED 3
98 /* events to be send to device */
100 WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
101 WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING
105 #define SIGNAL_OMR(omr) do { int x = 0; write((omr)->msg_pipe[1], &x, sizeof(x)); } while (0)
106 #define CLEAR_OMR(omr) do { int x = 0; read((omr)->msg_pipe[0], &x, sizeof(x)); } while (0)
107 #define RESET_OMR(omr) do { } while (0)
108 #define WAIT_OMR(omr, sleep) \
109 do { struct pollfd pfd; pfd.fd = (omr)->msg_pipe[0]; \
110 pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)
112 #define SIGNAL_OMR(omr) do { SetEvent((omr)->msg_event); } while (0)
113 #define CLEAR_OMR(omr) do { } while (0)
114 #define RESET_OMR(omr) do { ResetEvent((omr)->msg_event); } while (0)
115 #define WAIT_OMR(omr, sleep) \
116 do { WaitForSingleObject((omr)->msg_event, sleep); } while (0)
120 enum win_wm_message msg; /* message identifier */
121 DWORD param; /* parameter for this message */
122 HANDLE hEvent; /* if message is synchronous, handle of event for synchro */
125 /* implement an in-process message ring for better performance
126 * (compared to passing thru the server)
127 * this ring will be used by the input (resp output) record (resp playback) routine
129 #define ALSA_RING_BUFFER_INCREMENT 64
132 int ring_buffer_size;
140 CRITICAL_SECTION msg_crst;
144 /* Windows information */
145 volatile int state; /* one of the WINE_WS_ manifest constants */
146 WAVEOPENDESC waveDesc;
148 PCMWAVEFORMAT format;
151 /* ALSA information (ALSA 0.9/1.x uses two different devices for playback/capture) */
153 char interface_name[64];
154 snd_pcm_t* p_handle; /* handle to ALSA playback device */
155 snd_pcm_t* c_handle; /* handle to ALSA capture device */
156 snd_pcm_hw_params_t * hw_params; /* ALSA Hw params */
158 snd_ctl_t * ctl; /* control handle for the playback volume */
159 snd_ctl_elem_id_t * playback_eid; /* element id of the playback volume control */
160 snd_ctl_elem_value_t * playback_evalue; /* element value of the playback volume control */
161 snd_ctl_elem_info_t * playback_einfo; /* element info of the playback volume control */
163 snd_pcm_sframes_t (*write)(snd_pcm_t *, const void *, snd_pcm_uframes_t );
168 DWORD dwBufferSize; /* size of whole ALSA buffer in bytes */
169 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
170 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
171 DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
173 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
174 DWORD dwLoops; /* private copy of loop counter */
176 DWORD dwPlayedTotal; /* number of bytes actually played since opening */
177 DWORD dwWrittenTotal; /* number of bytes written to ALSA buffer since opening */
179 /* synchronization stuff */
180 HANDLE hStartUpEvent;
183 ALSA_MSG_RING msgRing;
185 /* DirectSound stuff */
186 DSDRIVERDESC ds_desc;
191 /* Windows information */
192 volatile int state; /* one of the WINE_WS_ manifest constants */
193 WAVEOPENDESC waveDesc;
195 PCMWAVEFORMAT format;
198 /* ALSA information (ALSA 0.9/1.x uses two different devices for playback/capture) */
200 char interface_name[64];
201 snd_pcm_t* p_handle; /* handle to ALSA playback device */
202 snd_pcm_t* c_handle; /* handle to ALSA capture device */
203 snd_pcm_hw_params_t * hw_params; /* ALSA Hw params */
205 snd_ctl_t * ctl; /* control handle for the playback volume */
206 snd_ctl_elem_id_t * playback_eid; /* element id of the playback volume control */
207 snd_ctl_elem_value_t * playback_evalue; /* element value of the playback volume control */
208 snd_ctl_elem_info_t * playback_einfo; /* element info of the playback volume control */
210 snd_pcm_sframes_t (*read)(snd_pcm_t *, void *, snd_pcm_uframes_t );
215 DWORD dwPeriodSize; /* size of OSS buffer period */
216 DWORD dwBufferSize; /* size of whole ALSA buffer in bytes */
217 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
218 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
220 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
221 DWORD dwLoops; /* private copy of loop counter */
223 /*DWORD dwPlayedTotal; */
224 DWORD dwTotalRecorded;
226 /* synchronization stuff */
227 HANDLE hStartUpEvent;
230 ALSA_MSG_RING msgRing;
232 /* DirectSound stuff */
233 DSDRIVERDESC ds_desc;
237 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
238 static DWORD ALSA_WodNumDevs;
239 static WINE_WAVEIN WInDev [MAX_WAVEINDRV];
240 static DWORD ALSA_WidNumDevs;
242 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
243 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
244 static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid);
246 /* These strings used only for tracing */
248 static const char *wodPlayerCmdString[] = {
250 "WINE_WM_RESTARTING",
261 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
262 PCMWAVEFORMAT* format)
264 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
265 lpTime->wType, format->wBitsPerSample, format->wf.nSamplesPerSec,
266 format->wf.nChannels, format->wf.nAvgBytesPerSec);
267 TRACE("Position in bytes=%lu\n", position);
269 switch (lpTime->wType) {
271 lpTime->u.sample = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
272 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
275 lpTime->u.ms = 1000.0 * position / (format->wBitsPerSample / 8 * format->wf.nChannels * format->wf.nSamplesPerSec);
276 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
279 position = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
280 lpTime->u.smpte.sec = position / format->wf.nSamplesPerSec;
281 position -= lpTime->u.smpte.sec * format->wf.nSamplesPerSec;
282 lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
283 lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
284 lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
285 lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
286 lpTime->u.smpte.fps = 30;
287 lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->wf.nSamplesPerSec;
288 position -= lpTime->u.smpte.frame * format->wf.nSamplesPerSec / lpTime->u.smpte.fps;
292 lpTime->u.smpte.frame++;
294 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
295 lpTime->u.smpte.hour, lpTime->u.smpte.min,
296 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
299 FIXME("Format %d not supported ! use TIME_BYTES !\n", lpTime->wType);
300 lpTime->wType = TIME_BYTES;
303 lpTime->u.cb = position;
304 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
307 return MMSYSERR_NOERROR;
310 /*======================================================================*
311 * Low level WAVE implementation *
312 *======================================================================*/
314 /**************************************************************************
315 * ALSA_InitializeVolumeCtl [internal]
317 * used to initialize the PCM Volume Control
319 static int ALSA_InitializeVolumeCtl(WINE_WAVEOUT * wwo)
321 snd_ctl_t * ctl = NULL;
322 snd_ctl_card_info_t * cardinfo;
323 snd_ctl_elem_list_t * elemlist;
324 snd_ctl_elem_id_t * e_id;
325 snd_ctl_elem_info_t * einfo;
326 snd_hctl_t * hctl = NULL;
327 snd_hctl_elem_t * elem;
331 snd_ctl_card_info_alloca(&cardinfo);
332 memset(cardinfo,0,snd_ctl_card_info_sizeof());
334 snd_ctl_elem_list_alloca(&elemlist);
335 memset(elemlist,0,snd_ctl_elem_list_sizeof());
337 snd_ctl_elem_id_alloca(&e_id);
338 memset(e_id,0,snd_ctl_elem_id_sizeof());
340 snd_ctl_elem_info_alloca(&einfo);
341 memset(einfo,0,snd_ctl_elem_info_sizeof());
343 #define EXIT_ON_ERROR(f,txt) do \
346 if ( (err = (f) ) < 0) \
348 ERR(txt ": %s\n", snd_strerror(err)); \
350 snd_hctl_close(hctl); \
352 snd_ctl_close(ctl); \
357 EXIT_ON_ERROR( snd_ctl_open(&ctl,"hw",0) , "ctl open failed" );
358 EXIT_ON_ERROR( snd_ctl_card_info(ctl, cardinfo), "card info failed");
359 EXIT_ON_ERROR( snd_ctl_elem_list(ctl, elemlist), "elem list failed");
361 nCtrls = snd_ctl_elem_list_get_count(elemlist);
363 EXIT_ON_ERROR( snd_hctl_open(&hctl,"hw",0), "hctl open failed");
364 EXIT_ON_ERROR( snd_hctl_load(hctl), "hctl load failed" );
366 elem=snd_hctl_first_elem(hctl);
367 for ( i= 0; i<nCtrls; i++) {
369 memset(e_id,0,snd_ctl_elem_id_sizeof());
371 snd_hctl_elem_get_id(elem,e_id);
373 TRACE("ctl: #%d '%s'%d\n",
374 snd_ctl_elem_id_get_numid(e_id),
375 snd_ctl_elem_id_get_name(e_id),
376 snd_ctl_elem_id_get_index(e_id));
378 if ( !strcmp("PCM Playback Volume", snd_ctl_elem_id_get_name(e_id)) )
380 EXIT_ON_ERROR( snd_hctl_elem_info(elem,einfo), "hctl elem info failed" );
382 /* few sanity checks... you'll never know... */
383 if ( snd_ctl_elem_info_get_type(einfo) != SND_CTL_ELEM_TYPE_INTEGER )
384 WARN("playback volume control is not an integer\n");
385 if ( !snd_ctl_elem_info_is_readable(einfo) )
386 WARN("playback volume control is readable\n");
387 if ( !snd_ctl_elem_info_is_writable(einfo) )
388 WARN("playback volume control is readable\n");
390 TRACE(" ctrl range: min=%ld max=%ld step=%ld\n",
391 snd_ctl_elem_info_get_min(einfo),
392 snd_ctl_elem_info_get_max(einfo),
393 snd_ctl_elem_info_get_step(einfo));
395 EXIT_ON_ERROR( snd_ctl_elem_id_malloc(&wwo->playback_eid), "elem id malloc failed" );
396 EXIT_ON_ERROR( snd_ctl_elem_info_malloc(&wwo->playback_einfo), "elem info malloc failed" );
397 EXIT_ON_ERROR( snd_ctl_elem_value_malloc(&wwo->playback_evalue), "elem value malloc failed" );
399 /* ok, now we can safely save these objects for later */
400 snd_ctl_elem_id_copy(wwo->playback_eid, e_id);
401 snd_ctl_elem_info_copy(wwo->playback_einfo, einfo);
402 snd_ctl_elem_value_set_id(wwo->playback_evalue, wwo->playback_eid);
406 elem=snd_hctl_elem_next(elem);
408 snd_hctl_close(hctl);
413 /**************************************************************************
414 * ALSA_XRUNRecovery [internal]
416 * used to recovery from XRUN errors (buffer underflow/overflow)
418 static int ALSA_XRUNRecovery(WINE_WAVEOUT * wwo, int err)
420 if (err == -EPIPE) { /* under-run */
421 err = snd_pcm_prepare(wwo->p_handle);
423 ERR( "underrun recovery failed. prepare failed: %s\n", snd_strerror(err));
425 } else if (err == -ESTRPIPE) {
426 while ((err = snd_pcm_resume(wwo->p_handle)) == -EAGAIN)
427 sleep(1); /* wait until the suspend flag is released */
429 err = snd_pcm_prepare(wwo->p_handle);
431 ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err));
438 /**************************************************************************
439 * ALSA_TraceParameters [internal]
441 * used to trace format changes, hw and sw parameters
443 static void ALSA_TraceParameters(snd_pcm_hw_params_t * hw_params, snd_pcm_sw_params_t * sw, int full)
446 snd_pcm_format_t format;
447 snd_pcm_access_t access;
449 err = snd_pcm_hw_params_get_access(hw_params, &access);
450 err = snd_pcm_hw_params_get_format(hw_params, &format);
452 #define X(x) ((x)? "true" : "false")
454 TRACE("FLAGS: sampleres=%s overrng=%s pause=%s resume=%s syncstart=%s batch=%s block=%s double=%s "
455 "halfd=%s joint=%s \n",
456 X(snd_pcm_hw_params_can_mmap_sample_resolution(hw_params)),
457 X(snd_pcm_hw_params_can_overrange(hw_params)),
458 X(snd_pcm_hw_params_can_pause(hw_params)),
459 X(snd_pcm_hw_params_can_resume(hw_params)),
460 X(snd_pcm_hw_params_can_sync_start(hw_params)),
461 X(snd_pcm_hw_params_is_batch(hw_params)),
462 X(snd_pcm_hw_params_is_block_transfer(hw_params)),
463 X(snd_pcm_hw_params_is_double(hw_params)),
464 X(snd_pcm_hw_params_is_half_duplex(hw_params)),
465 X(snd_pcm_hw_params_is_joint_duplex(hw_params)));
469 TRACE("access=%s\n", snd_pcm_access_name(access));
472 snd_pcm_access_mask_t * acmask;
473 snd_pcm_access_mask_alloca(&acmask);
474 snd_pcm_hw_params_get_access_mask(hw_params, acmask);
475 for ( access = SND_PCM_ACCESS_MMAP_INTERLEAVED; access <= SND_PCM_ACCESS_LAST; access++)
476 if (snd_pcm_access_mask_test(acmask, access))
477 TRACE("access=%s\n", snd_pcm_access_name(access));
482 TRACE("format=%s\n", snd_pcm_format_name(format));
487 snd_pcm_format_mask_t * fmask;
489 snd_pcm_format_mask_alloca(&fmask);
490 snd_pcm_hw_params_get_format_mask(hw_params, fmask);
491 for ( format = SND_PCM_FORMAT_S8; format <= SND_PCM_FORMAT_LAST ; format++)
492 if ( snd_pcm_format_mask_test(fmask, format) )
493 TRACE("format=%s\n", snd_pcm_format_name(format));
499 err = snd_pcm_hw_params_get_channels(hw_params, &val);
501 unsigned int min = 0;
502 unsigned int max = 0;
503 err = snd_pcm_hw_params_get_channels_min(hw_params, &min),
504 err = snd_pcm_hw_params_get_channels_max(hw_params, &max);
505 TRACE("channels_min=%u, channels_min_max=%u\n", min, max);
507 TRACE("channels_min=%d\n", val);
512 snd_pcm_uframes_t val=0;
513 err = snd_pcm_hw_params_get_buffer_size(hw_params, &val);
515 snd_pcm_uframes_t min = 0;
516 snd_pcm_uframes_t max = 0;
517 err = snd_pcm_hw_params_get_buffer_size_min(hw_params, &min),
518 err = snd_pcm_hw_params_get_buffer_size_max(hw_params, &max);
519 TRACE("buffer_size_min=%lu, buffer_size_min_max=%lu\n", min, max);
521 TRACE("buffer_size_min=%lu\n", val);
528 unsigned int val=0; \
529 err = snd_pcm_hw_params_get_##x(hw_params,&val, &dir); \
531 unsigned int min = 0; \
532 unsigned int max = 0; \
533 err = snd_pcm_hw_params_get_##x##_min(hw_params, &min, &dir); \
534 err = snd_pcm_hw_params_get_##x##_max(hw_params, &max, &dir); \
535 TRACE(#x "_min=%u " #x "_max=%u\n", min, max); \
537 TRACE(#x "=%d\n", val); \
546 snd_pcm_uframes_t val=0;
547 err = snd_pcm_hw_params_get_period_size(hw_params, &val, &dir);
549 snd_pcm_uframes_t min = 0;
550 snd_pcm_uframes_t max = 0;
551 err = snd_pcm_hw_params_get_period_size_min(hw_params, &min, &dir),
552 err = snd_pcm_hw_params_get_period_size_max(hw_params, &max, &dir);
553 TRACE("period_size_min=%lu, period_size_min_max=%lu\n", min, max);
555 TRACE("period_size_min=%lu\n", val);
569 /* return a string duplicated on the win32 process heap, free with HeapFree */
570 static char* ALSA_strdup(char *s) {
571 char *result = HeapAlloc(GetProcessHeap(), 0, strlen(s)+1);
576 /******************************************************************
577 * ALSA_GetDeviceFromReg
579 * Returns either "default" or reads the registry so the user can
580 * override the playback/record device used.
582 char *ALSA_GetDeviceFromReg(char *value)
586 HKEY playbackKey = 0;
590 res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\ALSA", 0, KEY_QUERY_VALUE, &playbackKey);
591 if (res != ERROR_SUCCESS) goto end;
593 res = RegQueryValueExA(playbackKey, value, NULL, &type, NULL, &resultSize);
594 if (res != ERROR_SUCCESS) goto end;
596 if (type != REG_SZ) {
597 ERR("Registry key [HKEY_LOCAL_MACHINE\\Software\\Wine\\Wine\\ALSA\\%s] must be a string\n", value);
601 result = HeapAlloc(GetProcessHeap(), 0, resultSize);
602 res = RegQueryValueExA(playbackKey, value, NULL, NULL, result, &resultSize);
605 if (!result) result = ALSA_strdup("default");
606 if (playbackKey) RegCloseKey(playbackKey);
610 /******************************************************************
613 * Initialize internal structures from ALSA information
615 LONG ALSA_WaveInit(void)
618 snd_pcm_info_t * info;
619 snd_pcm_hw_params_t * hw_params;
620 unsigned int ratemin=0;
621 unsigned int ratemax=0;
622 unsigned int chmin=0;
623 unsigned int chmax=0;
631 /* FIXME: use better values */
632 wwo->device = ALSA_GetDeviceFromReg("PlaybackDevice");
633 TRACE("using waveout device \"%s\"\n", wwo->device);
635 snprintf(wwo->interface_name, sizeof(wwo->interface_name), "winealsa: %s", wwo->device);
637 wwo->caps.wMid = 0x0002;
638 wwo->caps.wPid = 0x0104;
639 strcpy(wwo->caps.szPname, "SB16 Wave Out");
640 wwo->caps.vDriverVersion = 0x0100;
641 wwo->caps.dwFormats = 0x00000000;
642 wwo->caps.dwSupport = WAVECAPS_VOLUME;
643 strcpy(wwo->ds_desc.szDesc, "WineALSA DirectSound Driver");
644 strcpy(wwo->ds_desc.szDrvName, "winealsa.drv");
645 wwo->ds_guid = DSDEVID_DefaultPlayback;
647 if (!wine_dlopen("libasound.so.2", RTLD_LAZY|RTLD_GLOBAL, NULL, 0))
649 ERR("Error: ALSA lib needs to be loaded with flags RTLD_LAZY and RTLD_GLOBAL.\n");
653 snd_pcm_info_alloca(&info);
654 snd_pcm_hw_params_alloca(&hw_params);
656 #define EXIT_ON_ERROR(f,txt) do { int err; if ( (err = (f) ) < 0) { ERR(txt ": %s\n", snd_strerror(err)); if (h) snd_pcm_close(h); return -1; } } while(0)
660 EXIT_ON_ERROR( snd_pcm_open(&h, wwo->device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) , "open pcm" );
664 EXIT_ON_ERROR( snd_pcm_info(h, info) , "pcm info" );
666 TRACE("dev=%d id=%s name=%s subdev=%d subdev_name=%s subdev_avail=%d subdev_num=%d stream=%s subclass=%s \n",
667 snd_pcm_info_get_device(info),
668 snd_pcm_info_get_id(info),
669 snd_pcm_info_get_name(info),
670 snd_pcm_info_get_subdevice(info),
671 snd_pcm_info_get_subdevice_name(info),
672 snd_pcm_info_get_subdevices_avail(info),
673 snd_pcm_info_get_subdevices_count(info),
674 snd_pcm_stream_name(snd_pcm_info_get_stream(info)),
675 (snd_pcm_info_get_subclass(info) == SND_PCM_SUBCLASS_GENERIC_MIX ? "GENERIC MIX": "MULTI MIX"));
677 EXIT_ON_ERROR( snd_pcm_hw_params_any(h, hw_params) , "pcm hw params" );
680 err = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir);
681 err = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir);
682 err = snd_pcm_hw_params_get_channels_min(hw_params, &chmin);
683 err = snd_pcm_hw_params_get_channels_max(hw_params, &chmax);
685 ALSA_TraceParameters(hw_params, NULL, TRUE);
688 snd_pcm_format_mask_t * fmask;
690 snd_pcm_format_mask_alloca(&fmask);
691 snd_pcm_hw_params_get_format_mask(hw_params, fmask);
694 if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \
696 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \
698 if (chmin <= 1 && 1 <= chmax) \
699 wwo->caps.dwFormats |= WAVE_FORMAT_##v##M08; \
700 if (chmin <= 2 && 2 <= chmax) \
701 wwo->caps.dwFormats |= WAVE_FORMAT_##v##S08; \
703 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \
705 if (chmin <= 1 && 1 <= chmax) \
706 wwo->caps.dwFormats |= WAVE_FORMAT_##v##M16; \
707 if (chmin <= 2 && 2 <= chmax) \
708 wwo->caps.dwFormats |= WAVE_FORMAT_##v##S16; \
719 if ( chmin > 1) FIXME("-\n");
720 wwo->caps.wChannels = (chmax >= 2) ? 2 : 1;
721 if (chmin <= 2 && 2 <= chmax)
722 wwo->caps.dwSupport |= WAVECAPS_LRVOLUME;
724 /* FIXME: always true ? */
725 wwo->caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
728 snd_pcm_access_mask_t * acmask;
729 snd_pcm_access_mask_alloca(&acmask);
730 snd_pcm_hw_params_get_access_mask(hw_params, acmask);
732 /* FIXME: NONITERLEAVED and COMPLEX are not supported right now */
733 if ( snd_pcm_access_mask_test( acmask, SND_PCM_ACCESS_MMAP_INTERLEAVED ) )
734 wwo->caps.dwSupport |= WAVECAPS_DIRECTSOUND;
737 TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n",
738 wwo->caps.dwFormats, wwo->caps.dwSupport);
742 ALSA_InitializeVolumeCtl(wwo);
746 /* FIXME: use better values */
747 wwi->device = ALSA_GetDeviceFromReg("RecordDevice");
748 TRACE("using wavein device \"%s\"\n", wwi->device);
750 snprintf(wwi->interface_name, sizeof(wwi->interface_name), "winealsa: %s", wwi->device);
752 wwi->caps.wMid = 0x0002;
753 wwi->caps.wPid = 0x0104;
754 strcpy(wwi->caps.szPname, "SB16 Wave In");
755 wwi->caps.vDriverVersion = 0x0100;
756 wwi->caps.dwFormats = 0x00000000;
757 wwi->caps.dwSupport = WAVECAPS_VOLUME;
758 strcpy(wwi->ds_desc.szDesc, "WineALSA DirectSound Driver");
759 strcpy(wwi->ds_desc.szDrvName, "winealsa.drv");
760 wwi->ds_guid = DSDEVID_DefaultPlayback;
762 snd_pcm_info_alloca(&info);
763 snd_pcm_hw_params_alloca(&hw_params);
765 #define EXIT_ON_ERROR(f,txt) do { int err; if ( (err = (f) ) < 0) { ERR(txt ": %s\n", snd_strerror(err)); if (h) snd_pcm_close(h); return -1; } } while(0)
769 EXIT_ON_ERROR( snd_pcm_open(&h, wwi->device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) , "open pcm" );
773 EXIT_ON_ERROR( snd_pcm_info(h, info) , "pcm info" );
775 TRACE("dev=%d id=%s name=%s subdev=%d subdev_name=%s subdev_avail=%d subdev_num=%d stream=%s subclass=%s \n",
776 snd_pcm_info_get_device(info),
777 snd_pcm_info_get_id(info),
778 snd_pcm_info_get_name(info),
779 snd_pcm_info_get_subdevice(info),
780 snd_pcm_info_get_subdevice_name(info),
781 snd_pcm_info_get_subdevices_avail(info),
782 snd_pcm_info_get_subdevices_count(info),
783 snd_pcm_stream_name(snd_pcm_info_get_stream(info)),
784 (snd_pcm_info_get_subclass(info) == SND_PCM_SUBCLASS_GENERIC_MIX ? "GENERIC MIX": "MULTI MIX"));
786 EXIT_ON_ERROR( snd_pcm_hw_params_any(h, hw_params) , "pcm hw params" );
788 err = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir);
789 err = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir);
790 err = snd_pcm_hw_params_get_channels_min(hw_params, &chmin);
791 err = snd_pcm_hw_params_get_channels_max(hw_params, &chmax);
794 ALSA_TraceParameters(hw_params, NULL, TRUE);
797 snd_pcm_format_mask_t * fmask;
799 snd_pcm_format_mask_alloca(&fmask);
800 snd_pcm_hw_params_get_format_mask(hw_params, fmask);
803 if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \
805 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \
807 if (chmin <= 1 && 1 <= chmax) \
808 wwi->caps.dwFormats |= WAVE_FORMAT_##v##M08; \
809 if (chmin <= 2 && 2 <= chmax) \
810 wwi->caps.dwFormats |= WAVE_FORMAT_##v##S08; \
812 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \
814 if (chmin <= 1 && 1 <= chmax) \
815 wwi->caps.dwFormats |= WAVE_FORMAT_##v##M16; \
816 if (chmin <= 2 && 2 <= chmax) \
817 wwi->caps.dwFormats |= WAVE_FORMAT_##v##S16; \
828 if ( chmin > 1) FIXME("-\n");
829 wwi->caps.wChannels = (chmax >= 2) ? 2 : 1;
830 if (chmin <= 2 && 2 <= chmax)
831 wwi->caps.dwSupport |= WAVECAPS_LRVOLUME;
833 /* FIXME: always true ? */
834 wwi->caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
837 snd_pcm_access_mask_t * acmask;
838 snd_pcm_access_mask_alloca(&acmask);
839 snd_pcm_hw_params_get_access_mask(hw_params, acmask);
841 /* FIXME: NONITERLEAVED and COMPLEX are not supported right now */
842 if ( snd_pcm_access_mask_test( acmask, SND_PCM_ACCESS_MMAP_INTERLEAVED ) )
843 wwi->caps.dwSupport |= WAVECAPS_DIRECTSOUND;
846 TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n",
847 wwi->caps.dwFormats, wwi->caps.dwSupport);
854 /******************************************************************
855 * ALSA_InitRingMessage
857 * Initialize the ring of messages for passing between driver's caller and playback/record
860 static int ALSA_InitRingMessage(ALSA_MSG_RING* omr)
865 if (pipe(omr->msg_pipe) < 0) {
866 omr->msg_pipe[0] = -1;
867 omr->msg_pipe[1] = -1;
868 ERR("could not create pipe, error=%s\n", strerror(errno));
871 omr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);
873 omr->ring_buffer_size = ALSA_RING_BUFFER_INCREMENT;
874 omr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,omr->ring_buffer_size * sizeof(ALSA_MSG));
876 InitializeCriticalSection(&omr->msg_crst);
880 /******************************************************************
881 * ALSA_DestroyRingMessage
884 static int ALSA_DestroyRingMessage(ALSA_MSG_RING* omr)
887 close(omr->msg_pipe[0]);
888 close(omr->msg_pipe[1]);
890 CloseHandle(omr->msg_event);
892 HeapFree(GetProcessHeap(),0,omr->messages);
893 DeleteCriticalSection(&omr->msg_crst);
897 /******************************************************************
898 * ALSA_AddRingMessage
900 * Inserts a new message into the ring (should be called from DriverProc derivated routines)
902 static int ALSA_AddRingMessage(ALSA_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait)
904 HANDLE hEvent = INVALID_HANDLE_VALUE;
906 EnterCriticalSection(&omr->msg_crst);
907 if ((omr->msg_toget == ((omr->msg_tosave + 1) % omr->ring_buffer_size)))
909 int old_ring_buffer_size = omr->ring_buffer_size;
910 omr->ring_buffer_size += ALSA_RING_BUFFER_INCREMENT;
911 TRACE("omr->ring_buffer_size=%d\n",omr->ring_buffer_size);
912 omr->messages = HeapReAlloc(GetProcessHeap(),0,omr->messages, omr->ring_buffer_size * sizeof(ALSA_MSG));
913 /* Now we need to rearrange the ring buffer so that the new
914 buffers just allocated are in between omr->msg_tosave and
917 if (omr->msg_tosave < omr->msg_toget)
919 memmove(&(omr->messages[omr->msg_toget + ALSA_RING_BUFFER_INCREMENT]),
920 &(omr->messages[omr->msg_toget]),
921 sizeof(ALSA_MSG)*(old_ring_buffer_size - omr->msg_toget)
923 omr->msg_toget += ALSA_RING_BUFFER_INCREMENT;
928 hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
929 if (hEvent == INVALID_HANDLE_VALUE)
931 ERR("can't create event !?\n");
932 LeaveCriticalSection(&omr->msg_crst);
935 if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER)
936 FIXME("two fast messages in the queue!!!!\n");
938 /* fast messages have to be added at the start of the queue */
939 omr->msg_toget = (omr->msg_toget + omr->ring_buffer_size - 1) % omr->ring_buffer_size;
941 omr->messages[omr->msg_toget].msg = msg;
942 omr->messages[omr->msg_toget].param = param;
943 omr->messages[omr->msg_toget].hEvent = hEvent;
947 omr->messages[omr->msg_tosave].msg = msg;
948 omr->messages[omr->msg_tosave].param = param;
949 omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
950 omr->msg_tosave = (omr->msg_tosave + 1) % omr->ring_buffer_size;
952 LeaveCriticalSection(&omr->msg_crst);
953 /* signal a new message */
957 /* wait for playback/record thread to have processed the message */
958 WaitForSingleObject(hEvent, INFINITE);
964 /******************************************************************
965 * ALSA_RetrieveRingMessage
967 * Get a message from the ring. Should be called by the playback/record thread.
969 static int ALSA_RetrieveRingMessage(ALSA_MSG_RING* omr,
970 enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
972 EnterCriticalSection(&omr->msg_crst);
974 if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
976 LeaveCriticalSection(&omr->msg_crst);
980 *msg = omr->messages[omr->msg_toget].msg;
981 omr->messages[omr->msg_toget].msg = 0;
982 *param = omr->messages[omr->msg_toget].param;
983 *hEvent = omr->messages[omr->msg_toget].hEvent;
984 omr->msg_toget = (omr->msg_toget + 1) % omr->ring_buffer_size;
986 LeaveCriticalSection(&omr->msg_crst);
990 /******************************************************************
991 * ALSA_PeekRingMessage
993 * Peek at a message from the ring but do not remove it.
994 * Should be called by the playback/record thread.
996 static int ALSA_PeekRingMessage(ALSA_MSG_RING* omr,
997 enum win_wm_message *msg,
998 DWORD *param, HANDLE *hEvent)
1000 EnterCriticalSection(&omr->msg_crst);
1002 if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
1004 LeaveCriticalSection(&omr->msg_crst);
1008 *msg = omr->messages[omr->msg_toget].msg;
1009 *param = omr->messages[omr->msg_toget].param;
1010 *hEvent = omr->messages[omr->msg_toget].hEvent;
1011 LeaveCriticalSection(&omr->msg_crst);
1015 /*======================================================================*
1016 * Low level WAVE OUT implementation *
1017 *======================================================================*/
1019 /**************************************************************************
1020 * wodNotifyClient [internal]
1022 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1024 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
1030 if (wwo->wFlags != DCB_NULL &&
1031 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave,
1032 wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
1033 WARN("can't notify client !\n");
1034 return MMSYSERR_ERROR;
1038 FIXME("Unknown callback message %u\n", wMsg);
1039 return MMSYSERR_INVALPARAM;
1041 return MMSYSERR_NOERROR;
1044 /**************************************************************************
1045 * wodUpdatePlayedTotal [internal]
1048 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo, snd_pcm_status_t* ps)
1050 snd_pcm_sframes_t delay = 0;
1051 snd_pcm_delay(wwo->p_handle, &delay);
1052 if (snd_pcm_state(wwo->p_handle) != SND_PCM_STATE_RUNNING)
1054 wwo->dwPlayedTotal = wwo->dwWrittenTotal - snd_pcm_frames_to_bytes(wwo->p_handle, delay);
1058 /**************************************************************************
1059 * wodPlayer_BeginWaveHdr [internal]
1061 * Makes the specified lpWaveHdr the currently playing wave header.
1062 * If the specified wave header is a begin loop and we're not already in
1063 * a loop, setup the loop.
1065 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1067 wwo->lpPlayPtr = lpWaveHdr;
1069 if (!lpWaveHdr) return;
1071 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
1072 if (wwo->lpLoopPtr) {
1073 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
1075 TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
1076 wwo->lpLoopPtr = lpWaveHdr;
1077 /* Windows does not touch WAVEHDR.dwLoops,
1078 * so we need to make an internal copy */
1079 wwo->dwLoops = lpWaveHdr->dwLoops;
1082 wwo->dwPartialOffset = 0;
1085 /**************************************************************************
1086 * wodPlayer_PlayPtrNext [internal]
1088 * Advance the play pointer to the next waveheader, looping if required.
1090 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
1092 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
1094 wwo->dwPartialOffset = 0;
1095 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
1096 /* We're at the end of a loop, loop if required */
1097 if (--wwo->dwLoops > 0) {
1098 wwo->lpPlayPtr = wwo->lpLoopPtr;
1100 /* Handle overlapping loops correctly */
1101 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
1102 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
1103 /* shall we consider the END flag for the closing loop or for
1104 * the opening one or for both ???
1105 * code assumes for closing loop only
1108 lpWaveHdr = lpWaveHdr->lpNext;
1110 wwo->lpLoopPtr = NULL;
1111 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
1114 /* We're not in a loop. Advance to the next wave header */
1115 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
1121 /**************************************************************************
1122 * wodPlayer_DSPWait [internal]
1123 * Returns the number of milliseconds to wait for the DSP buffer to play a
1126 static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo)
1128 /* time for one period to be played */
1132 err = snd_pcm_hw_params_get_period_time(wwo->hw_params, &val, &dir);
1136 /**************************************************************************
1137 * wodPlayer_NotifyWait [internal]
1138 * Returns the number of milliseconds to wait before attempting to notify
1139 * completion of the specified wavehdr.
1140 * This is based on the number of bytes remaining to be written in the
1143 static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1147 if (lpWaveHdr->reserved < wwo->dwPlayedTotal) {
1150 dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec;
1151 if (!dwMillis) dwMillis = 1;
1158 /**************************************************************************
1159 * wodPlayer_WriteMaxFrags [internal]
1160 * Writes the maximum number of frames possible to the DSP and returns
1161 * the number of frames written.
1163 static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* frames)
1165 /* Only attempt to write to free frames */
1166 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
1167 DWORD dwLength = snd_pcm_bytes_to_frames(wwo->p_handle, lpWaveHdr->dwBufferLength - wwo->dwPartialOffset);
1168 int toWrite = min(dwLength, *frames);
1171 TRACE("Writing wavehdr %p.%lu[%lu]\n", lpWaveHdr, wwo->dwPartialOffset, lpWaveHdr->dwBufferLength);
1173 written = (wwo->write)(wwo->p_handle, lpWaveHdr->lpData + wwo->dwPartialOffset, toWrite);
1176 /* XRUN occurred. let's try to recover */
1177 ALSA_XRUNRecovery(wwo, written);
1178 written = (wwo->write)(wwo->p_handle, lpWaveHdr->lpData + wwo->dwPartialOffset, toWrite);
1182 /* still in error */
1183 ERR("Error in writing wavehdr. Reason: %s\n", snd_strerror(written));
1187 wwo->dwPartialOffset += snd_pcm_frames_to_bytes(wwo->p_handle, written);
1188 if ( wwo->dwPartialOffset >= lpWaveHdr->dwBufferLength) {
1189 /* this will be used to check if the given wave header has been fully played or not... */
1190 wwo->dwPartialOffset = lpWaveHdr->dwBufferLength;
1191 /* If we wrote all current wavehdr, skip to the next one */
1192 wodPlayer_PlayPtrNext(wwo);
1195 wwo->dwWrittenTotal += snd_pcm_frames_to_bytes(wwo->p_handle, written);
1201 /**************************************************************************
1202 * wodPlayer_NotifyCompletions [internal]
1204 * Notifies and remove from queue all wavehdrs which have been played to
1205 * the speaker (ie. they have cleared the ALSA buffer). If force is true,
1206 * we notify all wavehdrs and remove them all from the queue even if they
1207 * are unplayed or part of a loop.
1209 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
1211 LPWAVEHDR lpWaveHdr;
1213 /* Start from lpQueuePtr and keep notifying until:
1214 * - we hit an unwritten wavehdr
1215 * - we hit the beginning of a running loop
1216 * - we hit a wavehdr which hasn't finished playing
1219 while ((lpWaveHdr = wwo->lpQueuePtr) &&
1221 (lpWaveHdr != wwo->lpPlayPtr &&
1222 lpWaveHdr != wwo->lpLoopPtr &&
1223 lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
1225 wwo->lpQueuePtr = lpWaveHdr->lpNext;
1227 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1228 lpWaveHdr->dwFlags |= WHDR_DONE;
1230 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1235 lpWaveHdr = wwo->lpQueuePtr;
1236 if (!lpWaveHdr) {TRACE("Empty queue\n"); break;}
1239 if (lpWaveHdr == wwo->lpPlayPtr) {TRACE("play %p\n", lpWaveHdr); break;}
1240 if (lpWaveHdr == wwo->lpLoopPtr) {TRACE("loop %p\n", lpWaveHdr); break;}
1241 if (lpWaveHdr->reserved > wwo->dwPlayedTotal){TRACE("still playing %p (%lu/%lu)\n", lpWaveHdr, lpWaveHdr->reserved, wwo->dwPlayedTotal);break;}
1243 wwo->lpQueuePtr = lpWaveHdr->lpNext;
1245 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1246 lpWaveHdr->dwFlags |= WHDR_DONE;
1248 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1251 return (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
1252 wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
1256 void wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
1258 unsigned short revents;
1260 if (snd_pcm_state(handle) != SND_PCM_STATE_RUNNING)
1264 poll(ufds, count, -1);
1265 snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
1267 if (revents & POLLERR)
1270 /*if (revents & POLLOUT)
1276 /**************************************************************************
1277 * wodPlayer_Reset [internal]
1279 * wodPlayer helper. Resets current output stream.
1281 static void wodPlayer_Reset(WINE_WAVEOUT* wwo)
1283 enum win_wm_message msg;
1288 /* flush all possible output */
1289 wait_for_poll(wwo->p_handle, wwo->ufds, wwo->count);
1291 wodUpdatePlayedTotal(wwo, NULL);
1292 /* updates current notify list */
1293 wodPlayer_NotifyCompletions(wwo, FALSE);
1295 if ( (err = snd_pcm_drop(wwo->p_handle)) < 0) {
1296 FIXME("flush: %s\n", snd_strerror(err));
1298 wwo->state = WINE_WS_STOPPED;
1301 if ( (err = snd_pcm_prepare(wwo->p_handle)) < 0 )
1302 ERR("pcm prepare failed: %s\n", snd_strerror(err));
1304 /* remove any buffer */
1305 wodPlayer_NotifyCompletions(wwo, TRUE);
1307 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
1308 wwo->state = WINE_WS_STOPPED;
1309 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1310 /* Clear partial wavehdr */
1311 wwo->dwPartialOffset = 0;
1313 /* remove any existing message in the ring */
1314 EnterCriticalSection(&wwo->msgRing.msg_crst);
1315 /* return all pending headers in queue */
1316 while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev))
1318 if (msg != WINE_WM_HEADER)
1320 FIXME("shouldn't have headers left\n");
1324 ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
1325 ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
1327 wodNotifyClient(wwo, WOM_DONE, param, 0);
1329 RESET_OMR(&wwo->msgRing);
1330 LeaveCriticalSection(&wwo->msgRing.msg_crst);
1333 /**************************************************************************
1334 * wodPlayer_ProcessMessages [internal]
1336 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
1338 LPWAVEHDR lpWaveHdr;
1339 enum win_wm_message msg;
1344 while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) {
1345 TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
1348 case WINE_WM_PAUSING:
1349 if ( snd_pcm_state(wwo->p_handle) == SND_PCM_STATE_RUNNING )
1351 err = snd_pcm_pause(wwo->p_handle, 1);
1353 ERR("pcm_pause failed: %s\n", snd_strerror(err));
1355 wwo->state = WINE_WS_PAUSED;
1358 case WINE_WM_RESTARTING:
1359 if (wwo->state == WINE_WS_PAUSED)
1361 if ( snd_pcm_state(wwo->p_handle) == SND_PCM_STATE_PAUSED )
1363 err = snd_pcm_pause(wwo->p_handle, 0);
1365 ERR("pcm_pause failed: %s\n", snd_strerror(err));
1367 wwo->state = WINE_WS_PLAYING;
1371 case WINE_WM_HEADER:
1372 lpWaveHdr = (LPWAVEHDR)param;
1374 /* insert buffer at the end of queue */
1377 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
1380 if (!wwo->lpPlayPtr)
1381 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
1382 if (wwo->state == WINE_WS_STOPPED)
1383 wwo->state = WINE_WS_PLAYING;
1385 case WINE_WM_RESETTING:
1386 wodPlayer_Reset(wwo);
1389 case WINE_WM_UPDATE:
1390 wodUpdatePlayedTotal(wwo, NULL);
1393 case WINE_WM_BREAKLOOP:
1394 if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
1395 /* ensure exit at end of current loop */
1400 case WINE_WM_CLOSING:
1401 /* sanity check: this should not happen since the device must have been reset before */
1402 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
1404 wwo->state = WINE_WS_CLOSED;
1407 /* shouldn't go here */
1409 FIXME("unknown message %d\n", msg);
1415 /**************************************************************************
1416 * wodPlayer_FeedDSP [internal]
1417 * Feed as much sound data as we can into the DSP and return the number of
1418 * milliseconds before it will be necessary to feed the DSP again.
1420 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
1424 wodUpdatePlayedTotal(wwo, NULL);
1425 availInQ = snd_pcm_avail_update(wwo->p_handle);
1428 /* input queue empty and output buffer with less than one fragment to play */
1429 if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + wwo->dwFragmentSize) {
1430 TRACE("Run out of wavehdr:s...\n");
1435 /* no more room... no need to try to feed */
1437 /* Feed from partial wavehdr */
1438 if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) {
1439 wodPlayer_WriteMaxFrags(wwo, &availInQ);
1442 /* Feed wavehdrs until we run out of wavehdrs or DSP space */
1443 if (wwo->dwPartialOffset == 0 && wwo->lpPlayPtr) {
1445 TRACE("Setting time to elapse for %p to %lu\n",
1446 wwo->lpPlayPtr, wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength);
1447 /* note the value that dwPlayedTotal will return when this wave finishes playing */
1448 wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
1449 } while (wodPlayer_WriteMaxFrags(wwo, &availInQ) && wwo->lpPlayPtr && availInQ > 0);
1453 return wodPlayer_DSPWait(wwo);
1456 /**************************************************************************
1457 * wodPlayer [internal]
1459 static DWORD CALLBACK wodPlayer(LPVOID pmt)
1461 WORD uDevID = (DWORD)pmt;
1462 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
1463 DWORD dwNextFeedTime = INFINITE; /* Time before DSP needs feeding */
1464 DWORD dwNextNotifyTime = INFINITE; /* Time before next wave completion */
1467 wwo->state = WINE_WS_STOPPED;
1468 SetEvent(wwo->hStartUpEvent);
1471 /** Wait for the shortest time before an action is required. If there
1472 * are no pending actions, wait forever for a command.
1474 dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
1475 TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
1476 WAIT_OMR(&wwo->msgRing, dwSleepTime);
1477 wodPlayer_ProcessMessages(wwo);
1478 if (wwo->state == WINE_WS_PLAYING) {
1479 dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1480 dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
1481 if (dwNextFeedTime == INFINITE) {
1482 /* FeedDSP ran out of data, but before giving up, */
1483 /* check that a notification didn't give us more */
1484 wodPlayer_ProcessMessages(wwo);
1485 if (wwo->lpPlayPtr) {
1486 TRACE("recovering\n");
1487 dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1491 dwNextFeedTime = dwNextNotifyTime = INFINITE;
1496 /**************************************************************************
1497 * wodGetDevCaps [internal]
1499 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
1501 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1503 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1505 if (wDevID >= MAX_WAVEOUTDRV) {
1506 TRACE("MAX_WAVOUTDRV reached !\n");
1507 return MMSYSERR_BADDEVICEID;
1510 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1511 return MMSYSERR_NOERROR;
1514 /**************************************************************************
1515 * wodOpen [internal]
1517 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1520 snd_pcm_hw_params_t * hw_params;
1521 snd_pcm_sw_params_t * sw_params;
1522 snd_pcm_access_t access;
1523 snd_pcm_format_t format;
1525 unsigned int buffer_time = 500000;
1526 unsigned int period_time = 10000;
1527 snd_pcm_uframes_t buffer_size;
1528 snd_pcm_uframes_t period_size;
1534 snd_pcm_hw_params_alloca(&hw_params);
1535 snd_pcm_sw_params_alloca(&sw_params);
1537 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1538 if (lpDesc == NULL) {
1539 WARN("Invalid Parameter !\n");
1540 return MMSYSERR_INVALPARAM;
1542 if (wDevID >= MAX_WAVEOUTDRV) {
1543 TRACE("MAX_WAVOUTDRV reached !\n");
1544 return MMSYSERR_BADDEVICEID;
1547 /* only PCM format is supported so far... */
1548 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1549 lpDesc->lpFormat->nChannels == 0 ||
1550 lpDesc->lpFormat->nSamplesPerSec < DSBFREQUENCY_MIN ||
1551 lpDesc->lpFormat->nSamplesPerSec > DSBFREQUENCY_MAX ||
1552 (lpDesc->lpFormat->wBitsPerSample!=8 && lpDesc->lpFormat->wBitsPerSample!=16)) {
1553 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1554 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1555 lpDesc->lpFormat->nSamplesPerSec);
1556 return WAVERR_BADFORMAT;
1559 if (dwFlags & WAVE_FORMAT_QUERY) {
1560 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1561 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1562 lpDesc->lpFormat->nSamplesPerSec);
1563 return MMSYSERR_NOERROR;
1566 wwo = &WOutDev[wDevID];
1568 if ((dwFlags & WAVE_DIRECTSOUND) && !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
1569 /* not supported, ignore it */
1570 dwFlags &= ~WAVE_DIRECTSOUND;
1573 flags = SND_PCM_NONBLOCK;
1574 if ( dwFlags & WAVE_DIRECTSOUND )
1575 flags |= SND_PCM_ASYNC;
1577 if ( (err = snd_pcm_open(&pcm, wwo->device, SND_PCM_STREAM_PLAYBACK, flags)) < 0)
1579 ERR("Error open: %s\n", snd_strerror(err));
1580 return MMSYSERR_NOTENABLED;
1583 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1585 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1586 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1588 if (wwo->format.wBitsPerSample == 0) {
1589 WARN("Resetting zeroed wBitsPerSample\n");
1590 wwo->format.wBitsPerSample = 8 *
1591 (wwo->format.wf.nAvgBytesPerSec /
1592 wwo->format.wf.nSamplesPerSec) /
1593 wwo->format.wf.nChannels;
1596 snd_pcm_hw_params_any(pcm, hw_params);
1598 #define EXIT_ON_ERROR(f,e,txt) do \
1601 if ( (err = (f) ) < 0) \
1603 ERR(txt ": %s\n", snd_strerror(err)); \
1604 snd_pcm_close(pcm); \
1609 access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
1610 if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {
1611 WARN("mmap not available. switching to standard write.\n");
1612 access = SND_PCM_ACCESS_RW_INTERLEAVED;
1613 EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");
1614 wwo->write = snd_pcm_writei;
1617 wwo->write = snd_pcm_mmap_writei;
1619 EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwo->format.wf.nChannels), MMSYSERR_INVALPARAM, "unable to set required channels");
1621 format = (wwo->format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8;
1622 EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), MMSYSERR_INVALPARAM, "unable to set required format");
1624 rate = wwo->format.wf.nSamplesPerSec;
1626 err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir);
1628 ERR("Rate %ld Hz not available for playback: %s\n", wwo->format.wf.nSamplesPerSec, snd_strerror(rate));
1630 return WAVERR_BADFORMAT;
1632 if (rate != wwo->format.wf.nSamplesPerSec) {
1633 ERR("Rate doesn't match (requested %ld Hz, got %d Hz)\n", wwo->format.wf.nSamplesPerSec, rate);
1635 return WAVERR_BADFORMAT;
1638 EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time");
1640 EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time");
1642 EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");
1644 err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
1645 err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
1647 snd_pcm_sw_params_current(pcm, sw_params);
1648 EXIT_ON_ERROR( snd_pcm_sw_params_set_start_threshold(pcm, sw_params, dwFlags & WAVE_DIRECTSOUND ? INT_MAX : 1 ), MMSYSERR_ERROR, "unable to set start threshold");
1649 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");
1650 EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min");
1651 EXIT_ON_ERROR( snd_pcm_sw_params_set_xfer_align(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set xfer align");
1652 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold");
1653 EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback");
1654 #undef EXIT_ON_ERROR
1656 snd_pcm_prepare(pcm);
1659 ALSA_TraceParameters(hw_params, sw_params, FALSE);
1661 /* now, we can save all required data for later use... */
1662 if ( wwo->hw_params )
1663 snd_pcm_hw_params_free(wwo->hw_params);
1664 snd_pcm_hw_params_malloc(&(wwo->hw_params));
1665 snd_pcm_hw_params_copy(wwo->hw_params, hw_params);
1667 wwo->dwBufferSize = buffer_size;
1668 wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL;
1669 wwo->p_handle = pcm;
1670 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1671 wwo->dwPartialOffset = 0;
1673 ALSA_InitRingMessage(&wwo->msgRing);
1675 wwo->count = snd_pcm_poll_descriptors_count (wwo->p_handle);
1676 if (wwo->count <= 0) {
1677 ERR("Invalid poll descriptors count\n");
1678 return MMSYSERR_ERROR;
1681 wwo->ufds = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, sizeof(struct pollfd) * wwo->count);
1682 if (wwo->ufds == NULL) {
1683 ERR("No enough memory\n");
1684 return MMSYSERR_NOMEM;
1686 if ((err = snd_pcm_poll_descriptors(wwo->p_handle, wwo->ufds, wwo->count)) < 0) {
1687 ERR("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
1688 return MMSYSERR_ERROR;
1691 if (!(dwFlags & WAVE_DIRECTSOUND)) {
1692 wwo->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1693 wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
1694 WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
1695 CloseHandle(wwo->hStartUpEvent);
1697 wwo->hThread = INVALID_HANDLE_VALUE;
1698 wwo->dwThreadID = 0;
1700 wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
1702 TRACE("handle=%08lx \n", (DWORD)wwo->p_handle);
1703 /* if (wwo->dwFragmentSize % wwo->format.wf.nBlockAlign)
1704 ERR("Fragment doesn't contain an integral number of data blocks\n");
1706 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1707 wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
1708 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1709 wwo->format.wf.nBlockAlign);
1711 return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
1715 /**************************************************************************
1716 * wodClose [internal]
1718 static DWORD wodClose(WORD wDevID)
1720 DWORD ret = MMSYSERR_NOERROR;
1723 TRACE("(%u);\n", wDevID);
1725 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1726 WARN("bad device ID !\n");
1727 return MMSYSERR_BADDEVICEID;
1730 wwo = &WOutDev[wDevID];
1731 if (wwo->lpQueuePtr) {
1732 WARN("buffers still playing !\n");
1733 ret = WAVERR_STILLPLAYING;
1735 if (wwo->hThread != INVALID_HANDLE_VALUE) {
1736 ALSA_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
1738 ALSA_DestroyRingMessage(&wwo->msgRing);
1740 snd_pcm_hw_params_free(wwo->hw_params);
1741 wwo->hw_params = NULL;
1743 snd_pcm_close(wwo->p_handle);
1744 wwo->p_handle = NULL;
1746 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
1749 HeapFree(GetProcessHeap(), 0, wwo->ufds);
1754 /**************************************************************************
1755 * wodWrite [internal]
1758 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1760 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1762 /* first, do the sanity checks... */
1763 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1764 WARN("bad dev ID !\n");
1765 return MMSYSERR_BADDEVICEID;
1768 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1769 return WAVERR_UNPREPARED;
1771 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1772 return WAVERR_STILLPLAYING;
1774 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1775 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1776 lpWaveHdr->lpNext = 0;
1778 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
1780 return MMSYSERR_NOERROR;
1783 /**************************************************************************
1784 * wodPrepare [internal]
1786 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1788 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1790 if (wDevID >= MAX_WAVEOUTDRV) {
1791 WARN("bad device ID !\n");
1792 return MMSYSERR_BADDEVICEID;
1795 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1796 return WAVERR_STILLPLAYING;
1798 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1799 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1800 return MMSYSERR_NOERROR;
1803 /**************************************************************************
1804 * wodUnprepare [internal]
1806 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1808 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1810 if (wDevID >= MAX_WAVEOUTDRV) {
1811 WARN("bad device ID !\n");
1812 return MMSYSERR_BADDEVICEID;
1815 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1816 return WAVERR_STILLPLAYING;
1818 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1819 lpWaveHdr->dwFlags |= WHDR_DONE;
1821 return MMSYSERR_NOERROR;
1824 /**************************************************************************
1825 * wodPause [internal]
1827 static DWORD wodPause(WORD wDevID)
1829 TRACE("(%u);!\n", wDevID);
1831 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1832 WARN("bad device ID !\n");
1833 return MMSYSERR_BADDEVICEID;
1836 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
1838 return MMSYSERR_NOERROR;
1841 /**************************************************************************
1842 * wodRestart [internal]
1844 static DWORD wodRestart(WORD wDevID)
1846 TRACE("(%u);\n", wDevID);
1848 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1849 WARN("bad device ID !\n");
1850 return MMSYSERR_BADDEVICEID;
1853 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
1854 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
1857 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1858 /* FIXME: Myst crashes with this ... hmm -MM
1859 return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1862 return MMSYSERR_NOERROR;
1865 /**************************************************************************
1866 * wodReset [internal]
1868 static DWORD wodReset(WORD wDevID)
1870 TRACE("(%u);\n", wDevID);
1872 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1873 WARN("bad device ID !\n");
1874 return MMSYSERR_BADDEVICEID;
1877 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
1879 return MMSYSERR_NOERROR;
1882 /**************************************************************************
1883 * wodGetPosition [internal]
1885 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1889 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1891 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1892 WARN("bad device ID !\n");
1893 return MMSYSERR_BADDEVICEID;
1896 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1898 wwo = &WOutDev[wDevID];
1899 ALSA_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
1901 return bytes_to_mmtime(lpTime, wwo->dwPlayedTotal, &wwo->format);
1904 /**************************************************************************
1905 * wodBreakLoop [internal]
1907 static DWORD wodBreakLoop(WORD wDevID)
1909 TRACE("(%u);\n", wDevID);
1911 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1912 WARN("bad device ID !\n");
1913 return MMSYSERR_BADDEVICEID;
1915 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
1916 return MMSYSERR_NOERROR;
1919 /**************************************************************************
1920 * wodGetVolume [internal]
1922 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1929 TRACE("(%u, %p);\n", wDevID, lpdwVol);
1930 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1931 WARN("bad device ID !\n");
1932 return MMSYSERR_BADDEVICEID;
1934 wwo = &WOutDev[wDevID];
1935 count = snd_ctl_elem_info_get_count(wwo->playback_einfo);
1936 min = snd_ctl_elem_info_get_min(wwo->playback_einfo);
1937 max = snd_ctl_elem_info_get_max(wwo->playback_einfo);
1939 #define VOLUME_ALSA_TO_WIN(x) (((x)-min) * 65536 /(max-min))
1940 if (lpdwVol == NULL)
1941 return MMSYSERR_NOTENABLED;
1946 left = VOLUME_ALSA_TO_WIN(snd_ctl_elem_value_get_integer(wwo->playback_evalue, 0));
1947 right = VOLUME_ALSA_TO_WIN(snd_ctl_elem_value_get_integer(wwo->playback_evalue, 1));
1950 left = right = VOLUME_ALSA_TO_WIN(snd_ctl_elem_value_get_integer(wwo->playback_evalue, 0));
1953 WARN("%d channels mixer not supported\n", count);
1954 return MMSYSERR_NOERROR;
1956 #undef VOLUME_ALSA_TO_WIN
1958 TRACE("left=%d right=%d !\n", left, right);
1959 *lpdwVol = MAKELONG( left, right );
1960 return MMSYSERR_NOERROR;
1963 /**************************************************************************
1964 * wodSetVolume [internal]
1966 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1973 TRACE("(%u, %08lX);\n", wDevID, dwParam);
1974 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].p_handle == NULL) {
1975 WARN("bad device ID !\n");
1976 return MMSYSERR_BADDEVICEID;
1978 wwo = &WOutDev[wDevID];
1979 count=snd_ctl_elem_info_get_count(wwo->playback_einfo);
1980 min = snd_ctl_elem_info_get_min(wwo->playback_einfo);
1981 max = snd_ctl_elem_info_get_max(wwo->playback_einfo);
1983 left = LOWORD(dwParam);
1984 right = HIWORD(dwParam);
1986 #define VOLUME_WIN_TO_ALSA(x) ( (((x) * (max-min)) / 65536) + min )
1990 snd_ctl_elem_value_set_integer(wwo->playback_evalue, 0, VOLUME_WIN_TO_ALSA(left));
1991 snd_ctl_elem_value_set_integer(wwo->playback_evalue, 1, VOLUME_WIN_TO_ALSA(right));
1994 snd_ctl_elem_value_set_integer(wwo->playback_evalue, 0, VOLUME_WIN_TO_ALSA(left));
1997 WARN("%d channels mixer not supported\n", count);
1999 #undef VOLUME_WIN_TO_ALSA
2000 if ( (err = snd_ctl_elem_write(wwo->ctl, wwo->playback_evalue)) < 0)
2002 ERR("error writing snd_ctl_elem_value: %s\n", snd_strerror(err));
2004 return MMSYSERR_NOERROR;
2007 /**************************************************************************
2008 * wodGetNumDevs [internal]
2010 static DWORD wodGetNumDevs(void)
2012 return ALSA_WodNumDevs;
2015 /**************************************************************************
2016 * wodDevInterfaceSize [internal]
2018 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
2020 TRACE("(%u, %p)\n", wDevID, dwParam1);
2022 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
2023 NULL, 0 ) * sizeof(WCHAR);
2024 return MMSYSERR_NOERROR;
2027 /**************************************************************************
2028 * wodDevInterface [internal]
2030 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
2032 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
2033 NULL, 0 ) * sizeof(WCHAR))
2035 MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
2036 dwParam1, dwParam2 / sizeof(WCHAR));
2037 return MMSYSERR_NOERROR;
2039 return MMSYSERR_INVALPARAM;
2042 /**************************************************************************
2043 * wodMessage (WINEALSA.@)
2045 DWORD WINAPI ALSA_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2046 DWORD dwParam1, DWORD dwParam2)
2048 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
2049 wDevID, wMsg, dwUser, dwParam1, dwParam2);
2056 /* FIXME: Pretend this is supported */
2058 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
2059 case WODM_CLOSE: return wodClose (wDevID);
2060 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2);
2061 case WODM_GETNUMDEVS: return wodGetNumDevs ();
2062 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
2063 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
2064 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
2065 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
2066 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2067 case WODM_PAUSE: return wodPause (wDevID);
2068 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
2069 case WODM_BREAKLOOP: return wodBreakLoop (wDevID);
2070 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2071 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2072 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
2073 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
2074 case WODM_RESTART: return wodRestart (wDevID);
2075 case WODM_RESET: return wodReset (wDevID);
2076 case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
2077 case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
2078 case DRV_QUERYDSOUNDIFACE: return wodDsCreate (wDevID, (PIDSDRIVER*)dwParam1);
2079 case DRV_QUERYDSOUNDDESC: return wodDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
2080 case DRV_QUERYDSOUNDGUID: return wodDsGuid (wDevID, (LPGUID)dwParam1);
2083 FIXME("unknown message %d!\n", wMsg);
2085 return MMSYSERR_NOTSUPPORTED;
2088 /*======================================================================*
2089 * Low level DSOUND implementation *
2090 *======================================================================*/
2092 typedef struct IDsDriverImpl IDsDriverImpl;
2093 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
2095 struct IDsDriverImpl
2097 /* IUnknown fields */
2098 ICOM_VFIELD(IDsDriver);
2100 /* IDsDriverImpl fields */
2102 IDsDriverBufferImpl*primary;
2105 struct IDsDriverBufferImpl
2107 /* IUnknown fields */
2108 ICOM_VFIELD(IDsDriverBuffer);
2110 /* IDsDriverBufferImpl fields */
2113 CRITICAL_SECTION mmap_crst;
2115 DWORD mmap_buflen_bytes;
2116 snd_pcm_uframes_t mmap_buflen_frames;
2117 snd_pcm_channel_area_t * mmap_areas;
2118 snd_async_handler_t * mmap_async_handler;
2121 static void DSDB_CheckXRUN(IDsDriverBufferImpl* pdbi)
2123 WINE_WAVEOUT * wwo = &(WOutDev[pdbi->drv->wDevID]);
2124 snd_pcm_state_t state = snd_pcm_state(wwo->p_handle);
2126 if ( state == SND_PCM_STATE_XRUN )
2128 int err = snd_pcm_prepare(wwo->p_handle);
2129 TRACE("xrun occurred\n");
2131 ERR("recovery from xrun failed, prepare failed: %s\n", snd_strerror(err));
2133 else if ( state == SND_PCM_STATE_SUSPENDED )
2135 int err = snd_pcm_resume(wwo->p_handle);
2136 TRACE("recovery from suspension occurred\n");
2137 if (err < 0 && err != -EAGAIN){
2138 err = snd_pcm_prepare(wwo->p_handle);
2140 ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err));
2145 static void DSDB_MMAPCopy(IDsDriverBufferImpl* pdbi)
2147 WINE_WAVEOUT * wwo = &(WOutDev[pdbi->drv->wDevID]);
2148 unsigned int channels;
2149 snd_pcm_format_t format;
2150 snd_pcm_uframes_t period_size;
2151 snd_pcm_sframes_t avail;
2155 if ( !pdbi->mmap_buffer || !wwo->hw_params || !wwo->p_handle)
2158 err = snd_pcm_hw_params_get_channels(wwo->hw_params, &channels);
2159 err = snd_pcm_hw_params_get_format(wwo->hw_params, &format);
2161 err = snd_pcm_hw_params_get_period_size(wwo->hw_params, &period_size, &dir);
2162 avail = snd_pcm_avail_update(wwo->p_handle);
2164 DSDB_CheckXRUN(pdbi);
2166 TRACE("avail=%d format=%s channels=%d\n", (int)avail, snd_pcm_format_name(format), channels );
2168 while (avail >= period_size)
2170 const snd_pcm_channel_area_t *areas;
2171 snd_pcm_uframes_t ofs;
2172 snd_pcm_uframes_t frames;
2175 frames = avail / period_size * period_size; /* round down to a multiple of period_size */
2177 EnterCriticalSection(&pdbi->mmap_crst);
2179 snd_pcm_mmap_begin(wwo->p_handle, &areas, &ofs, &frames);
2180 snd_pcm_areas_copy(areas, ofs, pdbi->mmap_areas, ofs, channels, frames, format);
2181 err = snd_pcm_mmap_commit(wwo->p_handle, ofs, frames);
2183 LeaveCriticalSection(&pdbi->mmap_crst);
2185 if ( err != (snd_pcm_sframes_t) frames)
2186 ERR("mmap partially failed.\n");
2188 avail = snd_pcm_avail_update(wwo->p_handle);
2192 static void DSDB_PCMCallback(snd_async_handler_t *ahandler)
2194 /* snd_pcm_t * handle = snd_async_handler_get_pcm(ahandler); */
2195 IDsDriverBufferImpl* pdbi = snd_async_handler_get_callback_private(ahandler);
2196 TRACE("callback called\n");
2197 DSDB_MMAPCopy(pdbi);
2200 static int DSDB_CreateMMAP(IDsDriverBufferImpl* pdbi)
2202 WINE_WAVEOUT * wwo = &(WOutDev[pdbi->drv->wDevID]);
2203 snd_pcm_format_t format;
2204 snd_pcm_uframes_t frames;
2205 unsigned int channels;
2206 unsigned int bits_per_sample;
2207 unsigned int bits_per_frame;
2208 snd_pcm_channel_area_t * a;
2212 err = snd_pcm_hw_params_get_format(wwo->hw_params, &format);
2213 err = snd_pcm_hw_params_get_buffer_size(wwo->hw_params, &frames);
2214 err = snd_pcm_hw_params_get_channels(wwo->hw_params, &channels);
2215 bits_per_sample = snd_pcm_format_physical_width(format);
2216 bits_per_frame = bits_per_sample * channels;
2220 ALSA_TraceParameters(wwo->hw_params, NULL, FALSE);
2222 TRACE("format=%s frames=%ld channels=%d bits_per_sample=%d bits_per_frame=%d\n",
2223 snd_pcm_format_name(format), frames, channels, bits_per_sample, bits_per_frame);
2225 pdbi->mmap_buflen_frames = frames;
2226 pdbi->mmap_buflen_bytes = snd_pcm_frames_to_bytes( wwo->p_handle, frames );
2227 pdbi->mmap_buffer = HeapAlloc(GetProcessHeap(),0,pdbi->mmap_buflen_bytes);
2228 if (!pdbi->mmap_buffer)
2229 return DSERR_OUTOFMEMORY;
2231 snd_pcm_format_set_silence(format, pdbi->mmap_buffer, frames );
2233 TRACE("created mmap buffer of %ld frames (%ld bytes) at %p\n",
2234 frames, pdbi->mmap_buflen_bytes, pdbi->mmap_buffer);
2236 pdbi->mmap_areas = HeapAlloc(GetProcessHeap(),0,channels*sizeof(snd_pcm_channel_area_t));
2237 if (!pdbi->mmap_areas)
2238 return DSERR_OUTOFMEMORY;
2240 a = pdbi->mmap_areas;
2241 for (c = 0; c < channels; c++, a++)
2243 a->addr = pdbi->mmap_buffer;
2244 a->first = bits_per_sample * c;
2245 a->step = bits_per_frame;
2246 TRACE("Area %d: addr=%p first=%d step=%d\n", c, a->addr, a->first, a->step);
2249 InitializeCriticalSection(&pdbi->mmap_crst);
2251 err = snd_async_add_pcm_handler(&pdbi->mmap_async_handler, wwo->p_handle, DSDB_PCMCallback, pdbi);
2254 ERR("add_pcm_handler failed. reason: %s\n", snd_strerror(err));
2255 return DSERR_GENERIC;
2261 static void DSDB_DestroyMMAP(IDsDriverBufferImpl* pdbi)
2263 TRACE("mmap buffer %p destroyed\n", pdbi->mmap_buffer);
2264 HeapFree(GetProcessHeap(), 0, pdbi->mmap_areas);
2265 HeapFree(GetProcessHeap(), 0, pdbi->mmap_buffer);
2266 pdbi->mmap_areas = NULL;
2267 pdbi->mmap_buffer = NULL;
2268 DeleteCriticalSection(&pdbi->mmap_crst);
2272 static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
2274 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2275 FIXME("(): stub!\n");
2276 return DSERR_UNSUPPORTED;
2279 static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface)
2281 ICOM_THIS(IDsDriverBufferImpl,iface);
2282 TRACE("(%p)\n",iface);
2286 static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface)
2288 ICOM_THIS(IDsDriverBufferImpl,iface);
2289 TRACE("(%p)\n",iface);
2292 if (This == This->drv->primary)
2293 This->drv->primary = NULL;
2294 DSDB_DestroyMMAP(This);
2295 HeapFree(GetProcessHeap(), 0, This);
2299 static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
2300 LPVOID*ppvAudio1,LPDWORD pdwLen1,
2301 LPVOID*ppvAudio2,LPDWORD pdwLen2,
2302 DWORD dwWritePosition,DWORD dwWriteLen,
2305 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2306 TRACE("(%p)\n",iface);
2307 return DSERR_UNSUPPORTED;
2310 static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
2311 LPVOID pvAudio1,DWORD dwLen1,
2312 LPVOID pvAudio2,DWORD dwLen2)
2314 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2315 TRACE("(%p)\n",iface);
2316 return DSERR_UNSUPPORTED;
2319 static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface,
2320 LPWAVEFORMATEX pwfx)
2322 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2323 TRACE("(%p,%p)\n",iface,pwfx);
2324 return DSERR_BUFFERLOST;
2327 static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq)
2329 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2330 TRACE("(%p,%ld): stub\n",iface,dwFreq);
2331 return DSERR_UNSUPPORTED;
2334 static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan)
2337 ICOM_THIS(IDsDriverBufferImpl,iface);
2338 TRACE("(%p,%p)\n",iface,pVolPan);
2339 vol = pVolPan->dwTotalLeftAmpFactor | (pVolPan->dwTotalRightAmpFactor << 16);
2341 if (wodSetVolume(This->drv->wDevID, vol) != MMSYSERR_NOERROR) {
2342 WARN("wodSetVolume failed\n");
2343 return DSERR_INVALIDPARAM;
2349 static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos)
2351 /* ICOM_THIS(IDsDriverImpl,iface); */
2352 TRACE("(%p,%ld): stub\n",iface,dwNewPos);
2353 return DSERR_UNSUPPORTED;
2356 static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
2357 LPDWORD lpdwPlay, LPDWORD lpdwWrite)
2359 ICOM_THIS(IDsDriverBufferImpl,iface);
2360 WINE_WAVEOUT * wwo = &(WOutDev[This->drv->wDevID]);
2361 snd_pcm_uframes_t hw_ptr;
2362 snd_pcm_uframes_t period_size;
2366 if (wwo->hw_params == NULL) return DSERR_GENERIC;
2369 err = snd_pcm_hw_params_get_period_size(wwo->hw_params, &period_size, &dir);
2371 if (wwo->p_handle == NULL) return DSERR_GENERIC;
2372 /** we need to track down buffer underruns */
2373 DSDB_CheckXRUN(This);
2375 EnterCriticalSection(&This->mmap_crst);
2376 /* FIXME: snd_pcm_mmap_hw_ptr() should not be accessed by a user app. */
2377 /* It will NOT return what why want anyway. */
2378 hw_ptr = _snd_pcm_mmap_hw_ptr(wwo->p_handle);
2380 *lpdwPlay = snd_pcm_frames_to_bytes(wwo->p_handle, hw_ptr/ period_size * period_size) % This->mmap_buflen_bytes;
2382 *lpdwWrite = snd_pcm_frames_to_bytes(wwo->p_handle, (hw_ptr / period_size + 1) * period_size ) % This->mmap_buflen_bytes;
2383 LeaveCriticalSection(&This->mmap_crst);
2385 TRACE("hw_ptr=0x%08x, playpos=%ld, writepos=%ld\n", (unsigned int)hw_ptr, lpdwPlay?*lpdwPlay:-1, lpdwWrite?*lpdwWrite:-1);
2389 static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags)
2391 ICOM_THIS(IDsDriverBufferImpl,iface);
2392 WINE_WAVEOUT * wwo = &(WOutDev[This->drv->wDevID]);
2393 snd_pcm_state_t state;
2396 TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags);
2398 if (wwo->p_handle == NULL) return DSERR_GENERIC;
2400 state = snd_pcm_state(wwo->p_handle);
2401 if ( state == SND_PCM_STATE_SETUP )
2403 err = snd_pcm_prepare(wwo->p_handle);
2404 state = snd_pcm_state(wwo->p_handle);
2406 if ( state == SND_PCM_STATE_PREPARED )
2408 DSDB_MMAPCopy(This);
2409 err = snd_pcm_start(wwo->p_handle);
2414 static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface)
2416 ICOM_THIS(IDsDriverBufferImpl,iface);
2417 WINE_WAVEOUT * wwo = &(WOutDev[This->drv->wDevID]);
2422 TRACE("(%p)\n",iface);
2424 if (wwo->p_handle == NULL) return DSERR_GENERIC;
2426 /* ring buffer wrap up detection */
2427 IDsDriverBufferImpl_GetPosition(iface, &play, &write);
2430 TRACE("writepos wrapper up\n");
2434 if ( ( err = snd_pcm_drop(wwo->p_handle)) < 0 )
2436 ERR("error while stopping pcm: %s\n", snd_strerror(err));
2437 return DSERR_GENERIC;
2442 static ICOM_VTABLE(IDsDriverBuffer) dsdbvt =
2444 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2445 IDsDriverBufferImpl_QueryInterface,
2446 IDsDriverBufferImpl_AddRef,
2447 IDsDriverBufferImpl_Release,
2448 IDsDriverBufferImpl_Lock,
2449 IDsDriverBufferImpl_Unlock,
2450 IDsDriverBufferImpl_SetFormat,
2451 IDsDriverBufferImpl_SetFrequency,
2452 IDsDriverBufferImpl_SetVolumePan,
2453 IDsDriverBufferImpl_SetPosition,
2454 IDsDriverBufferImpl_GetPosition,
2455 IDsDriverBufferImpl_Play,
2456 IDsDriverBufferImpl_Stop
2459 static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj)
2461 /* ICOM_THIS(IDsDriverImpl,iface); */
2462 FIXME("(%p): stub!\n",iface);
2463 return DSERR_UNSUPPORTED;
2466 static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface)
2468 ICOM_THIS(IDsDriverImpl,iface);
2469 TRACE("(%p)\n",iface);
2474 static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface)
2476 ICOM_THIS(IDsDriverImpl,iface);
2477 TRACE("(%p)\n",iface);
2480 HeapFree(GetProcessHeap(),0,This);
2484 static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc)
2486 ICOM_THIS(IDsDriverImpl,iface);
2487 TRACE("(%p,%p)\n",iface,pDesc);
2488 memcpy(pDesc, &(WOutDev[This->wDevID].ds_desc), sizeof(DSDRIVERDESC));
2489 pDesc->dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
2490 DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK;
2491 pDesc->dnDevNode = WOutDev[This->wDevID].waveDesc.dnDevNode;
2493 pDesc->wReserved = 0;
2494 pDesc->ulDeviceNum = This->wDevID;
2495 pDesc->dwHeapType = DSDHEAP_NOHEAP;
2496 pDesc->pvDirectDrawHeap = NULL;
2497 pDesc->dwMemStartAddress = 0;
2498 pDesc->dwMemEndAddress = 0;
2499 pDesc->dwMemAllocExtra = 0;
2500 pDesc->pvReserved1 = NULL;
2501 pDesc->pvReserved2 = NULL;
2505 static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface)
2507 /* ICOM_THIS(IDsDriverImpl,iface); */
2508 TRACE("(%p)\n",iface);
2512 static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface)
2514 /* ICOM_THIS(IDsDriverImpl,iface); */
2515 TRACE("(%p)\n",iface);
2519 static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps)
2521 ICOM_THIS(IDsDriverImpl,iface);
2522 TRACE("(%p,%p)\n",iface,pCaps);
2523 memset(pCaps, 0, sizeof(*pCaps));
2525 pCaps->dwFlags = DSCAPS_PRIMARYMONO;
2526 if ( WOutDev[This->wDevID].caps.wChannels == 2 )
2527 pCaps->dwFlags |= DSCAPS_PRIMARYSTEREO;
2529 if ( WOutDev[This->wDevID].caps.dwFormats & (WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 ) )
2530 pCaps->dwFlags |= DSCAPS_PRIMARY8BIT;
2532 if ( WOutDev[This->wDevID].caps.dwFormats & (WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16))
2533 pCaps->dwFlags |= DSCAPS_PRIMARY16BIT;
2535 pCaps->dwPrimaryBuffers = 1;
2536 TRACE("caps=0x%X\n",(unsigned int)pCaps->dwFlags);
2537 pCaps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
2538 pCaps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
2540 /* the other fields only apply to secondary buffers, which we don't support
2541 * (unless we want to mess with wavetable synthesizers and MIDI) */
2545 static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
2546 LPWAVEFORMATEX pwfx,
2547 DWORD dwFlags, DWORD dwCardAddress,
2548 LPDWORD pdwcbBufferSize,
2552 ICOM_THIS(IDsDriverImpl,iface);
2553 IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
2556 TRACE("(%p,%p,%lx,%lx)\n",iface,pwfx,dwFlags,dwCardAddress);
2557 /* we only support primary buffers */
2558 if (!(dwFlags & DSBCAPS_PRIMARYBUFFER))
2559 return DSERR_UNSUPPORTED;
2561 return DSERR_ALLOCATED;
2562 if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN))
2563 return DSERR_CONTROLUNAVAIL;
2565 *ippdsdb = (IDsDriverBufferImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverBufferImpl));
2566 if (*ippdsdb == NULL)
2567 return DSERR_OUTOFMEMORY;
2568 (*ippdsdb)->lpVtbl = &dsdbvt;
2569 (*ippdsdb)->ref = 1;
2570 (*ippdsdb)->drv = This;
2572 err = DSDB_CreateMMAP((*ippdsdb));
2575 HeapFree(GetProcessHeap(), 0, *ippdsdb);
2579 *ppbBuffer = (*ippdsdb)->mmap_buffer;
2580 *pdwcbBufferSize = (*ippdsdb)->mmap_buflen_bytes;
2582 This->primary = *ippdsdb;
2584 /* buffer is ready to go */
2585 TRACE("buffer created at %p\n", *ippdsdb);
2589 static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
2590 PIDSDRIVERBUFFER pBuffer,
2593 /* ICOM_THIS(IDsDriverImpl,iface); */
2594 TRACE("(%p,%p): stub\n",iface,pBuffer);
2595 return DSERR_INVALIDCALL;
2598 static ICOM_VTABLE(IDsDriver) dsdvt =
2600 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2601 IDsDriverImpl_QueryInterface,
2602 IDsDriverImpl_AddRef,
2603 IDsDriverImpl_Release,
2604 IDsDriverImpl_GetDriverDesc,
2606 IDsDriverImpl_Close,
2607 IDsDriverImpl_GetCaps,
2608 IDsDriverImpl_CreateSoundBuffer,
2609 IDsDriverImpl_DuplicateSoundBuffer
2612 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
2614 IDsDriverImpl** idrv = (IDsDriverImpl**)drv;
2616 TRACE("driver created\n");
2618 /* the HAL isn't much better than the HEL if we can't do mmap() */
2619 if (!(WOutDev[wDevID].caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
2620 ERR("DirectSound flag not set\n");
2621 MESSAGE("This sound card's driver does not support direct access\n");
2622 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
2623 return MMSYSERR_NOTSUPPORTED;
2626 *idrv = (IDsDriverImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverImpl));
2628 return MMSYSERR_NOMEM;
2629 (*idrv)->lpVtbl = &dsdvt;
2632 (*idrv)->wDevID = wDevID;
2633 (*idrv)->primary = NULL;
2634 return MMSYSERR_NOERROR;
2637 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
2639 memcpy(desc, &(WOutDev[wDevID].ds_desc), sizeof(DSDRIVERDESC));
2640 return MMSYSERR_NOERROR;
2643 static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid)
2645 TRACE("(%d,%p)\n",wDevID,pGuid);
2647 memcpy(pGuid, &(WOutDev[wDevID].ds_guid), sizeof(GUID));
2649 return MMSYSERR_NOERROR;
2652 /*======================================================================*
2653 * Low level WAVE IN implementation *
2654 *======================================================================*/
2656 /**************************************************************************
2657 * widNotifyClient [internal]
2659 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
2661 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
2667 if (wwi->wFlags != DCB_NULL &&
2668 !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags, (HDRVR)wwi->waveDesc.hWave,
2669 wMsg, wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
2670 WARN("can't notify client !\n");
2671 return MMSYSERR_ERROR;
2675 FIXME("Unknown callback message %u\n", wMsg);
2676 return MMSYSERR_INVALPARAM;
2678 return MMSYSERR_NOERROR;
2681 /**************************************************************************
2682 * widGetDevCaps [internal]
2684 static DWORD widGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
2686 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
2688 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
2690 if (wDevID >= MAX_WAVEINDRV) {
2691 TRACE("MAX_WAVOUTDRV reached !\n");
2692 return MMSYSERR_BADDEVICEID;
2695 memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
2696 return MMSYSERR_NOERROR;
2699 /**************************************************************************
2700 * widRecorder_ReadHeaders [internal]
2702 static void widRecorder_ReadHeaders(WINE_WAVEIN * wwi)
2704 enum win_wm_message tmp_msg;
2709 while (ALSA_RetrieveRingMessage(&wwi->msgRing, &tmp_msg, &tmp_param, &tmp_ev)) {
2710 if (tmp_msg == WINE_WM_HEADER) {
2712 lpWaveHdr = (LPWAVEHDR)tmp_param;
2713 lpWaveHdr->lpNext = 0;
2715 if (wwi->lpQueuePtr == 0)
2716 wwi->lpQueuePtr = lpWaveHdr;
2718 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
2722 ERR("should only have headers left\n");
2727 /**************************************************************************
2728 * widRecorder [internal]
2730 static DWORD CALLBACK widRecorder(LPVOID pmt)
2732 WORD uDevID = (DWORD)pmt;
2733 WINE_WAVEIN* wwi = (WINE_WAVEIN*)&WInDev[uDevID];
2737 LPVOID buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, wwi->dwPeriodSize);
2738 char *pOffset = buffer;
2739 enum win_wm_message msg;
2742 DWORD frames_per_period;
2744 wwi->state = WINE_WS_STOPPED;
2745 wwi->dwTotalRecorded = 0;
2746 wwi->lpQueuePtr = NULL;
2748 SetEvent(wwi->hStartUpEvent);
2750 /* make sleep time to be # of ms to output a period */
2751 dwSleepTime = (1024/*wwi-dwPeriodSize => overrun!*/ * 1000) / wwi->format.wf.nAvgBytesPerSec;
2752 frames_per_period = snd_pcm_bytes_to_frames(wwi->p_handle, wwi->dwPeriodSize);
2753 TRACE("sleeptime=%ld ms\n", dwSleepTime);
2756 /* wait for dwSleepTime or an event in thread's queue */
2757 /* FIXME: could improve wait time depending on queue state,
2758 * ie, number of queued fragments
2760 if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING)
2767 lpWaveHdr = wwi->lpQueuePtr;
2768 /* read all the fragments accumulated so far */
2769 frames = snd_pcm_avail_update(wwi->p_handle);
2770 bytes = snd_pcm_frames_to_bytes(wwi->p_handle, frames);
2771 TRACE("frames = %ld bytes = %ld\n", frames, bytes);
2772 periods = bytes / wwi->dwPeriodSize;
2773 while ((periods > 0) && (wwi->lpQueuePtr))
2776 bytes = wwi->dwPeriodSize;
2777 TRACE("bytes = %ld\n",bytes);
2778 if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded >= wwi->dwPeriodSize)
2780 /* directly read fragment in wavehdr */
2781 read = wwi->read(wwi->p_handle, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, frames_per_period);
2782 bytesRead = snd_pcm_frames_to_bytes(wwi->p_handle, read);
2784 TRACE("bytesRead=%ld (direct)\n", bytesRead);
2785 if (bytesRead != (DWORD) -1)
2787 /* update number of bytes recorded in current buffer and by this device */
2788 lpWaveHdr->dwBytesRecorded += bytesRead;
2789 wwi->dwTotalRecorded += bytesRead;
2791 /* buffer is full. notify client */
2792 if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
2794 /* must copy the value of next waveHdr, because we have no idea of what
2795 * will be done with the content of lpWaveHdr in callback
2797 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
2799 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2800 lpWaveHdr->dwFlags |= WHDR_DONE;
2802 wwi->lpQueuePtr = lpNext;
2803 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2810 /* read the fragment in a local buffer */
2811 read = wwi->read(wwi->p_handle, buffer, frames_per_period);
2812 bytesRead = snd_pcm_frames_to_bytes(wwi->p_handle, read);
2815 TRACE("bytesRead=%ld (local)\n", bytesRead);
2817 /* copy data in client buffers */
2818 while (bytesRead != (DWORD) -1 && bytesRead > 0)
2820 DWORD dwToCopy = min (bytesRead, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
2822 memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
2826 /* update number of bytes recorded in current buffer and by this device */
2827 lpWaveHdr->dwBytesRecorded += dwToCopy;
2828 wwi->dwTotalRecorded += dwToCopy;
2829 bytesRead -= dwToCopy;
2830 pOffset += dwToCopy;
2832 /* client buffer is full. notify client */
2833 if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
2835 /* must copy the value of next waveHdr, because we have no idea of what
2836 * will be done with the content of lpWaveHdr in callback
2838 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
2839 TRACE("lpNext=%p\n", lpNext);
2841 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2842 lpWaveHdr->dwFlags |= WHDR_DONE;
2844 wwi->lpQueuePtr = lpNext;
2845 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2848 if (!lpNext && bytesRead) {
2849 /* before we give up, check for more header messages */
2850 while (ALSA_PeekRingMessage(&wwi->msgRing, &msg, ¶m, &ev))
2852 if (msg == WINE_WM_HEADER) {
2854 ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev);
2855 hdr = ((LPWAVEHDR)param);
2856 TRACE("msg = %s, hdr = %p, ev = %p\n", wodPlayerCmdString[msg - WM_USER - 1], hdr, ev);
2858 if (lpWaveHdr == 0) {
2859 /* new head of queue */
2860 wwi->lpQueuePtr = lpWaveHdr = hdr;
2862 /* insert buffer at the end of queue */
2864 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
2871 if (lpWaveHdr == 0) {
2872 /* no more buffer to copy data to, but we did read more.
2873 * what hasn't been copied will be dropped
2875 WARN("buffer under run! %lu bytes dropped.\n", bytesRead);
2876 wwi->lpQueuePtr = NULL;
2886 WAIT_OMR(&wwi->msgRing, dwSleepTime);
2888 while (ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev))
2890 TRACE("msg=%s param=0x%lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
2892 case WINE_WM_PAUSING:
2893 wwi->state = WINE_WS_PAUSED;
2894 /*FIXME("Device should stop recording\n");*/
2897 case WINE_WM_STARTING:
2898 wwi->state = WINE_WS_PLAYING;
2899 snd_pcm_start(wwi->p_handle);
2902 case WINE_WM_HEADER:
2903 lpWaveHdr = (LPWAVEHDR)param;
2904 lpWaveHdr->lpNext = 0;
2906 /* insert buffer at the end of queue */
2909 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
2913 case WINE_WM_STOPPING:
2914 if (wwi->state != WINE_WS_STOPPED)
2916 snd_pcm_drain(wwi->p_handle);
2918 /* read any headers in queue */
2919 widRecorder_ReadHeaders(wwi);
2921 /* return current buffer to app */
2922 lpWaveHdr = wwi->lpQueuePtr;
2925 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
2926 TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
2927 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2928 lpWaveHdr->dwFlags |= WHDR_DONE;
2929 wwi->lpQueuePtr = lpNext;
2930 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2933 wwi->state = WINE_WS_STOPPED;
2936 case WINE_WM_RESETTING:
2937 if (wwi->state != WINE_WS_STOPPED)
2939 snd_pcm_drain(wwi->p_handle);
2941 wwi->state = WINE_WS_STOPPED;
2942 wwi->dwTotalRecorded = 0;
2944 /* read any headers in queue */
2945 widRecorder_ReadHeaders(wwi);
2947 /* return all buffers to the app */
2948 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
2949 TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
2950 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2951 lpWaveHdr->dwFlags |= WHDR_DONE;
2952 wwi->lpQueuePtr = lpWaveHdr->lpNext;
2953 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2956 wwi->lpQueuePtr = NULL;
2959 case WINE_WM_CLOSING:
2961 wwi->state = WINE_WS_CLOSED;
2963 HeapFree(GetProcessHeap(), 0, buffer);
2965 /* shouldn't go here */
2966 case WINE_WM_UPDATE:
2971 FIXME("unknown message %d\n", msg);
2977 /* just for not generating compilation warnings... should never be executed */
2981 /**************************************************************************
2982 * widOpen [internal]
2984 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
2987 snd_pcm_hw_params_t * hw_params;
2988 snd_pcm_sw_params_t * sw_params;
2989 snd_pcm_access_t access;
2990 snd_pcm_format_t format;
2992 unsigned int buffer_time = 500000;
2993 unsigned int period_time = 10000;
2994 snd_pcm_uframes_t buffer_size;
2995 snd_pcm_uframes_t period_size;
3001 snd_pcm_hw_params_alloca(&hw_params);
3002 snd_pcm_sw_params_alloca(&sw_params);
3004 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
3005 if (lpDesc == NULL) {
3006 WARN("Invalid Parameter !\n");
3007 return MMSYSERR_INVALPARAM;
3009 if (wDevID >= MAX_WAVEOUTDRV) {
3010 TRACE("MAX_WAVOUTDRV reached !\n");
3011 return MMSYSERR_BADDEVICEID;
3014 /* only PCM format is supported so far... */
3015 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
3016 lpDesc->lpFormat->nChannels == 0 ||
3017 lpDesc->lpFormat->nSamplesPerSec < DSBFREQUENCY_MIN ||
3018 lpDesc->lpFormat->nSamplesPerSec > DSBFREQUENCY_MAX ||
3019 (lpDesc->lpFormat->wBitsPerSample!=8 && lpDesc->lpFormat->wBitsPerSample!=16)) {
3020 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3021 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
3022 lpDesc->lpFormat->nSamplesPerSec);
3023 return WAVERR_BADFORMAT;
3026 if (dwFlags & WAVE_FORMAT_QUERY) {
3027 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3028 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
3029 lpDesc->lpFormat->nSamplesPerSec);
3030 return MMSYSERR_NOERROR;
3033 wwi = &WInDev[wDevID];
3035 if ((dwFlags & WAVE_DIRECTSOUND) && !(wwi->caps.dwSupport & WAVECAPS_DIRECTSOUND))
3036 /* not supported, ignore it */
3037 dwFlags &= ~WAVE_DIRECTSOUND;
3040 flags = SND_PCM_NONBLOCK;
3041 if ( dwFlags & WAVE_DIRECTSOUND )
3042 flags |= SND_PCM_ASYNC;
3044 if ( (err=snd_pcm_open(&pcm, wwi->device, SND_PCM_STREAM_CAPTURE, dwFlags)) < 0 )
3046 ERR("Error open: %s\n", snd_strerror(err));
3047 return MMSYSERR_NOTENABLED;
3050 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
3052 memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
3053 memcpy(&wwi->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
3055 if (wwi->format.wBitsPerSample == 0) {
3056 WARN("Resetting zeroed wBitsPerSample\n");
3057 wwi->format.wBitsPerSample = 8 *
3058 (wwi->format.wf.nAvgBytesPerSec /
3059 wwi->format.wf.nSamplesPerSec) /
3060 wwi->format.wf.nChannels;
3063 snd_pcm_hw_params_any(pcm, hw_params);
3065 #define EXIT_ON_ERROR(f,e,txt) do \
3068 if ( (err = (f) ) < 0) \
3070 ERR(txt ": %s\n", snd_strerror(err)); \
3071 snd_pcm_close(pcm); \
3076 access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
3077 if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {
3078 WARN("mmap not available. switching to standard write.\n");
3079 access = SND_PCM_ACCESS_RW_INTERLEAVED;
3080 EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");
3081 wwi->read = snd_pcm_readi;
3084 wwi->read = snd_pcm_mmap_readi;
3086 EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwi->format.wf.nChannels), MMSYSERR_INVALPARAM, "unable to set required channels");
3088 format = (wwi->format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8;
3089 EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), MMSYSERR_INVALPARAM, "unable to set required format");
3091 rate = wwi->format.wf.nSamplesPerSec;
3093 err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir);
3095 ERR("Rate %ld Hz not available for playback: %s\n", wwi->format.wf.nSamplesPerSec, snd_strerror(rate));
3097 return WAVERR_BADFORMAT;
3099 if (rate != wwi->format.wf.nSamplesPerSec) {
3100 ERR("Rate doesn't match (requested %ld Hz, got %d Hz)\n", wwi->format.wf.nSamplesPerSec, rate);
3102 return WAVERR_BADFORMAT;
3106 EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time");
3108 EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time");
3110 EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");
3113 err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
3114 err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
3116 snd_pcm_sw_params_current(pcm, sw_params);
3117 EXIT_ON_ERROR( snd_pcm_sw_params_set_start_threshold(pcm, sw_params, dwFlags & WAVE_DIRECTSOUND ? INT_MAX : 1 ), MMSYSERR_ERROR, "unable to set start threshold");
3118 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");
3119 EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min");
3120 EXIT_ON_ERROR( snd_pcm_sw_params_set_xfer_align(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set xfer align");
3121 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold");
3122 EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback");
3123 #undef EXIT_ON_ERROR
3125 snd_pcm_prepare(pcm);
3128 ALSA_TraceParameters(hw_params, sw_params, FALSE);
3130 /* now, we can save all required data for later use... */
3131 if ( wwi->hw_params )
3132 snd_pcm_hw_params_free(wwi->hw_params);
3133 snd_pcm_hw_params_malloc(&(wwi->hw_params));
3134 snd_pcm_hw_params_copy(wwi->hw_params, hw_params);
3136 wwi->dwBufferSize = buffer_size;
3137 wwi->lpQueuePtr = wwi->lpPlayPtr = wwi->lpLoopPtr = NULL;
3138 wwi->p_handle = pcm;
3140 ALSA_InitRingMessage(&wwi->msgRing);
3142 wwi->count = snd_pcm_poll_descriptors_count (wwi->p_handle);
3143 if (wwi->count <= 0) {
3144 ERR("Invalid poll descriptors count\n");
3145 return MMSYSERR_ERROR;
3148 wwi->ufds = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, sizeof(struct pollfd) * wwi->count);
3149 if (wwi->ufds == NULL) {
3150 ERR("No enough memory\n");
3151 return MMSYSERR_NOMEM;
3153 if ((err = snd_pcm_poll_descriptors(wwi->p_handle, wwi->ufds, wwi->count)) < 0) {
3154 ERR("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
3155 return MMSYSERR_ERROR;
3158 wwi->dwPeriodSize = period_size;
3159 /*if (wwi->dwFragmentSize % wwi->format.wf.nBlockAlign)
3160 ERR("Fragment doesn't contain an integral number of data blocks\n");
3162 TRACE("dwPeriodSize=%lu\n", wwi->dwPeriodSize);
3163 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
3164 wwi->format.wBitsPerSample, wwi->format.wf.nAvgBytesPerSec,
3165 wwi->format.wf.nSamplesPerSec, wwi->format.wf.nChannels,
3166 wwi->format.wf.nBlockAlign);
3168 if (!(dwFlags & WAVE_DIRECTSOUND)) {
3169 wwi->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
3170 wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
3171 WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
3172 CloseHandle(wwi->hStartUpEvent);
3174 wwi->hThread = INVALID_HANDLE_VALUE;
3175 wwi->dwThreadID = 0;
3177 wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
3179 return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
3183 /**************************************************************************
3184 * widClose [internal]
3186 static DWORD widClose(WORD wDevID)
3188 DWORD ret = MMSYSERR_NOERROR;
3191 TRACE("(%u);\n", wDevID);
3193 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].p_handle == NULL) {
3194 WARN("bad device ID !\n");
3195 return MMSYSERR_BADDEVICEID;
3198 wwi = &WInDev[wDevID];
3199 if (wwi->lpQueuePtr) {
3200 WARN("buffers still playing !\n");
3201 ret = WAVERR_STILLPLAYING;
3203 if (wwi->hThread != INVALID_HANDLE_VALUE) {
3204 ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
3206 ALSA_DestroyRingMessage(&wwi->msgRing);
3208 snd_pcm_hw_params_free(wwi->hw_params);
3209 wwi->hw_params = NULL;
3211 snd_pcm_close(wwi->p_handle);
3212 wwi->p_handle = NULL;
3214 ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
3217 HeapFree(GetProcessHeap(), 0, wwi->ufds);
3221 /**************************************************************************
3222 * widAddBuffer [internal]
3225 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3227 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3229 /* first, do the sanity checks... */
3230 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].p_handle == NULL) {
3231 WARN("bad dev ID !\n");
3232 return MMSYSERR_BADDEVICEID;
3235 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
3236 return WAVERR_UNPREPARED;
3238 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
3239 return WAVERR_STILLPLAYING;
3241 lpWaveHdr->dwFlags &= ~WHDR_DONE;
3242 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
3243 lpWaveHdr->lpNext = 0;
3245 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
3247 return MMSYSERR_NOERROR;
3250 /**************************************************************************
3251 * widPrepare [internal]
3253 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3255 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3257 if (wDevID >= MAX_WAVEINDRV) {
3258 WARN("bad device ID !\n");
3259 return MMSYSERR_BADDEVICEID;
3262 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
3263 return WAVERR_STILLPLAYING;
3265 lpWaveHdr->dwFlags |= WHDR_PREPARED;
3266 lpWaveHdr->dwFlags &= ~WHDR_DONE;
3267 return MMSYSERR_NOERROR;
3270 /**************************************************************************
3271 * widUnprepare [internal]
3273 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3275 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3277 if (wDevID >= MAX_WAVEINDRV) {
3278 WARN("bad device ID !\n");
3279 return MMSYSERR_BADDEVICEID;
3282 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
3283 return WAVERR_STILLPLAYING;
3285 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
3286 lpWaveHdr->dwFlags |= WHDR_DONE;
3288 return MMSYSERR_NOERROR;
3291 /**************************************************************************
3292 * widStart [internal]
3295 static DWORD widStart(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3297 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3299 /* first, do the sanity checks... */
3300 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].p_handle == NULL) {
3301 WARN("bad dev ID !\n");
3302 return MMSYSERR_BADDEVICEID;
3305 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
3309 return MMSYSERR_NOERROR;
3312 /**************************************************************************
3313 * widStop [internal]
3316 static DWORD widStop(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3318 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3320 /* first, do the sanity checks... */
3321 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].p_handle == NULL) {
3322 WARN("bad dev ID !\n");
3323 return MMSYSERR_BADDEVICEID;
3326 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
3328 return MMSYSERR_NOERROR;
3331 /**************************************************************************
3332 * widReset [internal]
3334 static DWORD widReset(WORD wDevID)
3336 TRACE("(%u);\n", wDevID);
3337 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
3338 WARN("can't reset !\n");
3339 return MMSYSERR_INVALHANDLE;
3341 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
3342 return MMSYSERR_NOERROR;
3345 /**************************************************************************
3346 * widGetPosition [internal]
3348 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
3352 FIXME("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
3354 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
3355 WARN("can't get pos !\n");
3356 return MMSYSERR_INVALHANDLE;
3358 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
3360 wwi = &WInDev[wDevID];
3361 ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_UPDATE, 0, TRUE);
3363 return bytes_to_mmtime(lpTime, wwi->dwTotalRecorded, &wwi->format);
3366 /**************************************************************************
3367 * widGetNumDevs [internal]
3369 static DWORD widGetNumDevs(void)
3371 return ALSA_WidNumDevs;
3374 /**************************************************************************
3375 * widDevInterfaceSize [internal]
3377 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
3379 TRACE("(%u, %p)\n", wDevID, dwParam1);
3381 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
3382 NULL, 0 ) * sizeof(WCHAR);
3383 return MMSYSERR_NOERROR;
3386 /**************************************************************************
3387 * widDevInterface [internal]
3389 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
3391 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
3392 NULL, 0 ) * sizeof(WCHAR))
3394 MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
3395 dwParam1, dwParam2 / sizeof(WCHAR));
3396 return MMSYSERR_NOERROR;
3398 return MMSYSERR_INVALPARAM;
3401 /**************************************************************************
3402 * widMessage (WINEALSA.@)
3404 DWORD WINAPI ALSA_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
3405 DWORD dwParam1, DWORD dwParam2)
3407 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
3408 wDevID, wMsg, dwUser, dwParam1, dwParam2);
3415 /* FIXME: Pretend this is supported */
3417 case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
3418 case WIDM_CLOSE: return widClose (wDevID);
3419 case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3420 case WIDM_PREPARE: return widPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3421 case WIDM_UNPREPARE: return widUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3422 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2);
3423 case WIDM_GETNUMDEVS: return widGetNumDevs ();
3424 case WIDM_GETPOS: return widGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
3425 case WIDM_RESET: return widReset (wDevID);
3426 case WIDM_START: return widStart (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3427 case WIDM_STOP: return widStop (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3428 case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
3429 case DRV_QUERYDEVICEINTERFACE: return widDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
3430 /*case DRV_QUERYDSOUNDIFACE: return widDsCreate (wDevID, (PIDSCDRIVER*)dwParam1);
3431 case DRV_QUERYDSOUNDDESC: return widDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
3432 case DRV_QUERYDSOUNDGUID: return widDsGuid (wDevID, (LPGUID)dwParam1);*/
3434 FIXME("unknown message %d!\n", wMsg);
3436 return MMSYSERR_NOTSUPPORTED;
3441 #if !(defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1))
3443 /**************************************************************************
3444 * widMessage (WINEALSA.@)
3446 DWORD WINAPI ALSA_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
3447 DWORD dwParam1, DWORD dwParam2)
3449 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
3450 return MMSYSERR_NOTENABLED;
3457 /**************************************************************************
3458 * wodMessage (WINEALSA.@)
3460 DWORD WINAPI ALSA_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
3461 DWORD dwParam1, DWORD dwParam2)
3463 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
3464 return MMSYSERR_NOTENABLED;
3467 #endif /* HAVE_ALSA */