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 snd_ctl_t * ctl; /* control handle for the playback volume */
163 snd_ctl_elem_id_t * playback_eid; /* element id of the playback volume control */
164 snd_ctl_elem_value_t * playback_evalue; /* element value of the playback volume control */
165 snd_ctl_elem_info_t * playback_einfo; /* element info of the playback volume control */
167 snd_pcm_sframes_t (*write)(snd_pcm_t *, const void *, snd_pcm_uframes_t );
172 DWORD dwBufferSize; /* size of whole ALSA buffer in bytes */
173 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
174 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
175 DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
177 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
178 DWORD dwLoops; /* private copy of loop counter */
180 DWORD dwPlayedTotal; /* number of bytes actually played since opening */
181 DWORD dwWrittenTotal; /* number of bytes written to ALSA buffer since opening */
183 /* synchronization stuff */
184 HANDLE hStartUpEvent;
187 ALSA_MSG_RING msgRing;
189 /* DirectSound stuff */
190 DSDRIVERDESC ds_desc;
194 /* Windows information */
195 volatile int state; /* one of the WINE_WS_ manifest constants */
196 WAVEOPENDESC waveDesc;
198 WAVEFORMATPCMEX format;
202 /* ALSA information (ALSA 0.9/1.x uses two different devices for playback/capture) */
204 char interface_name[64];
205 snd_pcm_t* handle; /* handle to ALSA capture device */
206 snd_pcm_hw_params_t * hw_params; /* ALSA Hw params */
208 snd_ctl_t * ctl; /* control handle for the playback volume */
209 snd_ctl_elem_id_t * playback_eid; /* element id of the playback volume control */
210 snd_ctl_elem_value_t * playback_evalue; /* element value of the playback volume control */
211 snd_ctl_elem_info_t * playback_einfo; /* element info of the playback volume control */
213 snd_pcm_sframes_t (*read)(snd_pcm_t *, void *, snd_pcm_uframes_t );
218 DWORD dwPeriodSize; /* size of OSS buffer period */
219 DWORD dwBufferSize; /* size of whole ALSA buffer in bytes */
220 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
221 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
223 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
224 DWORD dwLoops; /* private copy of loop counter */
226 /*DWORD dwPlayedTotal; */
227 DWORD dwTotalRecorded;
229 /* synchronization stuff */
230 HANDLE hStartUpEvent;
233 ALSA_MSG_RING msgRing;
235 /* DirectSound stuff */
236 DSDRIVERDESC ds_desc;
239 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
240 static DWORD ALSA_WodNumDevs;
241 static WINE_WAVEIN WInDev [MAX_WAVEINDRV];
242 static DWORD ALSA_WidNumDevs;
244 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
245 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
247 /* These strings used only for tracing */
248 static const char * getCmdString(enum win_wm_message msg)
250 static char unknown[32];
251 #define MSG_TO_STR(x) case x: return #x
253 MSG_TO_STR(WINE_WM_PAUSING);
254 MSG_TO_STR(WINE_WM_RESTARTING);
255 MSG_TO_STR(WINE_WM_RESETTING);
256 MSG_TO_STR(WINE_WM_HEADER);
257 MSG_TO_STR(WINE_WM_UPDATE);
258 MSG_TO_STR(WINE_WM_BREAKLOOP);
259 MSG_TO_STR(WINE_WM_CLOSING);
260 MSG_TO_STR(WINE_WM_STARTING);
261 MSG_TO_STR(WINE_WM_STOPPING);
264 sprintf(unknown, "UNKNOWN(0x%08x)", msg);
268 static const char * getMessage(UINT msg)
270 static char unknown[32];
271 #define MSG_TO_STR(x) case x: return #x
273 MSG_TO_STR(DRVM_INIT);
274 MSG_TO_STR(DRVM_EXIT);
275 MSG_TO_STR(DRVM_ENABLE);
276 MSG_TO_STR(DRVM_DISABLE);
277 MSG_TO_STR(WIDM_OPEN);
278 MSG_TO_STR(WIDM_CLOSE);
279 MSG_TO_STR(WIDM_ADDBUFFER);
280 MSG_TO_STR(WIDM_PREPARE);
281 MSG_TO_STR(WIDM_UNPREPARE);
282 MSG_TO_STR(WIDM_GETDEVCAPS);
283 MSG_TO_STR(WIDM_GETNUMDEVS);
284 MSG_TO_STR(WIDM_GETPOS);
285 MSG_TO_STR(WIDM_RESET);
286 MSG_TO_STR(WIDM_START);
287 MSG_TO_STR(WIDM_STOP);
288 MSG_TO_STR(WODM_OPEN);
289 MSG_TO_STR(WODM_CLOSE);
290 MSG_TO_STR(WODM_WRITE);
291 MSG_TO_STR(WODM_PAUSE);
292 MSG_TO_STR(WODM_GETPOS);
293 MSG_TO_STR(WODM_BREAKLOOP);
294 MSG_TO_STR(WODM_PREPARE);
295 MSG_TO_STR(WODM_UNPREPARE);
296 MSG_TO_STR(WODM_GETDEVCAPS);
297 MSG_TO_STR(WODM_GETNUMDEVS);
298 MSG_TO_STR(WODM_GETPITCH);
299 MSG_TO_STR(WODM_SETPITCH);
300 MSG_TO_STR(WODM_GETPLAYBACKRATE);
301 MSG_TO_STR(WODM_SETPLAYBACKRATE);
302 MSG_TO_STR(WODM_GETVOLUME);
303 MSG_TO_STR(WODM_SETVOLUME);
304 MSG_TO_STR(WODM_RESTART);
305 MSG_TO_STR(WODM_RESET);
306 MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE);
307 MSG_TO_STR(DRV_QUERYDEVICEINTERFACE);
308 MSG_TO_STR(DRV_QUERYDSOUNDIFACE);
309 MSG_TO_STR(DRV_QUERYDSOUNDDESC);
312 sprintf(unknown, "UNKNOWN(0x%04x)", msg);
316 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
317 WAVEFORMATPCMEX* format)
319 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
320 lpTime->wType, format->Format.wBitsPerSample, format->Format.nSamplesPerSec,
321 format->Format.nChannels, format->Format.nAvgBytesPerSec);
322 TRACE("Position in bytes=%lu\n", position);
324 switch (lpTime->wType) {
326 lpTime->u.sample = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
327 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
330 lpTime->u.ms = 1000.0 * position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels * format->Format.nSamplesPerSec);
331 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
334 lpTime->u.smpte.fps = 30;
335 position = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
336 position += (format->Format.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
337 lpTime->u.smpte.sec = position / format->Format.nSamplesPerSec;
338 position -= lpTime->u.smpte.sec * format->Format.nSamplesPerSec;
339 lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
340 lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
341 lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
342 lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
343 lpTime->u.smpte.fps = 30;
344 lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->Format.nSamplesPerSec;
345 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
346 lpTime->u.smpte.hour, lpTime->u.smpte.min,
347 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
350 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
351 lpTime->wType = TIME_BYTES;
354 lpTime->u.cb = position;
355 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
358 return MMSYSERR_NOERROR;
361 static BOOL supportedFormat(LPWAVEFORMATEX wf)
365 if (wf->nSamplesPerSec<DSBFREQUENCY_MIN||wf->nSamplesPerSec>DSBFREQUENCY_MAX)
368 if (wf->wFormatTag == WAVE_FORMAT_PCM) {
369 if (wf->nChannels==1||wf->nChannels==2) {
370 if (wf->wBitsPerSample==8||wf->wBitsPerSample==16)
373 } else if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
374 WAVEFORMATEXTENSIBLE * wfex = (WAVEFORMATEXTENSIBLE *)wf;
376 if (wf->cbSize == 22 &&
377 (IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) ||
378 IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) {
379 if (wf->nChannels>=1 && wf->nChannels<=6) {
380 if (wf->wBitsPerSample==wfex->Samples.wValidBitsPerSample) {
381 if (wf->wBitsPerSample==8||wf->wBitsPerSample==16||
382 wf->wBitsPerSample==24||wf->wBitsPerSample==32) {
386 WARN("wBitsPerSample != wValidBitsPerSample not supported yet\n");
389 WARN("only KSDATAFORMAT_SUBTYPE_PCM and KSDATAFORMAT_SUBTYPE_IEEE_FLOAT "
391 } else if (wf->wFormatTag == WAVE_FORMAT_MULAW || wf->wFormatTag == WAVE_FORMAT_ALAW) {
392 if (wf->wBitsPerSample==8)
395 ERR("WAVE_FORMAT_MULAW and WAVE_FORMAT_ALAW wBitsPerSample must = 8\n");
397 } else if (wf->wFormatTag == WAVE_FORMAT_ADPCM) {
398 if (wf->wBitsPerSample==4)
401 ERR("WAVE_FORMAT_ADPCM wBitsPerSample must = 4\n");
403 WARN("only WAVE_FORMAT_PCM and WAVE_FORMAT_EXTENSIBLE supported\n");
408 static void copy_format(LPWAVEFORMATEX wf1, LPWAVEFORMATPCMEX wf2)
410 ZeroMemory(wf2, sizeof(wf2));
411 if (wf1->wFormatTag == WAVE_FORMAT_PCM)
412 memcpy(wf2, wf1, sizeof(PCMWAVEFORMAT));
413 else if (wf1->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
414 memcpy(wf2, wf1, sizeof(WAVEFORMATPCMEX));
416 memcpy(wf2, wf1, sizeof(WAVEFORMATEX) + wf1->cbSize);
419 /*======================================================================*
420 * Low level WAVE implementation *
421 *======================================================================*/
423 /**************************************************************************
424 * ALSA_InitializeVolumeCtl [internal]
426 * used to initialize the PCM Volume Control
428 static int ALSA_InitializeVolumeCtl(WINE_WAVEOUT * wwo)
430 snd_ctl_t * ctl = NULL;
431 snd_ctl_card_info_t * cardinfo;
432 snd_ctl_elem_list_t * elemlist;
433 snd_ctl_elem_id_t * e_id;
434 snd_ctl_elem_info_t * einfo;
435 snd_hctl_t * hctl = NULL;
436 snd_hctl_elem_t * elem;
441 snd_ctl_card_info_alloca(&cardinfo);
442 memset(cardinfo,0,snd_ctl_card_info_sizeof());
444 snd_ctl_elem_list_alloca(&elemlist);
445 memset(elemlist,0,snd_ctl_elem_list_sizeof());
447 snd_ctl_elem_id_alloca(&e_id);
448 memset(e_id,0,snd_ctl_elem_id_sizeof());
450 snd_ctl_elem_info_alloca(&einfo);
451 memset(einfo,0,snd_ctl_elem_info_sizeof());
453 #define EXIT_ON_ERROR(f,txt) do \
456 if ( (err = (f) ) < 0) \
458 ERR(txt ": %s\n", snd_strerror(err)); \
460 snd_hctl_close(hctl); \
462 snd_ctl_close(ctl); \
467 sprintf(device, "hw:%ld", ALSA_WodNumDevs);
469 EXIT_ON_ERROR( snd_ctl_open(&ctl, device, 0) , "ctl open failed" );
470 EXIT_ON_ERROR( snd_ctl_card_info(ctl, cardinfo), "card info failed");
471 EXIT_ON_ERROR( snd_ctl_elem_list(ctl, elemlist), "elem list failed");
473 nCtrls = snd_ctl_elem_list_get_count(elemlist);
475 EXIT_ON_ERROR( snd_hctl_open(&hctl, device, 0), "hctl open failed");
476 EXIT_ON_ERROR( snd_hctl_load(hctl), "hctl load failed" );
478 elem=snd_hctl_first_elem(hctl);
479 for ( i= 0; i<nCtrls; i++) {
481 memset(e_id,0,snd_ctl_elem_id_sizeof());
483 snd_hctl_elem_get_id(elem,e_id);
485 TRACE("ctl: #%d '%s'%d\n",
486 snd_ctl_elem_id_get_numid(e_id),
487 snd_ctl_elem_id_get_name(e_id),
488 snd_ctl_elem_id_get_index(e_id));
490 if ( !strcmp("PCM Playback Volume", snd_ctl_elem_id_get_name(e_id)) )
492 EXIT_ON_ERROR( snd_hctl_elem_info(elem,einfo), "hctl elem info failed" );
494 /* few sanity checks... you'll never know... */
495 if ( snd_ctl_elem_info_get_type(einfo) != SND_CTL_ELEM_TYPE_INTEGER )
496 WARN("playback volume control is not an integer\n");
497 if ( !snd_ctl_elem_info_is_readable(einfo) )
498 WARN("playback volume control is readable\n");
499 if ( !snd_ctl_elem_info_is_writable(einfo) )
500 WARN("playback volume control is readable\n");
502 TRACE(" ctrl range: min=%ld max=%ld step=%ld\n",
503 snd_ctl_elem_info_get_min(einfo),
504 snd_ctl_elem_info_get_max(einfo),
505 snd_ctl_elem_info_get_step(einfo));
507 EXIT_ON_ERROR( snd_ctl_elem_id_malloc(&wwo->playback_eid), "elem id malloc failed" );
508 EXIT_ON_ERROR( snd_ctl_elem_info_malloc(&wwo->playback_einfo), "elem info malloc failed" );
509 EXIT_ON_ERROR( snd_ctl_elem_value_malloc(&wwo->playback_evalue), "elem value malloc failed" );
511 /* ok, now we can safely save these objects for later */
512 snd_ctl_elem_id_copy(wwo->playback_eid, e_id);
513 snd_ctl_elem_info_copy(wwo->playback_einfo, einfo);
514 snd_ctl_elem_value_set_id(wwo->playback_evalue, wwo->playback_eid);
518 elem=snd_hctl_elem_next(elem);
520 snd_hctl_close(hctl);
525 /**************************************************************************
526 * ALSA_XRUNRecovery [internal]
528 * used to recovery from XRUN errors (buffer underflow/overflow)
530 static int ALSA_XRUNRecovery(WINE_WAVEOUT * wwo, int err)
532 if (err == -EPIPE) { /* under-run */
533 err = snd_pcm_prepare(wwo->handle);
535 ERR( "underrun recovery failed. prepare failed: %s\n", snd_strerror(err));
537 } else if (err == -ESTRPIPE) {
538 while ((err = snd_pcm_resume(wwo->handle)) == -EAGAIN)
539 sleep(1); /* wait until the suspend flag is released */
541 err = snd_pcm_prepare(wwo->handle);
543 ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err));
550 /**************************************************************************
551 * ALSA_TraceParameters [internal]
553 * used to trace format changes, hw and sw parameters
555 static void ALSA_TraceParameters(snd_pcm_hw_params_t * hw_params, snd_pcm_sw_params_t * sw, int full)
558 snd_pcm_format_t format;
559 snd_pcm_access_t access;
561 err = snd_pcm_hw_params_get_access(hw_params, &access);
562 err = snd_pcm_hw_params_get_format(hw_params, &format);
564 #define X(x) ((x)? "true" : "false")
566 TRACE("FLAGS: sampleres=%s overrng=%s pause=%s resume=%s syncstart=%s batch=%s block=%s double=%s "
567 "halfd=%s joint=%s \n",
568 X(snd_pcm_hw_params_can_mmap_sample_resolution(hw_params)),
569 X(snd_pcm_hw_params_can_overrange(hw_params)),
570 X(snd_pcm_hw_params_can_pause(hw_params)),
571 X(snd_pcm_hw_params_can_resume(hw_params)),
572 X(snd_pcm_hw_params_can_sync_start(hw_params)),
573 X(snd_pcm_hw_params_is_batch(hw_params)),
574 X(snd_pcm_hw_params_is_block_transfer(hw_params)),
575 X(snd_pcm_hw_params_is_double(hw_params)),
576 X(snd_pcm_hw_params_is_half_duplex(hw_params)),
577 X(snd_pcm_hw_params_is_joint_duplex(hw_params)));
581 TRACE("access=%s\n", snd_pcm_access_name(access));
584 snd_pcm_access_mask_t * acmask;
585 snd_pcm_access_mask_alloca(&acmask);
586 snd_pcm_hw_params_get_access_mask(hw_params, acmask);
587 for ( access = SND_PCM_ACCESS_MMAP_INTERLEAVED; access <= SND_PCM_ACCESS_LAST; access++)
588 if (snd_pcm_access_mask_test(acmask, access))
589 TRACE("access=%s\n", snd_pcm_access_name(access));
594 TRACE("format=%s\n", snd_pcm_format_name(format));
599 snd_pcm_format_mask_t * fmask;
601 snd_pcm_format_mask_alloca(&fmask);
602 snd_pcm_hw_params_get_format_mask(hw_params, fmask);
603 for ( format = SND_PCM_FORMAT_S8; format <= SND_PCM_FORMAT_LAST ; format++)
604 if ( snd_pcm_format_mask_test(fmask, format) )
605 TRACE("format=%s\n", snd_pcm_format_name(format));
611 err = snd_pcm_hw_params_get_channels(hw_params, &val);
613 unsigned int min = 0;
614 unsigned int max = 0;
615 err = snd_pcm_hw_params_get_channels_min(hw_params, &min),
616 err = snd_pcm_hw_params_get_channels_max(hw_params, &max);
617 TRACE("channels_min=%u, channels_min_max=%u\n", min, max);
619 TRACE("channels_min=%d\n", val);
624 snd_pcm_uframes_t val=0;
625 err = snd_pcm_hw_params_get_buffer_size(hw_params, &val);
627 snd_pcm_uframes_t min = 0;
628 snd_pcm_uframes_t max = 0;
629 err = snd_pcm_hw_params_get_buffer_size_min(hw_params, &min),
630 err = snd_pcm_hw_params_get_buffer_size_max(hw_params, &max);
631 TRACE("buffer_size_min=%lu, buffer_size_min_max=%lu\n", min, max);
633 TRACE("buffer_size_min=%lu\n", val);
640 unsigned int val=0; \
641 err = snd_pcm_hw_params_get_##x(hw_params,&val, &dir); \
643 unsigned int min = 0; \
644 unsigned int max = 0; \
645 err = snd_pcm_hw_params_get_##x##_min(hw_params, &min, &dir); \
646 err = snd_pcm_hw_params_get_##x##_max(hw_params, &max, &dir); \
647 TRACE(#x "_min=%u " #x "_max=%u\n", min, max); \
649 TRACE(#x "=%d\n", val); \
658 snd_pcm_uframes_t val=0;
659 err = snd_pcm_hw_params_get_period_size(hw_params, &val, &dir);
661 snd_pcm_uframes_t min = 0;
662 snd_pcm_uframes_t max = 0;
663 err = snd_pcm_hw_params_get_period_size_min(hw_params, &min, &dir),
664 err = snd_pcm_hw_params_get_period_size_max(hw_params, &max, &dir);
665 TRACE("period_size_min=%lu, period_size_min_max=%lu\n", min, max);
667 TRACE("period_size_min=%lu\n", val);
679 /* return a string duplicated on the win32 process heap, free with HeapFree */
680 static char* ALSA_strdup(char *s) {
681 char *result = HeapAlloc(GetProcessHeap(), 0, strlen(s)+1);
686 /******************************************************************
687 * ALSA_GetDeviceFromReg
689 * Returns either "plug:hw" or reads the registry so the user can
690 * override the playback/record device used.
692 static char *ALSA_GetDeviceFromReg(const char *value)
700 res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\ALSA", 0, KEY_QUERY_VALUE, &key);
701 if (res != ERROR_SUCCESS) goto end;
703 res = RegQueryValueExA(key, value, NULL, &type, NULL, &resultSize);
704 if (res != ERROR_SUCCESS) goto end;
706 if (type != REG_SZ) {
707 ERR("Registry key [HKEY_LOCAL_MACHINE\\Software\\Wine\\Wine\\ALSA\\%s] must be a string\n", value);
711 result = HeapAlloc(GetProcessHeap(), 0, resultSize);
712 res = RegQueryValueExA(key, value, NULL, NULL, result, &resultSize);
716 result = ALSA_strdup("plug:hw");
724 /******************************************************************
727 * Initialize internal structures from ALSA information
729 LONG ALSA_WaveInit(void)
732 snd_pcm_info_t * info;
733 snd_pcm_hw_params_t * hw_params;
734 unsigned int ratemin=0;
735 unsigned int ratemax=0;
736 unsigned int chmin=0;
737 unsigned int chmax=0;
744 if (!wine_dlopen("libasound.so.2", RTLD_LAZY|RTLD_GLOBAL, NULL, 0))
746 ERR("Error: ALSA lib needs to be loaded with flags RTLD_LAZY and RTLD_GLOBAL.\n");
752 for (i = 0; i < MAX_WAVEOUTDRV; i++)
757 snd_pcm_format_mask_t * fmask;
758 snd_pcm_access_mask_t * acmask;
760 wwo = &WOutDev[ALSA_WodNumDevs];
762 regdev = ALSA_GetDeviceFromReg("PlaybackDevice");
763 sprintf(device, "%s:%d", regdev, i);
764 HeapFree(GetProcessHeap(), 0, regdev);
765 wwo->device = HeapAlloc(GetProcessHeap(), 0, strlen(device));
766 strcpy(wwo->device, device);
767 TRACE("using waveout device \"%s\"\n", wwo->device);
769 snprintf(wwo->interface_name, sizeof(wwo->interface_name), "winealsa: %s", wwo->device);
771 wwo->caps.wMid = 0x0002;
772 wwo->caps.wPid = 0x0104;
773 wwo->caps.vDriverVersion = 0x0100;
774 wwo->caps.dwFormats = 0x00000000;
775 wwo->caps.dwSupport = 0;
776 strcpy(wwo->ds_desc.szDrvname, "winealsa.drv");
778 snd_pcm_info_alloca(&info);
779 snd_pcm_hw_params_alloca(&hw_params);
781 #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)
784 snd_pcm_open(&h, wwo->device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
788 EXIT_ON_ERROR( snd_pcm_info(h, info) , "pcm info" );
790 TRACE("dev=%d id=%s name=%s subdev=%d subdev_name=%s subdev_avail=%d subdev_num=%d stream=%s subclass=%s \n",
791 snd_pcm_info_get_device(info),
792 snd_pcm_info_get_id(info),
793 snd_pcm_info_get_name(info),
794 snd_pcm_info_get_subdevice(info),
795 snd_pcm_info_get_subdevice_name(info),
796 snd_pcm_info_get_subdevices_avail(info),
797 snd_pcm_info_get_subdevices_count(info),
798 snd_pcm_stream_name(snd_pcm_info_get_stream(info)),
799 (snd_pcm_info_get_subclass(info) == SND_PCM_SUBCLASS_GENERIC_MIX ? "GENERIC MIX": "MULTI MIX"));
801 strcpy(wwo->ds_desc.szDesc, snd_pcm_info_get_name(info));
802 MultiByteToWideChar(CP_ACP, 0, wwo->ds_desc.szDesc, -1, nameW, sizeof(nameW)/sizeof(WCHAR));
803 strcpyW(wwo->caps.szPname, nameW);
804 EXIT_ON_ERROR( snd_pcm_hw_params_any(h, hw_params) , "pcm hw params" );
807 err = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir);
808 err = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir);
809 err = snd_pcm_hw_params_get_channels_min(hw_params, &chmin);
810 err = snd_pcm_hw_params_get_channels_max(hw_params, &chmax);
812 ALSA_TraceParameters(hw_params, NULL, TRUE);
814 snd_pcm_format_mask_alloca(&fmask);
815 snd_pcm_hw_params_get_format_mask(hw_params, fmask);
818 if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \
820 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \
822 if (chmin <= 1 && 1 <= chmax) \
823 wwo->caps.dwFormats |= WAVE_FORMAT_##v##M08; \
824 if (chmin <= 2 && 2 <= chmax) \
825 wwo->caps.dwFormats |= WAVE_FORMAT_##v##S08; \
827 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \
829 if (chmin <= 1 && 1 <= chmax) \
830 wwo->caps.dwFormats |= WAVE_FORMAT_##v##M16; \
831 if (chmin <= 2 && 2 <= chmax) \
832 wwo->caps.dwFormats |= WAVE_FORMAT_##v##S16; \
844 wwo->caps.wChannels = chmax;
846 /* FIXME: always true ? */
847 wwo->caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
849 snd_pcm_access_mask_alloca(&acmask);
850 snd_pcm_hw_params_get_access_mask(hw_params, acmask);
852 /* FIXME: NONITERLEAVED and COMPLEX are not supported right now */
853 if ( snd_pcm_access_mask_test( acmask, SND_PCM_ACCESS_MMAP_INTERLEAVED ) )
854 wwo->caps.dwSupport |= WAVECAPS_DIRECTSOUND;
856 TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n",
857 wwo->caps.dwFormats, wwo->caps.dwSupport);
861 ALSA_InitializeVolumeCtl(wwo);
863 /* check for volume control support */
865 wwo->caps.dwSupport |= WAVECAPS_VOLUME;
867 if (chmin <= 2 && 2 <= chmax)
868 wwo->caps.dwSupport |= WAVECAPS_LRVOLUME;
876 for (i = 0; i < MAX_WAVEINDRV; i++)
881 snd_pcm_format_mask_t * fmask;
882 snd_pcm_access_mask_t * acmask;
884 wwi = &WInDev[ALSA_WidNumDevs];
886 regdev = ALSA_GetDeviceFromReg("CaptureDevice");
887 sprintf(device, "%s:%d", regdev, i);
888 HeapFree(GetProcessHeap(), 0, regdev);
889 wwi->device = HeapAlloc(GetProcessHeap(), 0, strlen(device));
890 strcpy(wwi->device, device);
892 TRACE("using wavein device \"%s\"\n", wwi->device);
894 snprintf(wwi->interface_name, sizeof(wwi->interface_name), "winealsa: %s", wwi->device);
896 wwi->caps.wMid = 0x0002;
897 wwi->caps.wPid = 0x0104;
898 wwi->caps.vDriverVersion = 0x0100;
899 wwi->caps.dwFormats = 0x00000000;
900 strcpy(wwi->ds_desc.szDrvname, "winealsa.drv");
903 snd_pcm_info_alloca(&info);
904 snd_pcm_hw_params_alloca(&hw_params);
906 #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)
909 snd_pcm_open(&h, wwi->device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
913 EXIT_ON_ERROR( snd_pcm_info(h, info) , "pcm info" );
915 TRACE("dev=%d id=%s name=%s subdev=%d subdev_name=%s subdev_avail=%d subdev_num=%d stream=%s subclass=%s \n",
916 snd_pcm_info_get_device(info),
917 snd_pcm_info_get_id(info),
918 snd_pcm_info_get_name(info),
919 snd_pcm_info_get_subdevice(info),
920 snd_pcm_info_get_subdevice_name(info),
921 snd_pcm_info_get_subdevices_avail(info),
922 snd_pcm_info_get_subdevices_count(info),
923 snd_pcm_stream_name(snd_pcm_info_get_stream(info)),
924 (snd_pcm_info_get_subclass(info) == SND_PCM_SUBCLASS_GENERIC_MIX ? "GENERIC MIX": "MULTI MIX"));
926 strcpy(wwi->ds_desc.szDesc, snd_pcm_info_get_name(info));
927 MultiByteToWideChar(CP_ACP, 0, wwi->ds_desc.szDesc, -1, nameW, sizeof(nameW)/sizeof(WCHAR));
928 strcpyW(wwi->caps.szPname, nameW);
929 EXIT_ON_ERROR( snd_pcm_hw_params_any(h, hw_params) , "pcm hw params" );
931 err = snd_pcm_hw_params_get_rate_min(hw_params, &ratemin, &dir);
932 err = snd_pcm_hw_params_get_rate_max(hw_params, &ratemax, &dir);
933 err = snd_pcm_hw_params_get_channels_min(hw_params, &chmin);
934 err = snd_pcm_hw_params_get_channels_max(hw_params, &chmax);
937 ALSA_TraceParameters(hw_params, NULL, TRUE);
939 snd_pcm_format_mask_alloca(&fmask);
940 snd_pcm_hw_params_get_format_mask(hw_params, fmask);
943 if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \
945 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \
947 if (chmin <= 1 && 1 <= chmax) \
948 wwi->caps.dwFormats |= WAVE_FORMAT_##v##M08; \
949 if (chmin <= 2 && 2 <= chmax) \
950 wwi->caps.dwFormats |= WAVE_FORMAT_##v##S08; \
952 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \
954 if (chmin <= 1 && 1 <= chmax) \
955 wwi->caps.dwFormats |= WAVE_FORMAT_##v##M16; \
956 if (chmin <= 2 && 2 <= chmax) \
957 wwi->caps.dwFormats |= WAVE_FORMAT_##v##S16; \
969 wwi->caps.wChannels = chmax;
971 snd_pcm_access_mask_alloca(&acmask);
972 snd_pcm_hw_params_get_access_mask(hw_params, acmask);
974 /* FIXME: NONITERLEAVED and COMPLEX are not supported right now */
975 if ( snd_pcm_access_mask_test( acmask, SND_PCM_ACCESS_MMAP_INTERLEAVED ) ) {
977 wwi->dwSupport |= WAVECAPS_DIRECTSOUND;
981 TRACE("Configured with dwFmts=%08lx\n", wwi->caps.dwFormats);
991 /******************************************************************
992 * ALSA_InitRingMessage
994 * Initialize the ring of messages for passing between driver's caller and playback/record
997 static int ALSA_InitRingMessage(ALSA_MSG_RING* omr)
1000 omr->msg_tosave = 0;
1001 #ifdef USE_PIPE_SYNC
1002 if (pipe(omr->msg_pipe) < 0) {
1003 omr->msg_pipe[0] = -1;
1004 omr->msg_pipe[1] = -1;
1005 ERR("could not create pipe, error=%s\n", strerror(errno));
1008 omr->msg_event = CreateEventW(NULL, FALSE, FALSE, NULL);
1010 omr->ring_buffer_size = ALSA_RING_BUFFER_INCREMENT;
1011 omr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,omr->ring_buffer_size * sizeof(ALSA_MSG));
1013 InitializeCriticalSection(&omr->msg_crst);
1017 /******************************************************************
1018 * ALSA_DestroyRingMessage
1021 static int ALSA_DestroyRingMessage(ALSA_MSG_RING* omr)
1023 #ifdef USE_PIPE_SYNC
1024 close(omr->msg_pipe[0]);
1025 close(omr->msg_pipe[1]);
1027 CloseHandle(omr->msg_event);
1029 HeapFree(GetProcessHeap(),0,omr->messages);
1030 DeleteCriticalSection(&omr->msg_crst);
1034 /******************************************************************
1035 * ALSA_AddRingMessage
1037 * Inserts a new message into the ring (should be called from DriverProc derivated routines)
1039 static int ALSA_AddRingMessage(ALSA_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait)
1041 HANDLE hEvent = INVALID_HANDLE_VALUE;
1043 EnterCriticalSection(&omr->msg_crst);
1044 if ((omr->msg_toget == ((omr->msg_tosave + 1) % omr->ring_buffer_size)))
1046 int old_ring_buffer_size = omr->ring_buffer_size;
1047 omr->ring_buffer_size += ALSA_RING_BUFFER_INCREMENT;
1048 TRACE("omr->ring_buffer_size=%d\n",omr->ring_buffer_size);
1049 omr->messages = HeapReAlloc(GetProcessHeap(),0,omr->messages, omr->ring_buffer_size * sizeof(ALSA_MSG));
1050 /* Now we need to rearrange the ring buffer so that the new
1051 buffers just allocated are in between omr->msg_tosave and
1054 if (omr->msg_tosave < omr->msg_toget)
1056 memmove(&(omr->messages[omr->msg_toget + ALSA_RING_BUFFER_INCREMENT]),
1057 &(omr->messages[omr->msg_toget]),
1058 sizeof(ALSA_MSG)*(old_ring_buffer_size - omr->msg_toget)
1060 omr->msg_toget += ALSA_RING_BUFFER_INCREMENT;
1065 hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1066 if (hEvent == INVALID_HANDLE_VALUE)
1068 ERR("can't create event !?\n");
1069 LeaveCriticalSection(&omr->msg_crst);
1072 if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER)
1073 FIXME("two fast messages in the queue!!!! toget = %d(%s), tosave=%d(%s)\n",
1074 omr->msg_toget,getCmdString(omr->messages[omr->msg_toget].msg),
1075 omr->msg_tosave,getCmdString(omr->messages[omr->msg_tosave].msg));
1077 /* fast messages have to be added at the start of the queue */
1078 omr->msg_toget = (omr->msg_toget + omr->ring_buffer_size - 1) % omr->ring_buffer_size;
1080 omr->messages[omr->msg_toget].msg = msg;
1081 omr->messages[omr->msg_toget].param = param;
1082 omr->messages[omr->msg_toget].hEvent = hEvent;
1086 omr->messages[omr->msg_tosave].msg = msg;
1087 omr->messages[omr->msg_tosave].param = param;
1088 omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
1089 omr->msg_tosave = (omr->msg_tosave + 1) % omr->ring_buffer_size;
1091 LeaveCriticalSection(&omr->msg_crst);
1092 /* signal a new message */
1096 /* wait for playback/record thread to have processed the message */
1097 WaitForSingleObject(hEvent, INFINITE);
1098 CloseHandle(hEvent);
1103 /******************************************************************
1104 * ALSA_RetrieveRingMessage
1106 * Get a message from the ring. Should be called by the playback/record thread.
1108 static int ALSA_RetrieveRingMessage(ALSA_MSG_RING* omr,
1109 enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
1111 EnterCriticalSection(&omr->msg_crst);
1113 if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
1115 LeaveCriticalSection(&omr->msg_crst);
1119 *msg = omr->messages[omr->msg_toget].msg;
1120 omr->messages[omr->msg_toget].msg = 0;
1121 *param = omr->messages[omr->msg_toget].param;
1122 *hEvent = omr->messages[omr->msg_toget].hEvent;
1123 omr->msg_toget = (omr->msg_toget + 1) % omr->ring_buffer_size;
1125 LeaveCriticalSection(&omr->msg_crst);
1129 /******************************************************************
1130 * ALSA_PeekRingMessage
1132 * Peek at a message from the ring but do not remove it.
1133 * Should be called by the playback/record thread.
1135 static int ALSA_PeekRingMessage(ALSA_MSG_RING* omr,
1136 enum win_wm_message *msg,
1137 DWORD *param, HANDLE *hEvent)
1139 EnterCriticalSection(&omr->msg_crst);
1141 if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
1143 LeaveCriticalSection(&omr->msg_crst);
1147 *msg = omr->messages[omr->msg_toget].msg;
1148 *param = omr->messages[omr->msg_toget].param;
1149 *hEvent = omr->messages[omr->msg_toget].hEvent;
1150 LeaveCriticalSection(&omr->msg_crst);
1154 /*======================================================================*
1155 * Low level WAVE OUT implementation *
1156 *======================================================================*/
1158 /**************************************************************************
1159 * wodNotifyClient [internal]
1161 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1163 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
1169 if (wwo->wFlags != DCB_NULL &&
1170 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave,
1171 wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
1172 WARN("can't notify client !\n");
1173 return MMSYSERR_ERROR;
1177 FIXME("Unknown callback message %u\n", wMsg);
1178 return MMSYSERR_INVALPARAM;
1180 return MMSYSERR_NOERROR;
1183 /**************************************************************************
1184 * wodUpdatePlayedTotal [internal]
1187 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo, snd_pcm_status_t* ps)
1189 snd_pcm_sframes_t delay = 0;
1190 snd_pcm_delay(wwo->handle, &delay);
1191 if (snd_pcm_state(wwo->handle) != SND_PCM_STATE_RUNNING)
1193 wwo->dwPlayedTotal = wwo->dwWrittenTotal - snd_pcm_frames_to_bytes(wwo->handle, delay);
1197 /**************************************************************************
1198 * wodPlayer_BeginWaveHdr [internal]
1200 * Makes the specified lpWaveHdr the currently playing wave header.
1201 * If the specified wave header is a begin loop and we're not already in
1202 * a loop, setup the loop.
1204 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1206 wwo->lpPlayPtr = lpWaveHdr;
1208 if (!lpWaveHdr) return;
1210 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
1211 if (wwo->lpLoopPtr) {
1212 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
1214 TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
1215 wwo->lpLoopPtr = lpWaveHdr;
1216 /* Windows does not touch WAVEHDR.dwLoops,
1217 * so we need to make an internal copy */
1218 wwo->dwLoops = lpWaveHdr->dwLoops;
1221 wwo->dwPartialOffset = 0;
1224 /**************************************************************************
1225 * wodPlayer_PlayPtrNext [internal]
1227 * Advance the play pointer to the next waveheader, looping if required.
1229 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
1231 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
1233 wwo->dwPartialOffset = 0;
1234 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
1235 /* We're at the end of a loop, loop if required */
1236 if (--wwo->dwLoops > 0) {
1237 wwo->lpPlayPtr = wwo->lpLoopPtr;
1239 /* Handle overlapping loops correctly */
1240 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
1241 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
1242 /* shall we consider the END flag for the closing loop or for
1243 * the opening one or for both ???
1244 * code assumes for closing loop only
1247 lpWaveHdr = lpWaveHdr->lpNext;
1249 wwo->lpLoopPtr = NULL;
1250 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
1253 /* We're not in a loop. Advance to the next wave header */
1254 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
1260 /**************************************************************************
1261 * wodPlayer_DSPWait [internal]
1262 * Returns the number of milliseconds to wait for the DSP buffer to play a
1265 static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo)
1267 /* time for one period to be played */
1271 err = snd_pcm_hw_params_get_period_time(wwo->hw_params, &val, &dir);
1275 /**************************************************************************
1276 * wodPlayer_NotifyWait [internal]
1277 * Returns the number of milliseconds to wait before attempting to notify
1278 * completion of the specified wavehdr.
1279 * This is based on the number of bytes remaining to be written in the
1282 static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
1286 if (lpWaveHdr->reserved < wwo->dwPlayedTotal) {
1289 dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.Format.nAvgBytesPerSec;
1290 if (!dwMillis) dwMillis = 1;
1297 /**************************************************************************
1298 * wodPlayer_WriteMaxFrags [internal]
1299 * Writes the maximum number of frames possible to the DSP and returns
1300 * the number of frames written.
1302 static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* frames)
1304 /* Only attempt to write to free frames */
1305 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
1306 DWORD dwLength = snd_pcm_bytes_to_frames(wwo->handle, lpWaveHdr->dwBufferLength - wwo->dwPartialOffset);
1307 int toWrite = min(dwLength, *frames);
1310 TRACE("Writing wavehdr %p.%lu[%lu]\n", lpWaveHdr, wwo->dwPartialOffset, lpWaveHdr->dwBufferLength);
1313 written = (wwo->write)(wwo->handle, lpWaveHdr->lpData + wwo->dwPartialOffset, toWrite);
1315 /* XRUN occurred. let's try to recover */
1316 ALSA_XRUNRecovery(wwo, written);
1317 written = (wwo->write)(wwo->handle, lpWaveHdr->lpData + wwo->dwPartialOffset, toWrite);
1320 /* still in error */
1321 ERR("Error in writing wavehdr. Reason: %s\n", snd_strerror(written));
1327 wwo->dwPartialOffset += snd_pcm_frames_to_bytes(wwo->handle, written);
1328 if ( wwo->dwPartialOffset >= lpWaveHdr->dwBufferLength) {
1329 /* this will be used to check if the given wave header has been fully played or not... */
1330 wwo->dwPartialOffset = lpWaveHdr->dwBufferLength;
1331 /* If we wrote all current wavehdr, skip to the next one */
1332 wodPlayer_PlayPtrNext(wwo);
1335 wwo->dwWrittenTotal += snd_pcm_frames_to_bytes(wwo->handle, written);
1336 TRACE("dwWrittenTotal=%lu\n", wwo->dwWrittenTotal);
1342 /**************************************************************************
1343 * wodPlayer_NotifyCompletions [internal]
1345 * Notifies and remove from queue all wavehdrs which have been played to
1346 * the speaker (ie. they have cleared the ALSA buffer). If force is true,
1347 * we notify all wavehdrs and remove them all from the queue even if they
1348 * are unplayed or part of a loop.
1350 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
1352 LPWAVEHDR lpWaveHdr;
1354 /* Start from lpQueuePtr and keep notifying until:
1355 * - we hit an unwritten wavehdr
1356 * - we hit the beginning of a running loop
1357 * - we hit a wavehdr which hasn't finished playing
1360 while ((lpWaveHdr = wwo->lpQueuePtr) &&
1362 (lpWaveHdr != wwo->lpPlayPtr &&
1363 lpWaveHdr != wwo->lpLoopPtr &&
1364 lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
1366 wwo->lpQueuePtr = lpWaveHdr->lpNext;
1368 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1369 lpWaveHdr->dwFlags |= WHDR_DONE;
1371 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1376 lpWaveHdr = wwo->lpQueuePtr;
1377 if (!lpWaveHdr) {TRACE("Empty queue\n"); break;}
1380 if (lpWaveHdr == wwo->lpPlayPtr) {TRACE("play %p\n", lpWaveHdr); break;}
1381 if (lpWaveHdr == wwo->lpLoopPtr) {TRACE("loop %p\n", lpWaveHdr); break;}
1382 if (lpWaveHdr->reserved > wwo->dwPlayedTotal){TRACE("still playing %p (%lu/%lu)\n", lpWaveHdr, lpWaveHdr->reserved, wwo->dwPlayedTotal);break;}
1384 wwo->lpQueuePtr = lpWaveHdr->lpNext;
1386 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1387 lpWaveHdr->dwFlags |= WHDR_DONE;
1389 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
1392 return (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
1393 wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
1397 static void wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count)
1399 unsigned short revents;
1401 if (snd_pcm_state(handle) != SND_PCM_STATE_RUNNING)
1405 poll(ufds, count, -1);
1406 snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);
1408 if (revents & POLLERR)
1411 /*if (revents & POLLOUT)
1417 /**************************************************************************
1418 * wodPlayer_Reset [internal]
1420 * wodPlayer helper. Resets current output stream.
1422 static void wodPlayer_Reset(WINE_WAVEOUT* wwo)
1424 enum win_wm_message msg;
1429 /* flush all possible output */
1430 wait_for_poll(wwo->handle, wwo->ufds, wwo->count);
1432 wodUpdatePlayedTotal(wwo, NULL);
1433 /* updates current notify list */
1434 wodPlayer_NotifyCompletions(wwo, FALSE);
1436 if ( (err = snd_pcm_drop(wwo->handle)) < 0) {
1437 FIXME("flush: %s\n", snd_strerror(err));
1439 wwo->state = WINE_WS_STOPPED;
1442 if ( (err = snd_pcm_prepare(wwo->handle)) < 0 )
1443 ERR("pcm prepare failed: %s\n", snd_strerror(err));
1445 /* remove any buffer */
1446 wodPlayer_NotifyCompletions(wwo, TRUE);
1448 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
1449 wwo->state = WINE_WS_STOPPED;
1450 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1451 /* Clear partial wavehdr */
1452 wwo->dwPartialOffset = 0;
1454 /* remove any existing message in the ring */
1455 EnterCriticalSection(&wwo->msgRing.msg_crst);
1456 /* return all pending headers in queue */
1457 while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev))
1459 if (msg != WINE_WM_HEADER)
1461 FIXME("shouldn't have headers left\n");
1465 ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
1466 ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
1468 wodNotifyClient(wwo, WOM_DONE, param, 0);
1470 RESET_OMR(&wwo->msgRing);
1471 LeaveCriticalSection(&wwo->msgRing.msg_crst);
1474 /**************************************************************************
1475 * wodPlayer_ProcessMessages [internal]
1477 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
1479 LPWAVEHDR lpWaveHdr;
1480 enum win_wm_message msg;
1485 while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) {
1486 TRACE("Received %s %lx\n", getCmdString(msg), param);
1489 case WINE_WM_PAUSING:
1490 if ( snd_pcm_state(wwo->handle) == SND_PCM_STATE_RUNNING )
1492 err = snd_pcm_pause(wwo->handle, 1);
1494 ERR("pcm_pause failed: %s\n", snd_strerror(err));
1496 wwo->state = WINE_WS_PAUSED;
1499 case WINE_WM_RESTARTING:
1500 if (wwo->state == WINE_WS_PAUSED)
1502 if ( snd_pcm_state(wwo->handle) == SND_PCM_STATE_PAUSED )
1504 err = snd_pcm_pause(wwo->handle, 0);
1506 ERR("pcm_pause failed: %s\n", snd_strerror(err));
1508 wwo->state = WINE_WS_PLAYING;
1512 case WINE_WM_HEADER:
1513 lpWaveHdr = (LPWAVEHDR)param;
1515 /* insert buffer at the end of queue */
1518 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
1521 if (!wwo->lpPlayPtr)
1522 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
1523 if (wwo->state == WINE_WS_STOPPED)
1524 wwo->state = WINE_WS_PLAYING;
1526 case WINE_WM_RESETTING:
1527 wodPlayer_Reset(wwo);
1530 case WINE_WM_UPDATE:
1531 wodUpdatePlayedTotal(wwo, NULL);
1534 case WINE_WM_BREAKLOOP:
1535 if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
1536 /* ensure exit at end of current loop */
1541 case WINE_WM_CLOSING:
1542 /* sanity check: this should not happen since the device must have been reset before */
1543 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
1545 wwo->state = WINE_WS_CLOSED;
1548 /* shouldn't go here */
1550 FIXME("unknown message %d\n", msg);
1556 /**************************************************************************
1557 * wodPlayer_FeedDSP [internal]
1558 * Feed as much sound data as we can into the DSP and return the number of
1559 * milliseconds before it will be necessary to feed the DSP again.
1561 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
1565 wodUpdatePlayedTotal(wwo, NULL);
1566 availInQ = snd_pcm_avail_update(wwo->handle);
1569 /* input queue empty and output buffer with less than one fragment to play */
1570 if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + wwo->dwFragmentSize) {
1571 TRACE("Run out of wavehdr:s...\n");
1576 /* no more room... no need to try to feed */
1578 /* Feed from partial wavehdr */
1579 if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) {
1580 wodPlayer_WriteMaxFrags(wwo, &availInQ);
1583 /* Feed wavehdrs until we run out of wavehdrs or DSP space */
1584 if (wwo->dwPartialOffset == 0 && wwo->lpPlayPtr) {
1586 TRACE("Setting time to elapse for %p to %lu\n",
1587 wwo->lpPlayPtr, wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength);
1588 /* note the value that dwPlayedTotal will return when this wave finishes playing */
1589 wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
1590 } while (wodPlayer_WriteMaxFrags(wwo, &availInQ) && wwo->lpPlayPtr && availInQ > 0);
1594 return wodPlayer_DSPWait(wwo);
1597 /**************************************************************************
1598 * wodPlayer [internal]
1600 static DWORD CALLBACK wodPlayer(LPVOID pmt)
1602 WORD uDevID = (DWORD)pmt;
1603 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
1604 DWORD dwNextFeedTime = INFINITE; /* Time before DSP needs feeding */
1605 DWORD dwNextNotifyTime = INFINITE; /* Time before next wave completion */
1608 wwo->state = WINE_WS_STOPPED;
1609 SetEvent(wwo->hStartUpEvent);
1612 /** Wait for the shortest time before an action is required. If there
1613 * are no pending actions, wait forever for a command.
1615 dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
1616 TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
1617 WAIT_OMR(&wwo->msgRing, dwSleepTime);
1618 wodPlayer_ProcessMessages(wwo);
1619 if (wwo->state == WINE_WS_PLAYING) {
1620 dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1621 dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
1622 if (dwNextFeedTime == INFINITE) {
1623 /* FeedDSP ran out of data, but before giving up, */
1624 /* check that a notification didn't give us more */
1625 wodPlayer_ProcessMessages(wwo);
1626 if (wwo->lpPlayPtr) {
1627 TRACE("recovering\n");
1628 dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1632 dwNextFeedTime = dwNextNotifyTime = INFINITE;
1637 /**************************************************************************
1638 * wodGetDevCaps [internal]
1640 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
1642 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1644 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1646 if (wDevID >= MAX_WAVEOUTDRV) {
1647 TRACE("MAX_WAVOUTDRV reached !\n");
1648 return MMSYSERR_BADDEVICEID;
1651 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1652 return MMSYSERR_NOERROR;
1655 /**************************************************************************
1656 * wodOpen [internal]
1658 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1661 snd_pcm_hw_params_t * hw_params;
1662 snd_pcm_sw_params_t * sw_params;
1663 snd_pcm_access_t access;
1664 snd_pcm_format_t format = -1;
1666 unsigned int buffer_time = 500000;
1667 unsigned int period_time = 10000;
1668 snd_pcm_uframes_t buffer_size;
1669 snd_pcm_uframes_t period_size;
1675 snd_pcm_hw_params_alloca(&hw_params);
1676 snd_pcm_sw_params_alloca(&sw_params);
1678 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1679 if (lpDesc == NULL) {
1680 WARN("Invalid Parameter !\n");
1681 return MMSYSERR_INVALPARAM;
1683 if (wDevID >= MAX_WAVEOUTDRV) {
1684 TRACE("MAX_WAVOUTDRV reached !\n");
1685 return MMSYSERR_BADDEVICEID;
1688 /* only PCM format is supported so far... */
1689 if (!supportedFormat(lpDesc->lpFormat)) {
1690 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1691 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1692 lpDesc->lpFormat->nSamplesPerSec);
1693 return WAVERR_BADFORMAT;
1696 if (dwFlags & WAVE_FORMAT_QUERY) {
1697 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1698 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1699 lpDesc->lpFormat->nSamplesPerSec);
1700 return MMSYSERR_NOERROR;
1703 wwo = &WOutDev[wDevID];
1705 if ((dwFlags & WAVE_DIRECTSOUND) && !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
1706 /* not supported, ignore it */
1707 dwFlags &= ~WAVE_DIRECTSOUND;
1710 flags = SND_PCM_NONBLOCK;
1712 if ( dwFlags & WAVE_DIRECTSOUND )
1713 flags |= SND_PCM_ASYNC;
1716 if ( (err = snd_pcm_open(&pcm, wwo->device, SND_PCM_STREAM_PLAYBACK, flags)) < 0)
1718 ERR("Error open: %s\n", snd_strerror(err));
1719 return MMSYSERR_NOTENABLED;
1722 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1724 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1725 copy_format(lpDesc->lpFormat, &wwo->format);
1727 if (wwo->format.Format.wBitsPerSample == 0) {
1728 WARN("Resetting zeroed wBitsPerSample\n");
1729 wwo->format.Format.wBitsPerSample = 8 *
1730 (wwo->format.Format.nAvgBytesPerSec /
1731 wwo->format.Format.nSamplesPerSec) /
1732 wwo->format.Format.nChannels;
1735 snd_pcm_hw_params_any(pcm, hw_params);
1737 #define EXIT_ON_ERROR(f,e,txt) do \
1740 if ( (err = (f) ) < 0) \
1742 ERR(txt ": %s\n", snd_strerror(err)); \
1743 snd_pcm_close(pcm); \
1748 access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
1749 if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {
1750 WARN("mmap not available. switching to standard write.\n");
1751 access = SND_PCM_ACCESS_RW_INTERLEAVED;
1752 EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");
1753 wwo->write = snd_pcm_writei;
1756 wwo->write = snd_pcm_mmap_writei;
1758 EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwo->format.Format.nChannels), MMSYSERR_INVALPARAM, "unable to set required channels");
1760 if ((wwo->format.Format.wFormatTag == WAVE_FORMAT_PCM) ||
1761 ((wwo->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
1762 IsEqualGUID(&wwo->format.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) {
1763 format = (wwo->format.Format.wBitsPerSample == 8) ? SND_PCM_FORMAT_U8 :
1764 (wwo->format.Format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE :
1765 (wwo->format.Format.wBitsPerSample == 24) ? SND_PCM_FORMAT_S24_LE :
1766 (wwo->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_S32_LE : -1;
1767 } else if ((wwo->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
1768 IsEqualGUID(&wwo->format.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)){
1769 format = (wwo->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_FLOAT_LE : -1;
1770 } else if (wwo->format.Format.wFormatTag == WAVE_FORMAT_MULAW) {
1771 FIXME("unimplemented format: WAVE_FORMAT_MULAW\n");
1773 return WAVERR_BADFORMAT;
1774 } else if (wwo->format.Format.wFormatTag == WAVE_FORMAT_ALAW) {
1775 FIXME("unimplemented format: WAVE_FORMAT_ALAW\n");
1777 return WAVERR_BADFORMAT;
1778 } else if (wwo->format.Format.wFormatTag == WAVE_FORMAT_ADPCM) {
1779 FIXME("unimplemented format: WAVE_FORMAT_ADPCM\n");
1781 return WAVERR_BADFORMAT;
1783 ERR("invalid format: %0x04x\n", wwo->format.Format.wFormatTag);
1785 return WAVERR_BADFORMAT;
1788 EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), MMSYSERR_INVALPARAM, "unable to set required format");
1790 rate = wwo->format.Format.nSamplesPerSec;
1792 err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir);
1794 ERR("Rate %ld Hz not available for playback: %s\n", wwo->format.Format.nSamplesPerSec, snd_strerror(rate));
1796 return WAVERR_BADFORMAT;
1798 if (rate != wwo->format.Format.nSamplesPerSec) {
1799 ERR("Rate doesn't match (requested %ld Hz, got %d Hz)\n", wwo->format.Format.nSamplesPerSec, rate);
1801 return WAVERR_BADFORMAT;
1804 EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time");
1806 EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time");
1808 EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");
1810 err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
1811 err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
1813 snd_pcm_sw_params_current(pcm, sw_params);
1814 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");
1815 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");
1816 EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min");
1817 EXIT_ON_ERROR( snd_pcm_sw_params_set_xfer_align(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set xfer align");
1818 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold");
1819 EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback");
1820 #undef EXIT_ON_ERROR
1822 snd_pcm_prepare(pcm);
1825 ALSA_TraceParameters(hw_params, sw_params, FALSE);
1827 /* now, we can save all required data for later use... */
1828 if ( wwo->hw_params )
1829 snd_pcm_hw_params_free(wwo->hw_params);
1830 snd_pcm_hw_params_malloc(&(wwo->hw_params));
1831 snd_pcm_hw_params_copy(wwo->hw_params, hw_params);
1833 wwo->dwBufferSize = buffer_size;
1834 wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL;
1836 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
1837 wwo->dwPartialOffset = 0;
1839 ALSA_InitRingMessage(&wwo->msgRing);
1841 wwo->count = snd_pcm_poll_descriptors_count (wwo->handle);
1842 if (wwo->count <= 0) {
1843 ERR("Invalid poll descriptors count\n");
1844 return MMSYSERR_ERROR;
1847 wwo->ufds = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, sizeof(struct pollfd) * wwo->count);
1848 if (wwo->ufds == NULL) {
1849 ERR("No enough memory\n");
1850 return MMSYSERR_NOMEM;
1852 if ((err = snd_pcm_poll_descriptors(wwo->handle, wwo->ufds, wwo->count)) < 0) {
1853 ERR("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
1854 return MMSYSERR_ERROR;
1857 if (!(dwFlags & WAVE_DIRECTSOUND)) {
1858 wwo->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1859 wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
1860 WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
1861 CloseHandle(wwo->hStartUpEvent);
1863 wwo->hThread = INVALID_HANDLE_VALUE;
1864 wwo->dwThreadID = 0;
1866 wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
1868 TRACE("handle=%08lx \n", (DWORD)wwo->handle);
1869 /* if (wwo->dwFragmentSize % wwo->format.Format.nBlockAlign)
1870 ERR("Fragment doesn't contain an integral number of data blocks\n");
1872 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1873 wwo->format.Format.wBitsPerSample, wwo->format.Format.nAvgBytesPerSec,
1874 wwo->format.Format.nSamplesPerSec, wwo->format.Format.nChannels,
1875 wwo->format.Format.nBlockAlign);
1877 return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
1881 /**************************************************************************
1882 * wodClose [internal]
1884 static DWORD wodClose(WORD wDevID)
1886 DWORD ret = MMSYSERR_NOERROR;
1889 TRACE("(%u);\n", wDevID);
1891 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
1892 WARN("bad device ID !\n");
1893 return MMSYSERR_BADDEVICEID;
1896 wwo = &WOutDev[wDevID];
1897 if (wwo->lpQueuePtr) {
1898 WARN("buffers still playing !\n");
1899 ret = WAVERR_STILLPLAYING;
1901 if (wwo->hThread != INVALID_HANDLE_VALUE) {
1902 ALSA_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
1904 ALSA_DestroyRingMessage(&wwo->msgRing);
1906 snd_pcm_hw_params_free(wwo->hw_params);
1907 wwo->hw_params = NULL;
1909 snd_pcm_close(wwo->handle);
1912 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
1915 HeapFree(GetProcessHeap(), 0, wwo->ufds);
1920 /**************************************************************************
1921 * wodWrite [internal]
1924 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1926 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1928 /* first, do the sanity checks... */
1929 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
1930 WARN("bad dev ID !\n");
1931 return MMSYSERR_BADDEVICEID;
1934 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1935 return WAVERR_UNPREPARED;
1937 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1938 return WAVERR_STILLPLAYING;
1940 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1941 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1942 lpWaveHdr->lpNext = 0;
1944 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
1946 return MMSYSERR_NOERROR;
1949 /**************************************************************************
1950 * wodPause [internal]
1952 static DWORD wodPause(WORD wDevID)
1954 TRACE("(%u);!\n", wDevID);
1956 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
1957 WARN("bad device ID !\n");
1958 return MMSYSERR_BADDEVICEID;
1961 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
1963 return MMSYSERR_NOERROR;
1966 /**************************************************************************
1967 * wodRestart [internal]
1969 static DWORD wodRestart(WORD wDevID)
1971 TRACE("(%u);\n", wDevID);
1973 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
1974 WARN("bad device ID !\n");
1975 return MMSYSERR_BADDEVICEID;
1978 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
1979 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
1982 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1983 /* FIXME: Myst crashes with this ... hmm -MM
1984 return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1987 return MMSYSERR_NOERROR;
1990 /**************************************************************************
1991 * wodReset [internal]
1993 static DWORD wodReset(WORD wDevID)
1995 TRACE("(%u);\n", wDevID);
1997 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
1998 WARN("bad device ID !\n");
1999 return MMSYSERR_BADDEVICEID;
2002 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
2004 return MMSYSERR_NOERROR;
2007 /**************************************************************************
2008 * wodGetPosition [internal]
2010 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
2014 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
2016 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2017 WARN("bad device ID !\n");
2018 return MMSYSERR_BADDEVICEID;
2021 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
2023 wwo = &WOutDev[wDevID];
2024 ALSA_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
2026 return bytes_to_mmtime(lpTime, wwo->dwPlayedTotal, &wwo->format);
2029 /**************************************************************************
2030 * wodBreakLoop [internal]
2032 static DWORD wodBreakLoop(WORD wDevID)
2034 TRACE("(%u);\n", wDevID);
2036 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2037 WARN("bad device ID !\n");
2038 return MMSYSERR_BADDEVICEID;
2040 ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
2041 return MMSYSERR_NOERROR;
2044 /**************************************************************************
2045 * wodGetVolume [internal]
2047 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
2054 TRACE("(%u, %p);\n", wDevID, lpdwVol);
2055 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2056 WARN("bad device ID !\n");
2057 return MMSYSERR_BADDEVICEID;
2060 if (lpdwVol == NULL)
2061 return MMSYSERR_NOTENABLED;
2063 wwo = &WOutDev[wDevID];
2066 return MMSYSERR_NOTSUPPORTED;
2068 count = snd_ctl_elem_info_get_count(wwo->playback_einfo);
2069 min = snd_ctl_elem_info_get_min(wwo->playback_einfo);
2070 max = snd_ctl_elem_info_get_max(wwo->playback_einfo);
2072 #define VOLUME_ALSA_TO_WIN(x) (((x)-min) * 65536 /(max-min))
2077 left = VOLUME_ALSA_TO_WIN(snd_ctl_elem_value_get_integer(wwo->playback_evalue, 0));
2078 right = VOLUME_ALSA_TO_WIN(snd_ctl_elem_value_get_integer(wwo->playback_evalue, 1));
2081 left = right = VOLUME_ALSA_TO_WIN(snd_ctl_elem_value_get_integer(wwo->playback_evalue, 0));
2084 WARN("%d channels mixer not supported\n", count);
2085 return MMSYSERR_NOERROR;
2087 #undef VOLUME_ALSA_TO_WIN
2089 TRACE("left=%d right=%d !\n", left, right);
2090 *lpdwVol = MAKELONG( left, right );
2091 return MMSYSERR_NOERROR;
2094 /**************************************************************************
2095 * wodSetVolume [internal]
2097 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
2104 TRACE("(%u, %08lX);\n", wDevID, dwParam);
2105 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
2106 WARN("bad device ID !\n");
2107 return MMSYSERR_BADDEVICEID;
2109 wwo = &WOutDev[wDevID];
2111 return MMSYSERR_NOTSUPPORTED;
2113 count=snd_ctl_elem_info_get_count(wwo->playback_einfo);
2114 min = snd_ctl_elem_info_get_min(wwo->playback_einfo);
2115 max = snd_ctl_elem_info_get_max(wwo->playback_einfo);
2117 left = LOWORD(dwParam);
2118 right = HIWORD(dwParam);
2120 #define VOLUME_WIN_TO_ALSA(x) ( (((x) * (max-min)) / 65536) + min )
2124 snd_ctl_elem_value_set_integer(wwo->playback_evalue, 0, VOLUME_WIN_TO_ALSA(left));
2125 snd_ctl_elem_value_set_integer(wwo->playback_evalue, 1, VOLUME_WIN_TO_ALSA(right));
2128 snd_ctl_elem_value_set_integer(wwo->playback_evalue, 0, VOLUME_WIN_TO_ALSA(left));
2131 WARN("%d channels mixer not supported\n", count);
2133 #undef VOLUME_WIN_TO_ALSA
2134 if ( (err = snd_ctl_elem_write(wwo->ctl, wwo->playback_evalue)) < 0)
2136 ERR("error writing snd_ctl_elem_value: %s\n", snd_strerror(err));
2138 return MMSYSERR_NOERROR;
2141 /**************************************************************************
2142 * wodGetNumDevs [internal]
2144 static DWORD wodGetNumDevs(void)
2146 return ALSA_WodNumDevs;
2149 /**************************************************************************
2150 * wodDevInterfaceSize [internal]
2152 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
2154 TRACE("(%u, %p)\n", wDevID, dwParam1);
2156 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
2157 NULL, 0 ) * sizeof(WCHAR);
2158 return MMSYSERR_NOERROR;
2161 /**************************************************************************
2162 * wodDevInterface [internal]
2164 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
2166 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
2167 NULL, 0 ) * sizeof(WCHAR))
2169 MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
2170 dwParam1, dwParam2 / sizeof(WCHAR));
2171 return MMSYSERR_NOERROR;
2173 return MMSYSERR_INVALPARAM;
2176 /**************************************************************************
2177 * wodMessage (WINEALSA.@)
2179 DWORD WINAPI ALSA_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2180 DWORD dwParam1, DWORD dwParam2)
2182 TRACE("(%u, %s, %08lX, %08lX, %08lX);\n",
2183 wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
2190 /* FIXME: Pretend this is supported */
2192 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
2193 case WODM_CLOSE: return wodClose (wDevID);
2194 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSW)dwParam1, dwParam2);
2195 case WODM_GETNUMDEVS: return wodGetNumDevs ();
2196 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
2197 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
2198 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
2199 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
2200 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2201 case WODM_PAUSE: return wodPause (wDevID);
2202 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
2203 case WODM_BREAKLOOP: return wodBreakLoop (wDevID);
2204 case WODM_PREPARE: return MMSYSERR_NOTSUPPORTED;
2205 case WODM_UNPREPARE: return MMSYSERR_NOTSUPPORTED;
2206 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
2207 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
2208 case WODM_RESTART: return wodRestart (wDevID);
2209 case WODM_RESET: return wodReset (wDevID);
2210 case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
2211 case DRV_QUERYDEVICEINTERFACE: return wodDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
2212 case DRV_QUERYDSOUNDIFACE: return wodDsCreate (wDevID, (PIDSDRIVER*)dwParam1);
2213 case DRV_QUERYDSOUNDDESC: return wodDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
2216 FIXME("unknown message %d!\n", wMsg);
2218 return MMSYSERR_NOTSUPPORTED;
2221 /*======================================================================*
2222 * Low level DSOUND implementation *
2223 *======================================================================*/
2225 typedef struct IDsDriverImpl IDsDriverImpl;
2226 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
2228 struct IDsDriverImpl
2230 /* IUnknown fields */
2231 IDsDriverVtbl *lpVtbl;
2233 /* IDsDriverImpl fields */
2235 IDsDriverBufferImpl*primary;
2238 struct IDsDriverBufferImpl
2240 /* IUnknown fields */
2241 IDsDriverBufferVtbl *lpVtbl;
2243 /* IDsDriverBufferImpl fields */
2246 CRITICAL_SECTION mmap_crst;
2248 DWORD mmap_buflen_bytes;
2249 snd_pcm_uframes_t mmap_buflen_frames;
2250 snd_pcm_channel_area_t * mmap_areas;
2251 snd_async_handler_t * mmap_async_handler;
2254 static void DSDB_CheckXRUN(IDsDriverBufferImpl* pdbi)
2256 WINE_WAVEOUT * wwo = &(WOutDev[pdbi->drv->wDevID]);
2257 snd_pcm_state_t state = snd_pcm_state(wwo->handle);
2259 if ( state == SND_PCM_STATE_XRUN )
2261 int err = snd_pcm_prepare(wwo->handle);
2262 TRACE("xrun occurred\n");
2264 ERR("recovery from xrun failed, prepare failed: %s\n", snd_strerror(err));
2266 else if ( state == SND_PCM_STATE_SUSPENDED )
2268 int err = snd_pcm_resume(wwo->handle);
2269 TRACE("recovery from suspension occurred\n");
2270 if (err < 0 && err != -EAGAIN){
2271 err = snd_pcm_prepare(wwo->handle);
2273 ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err));
2278 static void DSDB_MMAPCopy(IDsDriverBufferImpl* pdbi)
2280 WINE_WAVEOUT * wwo = &(WOutDev[pdbi->drv->wDevID]);
2281 unsigned int channels;
2282 snd_pcm_format_t format;
2283 snd_pcm_uframes_t period_size;
2284 snd_pcm_sframes_t avail;
2288 if ( !pdbi->mmap_buffer || !wwo->hw_params || !wwo->handle)
2291 err = snd_pcm_hw_params_get_channels(wwo->hw_params, &channels);
2292 err = snd_pcm_hw_params_get_format(wwo->hw_params, &format);
2294 err = snd_pcm_hw_params_get_period_size(wwo->hw_params, &period_size, &dir);
2295 avail = snd_pcm_avail_update(wwo->handle);
2297 DSDB_CheckXRUN(pdbi);
2299 TRACE("avail=%d format=%s channels=%d\n", (int)avail, snd_pcm_format_name(format), channels );
2301 while (avail >= period_size)
2303 const snd_pcm_channel_area_t *areas;
2304 snd_pcm_uframes_t ofs;
2305 snd_pcm_uframes_t frames;
2308 frames = avail / period_size * period_size; /* round down to a multiple of period_size */
2310 EnterCriticalSection(&pdbi->mmap_crst);
2312 snd_pcm_mmap_begin(wwo->handle, &areas, &ofs, &frames);
2313 snd_pcm_areas_copy(areas, ofs, pdbi->mmap_areas, ofs, channels, frames, format);
2314 err = snd_pcm_mmap_commit(wwo->handle, ofs, frames);
2316 LeaveCriticalSection(&pdbi->mmap_crst);
2318 if ( err != (snd_pcm_sframes_t) frames)
2319 ERR("mmap partially failed.\n");
2321 avail = snd_pcm_avail_update(wwo->handle);
2325 static void DSDB_PCMCallback(snd_async_handler_t *ahandler)
2327 /* snd_pcm_t * handle = snd_async_handler_get_pcm(ahandler); */
2328 IDsDriverBufferImpl* pdbi = snd_async_handler_get_callback_private(ahandler);
2329 TRACE("callback called\n");
2330 DSDB_MMAPCopy(pdbi);
2333 static int DSDB_CreateMMAP(IDsDriverBufferImpl* pdbi)
2335 WINE_WAVEOUT * wwo = &(WOutDev[pdbi->drv->wDevID]);
2336 snd_pcm_format_t format;
2337 snd_pcm_uframes_t frames;
2338 unsigned int channels;
2339 unsigned int bits_per_sample;
2340 unsigned int bits_per_frame;
2341 snd_pcm_channel_area_t * a;
2345 err = snd_pcm_hw_params_get_format(wwo->hw_params, &format);
2346 err = snd_pcm_hw_params_get_buffer_size(wwo->hw_params, &frames);
2347 err = snd_pcm_hw_params_get_channels(wwo->hw_params, &channels);
2348 bits_per_sample = snd_pcm_format_physical_width(format);
2349 bits_per_frame = bits_per_sample * channels;
2353 ALSA_TraceParameters(wwo->hw_params, NULL, FALSE);
2355 TRACE("format=%s frames=%ld channels=%d bits_per_sample=%d bits_per_frame=%d\n",
2356 snd_pcm_format_name(format), frames, channels, bits_per_sample, bits_per_frame);
2358 pdbi->mmap_buflen_frames = frames;
2359 pdbi->mmap_buflen_bytes = snd_pcm_frames_to_bytes( wwo->handle, frames );
2360 pdbi->mmap_buffer = HeapAlloc(GetProcessHeap(),0,pdbi->mmap_buflen_bytes);
2361 if (!pdbi->mmap_buffer)
2362 return DSERR_OUTOFMEMORY;
2364 snd_pcm_format_set_silence(format, pdbi->mmap_buffer, frames );
2366 TRACE("created mmap buffer of %ld frames (%ld bytes) at %p\n",
2367 frames, pdbi->mmap_buflen_bytes, pdbi->mmap_buffer);
2369 pdbi->mmap_areas = HeapAlloc(GetProcessHeap(),0,channels*sizeof(snd_pcm_channel_area_t));
2370 if (!pdbi->mmap_areas)
2371 return DSERR_OUTOFMEMORY;
2373 a = pdbi->mmap_areas;
2374 for (c = 0; c < channels; c++, a++)
2376 a->addr = pdbi->mmap_buffer;
2377 a->first = bits_per_sample * c;
2378 a->step = bits_per_frame;
2379 TRACE("Area %d: addr=%p first=%d step=%d\n", c, a->addr, a->first, a->step);
2382 InitializeCriticalSection(&pdbi->mmap_crst);
2384 err = snd_async_add_pcm_handler(&pdbi->mmap_async_handler, wwo->handle, DSDB_PCMCallback, pdbi);
2387 ERR("add_pcm_handler failed. reason: %s\n", snd_strerror(err));
2388 return DSERR_GENERIC;
2394 static void DSDB_DestroyMMAP(IDsDriverBufferImpl* pdbi)
2396 TRACE("mmap buffer %p destroyed\n", pdbi->mmap_buffer);
2397 HeapFree(GetProcessHeap(), 0, pdbi->mmap_areas);
2398 HeapFree(GetProcessHeap(), 0, pdbi->mmap_buffer);
2399 pdbi->mmap_areas = NULL;
2400 pdbi->mmap_buffer = NULL;
2401 DeleteCriticalSection(&pdbi->mmap_crst);
2405 static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
2407 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
2408 FIXME("(): stub!\n");
2409 return DSERR_UNSUPPORTED;
2412 static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface)
2414 IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
2415 ULONG refCount = InterlockedIncrement(&This->ref);
2417 TRACE("(%p)->(ref before=%lu)\n",This, refCount - 1);
2422 static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface)
2424 IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
2425 ULONG refCount = InterlockedDecrement(&This->ref);
2427 TRACE("(%p)->(ref before=%lu)\n",This, refCount + 1);
2431 if (This == This->drv->primary)
2432 This->drv->primary = NULL;
2433 DSDB_DestroyMMAP(This);
2434 HeapFree(GetProcessHeap(), 0, This);
2438 static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
2439 LPVOID*ppvAudio1,LPDWORD pdwLen1,
2440 LPVOID*ppvAudio2,LPDWORD pdwLen2,
2441 DWORD dwWritePosition,DWORD dwWriteLen,
2444 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
2445 TRACE("(%p)\n",iface);
2446 return DSERR_UNSUPPORTED;
2449 static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
2450 LPVOID pvAudio1,DWORD dwLen1,
2451 LPVOID pvAudio2,DWORD dwLen2)
2453 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
2454 TRACE("(%p)\n",iface);
2455 return DSERR_UNSUPPORTED;
2458 static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface,
2459 LPWAVEFORMATEX pwfx)
2461 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
2462 TRACE("(%p,%p)\n",iface,pwfx);
2463 return DSERR_BUFFERLOST;
2466 static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq)
2468 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
2469 TRACE("(%p,%ld): stub\n",iface,dwFreq);
2470 return DSERR_UNSUPPORTED;
2473 static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan)
2476 IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
2477 TRACE("(%p,%p)\n",iface,pVolPan);
2478 vol = pVolPan->dwTotalLeftAmpFactor | (pVolPan->dwTotalRightAmpFactor << 16);
2480 if (wodSetVolume(This->drv->wDevID, vol) != MMSYSERR_NOERROR) {
2481 WARN("wodSetVolume failed\n");
2482 return DSERR_INVALIDPARAM;
2488 static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos)
2490 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
2491 TRACE("(%p,%ld): stub\n",iface,dwNewPos);
2492 return DSERR_UNSUPPORTED;
2495 static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
2496 LPDWORD lpdwPlay, LPDWORD lpdwWrite)
2498 IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
2499 WINE_WAVEOUT * wwo = &(WOutDev[This->drv->wDevID]);
2500 snd_pcm_uframes_t hw_ptr;
2501 snd_pcm_uframes_t period_size;
2505 if (wwo->hw_params == NULL) return DSERR_GENERIC;
2508 err = snd_pcm_hw_params_get_period_size(wwo->hw_params, &period_size, &dir);
2510 if (wwo->handle == NULL) return DSERR_GENERIC;
2511 /** we need to track down buffer underruns */
2512 DSDB_CheckXRUN(This);
2514 EnterCriticalSection(&This->mmap_crst);
2515 /* FIXME: snd_pcm_mmap_hw_ptr() should not be accessed by a user app. */
2516 /* It will NOT return what why want anyway. */
2517 hw_ptr = _snd_pcm_mmap_hw_ptr(wwo->handle);
2519 *lpdwPlay = snd_pcm_frames_to_bytes(wwo->handle, hw_ptr/ period_size * period_size) % This->mmap_buflen_bytes;
2521 *lpdwWrite = snd_pcm_frames_to_bytes(wwo->handle, (hw_ptr / period_size + 1) * period_size ) % This->mmap_buflen_bytes;
2522 LeaveCriticalSection(&This->mmap_crst);
2524 TRACE("hw_ptr=0x%08x, playpos=%ld, writepos=%ld\n", (unsigned int)hw_ptr, lpdwPlay?*lpdwPlay:-1, lpdwWrite?*lpdwWrite:-1);
2528 static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags)
2530 IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
2531 WINE_WAVEOUT * wwo = &(WOutDev[This->drv->wDevID]);
2532 snd_pcm_state_t state;
2535 TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags);
2537 if (wwo->handle == NULL) return DSERR_GENERIC;
2539 state = snd_pcm_state(wwo->handle);
2540 if ( state == SND_PCM_STATE_SETUP )
2542 err = snd_pcm_prepare(wwo->handle);
2543 state = snd_pcm_state(wwo->handle);
2545 if ( state == SND_PCM_STATE_PREPARED )
2547 DSDB_MMAPCopy(This);
2548 err = snd_pcm_start(wwo->handle);
2553 static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface)
2555 IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
2556 WINE_WAVEOUT * wwo = &(WOutDev[This->drv->wDevID]);
2561 TRACE("(%p)\n",iface);
2563 if (wwo->handle == NULL) return DSERR_GENERIC;
2565 /* ring buffer wrap up detection */
2566 IDsDriverBufferImpl_GetPosition(iface, &play, &write);
2569 TRACE("writepos wrapper up\n");
2573 if ( ( err = snd_pcm_drop(wwo->handle)) < 0 )
2575 ERR("error while stopping pcm: %s\n", snd_strerror(err));
2576 return DSERR_GENERIC;
2581 static IDsDriverBufferVtbl dsdbvt =
2583 IDsDriverBufferImpl_QueryInterface,
2584 IDsDriverBufferImpl_AddRef,
2585 IDsDriverBufferImpl_Release,
2586 IDsDriverBufferImpl_Lock,
2587 IDsDriverBufferImpl_Unlock,
2588 IDsDriverBufferImpl_SetFormat,
2589 IDsDriverBufferImpl_SetFrequency,
2590 IDsDriverBufferImpl_SetVolumePan,
2591 IDsDriverBufferImpl_SetPosition,
2592 IDsDriverBufferImpl_GetPosition,
2593 IDsDriverBufferImpl_Play,
2594 IDsDriverBufferImpl_Stop
2597 static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj)
2599 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
2600 FIXME("(%p): stub!\n",iface);
2601 return DSERR_UNSUPPORTED;
2604 static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface)
2606 IDsDriverImpl *This = (IDsDriverImpl *)iface;
2607 ULONG refCount = InterlockedIncrement(&This->ref);
2609 TRACE("(%p)->(ref before=%lu)\n",This, refCount - 1);
2614 static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface)
2616 IDsDriverImpl *This = (IDsDriverImpl *)iface;
2617 ULONG refCount = InterlockedDecrement(&This->ref);
2619 TRACE("(%p)->(ref before=%lu)\n",This, refCount + 1);
2623 HeapFree(GetProcessHeap(),0,This);
2627 static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc)
2629 IDsDriverImpl *This = (IDsDriverImpl *)iface;
2630 TRACE("(%p,%p)\n",iface,pDesc);
2631 memcpy(pDesc, &(WOutDev[This->wDevID].ds_desc), sizeof(DSDRIVERDESC));
2632 pDesc->dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
2633 DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK;
2634 pDesc->dnDevNode = WOutDev[This->wDevID].waveDesc.dnDevNode;
2636 pDesc->wReserved = 0;
2637 pDesc->ulDeviceNum = This->wDevID;
2638 pDesc->dwHeapType = DSDHEAP_NOHEAP;
2639 pDesc->pvDirectDrawHeap = NULL;
2640 pDesc->dwMemStartAddress = 0;
2641 pDesc->dwMemEndAddress = 0;
2642 pDesc->dwMemAllocExtra = 0;
2643 pDesc->pvReserved1 = NULL;
2644 pDesc->pvReserved2 = NULL;
2648 static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface)
2650 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
2651 TRACE("(%p)\n",iface);
2655 static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface)
2657 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
2658 TRACE("(%p)\n",iface);
2662 static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps)
2664 IDsDriverImpl *This = (IDsDriverImpl *)iface;
2665 TRACE("(%p,%p)\n",iface,pCaps);
2666 memset(pCaps, 0, sizeof(*pCaps));
2668 pCaps->dwFlags = DSCAPS_PRIMARYMONO;
2669 if ( WOutDev[This->wDevID].caps.wChannels == 2 )
2670 pCaps->dwFlags |= DSCAPS_PRIMARYSTEREO;
2672 if ( WOutDev[This->wDevID].caps.dwFormats & (WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 ) )
2673 pCaps->dwFlags |= DSCAPS_PRIMARY8BIT;
2675 if ( WOutDev[This->wDevID].caps.dwFormats & (WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16))
2676 pCaps->dwFlags |= DSCAPS_PRIMARY16BIT;
2678 pCaps->dwPrimaryBuffers = 1;
2679 TRACE("caps=0x%X\n",(unsigned int)pCaps->dwFlags);
2680 pCaps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
2681 pCaps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
2683 /* the other fields only apply to secondary buffers, which we don't support
2684 * (unless we want to mess with wavetable synthesizers and MIDI) */
2688 static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
2689 LPWAVEFORMATEX pwfx,
2690 DWORD dwFlags, DWORD dwCardAddress,
2691 LPDWORD pdwcbBufferSize,
2695 IDsDriverImpl *This = (IDsDriverImpl *)iface;
2696 IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
2699 TRACE("(%p,%p,%lx,%lx)\n",iface,pwfx,dwFlags,dwCardAddress);
2700 /* we only support primary buffers */
2701 if (!(dwFlags & DSBCAPS_PRIMARYBUFFER))
2702 return DSERR_UNSUPPORTED;
2704 return DSERR_ALLOCATED;
2705 if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN))
2706 return DSERR_CONTROLUNAVAIL;
2708 *ippdsdb = (IDsDriverBufferImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverBufferImpl));
2709 if (*ippdsdb == NULL)
2710 return DSERR_OUTOFMEMORY;
2711 (*ippdsdb)->lpVtbl = &dsdbvt;
2712 (*ippdsdb)->ref = 1;
2713 (*ippdsdb)->drv = This;
2715 err = DSDB_CreateMMAP((*ippdsdb));
2718 HeapFree(GetProcessHeap(), 0, *ippdsdb);
2722 *ppbBuffer = (*ippdsdb)->mmap_buffer;
2723 *pdwcbBufferSize = (*ippdsdb)->mmap_buflen_bytes;
2725 This->primary = *ippdsdb;
2727 /* buffer is ready to go */
2728 TRACE("buffer created at %p\n", *ippdsdb);
2732 static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
2733 PIDSDRIVERBUFFER pBuffer,
2736 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
2737 TRACE("(%p,%p): stub\n",iface,pBuffer);
2738 return DSERR_INVALIDCALL;
2741 static IDsDriverVtbl dsdvt =
2743 IDsDriverImpl_QueryInterface,
2744 IDsDriverImpl_AddRef,
2745 IDsDriverImpl_Release,
2746 IDsDriverImpl_GetDriverDesc,
2748 IDsDriverImpl_Close,
2749 IDsDriverImpl_GetCaps,
2750 IDsDriverImpl_CreateSoundBuffer,
2751 IDsDriverImpl_DuplicateSoundBuffer
2754 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
2756 IDsDriverImpl** idrv = (IDsDriverImpl**)drv;
2758 TRACE("driver created\n");
2760 /* the HAL isn't much better than the HEL if we can't do mmap() */
2761 if (!(WOutDev[wDevID].caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
2762 ERR("DirectSound flag not set\n");
2763 MESSAGE("This sound card's driver does not support direct access\n");
2764 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
2765 return MMSYSERR_NOTSUPPORTED;
2768 *idrv = (IDsDriverImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverImpl));
2770 return MMSYSERR_NOMEM;
2771 (*idrv)->lpVtbl = &dsdvt;
2774 (*idrv)->wDevID = wDevID;
2775 (*idrv)->primary = NULL;
2776 return MMSYSERR_NOERROR;
2779 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
2781 memcpy(desc, &(WOutDev[wDevID].ds_desc), sizeof(DSDRIVERDESC));
2782 return MMSYSERR_NOERROR;
2785 /*======================================================================*
2786 * Low level WAVE IN implementation *
2787 *======================================================================*/
2789 /**************************************************************************
2790 * widNotifyClient [internal]
2792 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
2794 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
2800 if (wwi->wFlags != DCB_NULL &&
2801 !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags, (HDRVR)wwi->waveDesc.hWave,
2802 wMsg, wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
2803 WARN("can't notify client !\n");
2804 return MMSYSERR_ERROR;
2808 FIXME("Unknown callback message %u\n", wMsg);
2809 return MMSYSERR_INVALPARAM;
2811 return MMSYSERR_NOERROR;
2814 /**************************************************************************
2815 * widGetDevCaps [internal]
2817 static DWORD widGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
2819 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
2821 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
2823 if (wDevID >= MAX_WAVEINDRV) {
2824 TRACE("MAX_WAVOUTDRV reached !\n");
2825 return MMSYSERR_BADDEVICEID;
2828 memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
2829 return MMSYSERR_NOERROR;
2832 /**************************************************************************
2833 * widRecorder_ReadHeaders [internal]
2835 static void widRecorder_ReadHeaders(WINE_WAVEIN * wwi)
2837 enum win_wm_message tmp_msg;
2842 while (ALSA_RetrieveRingMessage(&wwi->msgRing, &tmp_msg, &tmp_param, &tmp_ev)) {
2843 if (tmp_msg == WINE_WM_HEADER) {
2845 lpWaveHdr = (LPWAVEHDR)tmp_param;
2846 lpWaveHdr->lpNext = 0;
2848 if (wwi->lpQueuePtr == 0)
2849 wwi->lpQueuePtr = lpWaveHdr;
2851 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
2855 ERR("should only have headers left\n");
2860 /**************************************************************************
2861 * widRecorder [internal]
2863 static DWORD CALLBACK widRecorder(LPVOID pmt)
2865 WORD uDevID = (DWORD)pmt;
2866 WINE_WAVEIN* wwi = (WINE_WAVEIN*)&WInDev[uDevID];
2870 LPVOID buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, wwi->dwPeriodSize);
2871 char *pOffset = buffer;
2872 enum win_wm_message msg;
2875 DWORD frames_per_period;
2877 wwi->state = WINE_WS_STOPPED;
2878 wwi->dwTotalRecorded = 0;
2879 wwi->lpQueuePtr = NULL;
2881 SetEvent(wwi->hStartUpEvent);
2883 /* make sleep time to be # of ms to output a period */
2884 dwSleepTime = (1024/*wwi-dwPeriodSize => overrun!*/ * 1000) / wwi->format.Format.nAvgBytesPerSec;
2885 frames_per_period = snd_pcm_bytes_to_frames(wwi->handle, wwi->dwPeriodSize);
2886 TRACE("sleeptime=%ld ms\n", dwSleepTime);
2889 /* wait for dwSleepTime or an event in thread's queue */
2890 /* FIXME: could improve wait time depending on queue state,
2891 * ie, number of queued fragments
2893 if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING)
2900 lpWaveHdr = wwi->lpQueuePtr;
2901 /* read all the fragments accumulated so far */
2902 frames = snd_pcm_avail_update(wwi->handle);
2903 bytes = snd_pcm_frames_to_bytes(wwi->handle, frames);
2904 TRACE("frames = %ld bytes = %ld\n", frames, bytes);
2905 periods = bytes / wwi->dwPeriodSize;
2906 while ((periods > 0) && (wwi->lpQueuePtr))
2909 bytes = wwi->dwPeriodSize;
2910 TRACE("bytes = %ld\n",bytes);
2911 if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded >= wwi->dwPeriodSize)
2913 /* directly read fragment in wavehdr */
2914 read = wwi->read(wwi->handle, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, frames_per_period);
2915 bytesRead = snd_pcm_frames_to_bytes(wwi->handle, read);
2917 TRACE("bytesRead=%ld (direct)\n", bytesRead);
2918 if (bytesRead != (DWORD) -1)
2920 /* update number of bytes recorded in current buffer and by this device */
2921 lpWaveHdr->dwBytesRecorded += bytesRead;
2922 wwi->dwTotalRecorded += bytesRead;
2924 /* buffer is full. notify client */
2925 if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
2927 /* must copy the value of next waveHdr, because we have no idea of what
2928 * will be done with the content of lpWaveHdr in callback
2930 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
2932 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2933 lpWaveHdr->dwFlags |= WHDR_DONE;
2935 wwi->lpQueuePtr = lpNext;
2936 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2940 TRACE("read(%s, %p, %ld) failed (%s)\n", wwi->device,
2941 lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
2942 frames_per_period, strerror(errno));
2947 /* read the fragment in a local buffer */
2948 read = wwi->read(wwi->handle, buffer, frames_per_period);
2949 bytesRead = snd_pcm_frames_to_bytes(wwi->handle, read);
2952 TRACE("bytesRead=%ld (local)\n", bytesRead);
2954 if (bytesRead == (DWORD) -1) {
2955 TRACE("read(%s, %p, %ld) failed (%s)\n", wwi->device,
2956 buffer, frames_per_period, strerror(errno));
2960 /* copy data in client buffers */
2961 while (bytesRead != (DWORD) -1 && bytesRead > 0)
2963 DWORD dwToCopy = min (bytesRead, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
2965 memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
2969 /* update number of bytes recorded in current buffer and by this device */
2970 lpWaveHdr->dwBytesRecorded += dwToCopy;
2971 wwi->dwTotalRecorded += dwToCopy;
2972 bytesRead -= dwToCopy;
2973 pOffset += dwToCopy;
2975 /* client buffer is full. notify client */
2976 if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
2978 /* must copy the value of next waveHdr, because we have no idea of what
2979 * will be done with the content of lpWaveHdr in callback
2981 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
2982 TRACE("lpNext=%p\n", lpNext);
2984 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
2985 lpWaveHdr->dwFlags |= WHDR_DONE;
2987 wwi->lpQueuePtr = lpNext;
2988 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
2991 if (!lpNext && bytesRead) {
2992 /* before we give up, check for more header messages */
2993 while (ALSA_PeekRingMessage(&wwi->msgRing, &msg, ¶m, &ev))
2995 if (msg == WINE_WM_HEADER) {
2997 ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev);
2998 hdr = ((LPWAVEHDR)param);
2999 TRACE("msg = %s, hdr = %p, ev = %p\n", getCmdString(msg), hdr, ev);
3001 if (lpWaveHdr == 0) {
3002 /* new head of queue */
3003 wwi->lpQueuePtr = lpWaveHdr = hdr;
3005 /* insert buffer at the end of queue */
3007 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
3014 if (lpWaveHdr == 0) {
3015 /* no more buffer to copy data to, but we did read more.
3016 * what hasn't been copied will be dropped
3018 WARN("buffer under run! %lu bytes dropped.\n", bytesRead);
3019 wwi->lpQueuePtr = NULL;
3029 WAIT_OMR(&wwi->msgRing, dwSleepTime);
3031 while (ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev))
3033 TRACE("msg=%s param=0x%lx\n", getCmdString(msg), param);
3035 case WINE_WM_PAUSING:
3036 wwi->state = WINE_WS_PAUSED;
3037 /*FIXME("Device should stop recording\n");*/
3040 case WINE_WM_STARTING:
3041 wwi->state = WINE_WS_PLAYING;
3042 snd_pcm_start(wwi->handle);
3045 case WINE_WM_HEADER:
3046 lpWaveHdr = (LPWAVEHDR)param;
3047 lpWaveHdr->lpNext = 0;
3049 /* insert buffer at the end of queue */
3052 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
3056 case WINE_WM_STOPPING:
3057 if (wwi->state != WINE_WS_STOPPED)
3059 snd_pcm_drain(wwi->handle);
3061 /* read any headers in queue */
3062 widRecorder_ReadHeaders(wwi);
3064 /* return current buffer to app */
3065 lpWaveHdr = wwi->lpQueuePtr;
3068 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
3069 TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
3070 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
3071 lpWaveHdr->dwFlags |= WHDR_DONE;
3072 wwi->lpQueuePtr = lpNext;
3073 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
3076 wwi->state = WINE_WS_STOPPED;
3079 case WINE_WM_RESETTING:
3080 if (wwi->state != WINE_WS_STOPPED)
3082 snd_pcm_drain(wwi->handle);
3084 wwi->state = WINE_WS_STOPPED;
3085 wwi->dwTotalRecorded = 0;
3087 /* read any headers in queue */
3088 widRecorder_ReadHeaders(wwi);
3090 /* return all buffers to the app */
3091 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
3092 TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
3093 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
3094 lpWaveHdr->dwFlags |= WHDR_DONE;
3095 wwi->lpQueuePtr = lpWaveHdr->lpNext;
3096 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
3099 wwi->lpQueuePtr = NULL;
3102 case WINE_WM_CLOSING:
3104 wwi->state = WINE_WS_CLOSED;
3106 HeapFree(GetProcessHeap(), 0, buffer);
3108 /* shouldn't go here */
3109 case WINE_WM_UPDATE:
3114 FIXME("unknown message %d\n", msg);
3120 /* just for not generating compilation warnings... should never be executed */
3124 /**************************************************************************
3125 * widOpen [internal]
3127 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
3130 snd_pcm_hw_params_t * hw_params;
3131 snd_pcm_sw_params_t * sw_params;
3132 snd_pcm_access_t access;
3133 snd_pcm_format_t format;
3135 unsigned int buffer_time = 500000;
3136 unsigned int period_time = 10000;
3137 snd_pcm_uframes_t buffer_size;
3138 snd_pcm_uframes_t period_size;
3144 snd_pcm_hw_params_alloca(&hw_params);
3145 snd_pcm_sw_params_alloca(&sw_params);
3147 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
3148 if (lpDesc == NULL) {
3149 WARN("Invalid Parameter !\n");
3150 return MMSYSERR_INVALPARAM;
3152 if (wDevID >= MAX_WAVEOUTDRV) {
3153 TRACE("MAX_WAVOUTDRV reached !\n");
3154 return MMSYSERR_BADDEVICEID;
3157 /* only PCM format is supported so far... */
3158 if (!supportedFormat(lpDesc->lpFormat)) {
3159 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3160 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
3161 lpDesc->lpFormat->nSamplesPerSec);
3162 return WAVERR_BADFORMAT;
3165 if (dwFlags & WAVE_FORMAT_QUERY) {
3166 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3167 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
3168 lpDesc->lpFormat->nSamplesPerSec);
3169 return MMSYSERR_NOERROR;
3172 wwi = &WInDev[wDevID];
3174 if ((dwFlags & WAVE_DIRECTSOUND) && !(wwi->dwSupport & WAVECAPS_DIRECTSOUND))
3175 /* not supported, ignore it */
3176 dwFlags &= ~WAVE_DIRECTSOUND;
3179 flags = SND_PCM_NONBLOCK;
3181 if ( dwFlags & WAVE_DIRECTSOUND )
3182 flags |= SND_PCM_ASYNC;
3185 if ( (err=snd_pcm_open(&pcm, wwi->device, SND_PCM_STREAM_CAPTURE, flags)) < 0 )
3187 ERR("Error open: %s\n", snd_strerror(err));
3188 return MMSYSERR_NOTENABLED;
3191 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
3193 memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
3194 copy_format(lpDesc->lpFormat, &wwi->format);
3196 if (wwi->format.Format.wBitsPerSample == 0) {
3197 WARN("Resetting zeroed wBitsPerSample\n");
3198 wwi->format.Format.wBitsPerSample = 8 *
3199 (wwi->format.Format.nAvgBytesPerSec /
3200 wwi->format.Format.nSamplesPerSec) /
3201 wwi->format.Format.nChannels;
3204 snd_pcm_hw_params_any(pcm, hw_params);
3206 #define EXIT_ON_ERROR(f,e,txt) do \
3209 if ( (err = (f) ) < 0) \
3211 ERR(txt ": %s\n", snd_strerror(err)); \
3212 snd_pcm_close(pcm); \
3217 access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
3218 if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {
3219 WARN("mmap not available. switching to standard write.\n");
3220 access = SND_PCM_ACCESS_RW_INTERLEAVED;
3221 EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");
3222 wwi->read = snd_pcm_readi;
3225 wwi->read = snd_pcm_mmap_readi;
3227 EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwi->format.Format.nChannels), MMSYSERR_INVALPARAM, "unable to set required channels");
3229 if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_PCM) ||
3230 ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
3231 IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) {
3232 format = (wwi->format.Format.wBitsPerSample == 8) ? SND_PCM_FORMAT_U8 :
3233 (wwi->format.Format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE :
3234 (wwi->format.Format.wBitsPerSample == 24) ? SND_PCM_FORMAT_S24_LE :
3235 (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_S32_LE : -1;
3236 } else if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
3237 IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)){
3238 format = (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_FLOAT_LE : -1;
3239 } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_MULAW) {
3240 FIXME("unimplemented format: WAVE_FORMAT_MULAW\n");
3242 return WAVERR_BADFORMAT;
3243 } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ALAW) {
3244 FIXME("unimplemented format: WAVE_FORMAT_ALAW\n");
3246 return WAVERR_BADFORMAT;
3247 } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ADPCM) {
3248 FIXME("unimplemented format: WAVE_FORMAT_ADPCM\n");
3250 return WAVERR_BADFORMAT;
3252 ERR("invalid format: %0x04x\n", wwi->format.Format.wFormatTag);
3254 return WAVERR_BADFORMAT;
3257 EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), MMSYSERR_INVALPARAM, "unable to set required format");
3259 rate = wwi->format.Format.nSamplesPerSec;
3261 err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir);
3263 ERR("Rate %ld Hz not available for playback: %s\n", wwi->format.Format.nSamplesPerSec, snd_strerror(rate));
3265 return WAVERR_BADFORMAT;
3267 if (rate != wwi->format.Format.nSamplesPerSec) {
3268 ERR("Rate doesn't match (requested %ld Hz, got %d Hz)\n", wwi->format.Format.nSamplesPerSec, rate);
3270 return WAVERR_BADFORMAT;
3274 EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time");
3276 EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time");
3278 EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");
3281 err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
3282 err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
3284 snd_pcm_sw_params_current(pcm, sw_params);
3285 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");
3286 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");
3287 EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min");
3288 EXIT_ON_ERROR( snd_pcm_sw_params_set_xfer_align(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set xfer align");
3289 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold");
3290 EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback");
3291 #undef EXIT_ON_ERROR
3293 snd_pcm_prepare(pcm);
3296 ALSA_TraceParameters(hw_params, sw_params, FALSE);
3298 /* now, we can save all required data for later use... */
3299 if ( wwi->hw_params )
3300 snd_pcm_hw_params_free(wwi->hw_params);
3301 snd_pcm_hw_params_malloc(&(wwi->hw_params));
3302 snd_pcm_hw_params_copy(wwi->hw_params, hw_params);
3304 wwi->dwBufferSize = buffer_size;
3305 wwi->lpQueuePtr = wwi->lpPlayPtr = wwi->lpLoopPtr = NULL;
3308 ALSA_InitRingMessage(&wwi->msgRing);
3310 wwi->count = snd_pcm_poll_descriptors_count (wwi->handle);
3311 if (wwi->count <= 0) {
3312 ERR("Invalid poll descriptors count\n");
3313 return MMSYSERR_ERROR;
3316 wwi->ufds = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, sizeof(struct pollfd) * wwi->count);
3317 if (wwi->ufds == NULL) {
3318 ERR("No enough memory\n");
3319 return MMSYSERR_NOMEM;
3321 if ((err = snd_pcm_poll_descriptors(wwi->handle, wwi->ufds, wwi->count)) < 0) {
3322 ERR("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
3323 return MMSYSERR_ERROR;
3326 wwi->dwPeriodSize = period_size;
3327 /*if (wwi->dwFragmentSize % wwi->format.Format.nBlockAlign)
3328 ERR("Fragment doesn't contain an integral number of data blocks\n");
3330 TRACE("dwPeriodSize=%lu\n", wwi->dwPeriodSize);
3331 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
3332 wwi->format.Format.wBitsPerSample, wwi->format.Format.nAvgBytesPerSec,
3333 wwi->format.Format.nSamplesPerSec, wwi->format.Format.nChannels,
3334 wwi->format.Format.nBlockAlign);
3336 if (!(dwFlags & WAVE_DIRECTSOUND)) {
3337 wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
3338 wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
3339 WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
3340 CloseHandle(wwi->hStartUpEvent);
3342 wwi->hThread = INVALID_HANDLE_VALUE;
3343 wwi->dwThreadID = 0;
3345 wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
3347 return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
3351 /**************************************************************************
3352 * widClose [internal]
3354 static DWORD widClose(WORD wDevID)
3356 DWORD ret = MMSYSERR_NOERROR;
3359 TRACE("(%u);\n", wDevID);
3361 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].handle == NULL) {
3362 WARN("bad device ID !\n");
3363 return MMSYSERR_BADDEVICEID;
3366 wwi = &WInDev[wDevID];
3367 if (wwi->lpQueuePtr) {
3368 WARN("buffers still playing !\n");
3369 ret = WAVERR_STILLPLAYING;
3371 if (wwi->hThread != INVALID_HANDLE_VALUE) {
3372 ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
3374 ALSA_DestroyRingMessage(&wwi->msgRing);
3376 snd_pcm_hw_params_free(wwi->hw_params);
3377 wwi->hw_params = NULL;
3379 snd_pcm_close(wwi->handle);
3382 ret = widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
3385 HeapFree(GetProcessHeap(), 0, wwi->ufds);
3389 /**************************************************************************
3390 * widAddBuffer [internal]
3393 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3395 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3397 /* first, do the sanity checks... */
3398 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].handle == NULL) {
3399 WARN("bad dev ID !\n");
3400 return MMSYSERR_BADDEVICEID;
3403 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
3404 return WAVERR_UNPREPARED;
3406 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
3407 return WAVERR_STILLPLAYING;
3409 lpWaveHdr->dwFlags &= ~WHDR_DONE;
3410 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
3411 lpWaveHdr->lpNext = 0;
3413 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
3415 return MMSYSERR_NOERROR;
3418 /**************************************************************************
3419 * widStart [internal]
3422 static DWORD widStart(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3424 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3426 /* first, do the sanity checks... */
3427 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].p_handle == NULL) {
3428 WARN("bad dev ID !\n");
3429 return MMSYSERR_BADDEVICEID;
3432 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
3436 return MMSYSERR_NOERROR;
3439 /**************************************************************************
3440 * widStop [internal]
3443 static DWORD widStop(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
3445 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
3447 /* first, do the sanity checks... */
3448 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].handle == NULL) {
3449 WARN("bad dev ID !\n");
3450 return MMSYSERR_BADDEVICEID;
3453 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
3455 return MMSYSERR_NOERROR;
3458 /**************************************************************************
3459 * widReset [internal]
3461 static DWORD widReset(WORD wDevID)
3463 TRACE("(%u);\n", wDevID);
3464 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
3465 WARN("can't reset !\n");
3466 return MMSYSERR_INVALHANDLE;
3468 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
3469 return MMSYSERR_NOERROR;
3472 /**************************************************************************
3473 * widGetPosition [internal]
3475 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
3479 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
3481 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
3482 WARN("can't get pos !\n");
3483 return MMSYSERR_INVALHANDLE;
3486 if (lpTime == NULL) {
3487 WARN("invalid parameter: lpTime = NULL\n");
3488 return MMSYSERR_INVALPARAM;
3491 wwi = &WInDev[wDevID];
3492 ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_UPDATE, 0, TRUE);
3494 return bytes_to_mmtime(lpTime, wwi->dwTotalRecorded, &wwi->format);
3497 /**************************************************************************
3498 * widGetNumDevs [internal]
3500 static DWORD widGetNumDevs(void)
3502 return ALSA_WidNumDevs;
3505 /**************************************************************************
3506 * widDevInterfaceSize [internal]
3508 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
3510 TRACE("(%u, %p)\n", wDevID, dwParam1);
3512 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
3513 NULL, 0 ) * sizeof(WCHAR);
3514 return MMSYSERR_NOERROR;
3517 /**************************************************************************
3518 * widDevInterface [internal]
3520 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
3522 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
3523 NULL, 0 ) * sizeof(WCHAR))
3525 MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
3526 dwParam1, dwParam2 / sizeof(WCHAR));
3527 return MMSYSERR_NOERROR;
3529 return MMSYSERR_INVALPARAM;
3532 /**************************************************************************
3533 * widDsCreate [internal]
3535 static DWORD widDsCreate(UINT wDevID, PIDSCDRIVER* drv)
3537 TRACE("(%d,%p)\n",wDevID,drv);
3539 /* the HAL isn't much better than the HEL if we can't do mmap() */
3540 FIXME("DirectSoundCapture not implemented\n");
3541 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
3542 return MMSYSERR_NOTSUPPORTED;
3545 /**************************************************************************
3546 * widDsDesc [internal]
3548 static DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc)
3550 memcpy(desc, &(WInDev[wDevID].ds_desc), sizeof(DSDRIVERDESC));
3551 return MMSYSERR_NOERROR;
3554 /**************************************************************************
3555 * widMessage (WINEALSA.@)
3557 DWORD WINAPI ALSA_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
3558 DWORD dwParam1, DWORD dwParam2)
3560 TRACE("(%u, %s, %08lX, %08lX, %08lX);\n",
3561 wDevID, getMessage(wMsg), dwUser, dwParam1, dwParam2);
3568 /* FIXME: Pretend this is supported */
3570 case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
3571 case WIDM_CLOSE: return widClose (wDevID);
3572 case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3573 case WIDM_PREPARE: return MMSYSERR_NOTSUPPORTED;
3574 case WIDM_UNPREPARE: return MMSYSERR_NOTSUPPORTED;
3575 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEOUTCAPSW)dwParam1, dwParam2);
3576 case WIDM_GETNUMDEVS: return widGetNumDevs ();
3577 case WIDM_GETPOS: return widGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
3578 case WIDM_RESET: return widReset (wDevID);
3579 case WIDM_START: return widStart (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3580 case WIDM_STOP: return widStop (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
3581 case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
3582 case DRV_QUERYDEVICEINTERFACE: return widDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
3583 case DRV_QUERYDSOUNDIFACE: return widDsCreate (wDevID, (PIDSCDRIVER*)dwParam1);
3584 case DRV_QUERYDSOUNDDESC: return widDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
3586 FIXME("unknown message %d!\n", wMsg);
3588 return MMSYSERR_NOTSUPPORTED;
3593 /**************************************************************************
3594 * widMessage (WINEALSA.@)
3596 DWORD WINAPI ALSA_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
3597 DWORD dwParam1, DWORD dwParam2)
3599 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
3600 return MMSYSERR_NOTENABLED;
3603 /**************************************************************************
3604 * wodMessage (WINEALSA.@)
3606 DWORD WINAPI ALSA_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
3607 DWORD dwParam1, DWORD dwParam2)
3609 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
3610 return MMSYSERR_NOTENABLED;
3613 #endif /* HAVE_ALSA */