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>
61 #define ALSA_PCM_NEW_HW_PARAMS_API
62 #define ALSA_PCM_NEW_SW_PARAMS_API
64 #include "wine/library.h"
65 #include "wine/unicode.h"
66 #include "wine/debug.h"
68 WINE_DEFAULT_DEBUG_CHANNEL(wave);
73 /* internal ALSALIB functions */
74 snd_pcm_uframes_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm);
77 #define MAX_WAVEOUTDRV (6)
78 #define MAX_WAVEINDRV (6)
80 /* state diagram for waveOut writing:
82 * +---------+-------------+---------------+---------------------------------+
83 * | state | function | event | new state |
84 * +---------+-------------+---------------+---------------------------------+
85 * | | open() | | STOPPED |
86 * | PAUSED | write() | | PAUSED |
87 * | STOPPED | write() | <thrd create> | PLAYING |
88 * | PLAYING | write() | HEADER | PLAYING |
89 * | (other) | write() | <error> | |
90 * | (any) | pause() | PAUSING | PAUSED |
91 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
92 * | (any) | reset() | RESETTING | STOPPED |
93 * | (any) | close() | CLOSING | CLOSED |
94 * +---------+-------------+---------------+---------------------------------+
97 /* states of the playing device */
98 #define WINE_WS_PLAYING 0
99 #define WINE_WS_PAUSED 1
100 #define WINE_WS_STOPPED 2
101 #define WINE_WS_CLOSED 3
103 /* events to be send to device */
104 enum win_wm_message {
105 WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
106 WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING
110 #define SIGNAL_OMR(omr) do { int x = 0; write((omr)->msg_pipe[1], &x, sizeof(x)); } while (0)
111 #define CLEAR_OMR(omr) do { int x = 0; read((omr)->msg_pipe[0], &x, sizeof(x)); } while (0)
112 #define RESET_OMR(omr) do { } while (0)
113 #define WAIT_OMR(omr, sleep) \
114 do { struct pollfd pfd; pfd.fd = (omr)->msg_pipe[0]; \
115 pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)
117 #define SIGNAL_OMR(omr) do { SetEvent((omr)->msg_event); } while (0)
118 #define CLEAR_OMR(omr) do { } while (0)
119 #define RESET_OMR(omr) do { ResetEvent((omr)->msg_event); } while (0)
120 #define WAIT_OMR(omr, sleep) \
121 do { WaitForSingleObject((omr)->msg_event, sleep); } while (0)
125 enum win_wm_message msg; /* message identifier */
126 DWORD param; /* parameter for this message */
127 HANDLE hEvent; /* if message is synchronous, handle of event for synchro */
130 /* implement an in-process message ring for better performance
131 * (compared to passing thru the server)
132 * this ring will be used by the input (resp output) record (resp playback) routine
134 #define ALSA_RING_BUFFER_INCREMENT 64
137 int ring_buffer_size;
145 CRITICAL_SECTION msg_crst;
149 /* Windows information */
150 volatile int state; /* one of the WINE_WS_ manifest constants */
151 WAVEOPENDESC waveDesc;
153 WAVEFORMATPCMEX format;
156 /* ALSA information (ALSA 0.9/1.x uses two different devices for playback/capture) */
158 char interface_name[64];
159 snd_pcm_t* handle; /* handle to ALSA playback device */
160 snd_pcm_hw_params_t * hw_params; /* ALSA Hw params */
162 char* mixer; /* mixer device name: hw:# */
163 snd_hctl_t * hctl; /* control handle for the playback volume */
165 snd_pcm_sframes_t (*write)(snd_pcm_t *, const void *, snd_pcm_uframes_t );
170 DWORD dwBufferSize; /* size of whole ALSA buffer in bytes */
171 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
172 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
173 DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
175 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
176 DWORD dwLoops; /* private copy of loop counter */
178 DWORD dwPlayedTotal; /* number of bytes actually played since opening */
179 DWORD dwWrittenTotal; /* number of bytes written to ALSA buffer since opening */
181 /* synchronization stuff */
182 HANDLE hStartUpEvent;
185 ALSA_MSG_RING msgRing;
187 /* DirectSound stuff */
188 DSDRIVERDESC ds_desc;
189 DSDRIVERCAPS ds_caps;
193 /* Windows information */
194 volatile int state; /* one of the WINE_WS_ manifest constants */
195 WAVEOPENDESC waveDesc;
197 WAVEFORMATPCMEX format;
201 /* ALSA information (ALSA 0.9/1.x uses two different devices for playback/capture) */
203 char interface_name[64];
204 snd_pcm_t* handle; /* handle to ALSA capture device */
205 snd_pcm_hw_params_t * hw_params; /* ALSA Hw params */
207 snd_pcm_sframes_t (*read)(snd_pcm_t *, void *, snd_pcm_uframes_t );
212 DWORD dwPeriodSize; /* size of OSS buffer period */
213 DWORD dwBufferSize; /* size of whole ALSA buffer in bytes */
214 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
215 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
217 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
218 DWORD dwLoops; /* private copy of loop counter */
220 /*DWORD dwPlayedTotal; */
221 DWORD dwTotalRecorded;
223 /* synchronization stuff */
224 HANDLE hStartUpEvent;
227 ALSA_MSG_RING msgRing;
229 /* DirectSound stuff */
230 DSDRIVERDESC ds_desc;
231 DSCDRIVERCAPS ds_caps;
234 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
235 static DWORD ALSA_WodNumDevs;
236 static WINE_WAVEIN WInDev [MAX_WAVEINDRV];
237 static DWORD ALSA_WidNumDevs;
239 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
240 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
242 /* These strings used only for tracing */
243 static const char * getCmdString(enum win_wm_message msg)
245 static char unknown[32];
246 #define MSG_TO_STR(x) case x: return #x
248 MSG_TO_STR(WINE_WM_PAUSING);
249 MSG_TO_STR(WINE_WM_RESTARTING);
250 MSG_TO_STR(WINE_WM_RESETTING);
251 MSG_TO_STR(WINE_WM_HEADER);
252 MSG_TO_STR(WINE_WM_UPDATE);
253 MSG_TO_STR(WINE_WM_BREAKLOOP);
254 MSG_TO_STR(WINE_WM_CLOSING);
255 MSG_TO_STR(WINE_WM_STARTING);
256 MSG_TO_STR(WINE_WM_STOPPING);
259 sprintf(unknown, "UNKNOWN(0x%08x)", msg);
263 static const char * getMessage(UINT msg)
265 static char unknown[32];
266 #define MSG_TO_STR(x) case x: return #x
268 MSG_TO_STR(DRVM_INIT);
269 MSG_TO_STR(DRVM_EXIT);
270 MSG_TO_STR(DRVM_ENABLE);
271 MSG_TO_STR(DRVM_DISABLE);
272 MSG_TO_STR(WIDM_OPEN);
273 MSG_TO_STR(WIDM_CLOSE);
274 MSG_TO_STR(WIDM_ADDBUFFER);
275 MSG_TO_STR(WIDM_PREPARE);
276 MSG_TO_STR(WIDM_UNPREPARE);
277 MSG_TO_STR(WIDM_GETDEVCAPS);
278 MSG_TO_STR(WIDM_GETNUMDEVS);
279 MSG_TO_STR(WIDM_GETPOS);
280 MSG_TO_STR(WIDM_RESET);
281 MSG_TO_STR(WIDM_START);
282 MSG_TO_STR(WIDM_STOP);
283 MSG_TO_STR(WODM_OPEN);
284 MSG_TO_STR(WODM_CLOSE);
285 MSG_TO_STR(WODM_WRITE);
286 MSG_TO_STR(WODM_PAUSE);
287 MSG_TO_STR(WODM_GETPOS);
288 MSG_TO_STR(WODM_BREAKLOOP);
289 MSG_TO_STR(WODM_PREPARE);
290 MSG_TO_STR(WODM_UNPREPARE);
291 MSG_TO_STR(WODM_GETDEVCAPS);
292 MSG_TO_STR(WODM_GETNUMDEVS);
293 MSG_TO_STR(WODM_GETPITCH);
294 MSG_TO_STR(WODM_SETPITCH);
295 MSG_TO_STR(WODM_GETPLAYBACKRATE);
296 MSG_TO_STR(WODM_SETPLAYBACKRATE);
297 MSG_TO_STR(WODM_GETVOLUME);
298 MSG_TO_STR(WODM_SETVOLUME);
299 MSG_TO_STR(WODM_RESTART);
300 MSG_TO_STR(WODM_RESET);
301 MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE);
302 MSG_TO_STR(DRV_QUERYDEVICEINTERFACE);
303 MSG_TO_STR(DRV_QUERYDSOUNDIFACE);
304 MSG_TO_STR(DRV_QUERYDSOUNDDESC);
307 sprintf(unknown, "UNKNOWN(0x%04x)", msg);
311 static const char * getFormat(WORD wFormatTag)
313 static char unknown[32];
314 #define FMT_TO_STR(x) case x: return #x
316 FMT_TO_STR(WAVE_FORMAT_PCM);
317 FMT_TO_STR(WAVE_FORMAT_EXTENSIBLE);
318 FMT_TO_STR(WAVE_FORMAT_MULAW);
319 FMT_TO_STR(WAVE_FORMAT_ALAW);
320 FMT_TO_STR(WAVE_FORMAT_ADPCM);
323 sprintf(unknown, "UNKNOWN(0x%04x)", wFormatTag);
327 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
328 WAVEFORMATPCMEX* format)
330 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
331 lpTime->wType, format->Format.wBitsPerSample, format->Format.nSamplesPerSec,
332 format->Format.nChannels, format->Format.nAvgBytesPerSec);
333 TRACE("Position in bytes=%lu\n", position);
335 switch (lpTime->wType) {
337 lpTime->u.sample = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
338 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
341 lpTime->u.ms = 1000.0 * position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels * format->Format.nSamplesPerSec);
342 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
345 lpTime->u.smpte.fps = 30;
346 position = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
347 position += (format->Format.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
348 lpTime->u.smpte.sec = position / format->Format.nSamplesPerSec;
349 position -= lpTime->u.smpte.sec * format->Format.nSamplesPerSec;
350 lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
351 lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
352 lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
353 lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
354 lpTime->u.smpte.fps = 30;
355 lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->Format.nSamplesPerSec;
356 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
357 lpTime->u.smpte.hour, lpTime->u.smpte.min,
358 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
361 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
362 lpTime->wType = TIME_BYTES;
365 lpTime->u.cb = position;
366 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
369 return MMSYSERR_NOERROR;
372 static BOOL supportedFormat(LPWAVEFORMATEX wf)
376 if (wf->nSamplesPerSec<DSBFREQUENCY_MIN||wf->nSamplesPerSec>DSBFREQUENCY_MAX)
379 if (wf->wFormatTag == WAVE_FORMAT_PCM) {
380 if (wf->nChannels==1||wf->nChannels==2) {
381 if (wf->wBitsPerSample==8||wf->wBitsPerSample==16)
384 } else if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
385 WAVEFORMATEXTENSIBLE * wfex = (WAVEFORMATEXTENSIBLE *)wf;
387 if (wf->cbSize == 22 &&
388 (IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) ||
389 IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) {
390 if (wf->nChannels>=1 && wf->nChannels<=6) {
391 if (wf->wBitsPerSample==wfex->Samples.wValidBitsPerSample) {
392 if (wf->wBitsPerSample==8||wf->wBitsPerSample==16||
393 wf->wBitsPerSample==24||wf->wBitsPerSample==32) {
397 WARN("wBitsPerSample != wValidBitsPerSample not supported yet\n");
400 WARN("only KSDATAFORMAT_SUBTYPE_PCM and KSDATAFORMAT_SUBTYPE_IEEE_FLOAT "
402 } else if (wf->wFormatTag == WAVE_FORMAT_MULAW || wf->wFormatTag == WAVE_FORMAT_ALAW) {
403 if (wf->wBitsPerSample==8)
406 ERR("WAVE_FORMAT_MULAW and WAVE_FORMAT_ALAW wBitsPerSample must = 8\n");
408 } else if (wf->wFormatTag == WAVE_FORMAT_ADPCM) {
409 if (wf->wBitsPerSample==4)
412 ERR("WAVE_FORMAT_ADPCM wBitsPerSample must = 4\n");
414 WARN("only WAVE_FORMAT_PCM and WAVE_FORMAT_EXTENSIBLE supported\n");
419 static void copy_format(LPWAVEFORMATEX wf1, LPWAVEFORMATPCMEX wf2)
421 ZeroMemory(wf2, sizeof(wf2));
422 if (wf1->wFormatTag == WAVE_FORMAT_PCM)
423 memcpy(wf2, wf1, sizeof(PCMWAVEFORMAT));
424 else if (wf1->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
425 memcpy(wf2, wf1, sizeof(WAVEFORMATPCMEX));
427 memcpy(wf2, wf1, sizeof(WAVEFORMATEX) + wf1->cbSize);
430 /*======================================================================*
431 * Low level WAVE implementation *
432 *======================================================================*/
434 /**************************************************************************
435 * ALSA_CheckSetVolume [internal]
437 * Helper function for Alsa volume queries. This tries to simplify
438 * the process of managing the volume. All parameters are optional
439 * (pass NULL to ignore or not use).
440 * Return values are MMSYSERR_NOERROR on success, or !0 on failure;
441 * error codes are normalized into the possible documented return
442 * values from waveOutGetVolume.
444 static int ALSA_CheckSetVolume(snd_hctl_t *hctl, int *out_left, int *out_right,
445 int *out_min, int *out_max, int *out_step,
446 int *new_left, int *new_right)
448 int rc = MMSYSERR_NOERROR;
450 snd_hctl_elem_t * elem = NULL;
451 snd_ctl_elem_info_t * eleminfop = NULL;
452 snd_ctl_elem_value_t * elemvaluep = NULL;
453 snd_ctl_elem_id_t * elemidp = NULL;
456 #define EXIT_ON_ERROR(f,txt,exitcode) do \
459 if ( (err = (f) ) < 0) \
461 ERR(txt " failed: %s\n", snd_strerror(err)); \
468 return MMSYSERR_NOTSUPPORTED;
470 /* Allocate areas to return information about the volume */
471 EXIT_ON_ERROR(snd_ctl_elem_id_malloc(&elemidp), "snd_ctl_elem_id_malloc", MMSYSERR_NOMEM);
472 EXIT_ON_ERROR(snd_ctl_elem_value_malloc (&elemvaluep), "snd_ctl_elem_value_malloc", MMSYSERR_NOMEM);
473 EXIT_ON_ERROR(snd_ctl_elem_info_malloc (&eleminfop), "snd_ctl_elem_info_malloc", MMSYSERR_NOMEM);
474 snd_ctl_elem_id_clear(elemidp);
475 snd_ctl_elem_value_clear(elemvaluep);
476 snd_ctl_elem_info_clear(eleminfop);
478 /* Setup and find an element id that exactly matches the characteristic we want
479 ** FIXME: It is probably short sighted to hard code and fixate on PCM Playback Volume */
480 snd_ctl_elem_id_set_name(elemidp, "PCM Playback Volume");
481 snd_ctl_elem_id_set_interface(elemidp, SND_CTL_ELEM_IFACE_MIXER);
482 elem = snd_hctl_find_elem(hctl, elemidp);
485 /* Read and return volume information */
486 EXIT_ON_ERROR(snd_hctl_elem_info(elem, eleminfop), "snd_hctl_elem_info", MMSYSERR_NOTSUPPORTED);
487 value_count = snd_ctl_elem_info_get_count(eleminfop);
488 if (out_min || out_max || out_step)
490 if (!snd_ctl_elem_info_is_readable(eleminfop))
492 ERR("snd_ctl_elem_info_is_readable returned false; cannot return info\n");
493 rc = MMSYSERR_NOTSUPPORTED;
498 *out_min = snd_ctl_elem_info_get_min(eleminfop);
501 *out_max = snd_ctl_elem_info_get_max(eleminfop);
504 *out_step = snd_ctl_elem_info_get_step(eleminfop);
507 if (out_left || out_right)
509 EXIT_ON_ERROR(snd_hctl_elem_read(elem, elemvaluep), "snd_hctl_elem_read", MMSYSERR_NOTSUPPORTED);
512 *out_left = snd_ctl_elem_value_get_integer(elemvaluep, 0);
516 if (value_count == 1)
517 *out_right = snd_ctl_elem_value_get_integer(elemvaluep, 0);
518 else if (value_count == 2)
519 *out_right = snd_ctl_elem_value_get_integer(elemvaluep, 1);
522 ERR("Unexpected value count %d from snd_ctl_elem_info_get_count while getting volume info\n", value_count);
530 if (new_left || new_right)
532 EXIT_ON_ERROR(snd_hctl_elem_read(elem, elemvaluep), "snd_hctl_elem_read", MMSYSERR_NOTSUPPORTED);
534 snd_ctl_elem_value_set_integer(elemvaluep, 0, *new_left);
537 if (value_count == 1)
538 snd_ctl_elem_value_set_integer(elemvaluep, 0, *new_right);
539 else if (value_count == 2)
540 snd_ctl_elem_value_set_integer(elemvaluep, 1, *new_right);
543 ERR("Unexpected value count %d from snd_ctl_elem_info_get_count while setting volume info\n", value_count);
549 EXIT_ON_ERROR(snd_hctl_elem_write(elem, elemvaluep), "snd_hctl_elem_write", MMSYSERR_NOTSUPPORTED);
554 ERR("Could not find 'PCM Playback Volume' element\n");
555 rc = MMSYSERR_NOTSUPPORTED;
564 snd_ctl_elem_value_free(elemvaluep);
566 snd_ctl_elem_info_free(eleminfop);
568 snd_ctl_elem_id_free(elemidp);
574 /**************************************************************************
575 * ALSA_XRUNRecovery [internal]
577 * used to recovery from XRUN errors (buffer underflow/overflow)
579 static int ALSA_XRUNRecovery(WINE_WAVEOUT * wwo, int err)
581 if (err == -EPIPE) { /* under-run */
582 err = snd_pcm_prepare(wwo->handle);
584 ERR( "underrun recovery failed. prepare failed: %s\n", snd_strerror(err));
586 } else if (err == -ESTRPIPE) {
587 while ((err = snd_pcm_resume(wwo->handle)) == -EAGAIN)
588 sleep(1); /* wait until the suspend flag is released */
590 err = snd_pcm_prepare(wwo->handle);
592 ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err));
599 /**************************************************************************
600 * ALSA_TraceParameters [internal]
602 * used to trace format changes, hw and sw parameters
604 static void ALSA_TraceParameters(snd_pcm_hw_params_t * hw_params, snd_pcm_sw_params_t * sw, int full)
607 snd_pcm_format_t format;
608 snd_pcm_access_t access;
610 err = snd_pcm_hw_params_get_access(hw_params, &access);
611 err = snd_pcm_hw_params_get_format(hw_params, &format);
613 #define X(x) ((x)? "true" : "false")
615 TRACE("FLAGS: sampleres=%s overrng=%s pause=%s resume=%s syncstart=%s batch=%s block=%s double=%s "
616 "halfd=%s joint=%s \n",
617 X(snd_pcm_hw_params_can_mmap_sample_resolution(hw_params)),
618 X(snd_pcm_hw_params_can_overrange(hw_params)),
619 X(snd_pcm_hw_params_can_pause(hw_params)),
620 X(snd_pcm_hw_params_can_resume(hw_params)),
621 X(snd_pcm_hw_params_can_sync_start(hw_params)),
622 X(snd_pcm_hw_params_is_batch(hw_params)),
623 X(snd_pcm_hw_params_is_block_transfer(hw_params)),
624 X(snd_pcm_hw_params_is_double(hw_params)),
625 X(snd_pcm_hw_params_is_half_duplex(hw_params)),
626 X(snd_pcm_hw_params_is_joint_duplex(hw_params)));
630 TRACE("access=%s\n", snd_pcm_access_name(access));
633 snd_pcm_access_mask_t * acmask;
634 snd_pcm_access_mask_alloca(&acmask);
635 snd_pcm_hw_params_get_access_mask(hw_params, acmask);
636 for ( access = SND_PCM_ACCESS_MMAP_INTERLEAVED; access <= SND_PCM_ACCESS_LAST; access++)
637 if (snd_pcm_access_mask_test(acmask, access))
638 TRACE("access=%s\n", snd_pcm_access_name(access));
643 TRACE("format=%s\n", snd_pcm_format_name(format));
648 snd_pcm_format_mask_t * fmask;
650 snd_pcm_format_mask_alloca(&fmask);
651 snd_pcm_hw_params_get_format_mask(hw_params, fmask);
652 for ( format = SND_PCM_FORMAT_S8; format <= SND_PCM_FORMAT_LAST ; format++)
653 if ( snd_pcm_format_mask_test(fmask, format) )
654 TRACE("format=%s\n", snd_pcm_format_name(format));
660 err = snd_pcm_hw_params_get_channels(hw_params, &val);
662 unsigned int min = 0;
663 unsigned int max = 0;
664 err = snd_pcm_hw_params_get_channels_min(hw_params, &min),
665 err = snd_pcm_hw_params_get_channels_max(hw_params, &max);
666 TRACE("channels_min=%u, channels_min_max=%u\n", min, max);
668 TRACE("channels=%d\n", val);
673 snd_pcm_uframes_t val=0;
674 err = snd_pcm_hw_params_get_buffer_size(hw_params, &val);
676 snd_pcm_uframes_t min = 0;
677 snd_pcm_uframes_t max = 0;
678 err = snd_pcm_hw_params_get_buffer_size_min(hw_params, &min),
679 err = snd_pcm_hw_params_get_buffer_size_max(hw_params, &max);
680 TRACE("buffer_size_min=%lu, buffer_size_min_max=%lu\n", min, max);
682 TRACE("buffer_size=%lu\n", val);
689 unsigned int val=0; \
690 err = snd_pcm_hw_params_get_##x(hw_params,&val, &dir); \
692 unsigned int min = 0; \
693 unsigned int max = 0; \
694 err = snd_pcm_hw_params_get_##x##_min(hw_params, &min, &dir); \
695 err = snd_pcm_hw_params_get_##x##_max(hw_params, &max, &dir); \
696 TRACE(#x "_min=%u " #x "_max=%u\n", min, max); \
698 TRACE(#x "=%d\n", val); \
707 snd_pcm_uframes_t val=0;
708 err = snd_pcm_hw_params_get_period_size(hw_params, &val, &dir);
710 snd_pcm_uframes_t min = 0;
711 snd_pcm_uframes_t max = 0;
712 err = snd_pcm_hw_params_get_period_size_min(hw_params, &min, &dir),
713 err = snd_pcm_hw_params_get_period_size_max(hw_params, &max, &dir);
714 TRACE("period_size_min=%lu, period_size_min_max=%lu\n", min, max);
716 TRACE("period_size=%lu\n", val);
728 /* return a string duplicated on the win32 process heap, free with HeapFree */
729 static char* ALSA_strdup(char *s) {
730 char *result = HeapAlloc(GetProcessHeap(), 0, strlen(s)+1);
735 /******************************************************************
736 * ALSA_GetDeviceFromReg
738 * Returns either "plug:hw" or reads the registry so the user can
739 * override the playback/record device used.
741 static char *ALSA_GetDeviceFromReg(const char *value)
749 res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\ALSA", 0, KEY_QUERY_VALUE, &key);
750 if (res != ERROR_SUCCESS) goto end;
752 res = RegQueryValueExA(key, value, NULL, &type, NULL, &resultSize);
753 if (res != ERROR_SUCCESS) goto end;
755 if (type != REG_SZ) {
756 ERR("Registry key [HKEY_LOCAL_MACHINE\\Software\\Wine\\Wine\\ALSA\\%s] must be a string\n", value);
760 result = HeapAlloc(GetProcessHeap(), 0, resultSize);
761 res = RegQueryValueExA(key, value, NULL, NULL, result, &resultSize);
765 result = ALSA_strdup("plug:hw");
773 /******************************************************************
776 * Initialize internal structures from ALSA information
778 LONG ALSA_WaveInit(void)
781 snd_pcm_info_t * info;
782 snd_pcm_hw_params_t * hw_params;
783 unsigned int ratemin=0;
784 unsigned int ratemax=0;
785 unsigned int chmin=0;
786 unsigned int chmax=0;
793 if (!wine_dlopen("libasound.so.2", RTLD_LAZY|RTLD_GLOBAL, NULL, 0))
795 ERR("Error: ALSA lib needs to be loaded with flags RTLD_LAZY and RTLD_GLOBAL.\n");
801 for (i = 0; i < MAX_WAVEOUTDRV; i++)
807 snd_pcm_format_mask_t * fmask;
808 snd_pcm_access_mask_t * acmask;
810 wwo = &WOutDev[ALSA_WodNumDevs];
812 regdev = ALSA_GetDeviceFromReg("PlaybackDevice");
813 sprintf(device, "%s:%d", regdev, i);
814 HeapFree(GetProcessHeap(), 0, regdev);
815 wwo->device = HeapAlloc(GetProcessHeap(), 0, strlen(device));
816 strcpy(wwo->device, device);
817 TRACE("using waveout device \"%s\"\n", wwo->device);
819 snprintf(wwo->interface_name, sizeof(wwo->interface_name), "winealsa: %s", wwo->device);
821 wwo->caps.wMid = 0x0002;
822 wwo->caps.wPid = 0x0104;
823 wwo->caps.vDriverVersion = 0x0100;
824 wwo->caps.dwFormats = 0x00000000;
825 wwo->caps.dwSupport = 0;
826 strcpy(wwo->ds_desc.szDrvname, "winealsa.drv");
828 snd_pcm_info_alloca(&info);
829 snd_pcm_hw_params_alloca(&hw_params);
831 #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)
834 snd_pcm_open(&h, wwo->device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
838 EXIT_ON_ERROR( snd_pcm_info(h, info) , "pcm info" );
840 TRACE("dev=%d id=%s name=%s subdev=%d subdev_name=%s subdev_avail=%d subdev_num=%d stream=%s subclass=%s \n",
841 snd_pcm_info_get_device(info),
842 snd_pcm_info_get_id(info),
843 snd_pcm_info_get_name(info),
844 snd_pcm_info_get_subdevice(info),
845 snd_pcm_info_get_subdevice_name(info),
846 snd_pcm_info_get_subdevices_avail(info),
847 snd_pcm_info_get_subdevices_count(info),
848 snd_pcm_stream_name(snd_pcm_info_get_stream(info)),
849 (snd_pcm_info_get_subclass(info) == SND_PCM_SUBCLASS_GENERIC_MIX ? "GENERIC MIX": "MULTI MIX"));
851 strcpy(wwo->ds_desc.szDesc, snd_pcm_info_get_name(info));
852 MultiByteToWideChar(CP_ACP, 0, wwo->ds_desc.szDesc, -1, nameW, sizeof(nameW)/sizeof(WCHAR));
853 strcpyW(wwo->caps.szPname, nameW);
854 EXIT_ON_ERROR( snd_pcm_hw_params_any(h, hw_params) , "pcm hw params" );
857 err = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir);
858 err = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir);
859 err = snd_pcm_hw_params_get_channels_min(hw_params, &chmin);
860 err = snd_pcm_hw_params_get_channels_max(hw_params, &chmax);
862 ALSA_TraceParameters(hw_params, NULL, TRUE);
864 snd_pcm_format_mask_alloca(&fmask);
865 snd_pcm_hw_params_get_format_mask(hw_params, fmask);
868 if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \
870 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \
872 if (chmin <= 1 && 1 <= chmax) \
873 wwo->caps.dwFormats |= WAVE_FORMAT_##v##M08; \
874 if (chmin <= 2 && 2 <= chmax) \
875 wwo->caps.dwFormats |= WAVE_FORMAT_##v##S08; \
877 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \
879 if (chmin <= 1 && 1 <= chmax) \
880 wwo->caps.dwFormats |= WAVE_FORMAT_##v##M16; \
881 if (chmin <= 2 && 2 <= chmax) \
882 wwo->caps.dwFormats |= WAVE_FORMAT_##v##S16; \
894 wwo->caps.wChannels = chmax;
896 /* FIXME: always true ? */
897 wwo->caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
899 snd_pcm_access_mask_alloca(&acmask);
900 snd_pcm_hw_params_get_access_mask(hw_params, acmask);
902 /* FIXME: NONITERLEAVED and COMPLEX are not supported right now */
903 if ( snd_pcm_access_mask_test( acmask, SND_PCM_ACCESS_MMAP_INTERLEAVED ) )
904 wwo->caps.dwSupport |= WAVECAPS_DIRECTSOUND;
906 TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n",
907 wwo->caps.dwFormats, wwo->caps.dwSupport);
911 /* Get a high level control handle for volume operations */
912 /* FIXME: This is never freed! (there are other things done in this function similarly not freed) */
913 sprintf(mixer, "hw:%d", i);
914 wwo->mixer = HeapAlloc(GetProcessHeap(), 0, strlen(mixer));
915 strcpy(wwo->mixer, mixer);
916 TRACE("using mixer device \"%s\"\n", wwo->mixer);
918 if (snd_hctl_open(&wwo->hctl, wwo->mixer, 0) >= 0)
919 snd_hctl_load(wwo->hctl);
923 /* check for volume control support */
925 wwo->caps.dwSupport |= WAVECAPS_VOLUME;
927 if (chmin <= 2 && 2 <= chmax)
928 wwo->caps.dwSupport |= WAVECAPS_LRVOLUME;
931 if (wwo->caps.dwFormats & (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 |
932 WAVE_FORMAT_4M08 | WAVE_FORMAT_48M08 |
933 WAVE_FORMAT_96M08 | WAVE_FORMAT_1M16 |
934 WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 |
935 WAVE_FORMAT_48M16 | WAVE_FORMAT_96M16) )
936 wwo->ds_caps.dwFlags |= DSCAPS_PRIMARYMONO;
938 if (wwo->caps.dwFormats & (WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 |
939 WAVE_FORMAT_4S08 | WAVE_FORMAT_48S08 |
940 WAVE_FORMAT_96S08 | WAVE_FORMAT_1S16 |
941 WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 |
942 WAVE_FORMAT_48S16 | WAVE_FORMAT_96S16) )
943 wwo->ds_caps.dwFlags |= DSCAPS_PRIMARYSTEREO;
945 if (wwo->caps.dwFormats & (WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 |
946 WAVE_FORMAT_4M08 | WAVE_FORMAT_48M08 |
947 WAVE_FORMAT_96M08 | WAVE_FORMAT_1S08 |
948 WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 |
949 WAVE_FORMAT_48S08 | WAVE_FORMAT_96S08) )
950 wwo->ds_caps.dwFlags |= DSCAPS_PRIMARY8BIT;
952 if (wwo->caps.dwFormats & (WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 |
953 WAVE_FORMAT_4M16 | WAVE_FORMAT_48M16 |
954 WAVE_FORMAT_96M16 | WAVE_FORMAT_1S16 |
955 WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 |
956 WAVE_FORMAT_48S16 | WAVE_FORMAT_96S16) )
957 wwo->ds_caps.dwFlags |= DSCAPS_PRIMARY16BIT;
959 wwo->ds_caps.dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
960 wwo->ds_caps.dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
961 wwo->ds_caps.dwPrimaryBuffers = 1;
968 for (i = 0; i < MAX_WAVEINDRV; i++)
973 snd_pcm_format_mask_t * fmask;
974 snd_pcm_access_mask_t * acmask;
976 wwi = &WInDev[ALSA_WidNumDevs];
978 regdev = ALSA_GetDeviceFromReg("CaptureDevice");
979 sprintf(device, "%s:%d", regdev, i);
980 HeapFree(GetProcessHeap(), 0, regdev);
981 wwi->device = HeapAlloc(GetProcessHeap(), 0, strlen(device));
982 strcpy(wwi->device, device);
984 TRACE("using wavein device \"%s\"\n", wwi->device);
986 snprintf(wwi->interface_name, sizeof(wwi->interface_name), "winealsa: %s", wwi->device);
988 wwi->caps.wMid = 0x0002;
989 wwi->caps.wPid = 0x0104;
990 wwi->caps.vDriverVersion = 0x0100;
991 wwi->caps.dwFormats = 0x00000000;
992 strcpy(wwi->ds_desc.szDrvname, "winealsa.drv");
995 snd_pcm_info_alloca(&info);
996 snd_pcm_hw_params_alloca(&hw_params);
998 #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)
1001 snd_pcm_open(&h, wwi->device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
1005 EXIT_ON_ERROR( snd_pcm_info(h, info) , "pcm info" );
1007 TRACE("dev=%d id=%s name=%s subdev=%d subdev_name=%s subdev_avail=%d subdev_num=%d stream=%s subclass=%s \n",
1008 snd_pcm_info_get_device(info),
1009 snd_pcm_info_get_id(info),
1010 snd_pcm_info_get_name(info),
1011 snd_pcm_info_get_subdevice(info),
1012 snd_pcm_info_get_subdevice_name(info),
1013 snd_pcm_info_get_subdevices_avail(info),
1014 snd_pcm_info_get_subdevices_count(info),
1015 snd_pcm_stream_name(snd_pcm_info_get_stream(info)),
1016 (snd_pcm_info_get_subclass(info) == SND_PCM_SUBCLASS_GENERIC_MIX ? "GENERIC MIX": "MULTI MIX"));
1018 strcpy(wwi->ds_desc.szDesc, snd_pcm_info_get_name(info));
1019 MultiByteToWideChar(CP_ACP, 0, wwi->ds_desc.szDesc, -1, nameW, sizeof(nameW)/sizeof(WCHAR));
1020 strcpyW(wwi->caps.szPname, nameW);
1021 EXIT_ON_ERROR( snd_pcm_hw_params_any(h, hw_params) , "pcm hw params" );
1022 #undef EXIT_ON_ERROR
1023 err = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir);
1024 err = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir);
1025 err = snd_pcm_hw_params_get_channels_min(hw_params, &chmin);
1026 err = snd_pcm_hw_params_get_channels_max(hw_params, &chmax);
1029 ALSA_TraceParameters(hw_params, NULL, TRUE);
1031 snd_pcm_format_mask_alloca(&fmask);
1032 snd_pcm_hw_params_get_format_mask(hw_params, fmask);
1035 if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \
1037 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \
1039 if (chmin <= 1 && 1 <= chmax) \
1040 wwi->caps.dwFormats |= WAVE_FORMAT_##v##M08; \
1041 if (chmin <= 2 && 2 <= chmax) \
1042 wwi->caps.dwFormats |= WAVE_FORMAT_##v##S08; \
1044 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \
1046 if (chmin <= 1 && 1 <= chmax) \
1047 wwi->caps.dwFormats |= WAVE_FORMAT_##v##M16; \
1048 if (chmin <= 2 && 2 <= chmax) \
1049 wwi->caps.dwFormats |= WAVE_FORMAT_##v##S16; \
1061 wwi->caps.wChannels = chmax;
1063 snd_pcm_access_mask_alloca(&acmask);
1064 snd_pcm_hw_params_get_access_mask(hw_params, acmask);
1066 /* FIXME: NONITERLEAVED and COMPLEX are not supported right now */
1067 if ( snd_pcm_access_mask_test( acmask, SND_PCM_ACCESS_MMAP_INTERLEAVED ) ) {
1069 wwi->dwSupport |= WAVECAPS_DIRECTSOUND;
1073 TRACE("Configured with dwFmts=%08lx\n", wwi->caps.dwFormats);
1083 /******************************************************************
1084 * ALSA_InitRingMessage
1086 * Initialize the ring of messages for passing between driver's caller and playback/record
1089 static int ALSA_InitRingMessage(ALSA_MSG_RING* omr)
1092 omr->msg_tosave = 0;
1093 #ifdef USE_PIPE_SYNC
1094 if (pipe(omr->msg_pipe) < 0) {
1095 omr->msg_pipe[0] = -1;
1096 omr->msg_pipe[1] = -1;
1097 ERR("could not create pipe, error=%s\n", strerror(errno));
1100 omr->msg_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1102 omr->ring_buffer_size = ALSA_RING_BUFFER_INCREMENT;
1103 omr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,omr->ring_buffer_size * sizeof(ALSA_MSG));
1105 InitializeCriticalSection(&omr->msg_crst);
1109 /******************************************************************
1110 * ALSA_DestroyRingMessage
1113 static int ALSA_DestroyRingMessage(ALSA_MSG_RING* omr)
1115 #ifdef USE_PIPE_SYNC
1116 close(omr->msg_pipe[0]);
1117 close(omr->msg_pipe[1]);
1119 CloseHandle(omr->msg_event);
1121 HeapFree(GetProcessHeap(),0,omr->messages);
1122 DeleteCriticalSection(&omr->msg_crst);
1126 /******************************************************************
1127 * ALSA_AddRingMessage
1129 * Inserts a new message into the ring (should be called from DriverProc derivated routines)
1131 static int ALSA_AddRingMessage(ALSA_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait)
1133 HANDLE hEvent = INVALID_HANDLE_VALUE;
1135 EnterCriticalSection(&omr->msg_crst);
1136 if ((omr->msg_toget == ((omr->msg_tosave + 1) % omr->ring_buffer_size)))
1138 int old_ring_buffer_size = omr->ring_buffer_size;
1139 omr->ring_buffer_size += ALSA_RING_BUFFER_INCREMENT;
1140 TRACE("omr->ring_buffer_size=%d\n",omr->ring_buffer_size);
1141 omr->messages = HeapReAlloc(GetProcessHeap(),0,omr->messages, omr->ring_buffer_size * sizeof(ALSA_MSG));
1142 /* Now we need to rearrange the ring buffer so that the new
1143 buffers just allocated are in between omr->msg_tosave and
1146 if (omr->msg_tosave < omr->msg_toget)
1148 memmove(&(omr->messages[omr->msg_toget + ALSA_RING_BUFFER_INCREMENT]),
1149 &(omr->messages[omr->msg_toget]),
1150 sizeof(ALSA_MSG)*(old_ring_buffer_size - omr->msg_toget)
1152 omr->msg_toget += ALSA_RING_BUFFER_INCREMENT;
1157 hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1158 if (hEvent == INVALID_HANDLE_VALUE)
1160 ERR("can't create event !?\n");
1161 LeaveCriticalSection(&omr->msg_crst);
1164 if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER)
1165 FIXME("two fast messages in the queue!!!! toget = %d(%s), tosave=%d(%s)\n",
1166 omr->msg_toget,getCmdString(omr->messages[omr->msg_toget].msg),
1167 omr->msg_tosave,getCmdString(omr->messages[omr->msg_tosave].msg));
1169 /* fast messages have to be added at the start of the queue */
1170 omr->msg_toget = (omr->msg_toget + omr->ring_buffer_size - 1) % omr->ring_buffer_size;
1172 omr->messages[omr->msg_toget].msg = msg;
1173 omr->messages[omr->msg_toget].param = param;
1174 omr->messages[omr->msg_toget].hEvent = hEvent;
1178 omr->messages[omr->msg_tosave].msg = msg;
1179 omr->messages[omr->msg_tosave].param = param;
1180 omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
1181 omr->msg_tosave = (omr->msg_tosave + 1) % omr->ring_buffer_size;
1183 LeaveCriticalSection(&omr->msg_crst);
1184 /* signal a new message */
1188 /* wait for playback/record thread to have processed the message */
1189 WaitForSingleObject(hEvent, INFINITE);
1190 CloseHandle(hEvent);
1195 /******************************************************************
1196 * ALSA_RetrieveRingMessage
1198 * Get a message from the ring. Should be called by the playback/record thread.
1200 static int ALSA_RetrieveRingMessage(ALSA_MSG_RING* omr,
1201 enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
1203 EnterCriticalSection(&omr->msg_crst);
1205 if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
1207 LeaveCriticalSection(&omr->msg_crst);
1211 *msg = omr->messages[omr->msg_toget].msg;
1212 omr->messages[omr->msg_toget].msg = 0;
1213 *param = omr->messages[omr->msg_toget].param;
1214 *hEvent = omr->messages[omr->msg_toget].hEvent;
1215 omr->msg_toget = (omr->msg_toget + 1) % omr->ring_buffer_size;
1217 LeaveCriticalSection(&omr->msg_crst);
1221 /******************************************************************
1222 * ALSA_PeekRingMessage
1224 * Peek at a message from the ring but do not remove it.
1225 * Should be called by the playback/record thread.
1227 static int ALSA_PeekRingMessage(ALSA_MSG_RING* omr,
1228 enum win_wm_message *msg,
1229 DWORD *param, HANDLE *hEvent)
1231 EnterCriticalSection(&omr->msg_crst);
1233 if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
1235 LeaveCriticalSection(&omr->msg_crst);
1239 *msg = omr->messages[omr->msg_toget].msg;
1240 *param = omr->messages[omr->msg_toget].param;
1241 *hEvent = omr->messages[omr->msg_toget].hEvent;
1242 LeaveCriticalSection(&omr->msg_crst);
1246 /*======================================================================*
1247 * Low level WAVE OUT implementation *
1248 *======================================================================*/
1250 /**************************************************************************
1251 * wodNotifyClient [internal]
1253 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1255 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
1261 if (wwo->wFlags != DCB_NULL &&
1262 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave,
1263 wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
1264 WARN("can't notify client !\n");
1265 return MMSYSERR_ERROR;
1269 FIXME("Unknown callback message %u\n", wMsg);
1270 return MMSYSERR_INVALPARAM;
1272 return MMSYSERR_NOERROR;
1275 /**************************************************************************
1276 * wodUpdatePlayedTotal [internal]
1279 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo, snd_pcm_status_t* ps)
1281 snd_pcm_sframes_t delay = 0;
1282 snd_pcm_delay(wwo->handle, &delay);
1283 if (snd_pcm_state(wwo->handle) != SND_PCM_STATE_RUNNING)
1285 wwo->dwPlayedTotal = wwo->dwWrittenTotal - snd_pcm_frames_to_bytes(wwo->handle, delay);
1289 /**************************************************************************
1290 * wodPlayer_BeginWaveHdr [internal]
1292 * Makes the specified lpWaveHdr the currently playing wave header.
1293 * If the specified wave header is a begin loop and we're not already in
1294 * a loop, setup the loop.
1296 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1298 wwo->lpPlayPtr = lpWaveHdr;
1300 if (!lpWaveHdr) return;
1302 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
1303 if (wwo->lpLoopPtr) {
1304 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
1306 TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
1307 wwo->lpLoopPtr = lpWaveHdr;
1308 /* Windows does not touch WAVEHDR.dwLoops,
1309 * so we need to make an internal copy */
1310 wwo->dwLoops = lpWaveHdr->dwLoops;
1313 wwo->dwPartialOffset = 0;
1316 /**************************************************************************
1317 * wodPlayer_PlayPtrNext [internal]
1319 * Advance the play pointer to the next waveheader, looping if required.
1321 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
1323 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
1325 wwo->dwPartialOffset = 0;
1326 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
1327 /* We're at the end of a loop, loop if required */
1328 if (--wwo->dwLoops > 0) {
1329 wwo->lpPlayPtr = wwo->lpLoopPtr;
1331 /* Handle overlapping loops correctly */
1332 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
1333 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
1334 /* shall we consider the END flag for the closing loop or for
1335 * the opening one or for both ???
1336 * code assumes for closing loop only
1339 lpWaveHdr = lpWaveHdr->lpNext;
1341 wwo->lpLoopPtr = NULL;
1342 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
1345 /* We're not in a loop. Advance to the next wave header */
1346 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
1352 /**************************************************************************
1353 * wodPlayer_DSPWait [internal]
1354 * Returns the number of milliseconds to wait for the DSP buffer to play a
1357 static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo)
1359 /* time for one period to be played */
1363 err = snd_pcm_hw_params_get_period_time(wwo->hw_params, &val, &dir);
1367 /**************************************************************************
1368 * wodPlayer_NotifyWait [internal]
1369 * Returns the number of milliseconds to wait before attempting to notify
1370 * completion of the specified wavehdr.
1371 * This is based on the number of bytes remaining to be written in the
1374 static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1378 if (lpWaveHdr->reserved < wwo->dwPlayedTotal) {
1381 dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.Format.nAvgBytesPerSec;
1382 if (!dwMillis) dwMillis = 1;
1389 /**************************************************************************
1390 * wodPlayer_WriteMaxFrags [internal]
1391 * Writes the maximum number of frames possible to the DSP and returns
1392 * the number of frames written.
1394 static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* frames)
1396 /* Only attempt to write to free frames */
1397 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
1398 DWORD dwLength = snd_pcm_bytes_to_frames(wwo->handle, lpWaveHdr->dwBufferLength - wwo->dwPartialOffset);
1399 int toWrite = min(dwLength, *frames);
1402 TRACE("Writing wavehdr %p.%lu[%lu]\n", lpWaveHdr, wwo->dwPartialOffset, lpWaveHdr->dwBufferLength);
1405 written = (wwo->write)(wwo->handle, lpWaveHdr->lpData + wwo->dwPartialOffset, toWrite);
1407 /* XRUN occurred. let's try to recover */
1408 ALSA_XRUNRecovery(wwo, written);
1409 written = (wwo->write)(wwo->handle, lpWaveHdr->lpData + wwo->dwPartialOffset, toWrite);
1412 /* still in error */
1413 ERR("Error in writing wavehdr. Reason: %s\n", snd_strerror(written));
1419 wwo->dwPartialOffset += snd_pcm_frames_to_bytes(wwo->handle, written);
1420 if ( wwo->dwPartialOffset >= lpWaveHdr->dwBufferLength) {
1421 /* this will be used to check if the given wave header has been fully played or not... */
1422 wwo->dwPartialOffset = lpWaveHdr->dwBufferLength;
1423 /* If we wrote all current wavehdr, skip to the next one */
1424 wodPlayer_PlayPtrNext(wwo);
1427 wwo->dwWrittenTotal += snd_pcm_frames_to_bytes(wwo->handle, written);
1428 TRACE("dwWrittenTotal=%lu\n", wwo->dwWrittenTotal);
1434 /**************************************************************************
1435 * wodPlayer_NotifyCompletions [internal]
1437 * Notifies and remove from queue all wavehdrs which have been played to
1438 * the speaker (ie. they have cleared the ALSA buffer). If force is true,
1439 * we notify all wavehdrs and remove them all from the queue even if they
1440 * are unplayed or part of a loop.
1442 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
1444 LPWAVEHDR lpWaveHdr;
1446 /* Start from lpQueuePtr and keep notifying until:
1447 * - we hit an unwritten wavehdr
1448 * - we hit the beginning of a running loop
1449 * - we hit a wavehdr which hasn't finished playing
1452 while ((lpWaveHdr = wwo->lpQueuePtr) &&
1454 (lpWaveHdr != wwo->lpPlayPtr &&
1455 lpWaveHdr != wwo->lpLoopPtr &&
1456 lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
1458 wwo->lpQueuePtr = lpWaveHdr->lpNext;
1460 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1461 lpWaveHdr->dwFlags |= WHDR_DONE;
1463 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1468 lpWaveHdr = wwo->lpQueuePtr;
1469 if (!lpWaveHdr) {TRACE("Empty queue\n"); break;}
1472 if (lpWaveHdr == wwo->lpPlayPtr) {TRACE("play %p\n", lpWaveHdr); break;}
1473 if (lpWaveHdr == wwo->lpLoopPtr) {TRACE("loop %p\n", lpWaveHdr); break;}
1474 if (lpWaveHdr->reserved > wwo->dwPlayedTotal){TRACE("still playing %p (%lu/%lu)\n", lpWaveHdr, lpWaveHdr->reserved, wwo->dwPlayedTotal);break;}
1476 wwo->lpQueuePtr = lpWaveHdr->lpNext;
1478 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1479 lpWaveHdr->dwFlags |= WHDR_DONE;
1481 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1484 return (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
1485 wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
1489 static void wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
1491 unsigned short revents;
1493 if (snd_pcm_state(handle) != SND_PCM_STATE_RUNNING)
1497 poll(ufds, count, -1);
1498 snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
1500 if (revents & POLLERR)
1503 /*if (revents & POLLOUT)
1509 /**************************************************************************
1510 * wodPlayer_Reset [internal]
1512 * wodPlayer helper. Resets current output stream.
1514 static void wodPlayer_Reset(WINE_WAVEOUT* wwo)
1516 enum win_wm_message msg;
1521 /* flush all possible output */
1522 wait_for_poll(wwo->handle, wwo->ufds, wwo->count);
1524 wodUpdatePlayedTotal(wwo, NULL);
1525 /* updates current notify list */
1526 wodPlayer_NotifyCompletions(wwo, FALSE);
1528 if ( (err = snd_pcm_drop(wwo->handle)) < 0) {
1529 FIXME("flush: %s\n", snd_strerror(err));
1531 wwo->state = WINE_WS_STOPPED;
1534 if ( (err = snd_pcm_prepare(wwo->handle)) < 0 )
1535 ERR("pcm prepare failed: %s\n", snd_strerror(err));
1537 /* remove any buffer */
1538 wodPlayer_NotifyCompletions(wwo, TRUE);
1540 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
1541 wwo->state = WINE_WS_STOPPED;
1542 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1543 /* Clear partial wavehdr */
1544 wwo->dwPartialOffset = 0;
1546 /* remove any existing message in the ring */
1547 EnterCriticalSection(&wwo->msgRing.msg_crst);
1548 /* return all pending headers in queue */
1549 while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev))
1551 if (msg != WINE_WM_HEADER)
1553 FIXME("shouldn't have headers left\n");
1557 ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
1558 ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
1560 wodNotifyClient(wwo, WOM_DONE, param, 0);
1562 RESET_OMR(&wwo->msgRing);
1563 LeaveCriticalSection(&wwo->msgRing.msg_crst);
1566 /**************************************************************************
1567 * wodPlayer_ProcessMessages [internal]
1569 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
1571 LPWAVEHDR lpWaveHdr;
1572 enum win_wm_message msg;
1577 while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) {
1578 TRACE("Received %s %lx\n", getCmdString(msg), param);
1581 case WINE_WM_PAUSING:
1582 if ( snd_pcm_state(wwo->handle) == SND_PCM_STATE_RUNNING )
1584 err = snd_pcm_pause(wwo->handle, 1);
1586 ERR("pcm_pause failed: %s\n", snd_strerror(err));
1588 wwo->state = WINE_WS_PAUSED;
1591 case WINE_WM_RESTARTING:
1592 if (wwo->state == WINE_WS_PAUSED)
1594 if ( snd_pcm_state(wwo->handle) == SND_PCM_STATE_PAUSED )
1596 err = snd_pcm_pause(wwo->handle, 0);
1598 ERR("pcm_pause failed: %s\n", snd_strerror(err));
1600 wwo->state = WINE_WS_PLAYING;
1604 case WINE_WM_HEADER:
1605 lpWaveHdr = (LPWAVEHDR)param;
1607 /* insert buffer at the end of queue */
1610 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
1613 if (!wwo->lpPlayPtr)
1614 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
1615 if (wwo->state == WINE_WS_STOPPED)
1616 wwo->state = WINE_WS_PLAYING;
1618 case WINE_WM_RESETTING:
1619 wodPlayer_Reset(wwo);
1622 case WINE_WM_UPDATE:
1623 wodUpdatePlayedTotal(wwo, NULL);
1626 case WINE_WM_BREAKLOOP:
1627 if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
1628 /* ensure exit at end of current loop */
1633 case WINE_WM_CLOSING:
1634 /* sanity check: this should not happen since the device must have been reset before */
1635 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
1637 wwo->state = WINE_WS_CLOSED;
1640 /* shouldn't go here */
1642 FIXME("unknown message %d\n", msg);
1648 /**************************************************************************
1649 * wodPlayer_FeedDSP [internal]
1650 * Feed as much sound data as we can into the DSP and return the number of
1651 * milliseconds before it will be necessary to feed the DSP again.
1653 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
1657 wodUpdatePlayedTotal(wwo, NULL);
1658 availInQ = snd_pcm_avail_update(wwo->handle);
1661 /* input queue empty and output buffer with less than one fragment to play */
1662 if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + wwo->dwFragmentSize) {
1663 TRACE("Run out of wavehdr:s...\n");
1668 /* no more room... no need to try to feed */
1670 /* Feed from partial wavehdr */
1671 if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) {
1672 wodPlayer_WriteMaxFrags(wwo, &availInQ);
1675 /* Feed wavehdrs until we run out of wavehdrs or DSP space */
1676 if (wwo->dwPartialOffset == 0 && wwo->lpPlayPtr) {
1678 TRACE("Setting time to elapse for %p to %lu\n",
1679 wwo->lpPlayPtr, wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength);
1680 /* note the value that dwPlayedTotal will return when this wave finishes playing */
1681 wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
1682 } while (wodPlayer_WriteMaxFrags(wwo, &availInQ) && wwo->lpPlayPtr && availInQ > 0);
1686 return wodPlayer_DSPWait(wwo);
1689 /**************************************************************************
1690 * wodPlayer [internal]
1692 static DWORD CALLBACK wodPlayer(LPVOID pmt)
1694 WORD uDevID = (DWORD)pmt;
1695 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
1696 DWORD dwNextFeedTime = INFINITE; /* Time before DSP needs feeding */
1697 DWORD dwNextNotifyTime = INFINITE; /* Time before next wave completion */
1700 wwo->state = WINE_WS_STOPPED;
1701 SetEvent(wwo->hStartUpEvent);
1704 /** Wait for the shortest time before an action is required. If there
1705 * are no pending actions, wait forever for a command.
1707 dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
1708 TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
1709 WAIT_OMR(&wwo->msgRing, dwSleepTime);
1710 wodPlayer_ProcessMessages(wwo);
1711 if (wwo->state == WINE_WS_PLAYING) {
1712 dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1713 dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
1714 if (dwNextFeedTime == INFINITE) {
1715 /* FeedDSP ran out of data, but before giving up, */
1716 /* check that a notification didn't give us more */
1717 wodPlayer_ProcessMessages(wwo);
1718 if (wwo->lpPlayPtr) {
1719 TRACE("recovering\n");
1720 dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1724 dwNextFeedTime = dwNextNotifyTime = INFINITE;
1729 /**************************************************************************
1730 * wodGetDevCaps [internal]
1732 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
1734 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1736 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1738 if (wDevID >= MAX_WAVEOUTDRV) {
1739 TRACE("MAX_WAVOUTDRV reached !\n");
1740 return MMSYSERR_BADDEVICEID;
1743 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1744 return MMSYSERR_NOERROR;
1747 /**************************************************************************
1748 * wodOpen [internal]
1750 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1753 snd_pcm_hw_params_t * hw_params;
1754 snd_pcm_sw_params_t * sw_params;
1755 snd_pcm_access_t access;
1756 snd_pcm_format_t format = -1;
1758 unsigned int buffer_time = 500000;
1759 unsigned int period_time = 10000;
1760 snd_pcm_uframes_t buffer_size;
1761 snd_pcm_uframes_t period_size;
1767 snd_pcm_hw_params_alloca(&hw_params);
1768 snd_pcm_sw_params_alloca(&sw_params);
1770 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1771 if (lpDesc == NULL) {
1772 WARN("Invalid Parameter !\n");
1773 return MMSYSERR_INVALPARAM;
1775 if (wDevID >= MAX_WAVEOUTDRV) {
1776 TRACE("MAX_WAVOUTDRV reached !\n");
1777 return MMSYSERR_BADDEVICEID;
1780 /* only PCM format is supported so far... */
1781 if (!supportedFormat(lpDesc->lpFormat)) {
1782 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1783 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1784 lpDesc->lpFormat->nSamplesPerSec);
1785 return WAVERR_BADFORMAT;
1788 if (dwFlags & WAVE_FORMAT_QUERY) {
1789 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1790 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1791 lpDesc->lpFormat->nSamplesPerSec);
1792 return MMSYSERR_NOERROR;
1795 wwo = &WOutDev[wDevID];
1797 if ((dwFlags & WAVE_DIRECTSOUND) && !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
1798 /* not supported, ignore it */
1799 dwFlags &= ~WAVE_DIRECTSOUND;
1802 flags = SND_PCM_NONBLOCK;
1804 if ( dwFlags & WAVE_DIRECTSOUND )
1805 flags |= SND_PCM_ASYNC;
1808 if ( (err = snd_pcm_open(&pcm, wwo->device, SND_PCM_STREAM_PLAYBACK, flags)) < 0)
1810 ERR("Error open: %s\n", snd_strerror(err));
1811 return MMSYSERR_NOTENABLED;
1814 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1816 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1817 copy_format(lpDesc->lpFormat, &wwo->format);
1819 TRACE("Requested this format: %ldx%dx%d %s\n",
1820 wwo->format.Format.nSamplesPerSec,
1821 wwo->format.Format.wBitsPerSample,
1822 wwo->format.Format.nChannels,
1823 getFormat(wwo->format.Format.wFormatTag));
1825 if (wwo->format.Format.wBitsPerSample == 0) {
1826 WARN("Resetting zeroed wBitsPerSample\n");
1827 wwo->format.Format.wBitsPerSample = 8 *
1828 (wwo->format.Format.nAvgBytesPerSec /
1829 wwo->format.Format.nSamplesPerSec) /
1830 wwo->format.Format.nChannels;
1833 snd_pcm_hw_params_any(pcm, hw_params);
1835 #define EXIT_ON_ERROR(f,e,txt) do \
1838 if ( (err = (f) ) < 0) \
1840 ERR(txt ": %s\n", snd_strerror(err)); \
1841 snd_pcm_close(pcm); \
1846 access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
1847 if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {
1848 WARN("mmap not available. switching to standard write.\n");
1849 access = SND_PCM_ACCESS_RW_INTERLEAVED;
1850 EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");
1851 wwo->write = snd_pcm_writei;
1854 wwo->write = snd_pcm_mmap_writei;
1856 if ((err = snd_pcm_hw_params_set_channels(pcm, hw_params, wwo->format.Format.nChannels)) < 0) {
1857 WARN("unable to set required channels: %d\n", wwo->format.Format.nChannels);
1858 if (dwFlags & WAVE_DIRECTSOUND) {
1859 if (wwo->format.Format.nChannels > 2)
1860 wwo->format.Format.nChannels = 2;
1861 else if (wwo->format.Format.nChannels == 2)
1862 wwo->format.Format.nChannels = 1;
1863 else if (wwo->format.Format.nChannels == 1)
1864 wwo->format.Format.nChannels = 2;
1865 /* recalculate block align and bytes per second */
1866 wwo->format.Format.nBlockAlign = (wwo->format.Format.wBitsPerSample * wwo->format.Format.nChannels) / 8;
1867 wwo->format.Format.nAvgBytesPerSec = wwo->format.Format.nSamplesPerSec * wwo->format.Format.nBlockAlign;
1868 WARN("changed number of channels from %d to %d\n", lpDesc->lpFormat->nChannels, wwo->format.Format.nChannels);
1870 EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwo->format.Format.nChannels ), MMSYSERR_INVALPARAM, "unable to set required channels" );
1873 if ((wwo->format.Format.wFormatTag == WAVE_FORMAT_PCM) ||
1874 ((wwo->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
1875 IsEqualGUID(&wwo->format.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) {
1876 format = (wwo->format.Format.wBitsPerSample == 8) ? SND_PCM_FORMAT_U8 :
1877 (wwo->format.Format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE :
1878 (wwo->format.Format.wBitsPerSample == 24) ? SND_PCM_FORMAT_S24_LE :
1879 (wwo->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_S32_LE : -1;
1880 } else if ((wwo->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
1881 IsEqualGUID(&wwo->format.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)){
1882 format = (wwo->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_FLOAT_LE : -1;
1883 } else if (wwo->format.Format.wFormatTag == WAVE_FORMAT_MULAW) {
1884 FIXME("unimplemented format: WAVE_FORMAT_MULAW\n");
1886 return WAVERR_BADFORMAT;
1887 } else if (wwo->format.Format.wFormatTag == WAVE_FORMAT_ALAW) {
1888 FIXME("unimplemented format: WAVE_FORMAT_ALAW\n");
1890 return WAVERR_BADFORMAT;
1891 } else if (wwo->format.Format.wFormatTag == WAVE_FORMAT_ADPCM) {
1892 FIXME("unimplemented format: WAVE_FORMAT_ADPCM\n");
1894 return WAVERR_BADFORMAT;
1896 ERR("invalid format: %0x04x\n", wwo->format.Format.wFormatTag);
1898 return WAVERR_BADFORMAT;
1901 if ((err = snd_pcm_hw_params_set_format(pcm, hw_params, format)) < 0) {
1902 WARN("unable to set required format: %s\n", snd_pcm_format_name(format));
1903 if (dwFlags & WAVE_DIRECTSOUND) {
1904 if ((wwo->format.Format.wFormatTag == WAVE_FORMAT_PCM) ||
1905 ((wwo->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
1906 IsEqualGUID(&wwo->format.SubFormat, & KSDATAFORMAT_SUBTYPE_PCM))) {
1907 if (wwo->format.Format.wBitsPerSample != 16) {
1908 wwo->format.Format.wBitsPerSample = 16;
1909 format = SND_PCM_FORMAT_S16_LE;
1911 wwo->format.Format.wBitsPerSample = 8;
1912 format = SND_PCM_FORMAT_U8;
1914 /* recalculate block align and bytes per second */
1915 wwo->format.Format.nBlockAlign = (wwo->format.Format.wBitsPerSample * wwo->format.Format.nChannels) / 8;
1916 wwo->format.Format.nAvgBytesPerSec = wwo->format.Format.nSamplesPerSec * wwo->format.Format.nBlockAlign;
1917 WARN("changed bits per sample from %d to %d\n", lpDesc->lpFormat->wBitsPerSample, wwo->format.Format.wBitsPerSample);
1920 EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), MMSYSERR_INVALPARAM, "unable to set required format" );
1923 rate = wwo->format.Format.nSamplesPerSec;
1925 err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir);
1927 ERR("Rate %ld Hz not available for playback: %s\n", wwo->format.Format.nSamplesPerSec, snd_strerror(rate));
1929 return WAVERR_BADFORMAT;
1931 if (rate != wwo->format.Format.nSamplesPerSec) {
1932 if (dwFlags & WAVE_DIRECTSOUND) {
1933 WARN("changed sample rate from %ld Hz to %d Hz\n", wwo->format.Format.nSamplesPerSec, rate);
1934 wwo->format.Format.nSamplesPerSec = rate;
1935 /* recalculate bytes per second */
1936 wwo->format.Format.nAvgBytesPerSec = wwo->format.Format.nSamplesPerSec * wwo->format.Format.nBlockAlign;
1938 ERR("Rate doesn't match (requested %ld Hz, got %d Hz)\n", wwo->format.Format.nSamplesPerSec, rate);
1940 return WAVERR_BADFORMAT;
1944 /* give the new format back to direct sound */
1945 if (dwFlags & WAVE_DIRECTSOUND) {
1946 lpDesc->lpFormat->wFormatTag = wwo->format.Format.wFormatTag;
1947 lpDesc->lpFormat->nChannels = wwo->format.Format.nChannels;
1948 lpDesc->lpFormat->nSamplesPerSec = wwo->format.Format.nSamplesPerSec;
1949 lpDesc->lpFormat->wBitsPerSample = wwo->format.Format.wBitsPerSample;
1950 lpDesc->lpFormat->nBlockAlign = wwo->format.Format.nBlockAlign;
1951 lpDesc->lpFormat->nAvgBytesPerSec = wwo->format.Format.nAvgBytesPerSec;
1954 TRACE("Got this format: %ldx%dx%d %s\n",
1955 wwo->format.Format.nSamplesPerSec,
1956 wwo->format.Format.wBitsPerSample,
1957 wwo->format.Format.nChannels,
1958 getFormat(wwo->format.Format.wFormatTag));
1961 EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time");
1963 EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time");
1965 EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");
1967 err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
1968 err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
1970 snd_pcm_sw_params_current(pcm, sw_params);
1971 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");
1972 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");
1973 EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min");
1974 EXIT_ON_ERROR( snd_pcm_sw_params_set_xfer_align(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set xfer align");
1975 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold");
1976 EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback");
1977 #undef EXIT_ON_ERROR
1979 snd_pcm_prepare(pcm);
1982 ALSA_TraceParameters(hw_params, sw_params, FALSE);
1984 /* now, we can save all required data for later use... */
1985 if ( wwo->hw_params )
1986 snd_pcm_hw_params_free(wwo->hw_params);
1987 snd_pcm_hw_params_malloc(&(wwo->hw_params));
1988 snd_pcm_hw_params_copy(wwo->hw_params, hw_params);
1990 wwo->dwBufferSize = snd_pcm_frames_to_bytes(pcm, buffer_size);
1991 wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL;
1993 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1994 wwo->dwPartialOffset = 0;
1996 ALSA_InitRingMessage(&wwo->msgRing);
1998 wwo->count = snd_pcm_poll_descriptors_count (wwo->handle);
1999 if (wwo->count <= 0) {
2000 ERR("Invalid poll descriptors count\n");
2001 return MMSYSERR_ERROR;
2004 wwo->ufds = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, sizeof(struct pollfd) * wwo->count);
2005 if (wwo->ufds == NULL) {
2006 ERR("No enough memory\n");
2007 return MMSYSERR_NOMEM;
2009 if ((err = snd_pcm_poll_descriptors(wwo->handle, wwo->ufds, wwo->count)) < 0) {
2010 ERR("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
2011 return MMSYSERR_ERROR;
2014 if (!(dwFlags & WAVE_DIRECTSOUND)) {
2015 wwo->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
2016 wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
2018 SetThreadPriority(wwo->hThread, THREAD_PRIORITY_TIME_CRITICAL);
2019 WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
2020 CloseHandle(wwo->hStartUpEvent);
2022 wwo->hThread = INVALID_HANDLE_VALUE;
2023 wwo->dwThreadID = 0;
2025 wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
2027 TRACE("handle=%08lx \n", (DWORD)wwo->handle);
2028 /* if (wwo->dwFragmentSize % wwo->format.Format.nBlockAlign)
2029 ERR("Fragment doesn't contain an integral number of data blocks\n");
2031 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
2032 wwo->format.Format.wBitsPerSample, wwo->format.Format.nAvgBytesPerSec,
2033 wwo->format.Format.nSamplesPerSec, wwo->format.Format.nChannels,
2034 wwo->format.Format.nBlockAlign);
2036 return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
2040 /**************************************************************************
2041 * wodClose [internal]
2043 static DWORD wodClose(WORD wDevID)
2045 DWORD ret = MMSYSERR_NOERROR;
2048 TRACE("(%u);\n", wDevID);
2050 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2051 WARN("bad device ID !\n");
2052 return MMSYSERR_BADDEVICEID;
2055 wwo = &WOutDev[wDevID];
2056 if (wwo->lpQueuePtr) {
2057 WARN("buffers still playing !\n");
2058 ret = WAVERR_STILLPLAYING;
2060 if (wwo->hThread != INVALID_HANDLE_VALUE) {
2061 ALSA_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
2063 ALSA_DestroyRingMessage(&wwo->msgRing);
2065 snd_pcm_hw_params_free(wwo->hw_params);
2066 wwo->hw_params = NULL;
2068 snd_pcm_close(wwo->handle);
2071 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
2074 HeapFree(GetProcessHeap(), 0, wwo->ufds);
2079 /**************************************************************************
2080 * wodWrite [internal]
2083 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
2085 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
2087 /* first, do the sanity checks... */
2088 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2089 WARN("bad dev ID !\n");
2090 return MMSYSERR_BADDEVICEID;
2093 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
2094 return WAVERR_UNPREPARED;
2096 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
2097 return WAVERR_STILLPLAYING;
2099 lpWaveHdr->dwFlags &= ~WHDR_DONE;
2100 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
2101 lpWaveHdr->lpNext = 0;
2103 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
2105 return MMSYSERR_NOERROR;
2108 /**************************************************************************
2109 * wodPause [internal]
2111 static DWORD wodPause(WORD wDevID)
2113 TRACE("(%u);!\n", wDevID);
2115 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2116 WARN("bad device ID !\n");
2117 return MMSYSERR_BADDEVICEID;
2120 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
2122 return MMSYSERR_NOERROR;
2125 /**************************************************************************
2126 * wodRestart [internal]
2128 static DWORD wodRestart(WORD wDevID)
2130 TRACE("(%u);\n", wDevID);
2132 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2133 WARN("bad device ID !\n");
2134 return MMSYSERR_BADDEVICEID;
2137 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
2138 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
2141 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
2142 /* FIXME: Myst crashes with this ... hmm -MM
2143 return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
2146 return MMSYSERR_NOERROR;
2149 /**************************************************************************
2150 * wodReset [internal]
2152 static DWORD wodReset(WORD wDevID)
2154 TRACE("(%u);\n", wDevID);
2156 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2157 WARN("bad device ID !\n");
2158 return MMSYSERR_BADDEVICEID;
2161 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
2163 return MMSYSERR_NOERROR;
2166 /**************************************************************************
2167 * wodGetPosition [internal]
2169 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
2173 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
2175 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2176 WARN("bad device ID !\n");
2177 return MMSYSERR_BADDEVICEID;
2180 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
2182 wwo = &WOutDev[wDevID];
2183 ALSA_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
2185 return bytes_to_mmtime(lpTime, wwo->dwPlayedTotal, &wwo->format);
2188 /**************************************************************************
2189 * wodBreakLoop [internal]
2191 static DWORD wodBreakLoop(WORD wDevID)
2193 TRACE("(%u);\n", wDevID);
2195 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2196 WARN("bad device ID !\n");
2197 return MMSYSERR_BADDEVICEID;
2199 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
2200 return MMSYSERR_NOERROR;
2203 /**************************************************************************
2204 * wodGetVolume [internal]
2206 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
2214 TRACE("(%u, %p);\n", wDevID, lpdwVol);
2215 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2216 WARN("bad device ID !\n");
2217 return MMSYSERR_BADDEVICEID;
2220 if (lpdwVol == NULL)
2221 return MMSYSERR_NOTENABLED;
2223 wwo = &WOutDev[wDevID];
2225 if (lpdwVol == NULL)
2226 return MMSYSERR_NOTENABLED;
2228 rc = ALSA_CheckSetVolume(wwo->hctl, &left, &right, &min, &max, NULL, NULL, NULL);
2229 if (rc == MMSYSERR_NOERROR)
2231 #define VOLUME_ALSA_TO_WIN(x) ( ( (((x)-min) * 65535) + (max-min)/2 ) /(max-min))
2232 wleft = VOLUME_ALSA_TO_WIN(left);
2233 wright = VOLUME_ALSA_TO_WIN(right);
2234 #undef VOLUME_ALSA_TO_WIN
2235 TRACE("left=%d,right=%d,converted to windows left %d, right %d\n", left, right, wleft, wright);
2236 *lpdwVol = MAKELONG( wleft, wright );
2239 TRACE("CheckSetVolume failed; rc %ld\n", rc);
2244 /**************************************************************************
2245 * wodSetVolume [internal]
2247 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
2255 TRACE("(%u, %08lX);\n", wDevID, dwParam);
2256 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2257 WARN("bad device ID !\n");
2258 return MMSYSERR_BADDEVICEID;
2260 wwo = &WOutDev[wDevID];
2262 rc = ALSA_CheckSetVolume(wwo->hctl, NULL, NULL, &min, &max, NULL, NULL, NULL);
2263 if (rc == MMSYSERR_NOERROR)
2265 wleft = LOWORD(dwParam);
2266 wright = HIWORD(dwParam);
2267 #define VOLUME_WIN_TO_ALSA(x) ( ( ( ((x) * (max-min)) + 32767) / 65535) + min )
2268 left = VOLUME_WIN_TO_ALSA(wleft);
2269 right = VOLUME_WIN_TO_ALSA(wright);
2270 #undef VOLUME_WIN_TO_ALSA
2271 rc = ALSA_CheckSetVolume(wwo->hctl, NULL, NULL, NULL, NULL, NULL, &left, &right);
2272 if (rc == MMSYSERR_NOERROR)
2273 TRACE("set volume: wleft=%d, wright=%d, converted to alsa left %d, right %d\n", wleft, wright, left, right);
2275 TRACE("SetVolume failed; rc %ld\n", rc);
2281 /**************************************************************************
2282 * wodGetNumDevs [internal]
2284 static DWORD wodGetNumDevs(void)
2286 return ALSA_WodNumDevs;
2289 /**************************************************************************
2290 * wodDevInterfaceSize [internal]
2292 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
2294 TRACE("(%u, %p)\n", wDevID, dwParam1);
2296 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
2297 NULL, 0 ) * sizeof(WCHAR);
2298 return MMSYSERR_NOERROR;
2301 /**************************************************************************
2302 * wodDevInterface [internal]
2304 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
2306 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
2307 NULL, 0 ) * sizeof(WCHAR))
2309 MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
2310 dwParam1, dwParam2 / sizeof(WCHAR));
2311 return MMSYSERR_NOERROR;
2313 return MMSYSERR_INVALPARAM;
2316 /**************************************************************************
2317 * wodMessage (WINEALSA.@)
2319 DWORD WINAPI ALSA_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2320 DWORD dwParam1, DWORD dwParam2)
2322 TRACE("(%u, %s, %08lX, %08lX, %08lX);\n",
2323 wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
2330 /* FIXME: Pretend this is supported */
2332 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
2333 case WODM_CLOSE: return wodClose (wDevID);
2334 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSW)dwParam1, dwParam2);
2335 case WODM_GETNUMDEVS: return wodGetNumDevs ();
2336 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
2337 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
2338 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
2339 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
2340 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2341 case WODM_PAUSE: return wodPause (wDevID);
2342 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
2343 case WODM_BREAKLOOP: return wodBreakLoop (wDevID);
2344 case WODM_PREPARE: return MMSYSERR_NOTSUPPORTED;
2345 case WODM_UNPREPARE: return MMSYSERR_NOTSUPPORTED;
2346 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
2347 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
2348 case WODM_RESTART: return wodRestart (wDevID);
2349 case WODM_RESET: return wodReset (wDevID);
2350 case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
2351 case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
2352 case DRV_QUERYDSOUNDIFACE: return wodDsCreate (wDevID, (PIDSDRIVER*)dwParam1);
2353 case DRV_QUERYDSOUNDDESC: return wodDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
2356 FIXME("unknown message %d!\n", wMsg);
2358 return MMSYSERR_NOTSUPPORTED;
2361 /*======================================================================*
2362 * Low level DSOUND implementation *
2363 *======================================================================*/
2365 typedef struct IDsDriverImpl IDsDriverImpl;
2366 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
2368 struct IDsDriverImpl
2370 /* IUnknown fields */
2371 IDsDriverVtbl *lpVtbl;
2373 /* IDsDriverImpl fields */
2375 IDsDriverBufferImpl*primary;
2378 struct IDsDriverBufferImpl
2380 /* IUnknown fields */
2381 IDsDriverBufferVtbl *lpVtbl;
2383 /* IDsDriverBufferImpl fields */
2386 CRITICAL_SECTION mmap_crst;
2388 DWORD mmap_buflen_bytes;
2389 snd_pcm_uframes_t mmap_buflen_frames;
2390 snd_pcm_channel_area_t * mmap_areas;
2391 snd_async_handler_t * mmap_async_handler;
2394 static void DSDB_CheckXRUN(IDsDriverBufferImpl* pdbi)
2396 WINE_WAVEOUT * wwo = &(WOutDev[pdbi->drv->wDevID]);
2397 snd_pcm_state_t state = snd_pcm_state(wwo->handle);
2399 if ( state == SND_PCM_STATE_XRUN )
2401 int err = snd_pcm_prepare(wwo->handle);
2402 TRACE("xrun occurred\n");
2404 ERR("recovery from xrun failed, prepare failed: %s\n", snd_strerror(err));
2406 else if ( state == SND_PCM_STATE_SUSPENDED )
2408 int err = snd_pcm_resume(wwo->handle);
2409 TRACE("recovery from suspension occurred\n");
2410 if (err < 0 && err != -EAGAIN){
2411 err = snd_pcm_prepare(wwo->handle);
2413 ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err));
2418 static void DSDB_MMAPCopy(IDsDriverBufferImpl* pdbi)
2420 WINE_WAVEOUT * wwo = &(WOutDev[pdbi->drv->wDevID]);
2421 unsigned int channels;
2422 snd_pcm_format_t format;
2423 snd_pcm_uframes_t period_size;
2424 snd_pcm_sframes_t avail;
2428 if ( !pdbi->mmap_buffer || !wwo->hw_params || !wwo->handle)
2431 err = snd_pcm_hw_params_get_channels(wwo->hw_params, &channels);
2432 err = snd_pcm_hw_params_get_format(wwo->hw_params, &format);
2434 err = snd_pcm_hw_params_get_period_size(wwo->hw_params, &period_size, &dir);
2435 avail = snd_pcm_avail_update(wwo->handle);
2437 DSDB_CheckXRUN(pdbi);
2439 TRACE("avail=%d format=%s channels=%d\n", (int)avail, snd_pcm_format_name(format), channels );
2441 while (avail >= period_size)
2443 const snd_pcm_channel_area_t *areas;
2444 snd_pcm_uframes_t ofs;
2445 snd_pcm_uframes_t frames;
2448 frames = avail / period_size * period_size; /* round down to a multiple of period_size */
2450 EnterCriticalSection(&pdbi->mmap_crst);
2452 snd_pcm_mmap_begin(wwo->handle, &areas, &ofs, &frames);
2453 if (areas != pdbi->mmap_areas || areas->addr != pdbi->mmap_areas->addr)
2454 FIXME("Can't access sound driver's buffer directly.\n");
2455 err = snd_pcm_mmap_commit(wwo->handle, ofs, frames);
2457 LeaveCriticalSection(&pdbi->mmap_crst);
2459 if ( err != (snd_pcm_sframes_t) frames)
2460 ERR("mmap partially failed.\n");
2462 avail = snd_pcm_avail_update(wwo->handle);
2467 const snd_pcm_channel_area_t *areas;
2468 snd_pcm_uframes_t ofs;
2469 snd_pcm_uframes_t frames;
2474 EnterCriticalSection(&pdbi->mmap_crst);
2476 snd_pcm_mmap_begin(wwo->handle, &areas, &ofs, &frames);
2477 if (areas != pdbi->mmap_areas || areas->addr != pdbi->mmap_areas->addr)
2478 FIXME("Can't access sound driver's buffer directly.\n");
2479 err = snd_pcm_mmap_commit(wwo->handle, ofs, frames);
2481 LeaveCriticalSection(&pdbi->mmap_crst);
2483 if ( err != (snd_pcm_sframes_t) frames)
2484 ERR("mmap partially failed.\n");
2486 avail = snd_pcm_avail_update(wwo->handle);
2490 static void DSDB_PCMCallback(snd_async_handler_t *ahandler)
2492 /* snd_pcm_t * handle = snd_async_handler_get_pcm(ahandler); */
2493 IDsDriverBufferImpl* pdbi = snd_async_handler_get_callback_private(ahandler);
2494 TRACE("callback called\n");
2495 DSDB_MMAPCopy(pdbi);
2498 static int DSDB_CreateMMAP(IDsDriverBufferImpl* pdbi)
2500 WINE_WAVEOUT * wwo = &(WOutDev[pdbi->drv->wDevID]);
2501 snd_pcm_format_t format;
2502 snd_pcm_uframes_t frames;
2503 snd_pcm_uframes_t ofs;
2504 snd_pcm_uframes_t avail;
2505 unsigned int channels;
2506 unsigned int bits_per_sample;
2507 unsigned int bits_per_frame;
2510 err = snd_pcm_hw_params_get_format(wwo->hw_params, &format);
2511 err = snd_pcm_hw_params_get_buffer_size(wwo->hw_params, &frames);
2512 err = snd_pcm_hw_params_get_channels(wwo->hw_params, &channels);
2513 bits_per_sample = snd_pcm_format_physical_width(format);
2514 bits_per_frame = bits_per_sample * channels;
2518 ALSA_TraceParameters(wwo->hw_params, NULL, FALSE);
2520 TRACE("format=%s frames=%ld channels=%d bits_per_sample=%d bits_per_frame=%d\n",
2521 snd_pcm_format_name(format), frames, channels, bits_per_sample, bits_per_frame);
2523 pdbi->mmap_buflen_frames = frames;
2524 pdbi->mmap_buflen_bytes = snd_pcm_frames_to_bytes( wwo->handle, frames );
2526 avail = snd_pcm_avail_update(wwo->handle);
2529 ERR("No buffer is available: %s.", snd_strerror(avail));
2530 return DSERR_GENERIC;
2532 err = snd_pcm_mmap_begin(wwo->handle, (const snd_pcm_channel_area_t **)&pdbi->mmap_areas, &ofs, &avail);
2535 ERR("Can't map sound device for direct access: %s\n", snd_strerror(err));
2536 return DSERR_GENERIC;
2538 avail = 0;/* We don't have any data to commit yet */
2539 err = snd_pcm_mmap_commit(wwo->handle, ofs, avail);
2541 err = snd_pcm_rewind(wwo->handle, ofs);
2542 pdbi->mmap_buffer = pdbi->mmap_areas->addr;
2544 snd_pcm_format_set_silence(format, pdbi->mmap_buffer, frames );
2546 TRACE("created mmap buffer of %ld frames (%ld bytes) at %p\n",
2547 frames, pdbi->mmap_buflen_bytes, pdbi->mmap_buffer);
2549 InitializeCriticalSection(&pdbi->mmap_crst);
2551 err = snd_async_add_pcm_handler(&pdbi->mmap_async_handler, wwo->handle, DSDB_PCMCallback, pdbi);
2554 ERR("add_pcm_handler failed. reason: %s\n", snd_strerror(err));
2555 return DSERR_GENERIC;
2561 static void DSDB_DestroyMMAP(IDsDriverBufferImpl* pdbi)
2563 TRACE("mmap buffer %p destroyed\n", pdbi->mmap_buffer);
2564 pdbi->mmap_areas = NULL;
2565 pdbi->mmap_buffer = NULL;
2566 DeleteCriticalSection(&pdbi->mmap_crst);
2570 static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
2572 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
2573 FIXME("(): stub!\n");
2574 return DSERR_UNSUPPORTED;
2577 static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface)
2579 IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
2580 ULONG refCount = InterlockedIncrement(&This->ref);
2582 TRACE("(%p)->(ref before=%lu)\n",This, refCount - 1);
2587 static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface)
2589 IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
2590 ULONG refCount = InterlockedDecrement(&This->ref);
2592 TRACE("(%p)->(ref before=%lu)\n",This, refCount + 1);
2596 if (This == This->drv->primary)
2597 This->drv->primary = NULL;
2598 DSDB_DestroyMMAP(This);
2599 HeapFree(GetProcessHeap(), 0, This);
2603 static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
2604 LPVOID*ppvAudio1,LPDWORD pdwLen1,
2605 LPVOID*ppvAudio2,LPDWORD pdwLen2,
2606 DWORD dwWritePosition,DWORD dwWriteLen,
2609 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
2610 TRACE("(%p)\n",iface);
2611 return DSERR_UNSUPPORTED;
2614 static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
2615 LPVOID pvAudio1,DWORD dwLen1,
2616 LPVOID pvAudio2,DWORD dwLen2)
2618 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
2619 TRACE("(%p)\n",iface);
2620 return DSERR_UNSUPPORTED;
2623 static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface,
2624 LPWAVEFORMATEX pwfx)
2626 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
2627 TRACE("(%p,%p)\n",iface,pwfx);
2628 return DSERR_BUFFERLOST;
2631 static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq)
2633 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
2634 TRACE("(%p,%ld): stub\n",iface,dwFreq);
2635 return DSERR_UNSUPPORTED;
2638 static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan)
2641 IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
2642 TRACE("(%p,%p)\n",iface,pVolPan);
2643 vol = pVolPan->dwTotalLeftAmpFactor | (pVolPan->dwTotalRightAmpFactor << 16);
2645 if (wodSetVolume(This->drv->wDevID, vol) != MMSYSERR_NOERROR) {
2646 WARN("wodSetVolume failed\n");
2647 return DSERR_INVALIDPARAM;
2653 static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos)
2655 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
2656 TRACE("(%p,%ld): stub\n",iface,dwNewPos);
2657 return DSERR_UNSUPPORTED;
2660 static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
2661 LPDWORD lpdwPlay, LPDWORD lpdwWrite)
2663 IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
2664 WINE_WAVEOUT * wwo = &(WOutDev[This->drv->wDevID]);
2665 snd_pcm_uframes_t hw_ptr;
2666 snd_pcm_uframes_t period_size;
2670 if (wwo->hw_params == NULL) return DSERR_GENERIC;
2673 err = snd_pcm_hw_params_get_period_size(wwo->hw_params, &period_size, &dir);
2675 if (wwo->handle == NULL) return DSERR_GENERIC;
2676 /** we need to track down buffer underruns */
2677 DSDB_CheckXRUN(This);
2679 EnterCriticalSection(&This->mmap_crst);
2680 /* FIXME: snd_pcm_mmap_hw_ptr() should not be accessed by a user app. */
2681 /* It will NOT return what why want anyway. */
2682 hw_ptr = _snd_pcm_mmap_hw_ptr(wwo->handle);
2684 *lpdwPlay = snd_pcm_frames_to_bytes(wwo->handle, hw_ptr/ period_size * period_size) % This->mmap_buflen_bytes;
2686 *lpdwWrite = snd_pcm_frames_to_bytes(wwo->handle, (hw_ptr / period_size + 1) * period_size ) % This->mmap_buflen_bytes;
2687 LeaveCriticalSection(&This->mmap_crst);
2689 TRACE("hw_ptr=0x%08x, playpos=%ld, writepos=%ld\n", (unsigned int)hw_ptr, lpdwPlay?*lpdwPlay:-1, lpdwWrite?*lpdwWrite:-1);
2693 static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags)
2695 IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
2696 WINE_WAVEOUT * wwo = &(WOutDev[This->drv->wDevID]);
2697 snd_pcm_state_t state;
2700 TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags);
2702 if (wwo->handle == NULL) return DSERR_GENERIC;
2704 state = snd_pcm_state(wwo->handle);
2705 if ( state == SND_PCM_STATE_SETUP )
2707 err = snd_pcm_prepare(wwo->handle);
2708 state = snd_pcm_state(wwo->handle);
2710 if ( state == SND_PCM_STATE_PREPARED )
2712 DSDB_MMAPCopy(This);
2713 err = snd_pcm_start(wwo->handle);
2718 static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface)
2720 IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
2721 WINE_WAVEOUT * wwo = &(WOutDev[This->drv->wDevID]);
2726 TRACE("(%p)\n",iface);
2728 if (wwo->handle == NULL) return DSERR_GENERIC;
2730 /* ring buffer wrap up detection */
2731 IDsDriverBufferImpl_GetPosition(iface, &play, &write);
2734 TRACE("writepos wrapper up\n");
2738 if ( ( err = snd_pcm_drop(wwo->handle)) < 0 )
2740 ERR("error while stopping pcm: %s\n", snd_strerror(err));
2741 return DSERR_GENERIC;
2746 static IDsDriverBufferVtbl dsdbvt =
2748 IDsDriverBufferImpl_QueryInterface,
2749 IDsDriverBufferImpl_AddRef,
2750 IDsDriverBufferImpl_Release,
2751 IDsDriverBufferImpl_Lock,
2752 IDsDriverBufferImpl_Unlock,
2753 IDsDriverBufferImpl_SetFormat,
2754 IDsDriverBufferImpl_SetFrequency,
2755 IDsDriverBufferImpl_SetVolumePan,
2756 IDsDriverBufferImpl_SetPosition,
2757 IDsDriverBufferImpl_GetPosition,
2758 IDsDriverBufferImpl_Play,
2759 IDsDriverBufferImpl_Stop
2762 static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj)
2764 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
2765 FIXME("(%p): stub!\n",iface);
2766 return DSERR_UNSUPPORTED;
2769 static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface)
2771 IDsDriverImpl *This = (IDsDriverImpl *)iface;
2772 ULONG refCount = InterlockedIncrement(&This->ref);
2774 TRACE("(%p)->(ref before=%lu)\n",This, refCount - 1);
2779 static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface)
2781 IDsDriverImpl *This = (IDsDriverImpl *)iface;
2782 ULONG refCount = InterlockedDecrement(&This->ref);
2784 TRACE("(%p)->(ref before=%lu)\n",This, refCount + 1);
2788 HeapFree(GetProcessHeap(),0,This);
2792 static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc)
2794 IDsDriverImpl *This = (IDsDriverImpl *)iface;
2795 TRACE("(%p,%p)\n",iface,pDesc);
2796 memcpy(pDesc, &(WOutDev[This->wDevID].ds_desc), sizeof(DSDRIVERDESC));
2797 pDesc->dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
2798 DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK;
2799 pDesc->dnDevNode = WOutDev[This->wDevID].waveDesc.dnDevNode;
2801 pDesc->wReserved = 0;
2802 pDesc->ulDeviceNum = This->wDevID;
2803 pDesc->dwHeapType = DSDHEAP_NOHEAP;
2804 pDesc->pvDirectDrawHeap = NULL;
2805 pDesc->dwMemStartAddress = 0;
2806 pDesc->dwMemEndAddress = 0;
2807 pDesc->dwMemAllocExtra = 0;
2808 pDesc->pvReserved1 = NULL;
2809 pDesc->pvReserved2 = NULL;
2813 static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface)
2815 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
2816 TRACE("(%p)\n",iface);
2820 static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface)
2822 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
2823 TRACE("(%p)\n",iface);
2827 static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps)
2829 IDsDriverImpl *This = (IDsDriverImpl *)iface;
2830 TRACE("(%p,%p)\n",iface,pCaps);
2831 memcpy(pCaps, &(WOutDev[This->wDevID].ds_caps), sizeof(DSDRIVERCAPS));
2835 static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
2836 LPWAVEFORMATEX pwfx,
2837 DWORD dwFlags, DWORD dwCardAddress,
2838 LPDWORD pdwcbBufferSize,
2842 IDsDriverImpl *This = (IDsDriverImpl *)iface;
2843 IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
2846 TRACE("(%p,%p,%lx,%lx)\n",iface,pwfx,dwFlags,dwCardAddress);
2847 /* we only support primary buffers */
2848 if (!(dwFlags & DSBCAPS_PRIMARYBUFFER))
2849 return DSERR_UNSUPPORTED;
2851 return DSERR_ALLOCATED;
2852 if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN))
2853 return DSERR_CONTROLUNAVAIL;
2855 *ippdsdb = HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverBufferImpl));
2856 if (*ippdsdb == NULL)
2857 return DSERR_OUTOFMEMORY;
2858 (*ippdsdb)->lpVtbl = &dsdbvt;
2859 (*ippdsdb)->ref = 1;
2860 (*ippdsdb)->drv = This;
2862 err = DSDB_CreateMMAP((*ippdsdb));
2865 HeapFree(GetProcessHeap(), 0, *ippdsdb);
2869 *ppbBuffer = (*ippdsdb)->mmap_buffer;
2870 *pdwcbBufferSize = (*ippdsdb)->mmap_buflen_bytes;
2872 This->primary = *ippdsdb;
2874 /* buffer is ready to go */
2875 TRACE("buffer created at %p\n", *ippdsdb);
2879 static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
2880 PIDSDRIVERBUFFER pBuffer,
2883 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
2884 TRACE("(%p,%p): stub\n",iface,pBuffer);
2885 return DSERR_INVALIDCALL;
2888 static IDsDriverVtbl dsdvt =
2890 IDsDriverImpl_QueryInterface,
2891 IDsDriverImpl_AddRef,
2892 IDsDriverImpl_Release,
2893 IDsDriverImpl_GetDriverDesc,
2895 IDsDriverImpl_Close,
2896 IDsDriverImpl_GetCaps,
2897 IDsDriverImpl_CreateSoundBuffer,
2898 IDsDriverImpl_DuplicateSoundBuffer
2901 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
2903 IDsDriverImpl** idrv = (IDsDriverImpl**)drv;
2905 TRACE("driver created\n");
2907 /* the HAL isn't much better than the HEL if we can't do mmap() */
2908 if (!(WOutDev[wDevID].caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
2909 ERR("DirectSound flag not set\n");
2910 MESSAGE("This sound card's driver does not support direct access\n");
2911 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
2912 return MMSYSERR_NOTSUPPORTED;
2915 *idrv = HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverImpl));
2917 return MMSYSERR_NOMEM;
2918 (*idrv)->lpVtbl = &dsdvt;
2921 (*idrv)->wDevID = wDevID;
2922 (*idrv)->primary = NULL;
2923 return MMSYSERR_NOERROR;
2926 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
2928 memcpy(desc, &(WOutDev[wDevID].ds_desc), sizeof(DSDRIVERDESC));
2929 return MMSYSERR_NOERROR;
2932 /*======================================================================*
2933 * Low level WAVE IN implementation *
2934 *======================================================================*/
2936 /**************************************************************************
2937 * widNotifyClient [internal]
2939 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
2941 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
2947 if (wwi->wFlags != DCB_NULL &&
2948 !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags, (HDRVR)wwi->waveDesc.hWave,
2949 wMsg, wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
2950 WARN("can't notify client !\n");
2951 return MMSYSERR_ERROR;
2955 FIXME("Unknown callback message %u\n", wMsg);
2956 return MMSYSERR_INVALPARAM;
2958 return MMSYSERR_NOERROR;
2961 /**************************************************************************
2962 * widGetDevCaps [internal]
2964 static DWORD widGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
2966 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
2968 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
2970 if (wDevID >= MAX_WAVEINDRV) {
2971 TRACE("MAX_WAVOUTDRV reached !\n");
2972 return MMSYSERR_BADDEVICEID;
2975 memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
2976 return MMSYSERR_NOERROR;
2979 /**************************************************************************
2980 * widRecorder_ReadHeaders [internal]
2982 static void widRecorder_ReadHeaders(WINE_WAVEIN * wwi)
2984 enum win_wm_message tmp_msg;
2989 while (ALSA_RetrieveRingMessage(&wwi->msgRing, &tmp_msg, &tmp_param, &tmp_ev)) {
2990 if (tmp_msg == WINE_WM_HEADER) {
2992 lpWaveHdr = (LPWAVEHDR)tmp_param;
2993 lpWaveHdr->lpNext = 0;
2995 if (wwi->lpQueuePtr == 0)
2996 wwi->lpQueuePtr = lpWaveHdr;
2998 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
3002 ERR("should only have headers left\n");
3007 /**************************************************************************
3008 * widRecorder [internal]
3010 static DWORD CALLBACK widRecorder(LPVOID pmt)
3012 WORD uDevID = (DWORD)pmt;
3013 WINE_WAVEIN* wwi = (WINE_WAVEIN*)&WInDev[uDevID];
3017 LPVOID buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, wwi->dwPeriodSize);
3018 char *pOffset = buffer;
3019 enum win_wm_message msg;
3022 DWORD frames_per_period;
3024 wwi->state = WINE_WS_STOPPED;
3025 wwi->dwTotalRecorded = 0;
3026 wwi->lpQueuePtr = NULL;
3028 SetEvent(wwi->hStartUpEvent);
3030 /* make sleep time to be # of ms to output a period */
3031 dwSleepTime = (1024/*wwi-dwPeriodSize => overrun!*/ * 1000) / wwi->format.Format.nAvgBytesPerSec;
3032 frames_per_period = snd_pcm_bytes_to_frames(wwi->handle, wwi->dwPeriodSize);
3033 TRACE("sleeptime=%ld ms\n", dwSleepTime);
3036 /* wait for dwSleepTime or an event in thread's queue */
3037 /* FIXME: could improve wait time depending on queue state,
3038 * ie, number of queued fragments
3040 if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING)
3047 lpWaveHdr = wwi->lpQueuePtr;
3048 /* read all the fragments accumulated so far */
3049 frames = snd_pcm_avail_update(wwi->handle);
3050 bytes = snd_pcm_frames_to_bytes(wwi->handle, frames);
3051 TRACE("frames = %ld bytes = %ld\n", frames, bytes);
3052 periods = bytes / wwi->dwPeriodSize;
3053 while ((periods > 0) && (wwi->lpQueuePtr))
3056 bytes = wwi->dwPeriodSize;
3057 TRACE("bytes = %ld\n",bytes);
3058 if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded >= wwi->dwPeriodSize)
3060 /* directly read fragment in wavehdr */
3061 read = wwi->read(wwi->handle, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, frames_per_period);
3062 bytesRead = snd_pcm_frames_to_bytes(wwi->handle, read);
3064 TRACE("bytesRead=%ld (direct)\n", bytesRead);
3065 if (bytesRead != (DWORD) -1)
3067 /* update number of bytes recorded in current buffer and by this device */
3068 lpWaveHdr->dwBytesRecorded += bytesRead;
3069 wwi->dwTotalRecorded += bytesRead;
3071 /* buffer is full. notify client */
3072 if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
3074 /* must copy the value of next waveHdr, because we have no idea of what
3075 * will be done with the content of lpWaveHdr in callback
3077 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
3079 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
3080 lpWaveHdr->dwFlags |= WHDR_DONE;
3082 wwi->lpQueuePtr = lpNext;
3083 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
3087 TRACE("read(%s, %p, %ld) failed (%s)\n", wwi->device,
3088 lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
3089 frames_per_period, strerror(errno));
3094 /* read the fragment in a local buffer */
3095 read = wwi->read(wwi->handle, buffer, frames_per_period);
3096 bytesRead = snd_pcm_frames_to_bytes(wwi->handle, read);
3099 TRACE("bytesRead=%ld (local)\n", bytesRead);
3101 if (bytesRead == (DWORD) -1) {
3102 TRACE("read(%s, %p, %ld) failed (%s)\n", wwi->device,
3103 buffer, frames_per_period, strerror(errno));
3107 /* copy data in client buffers */
3108 while (bytesRead != (DWORD) -1 && bytesRead > 0)
3110 DWORD dwToCopy = min (bytesRead, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
3112 memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
3116 /* update number of bytes recorded in current buffer and by this device */
3117 lpWaveHdr->dwBytesRecorded += dwToCopy;
3118 wwi->dwTotalRecorded += dwToCopy;
3119 bytesRead -= dwToCopy;
3120 pOffset += dwToCopy;
3122 /* client buffer is full. notify client */
3123 if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
3125 /* must copy the value of next waveHdr, because we have no idea of what
3126 * will be done with the content of lpWaveHdr in callback
3128 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
3129 TRACE("lpNext=%p\n", lpNext);
3131 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
3132 lpWaveHdr->dwFlags |= WHDR_DONE;
3134 wwi->lpQueuePtr = lpNext;
3135 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
3138 if (!lpNext && bytesRead) {
3139 /* before we give up, check for more header messages */
3140 while (ALSA_PeekRingMessage(&wwi->msgRing, &msg, ¶m, &ev))
3142 if (msg == WINE_WM_HEADER) {
3144 ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev);
3145 hdr = ((LPWAVEHDR)param);
3146 TRACE("msg = %s, hdr = %p, ev = %p\n", getCmdString(msg), hdr, ev);
3148 if (lpWaveHdr == 0) {
3149 /* new head of queue */
3150 wwi->lpQueuePtr = lpWaveHdr = hdr;
3152 /* insert buffer at the end of queue */
3154 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
3161 if (lpWaveHdr == 0) {
3162 /* no more buffer to copy data to, but we did read more.
3163 * what hasn't been copied will be dropped
3165 WARN("buffer under run! %lu bytes dropped.\n", bytesRead);
3166 wwi->lpQueuePtr = NULL;
3176 WAIT_OMR(&wwi->msgRing, dwSleepTime);
3178 while (ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev))
3180 TRACE("msg=%s param=0x%lx\n", getCmdString(msg), param);
3182 case WINE_WM_PAUSING:
3183 wwi->state = WINE_WS_PAUSED;
3184 /*FIXME("Device should stop recording\n");*/
3187 case WINE_WM_STARTING:
3188 wwi->state = WINE_WS_PLAYING;
3189 snd_pcm_start(wwi->handle);
3192 case WINE_WM_HEADER:
3193 lpWaveHdr = (LPWAVEHDR)param;
3194 lpWaveHdr->lpNext = 0;
3196 /* insert buffer at the end of queue */
3199 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
3203 case WINE_WM_STOPPING:
3204 if (wwi->state != WINE_WS_STOPPED)
3206 snd_pcm_drain(wwi->handle);
3208 /* read any headers in queue */
3209 widRecorder_ReadHeaders(wwi);
3211 /* return current buffer to app */
3212 lpWaveHdr = wwi->lpQueuePtr;
3215 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
3216 TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
3217 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
3218 lpWaveHdr->dwFlags |= WHDR_DONE;
3219 wwi->lpQueuePtr = lpNext;
3220 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
3223 wwi->state = WINE_WS_STOPPED;
3226 case WINE_WM_RESETTING:
3227 if (wwi->state != WINE_WS_STOPPED)
3229 snd_pcm_drain(wwi->handle);
3231 wwi->state = WINE_WS_STOPPED;
3232 wwi->dwTotalRecorded = 0;
3234 /* read any headers in queue */
3235 widRecorder_ReadHeaders(wwi);
3237 /* return all buffers to the app */
3238 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
3239 TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
3240 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
3241 lpWaveHdr->dwFlags |= WHDR_DONE;
3242 wwi->lpQueuePtr = lpWaveHdr->lpNext;
3243 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
3246 wwi->lpQueuePtr = NULL;
3249 case WINE_WM_CLOSING:
3251 wwi->state = WINE_WS_CLOSED;
3253 HeapFree(GetProcessHeap(), 0, buffer);
3255 /* shouldn't go here */
3256 case WINE_WM_UPDATE:
3261 FIXME("unknown message %d\n", msg);
3267 /* just for not generating compilation warnings... should never be executed */
3271 /**************************************************************************
3272 * widOpen [internal]
3274 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
3277 snd_pcm_hw_params_t * hw_params;
3278 snd_pcm_sw_params_t * sw_params;
3279 snd_pcm_access_t access;
3280 snd_pcm_format_t format;
3282 unsigned int buffer_time = 500000;
3283 unsigned int period_time = 10000;
3284 snd_pcm_uframes_t buffer_size;
3285 snd_pcm_uframes_t period_size;
3291 snd_pcm_hw_params_alloca(&hw_params);
3292 snd_pcm_sw_params_alloca(&sw_params);
3294 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
3295 if (lpDesc == NULL) {
3296 WARN("Invalid Parameter !\n");
3297 return MMSYSERR_INVALPARAM;
3299 if (wDevID >= MAX_WAVEOUTDRV) {
3300 TRACE("MAX_WAVOUTDRV reached !\n");
3301 return MMSYSERR_BADDEVICEID;
3304 /* only PCM format is supported so far... */
3305 if (!supportedFormat(lpDesc->lpFormat)) {
3306 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3307 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
3308 lpDesc->lpFormat->nSamplesPerSec);
3309 return WAVERR_BADFORMAT;
3312 if (dwFlags & WAVE_FORMAT_QUERY) {
3313 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3314 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
3315 lpDesc->lpFormat->nSamplesPerSec);
3316 return MMSYSERR_NOERROR;
3319 wwi = &WInDev[wDevID];
3321 if ((dwFlags & WAVE_DIRECTSOUND) && !(wwi->dwSupport & WAVECAPS_DIRECTSOUND))
3322 /* not supported, ignore it */
3323 dwFlags &= ~WAVE_DIRECTSOUND;
3326 flags = SND_PCM_NONBLOCK;
3328 if ( dwFlags & WAVE_DIRECTSOUND )
3329 flags |= SND_PCM_ASYNC;
3332 if ( (err=snd_pcm_open(&pcm, wwi->device, SND_PCM_STREAM_CAPTURE, flags)) < 0 )
3334 ERR("Error open: %s\n", snd_strerror(err));
3335 return MMSYSERR_NOTENABLED;
3338 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
3340 memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
3341 copy_format(lpDesc->lpFormat, &wwi->format);
3343 if (wwi->format.Format.wBitsPerSample == 0) {
3344 WARN("Resetting zeroed wBitsPerSample\n");
3345 wwi->format.Format.wBitsPerSample = 8 *
3346 (wwi->format.Format.nAvgBytesPerSec /
3347 wwi->format.Format.nSamplesPerSec) /
3348 wwi->format.Format.nChannels;
3351 snd_pcm_hw_params_any(pcm, hw_params);
3353 #define EXIT_ON_ERROR(f,e,txt) do \
3356 if ( (err = (f) ) < 0) \
3358 ERR(txt ": %s\n", snd_strerror(err)); \
3359 snd_pcm_close(pcm); \
3364 access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
3365 if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {
3366 WARN("mmap not available. switching to standard write.\n");
3367 access = SND_PCM_ACCESS_RW_INTERLEAVED;
3368 EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");
3369 wwi->read = snd_pcm_readi;
3372 wwi->read = snd_pcm_mmap_readi;
3374 EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwi->format.Format.nChannels), MMSYSERR_INVALPARAM, "unable to set required channels");
3376 if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_PCM) ||
3377 ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
3378 IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) {
3379 format = (wwi->format.Format.wBitsPerSample == 8) ? SND_PCM_FORMAT_U8 :
3380 (wwi->format.Format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE :
3381 (wwi->format.Format.wBitsPerSample == 24) ? SND_PCM_FORMAT_S24_LE :
3382 (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_S32_LE : -1;
3383 } else if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
3384 IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)){
3385 format = (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_FLOAT_LE : -1;
3386 } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_MULAW) {
3387 FIXME("unimplemented format: WAVE_FORMAT_MULAW\n");
3389 return WAVERR_BADFORMAT;
3390 } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ALAW) {
3391 FIXME("unimplemented format: WAVE_FORMAT_ALAW\n");
3393 return WAVERR_BADFORMAT;
3394 } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ADPCM) {
3395 FIXME("unimplemented format: WAVE_FORMAT_ADPCM\n");
3397 return WAVERR_BADFORMAT;
3399 ERR("invalid format: %0x04x\n", wwi->format.Format.wFormatTag);
3401 return WAVERR_BADFORMAT;
3404 EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), MMSYSERR_INVALPARAM, "unable to set required format");
3406 rate = wwi->format.Format.nSamplesPerSec;
3408 err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir);
3410 ERR("Rate %ld Hz not available for playback: %s\n", wwi->format.Format.nSamplesPerSec, snd_strerror(rate));
3412 return WAVERR_BADFORMAT;
3414 if (rate != wwi->format.Format.nSamplesPerSec) {
3415 ERR("Rate doesn't match (requested %ld Hz, got %d Hz)\n", wwi->format.Format.nSamplesPerSec, rate);
3417 return WAVERR_BADFORMAT;
3421 EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time");
3423 EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time");
3425 EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");
3428 err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
3429 err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
3431 snd_pcm_sw_params_current(pcm, sw_params);
3432 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");
3433 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");
3434 EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min");
3435 EXIT_ON_ERROR( snd_pcm_sw_params_set_xfer_align(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set xfer align");
3436 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold");
3437 EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback");
3438 #undef EXIT_ON_ERROR
3440 snd_pcm_prepare(pcm);
3443 ALSA_TraceParameters(hw_params, sw_params, FALSE);
3445 /* now, we can save all required data for later use... */
3446 if ( wwi->hw_params )
3447 snd_pcm_hw_params_free(wwi->hw_params);
3448 snd_pcm_hw_params_malloc(&(wwi->hw_params));
3449 snd_pcm_hw_params_copy(wwi->hw_params, hw_params);
3451 wwi->dwBufferSize = snd_pcm_frames_to_bytes(pcm, buffer_size);
3452 wwi->lpQueuePtr = wwi->lpPlayPtr = wwi->lpLoopPtr = NULL;
3455 ALSA_InitRingMessage(&wwi->msgRing);
3457 wwi->count = snd_pcm_poll_descriptors_count (wwi->handle);
3458 if (wwi->count <= 0) {
3459 ERR("Invalid poll descriptors count\n");
3460 return MMSYSERR_ERROR;
3463 wwi->ufds = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, sizeof(struct pollfd) * wwi->count);
3464 if (wwi->ufds == NULL) {
3465 ERR("No enough memory\n");
3466 return MMSYSERR_NOMEM;
3468 if ((err = snd_pcm_poll_descriptors(wwi->handle, wwi->ufds, wwi->count)) < 0) {
3469 ERR("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
3470 return MMSYSERR_ERROR;
3473 wwi->dwPeriodSize = period_size;
3474 /*if (wwi->dwFragmentSize % wwi->format.Format.nBlockAlign)
3475 ERR("Fragment doesn't contain an integral number of data blocks\n");
3477 TRACE("dwPeriodSize=%lu\n", wwi->dwPeriodSize);
3478 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
3479 wwi->format.Format.wBitsPerSample, wwi->format.Format.nAvgBytesPerSec,
3480 wwi->format.Format.nSamplesPerSec, wwi->format.Format.nChannels,
3481 wwi->format.Format.nBlockAlign);
3483 if (!(dwFlags & WAVE_DIRECTSOUND)) {
3484 wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
3485 wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
3487 SetThreadPriority(wwi->hThread, THREAD_PRIORITY_TIME_CRITICAL);
3488 WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
3489 CloseHandle(wwi->hStartUpEvent);
3491 wwi->hThread = INVALID_HANDLE_VALUE;
3492 wwi->dwThreadID = 0;
3494 wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
3496 return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
3500 /**************************************************************************
3501 * widClose [internal]
3503 static DWORD widClose(WORD wDevID)
3505 DWORD ret = MMSYSERR_NOERROR;
3508 TRACE("(%u);\n", wDevID);
3510 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].handle == NULL) {
3511 WARN("bad device ID !\n");
3512 return MMSYSERR_BADDEVICEID;
3515 wwi = &WInDev[wDevID];
3516 if (wwi->lpQueuePtr) {
3517 WARN("buffers still playing !\n");
3518 ret = WAVERR_STILLPLAYING;
3520 if (wwi->hThread != INVALID_HANDLE_VALUE) {
3521 ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
3523 ALSA_DestroyRingMessage(&wwi->msgRing);
3525 snd_pcm_hw_params_free(wwi->hw_params);
3526 wwi->hw_params = NULL;
3528 snd_pcm_close(wwi->handle);
3531 ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
3534 HeapFree(GetProcessHeap(), 0, wwi->ufds);
3538 /**************************************************************************
3539 * widAddBuffer [internal]
3542 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3544 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3546 /* first, do the sanity checks... */
3547 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].handle == NULL) {
3548 WARN("bad dev ID !\n");
3549 return MMSYSERR_BADDEVICEID;
3552 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
3553 return WAVERR_UNPREPARED;
3555 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
3556 return WAVERR_STILLPLAYING;
3558 lpWaveHdr->dwFlags &= ~WHDR_DONE;
3559 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
3560 lpWaveHdr->lpNext = 0;
3562 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
3564 return MMSYSERR_NOERROR;
3567 /**************************************************************************
3568 * widStart [internal]
3571 static DWORD widStart(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3573 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3575 /* first, do the sanity checks... */
3576 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].handle == NULL) {
3577 WARN("bad dev ID !\n");
3578 return MMSYSERR_BADDEVICEID;
3581 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
3585 return MMSYSERR_NOERROR;
3588 /**************************************************************************
3589 * widStop [internal]
3592 static DWORD widStop(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3594 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3596 /* first, do the sanity checks... */
3597 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].handle == NULL) {
3598 WARN("bad dev ID !\n");
3599 return MMSYSERR_BADDEVICEID;
3602 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
3604 return MMSYSERR_NOERROR;
3607 /**************************************************************************
3608 * widReset [internal]
3610 static DWORD widReset(WORD wDevID)
3612 TRACE("(%u);\n", wDevID);
3613 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
3614 WARN("can't reset !\n");
3615 return MMSYSERR_INVALHANDLE;
3617 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
3618 return MMSYSERR_NOERROR;
3621 /**************************************************************************
3622 * widGetPosition [internal]
3624 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
3628 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
3630 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
3631 WARN("can't get pos !\n");
3632 return MMSYSERR_INVALHANDLE;
3635 if (lpTime == NULL) {
3636 WARN("invalid parameter: lpTime = NULL\n");
3637 return MMSYSERR_INVALPARAM;
3640 wwi = &WInDev[wDevID];
3641 ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_UPDATE, 0, TRUE);
3643 return bytes_to_mmtime(lpTime, wwi->dwTotalRecorded, &wwi->format);
3646 /**************************************************************************
3647 * widGetNumDevs [internal]
3649 static DWORD widGetNumDevs(void)
3651 return ALSA_WidNumDevs;
3654 /**************************************************************************
3655 * widDevInterfaceSize [internal]
3657 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
3659 TRACE("(%u, %p)\n", wDevID, dwParam1);
3661 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
3662 NULL, 0 ) * sizeof(WCHAR);
3663 return MMSYSERR_NOERROR;
3666 /**************************************************************************
3667 * widDevInterface [internal]
3669 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
3671 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
3672 NULL, 0 ) * sizeof(WCHAR))
3674 MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
3675 dwParam1, dwParam2 / sizeof(WCHAR));
3676 return MMSYSERR_NOERROR;
3678 return MMSYSERR_INVALPARAM;
3681 /**************************************************************************
3682 * widDsCreate [internal]
3684 static DWORD widDsCreate(UINT wDevID, PIDSCDRIVER* drv)
3686 TRACE("(%d,%p)\n",wDevID,drv);
3688 /* the HAL isn't much better than the HEL if we can't do mmap() */
3689 FIXME("DirectSoundCapture not implemented\n");
3690 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
3691 return MMSYSERR_NOTSUPPORTED;
3694 /**************************************************************************
3695 * widDsDesc [internal]
3697 static DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc)
3699 memcpy(desc, &(WInDev[wDevID].ds_desc), sizeof(DSDRIVERDESC));
3700 return MMSYSERR_NOERROR;
3703 /**************************************************************************
3704 * widMessage (WINEALSA.@)
3706 DWORD WINAPI ALSA_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
3707 DWORD dwParam1, DWORD dwParam2)
3709 TRACE("(%u, %s, %08lX, %08lX, %08lX);\n",
3710 wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
3717 /* FIXME: Pretend this is supported */
3719 case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
3720 case WIDM_CLOSE: return widClose (wDevID);
3721 case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3722 case WIDM_PREPARE: return MMSYSERR_NOTSUPPORTED;
3723 case WIDM_UNPREPARE: return MMSYSERR_NOTSUPPORTED;
3724 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEOUTCAPSW)dwParam1, dwParam2);
3725 case WIDM_GETNUMDEVS: return widGetNumDevs ();
3726 case WIDM_GETPOS: return widGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
3727 case WIDM_RESET: return widReset (wDevID);
3728 case WIDM_START: return widStart (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3729 case WIDM_STOP: return widStop (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3730 case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
3731 case DRV_QUERYDEVICEINTERFACE: return widDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
3732 case DRV_QUERYDSOUNDIFACE: return widDsCreate (wDevID, (PIDSCDRIVER*)dwParam1);
3733 case DRV_QUERYDSOUNDDESC: return widDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
3735 FIXME("unknown message %d!\n", wMsg);
3737 return MMSYSERR_NOTSUPPORTED;
3742 /**************************************************************************
3743 * widMessage (WINEALSA.@)
3745 DWORD WINAPI ALSA_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
3746 DWORD dwParam1, DWORD dwParam2)
3748 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
3749 return MMSYSERR_NOTENABLED;
3752 /**************************************************************************
3753 * wodMessage (WINEALSA.@)
3755 DWORD WINAPI ALSA_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
3756 DWORD dwParam1, DWORD dwParam2)
3758 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
3759 return MMSYSERR_NOTENABLED;
3762 #endif /* HAVE_ALSA */