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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 /* FIXME: we shouldn't be using internal functions... */
75 snd_pcm_uframes_t
_snd_pcm_mmap_hw_ptr(snd_pcm_t
*pcm
);
78 /* state diagram for waveOut writing:
80 * +---------+-------------+---------------+---------------------------------+
81 * | state | function | event | new state |
82 * +---------+-------------+---------------+---------------------------------+
83 * | | open() | | STOPPED |
84 * | PAUSED | write() | | PAUSED |
85 * | STOPPED | write() | <thrd create> | PLAYING |
86 * | PLAYING | write() | HEADER | PLAYING |
87 * | (other) | write() | <error> | |
88 * | (any) | pause() | PAUSING | PAUSED |
89 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
90 * | (any) | reset() | RESETTING | STOPPED |
91 * | (any) | close() | CLOSING | CLOSED |
92 * +---------+-------------+---------------+---------------------------------+
95 /* states of the playing device */
96 #define WINE_WS_PLAYING 0
97 #define WINE_WS_PAUSED 1
98 #define WINE_WS_STOPPED 2
99 #define WINE_WS_CLOSED 3
101 /* events to be send to device */
102 enum win_wm_message
{
103 WINE_WM_PAUSING
= WM_USER
+ 1, WINE_WM_RESTARTING
, WINE_WM_RESETTING
, WINE_WM_HEADER
,
104 WINE_WM_UPDATE
, WINE_WM_BREAKLOOP
, WINE_WM_CLOSING
, WINE_WM_STARTING
, WINE_WM_STOPPING
108 #define SIGNAL_OMR(omr) do { int x = 0; write((omr)->msg_pipe[1], &x, sizeof(x)); } while (0)
109 #define CLEAR_OMR(omr) do { int x = 0; read((omr)->msg_pipe[0], &x, sizeof(x)); } while (0)
110 #define RESET_OMR(omr) do { } while (0)
111 #define WAIT_OMR(omr, sleep) \
112 do { struct pollfd pfd; pfd.fd = (omr)->msg_pipe[0]; \
113 pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)
115 #define SIGNAL_OMR(omr) do { SetEvent((omr)->msg_event); } while (0)
116 #define CLEAR_OMR(omr) do { } while (0)
117 #define RESET_OMR(omr) do { ResetEvent((omr)->msg_event); } while (0)
118 #define WAIT_OMR(omr, sleep) \
119 do { WaitForSingleObject((omr)->msg_event, sleep); } while (0)
123 enum win_wm_message msg
; /* message identifier */
124 DWORD param
; /* parameter for this message */
125 HANDLE hEvent
; /* if message is synchronous, handle of event for synchro */
128 /* implement an in-process message ring for better performance
129 * (compared to passing thru the server)
130 * this ring will be used by the input (resp output) record (resp playback) routine
132 #define ALSA_RING_BUFFER_INCREMENT 64
135 int ring_buffer_size
;
143 CRITICAL_SECTION msg_crst
;
147 volatile int state
; /* one of the WINE_WS_ manifest constants */
148 WAVEOPENDESC waveDesc
;
150 WAVEFORMATPCMEX format
;
152 char* pcmname
; /* string name of alsa PCM device */
153 char* ctlname
; /* string name of alsa control device */
154 char interface_name
[MAXPNAMELEN
* 2];
156 snd_pcm_t
* pcm
; /* handle to ALSA playback device */
158 snd_pcm_hw_params_t
* hw_params
;
160 DWORD dwBufferSize
; /* size of whole ALSA buffer in bytes */
161 LPWAVEHDR lpQueuePtr
; /* start of queued WAVEHDRs (waiting to be notified) */
162 LPWAVEHDR lpPlayPtr
; /* start of not yet fully played buffers */
164 LPWAVEHDR lpLoopPtr
; /* pointer of first buffer in loop, if any */
165 DWORD dwLoops
; /* private copy of loop counter */
167 DWORD dwPlayedTotal
; /* number of bytes actually played since opening */
168 DWORD dwWrittenTotal
; /* number of bytes written to ALSA buffer since opening */
170 /* synchronization stuff */
171 HANDLE hStartUpEvent
;
174 ALSA_MSG_RING msgRing
;
176 /* DirectSound stuff */
177 DSDRIVERDESC ds_desc
;
178 DSDRIVERCAPS ds_caps
;
180 /* Waveout only fields */
181 WAVEOUTCAPSW outcaps
;
183 snd_hctl_t
* hctl
; /* control handle for the playback volume */
185 snd_pcm_sframes_t (*write
)(snd_pcm_t
*, const void *, snd_pcm_uframes_t
);
187 DWORD dwPartialOffset
; /* Offset of not yet written bytes in lpPlayPtr */
189 /* Wavein only fields */
194 snd_pcm_sframes_t (*read
)(snd_pcm_t
*, void *, snd_pcm_uframes_t
);
196 DWORD dwPeriodSize
; /* size of OSS buffer period */
197 DWORD dwTotalRecorded
;
202 /*----------------------------------------------------------------------------
203 ** Global array of output and input devices, initialized via ALSA_WaveInit
205 #define WAVEDEV_ALLOC_EXTENT_SIZE 10
206 static WINE_WAVEDEV
*WOutDev
;
207 static DWORD ALSA_WodNumMallocedDevs
;
208 static DWORD ALSA_WodNumDevs
;
210 static WINE_WAVEDEV
*WInDev
;
211 static DWORD ALSA_WidNumMallocedDevs
;
212 static DWORD ALSA_WidNumDevs
;
214 static DWORD
wodDsCreate(UINT wDevID
, PIDSDRIVER
* drv
);
215 static DWORD
wodDsDesc(UINT wDevID
, PDSDRIVERDESC desc
);
218 /*======================================================================*
219 * Utility functions *
220 *======================================================================*/
222 /* These strings used only for tracing */
223 static const char * getCmdString(enum win_wm_message msg
)
225 static char unknown
[32];
226 #define MSG_TO_STR(x) case x: return #x
228 MSG_TO_STR(WINE_WM_PAUSING
);
229 MSG_TO_STR(WINE_WM_RESTARTING
);
230 MSG_TO_STR(WINE_WM_RESETTING
);
231 MSG_TO_STR(WINE_WM_HEADER
);
232 MSG_TO_STR(WINE_WM_UPDATE
);
233 MSG_TO_STR(WINE_WM_BREAKLOOP
);
234 MSG_TO_STR(WINE_WM_CLOSING
);
235 MSG_TO_STR(WINE_WM_STARTING
);
236 MSG_TO_STR(WINE_WM_STOPPING
);
239 sprintf(unknown
, "UNKNOWN(0x%08x)", msg
);
243 static const char * getMessage(UINT msg
)
245 static char unknown
[32];
246 #define MSG_TO_STR(x) case x: return #x
248 MSG_TO_STR(DRVM_INIT
);
249 MSG_TO_STR(DRVM_EXIT
);
250 MSG_TO_STR(DRVM_ENABLE
);
251 MSG_TO_STR(DRVM_DISABLE
);
252 MSG_TO_STR(WIDM_OPEN
);
253 MSG_TO_STR(WIDM_CLOSE
);
254 MSG_TO_STR(WIDM_ADDBUFFER
);
255 MSG_TO_STR(WIDM_PREPARE
);
256 MSG_TO_STR(WIDM_UNPREPARE
);
257 MSG_TO_STR(WIDM_GETDEVCAPS
);
258 MSG_TO_STR(WIDM_GETNUMDEVS
);
259 MSG_TO_STR(WIDM_GETPOS
);
260 MSG_TO_STR(WIDM_RESET
);
261 MSG_TO_STR(WIDM_START
);
262 MSG_TO_STR(WIDM_STOP
);
263 MSG_TO_STR(WODM_OPEN
);
264 MSG_TO_STR(WODM_CLOSE
);
265 MSG_TO_STR(WODM_WRITE
);
266 MSG_TO_STR(WODM_PAUSE
);
267 MSG_TO_STR(WODM_GETPOS
);
268 MSG_TO_STR(WODM_BREAKLOOP
);
269 MSG_TO_STR(WODM_PREPARE
);
270 MSG_TO_STR(WODM_UNPREPARE
);
271 MSG_TO_STR(WODM_GETDEVCAPS
);
272 MSG_TO_STR(WODM_GETNUMDEVS
);
273 MSG_TO_STR(WODM_GETPITCH
);
274 MSG_TO_STR(WODM_SETPITCH
);
275 MSG_TO_STR(WODM_GETPLAYBACKRATE
);
276 MSG_TO_STR(WODM_SETPLAYBACKRATE
);
277 MSG_TO_STR(WODM_GETVOLUME
);
278 MSG_TO_STR(WODM_SETVOLUME
);
279 MSG_TO_STR(WODM_RESTART
);
280 MSG_TO_STR(WODM_RESET
);
281 MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE
);
282 MSG_TO_STR(DRV_QUERYDEVICEINTERFACE
);
283 MSG_TO_STR(DRV_QUERYDSOUNDIFACE
);
284 MSG_TO_STR(DRV_QUERYDSOUNDDESC
);
287 sprintf(unknown
, "UNKNOWN(0x%04x)", msg
);
291 static const char * getFormat(WORD wFormatTag
)
293 static char unknown
[32];
294 #define FMT_TO_STR(x) case x: return #x
296 FMT_TO_STR(WAVE_FORMAT_PCM
);
297 FMT_TO_STR(WAVE_FORMAT_EXTENSIBLE
);
298 FMT_TO_STR(WAVE_FORMAT_MULAW
);
299 FMT_TO_STR(WAVE_FORMAT_ALAW
);
300 FMT_TO_STR(WAVE_FORMAT_ADPCM
);
303 sprintf(unknown
, "UNKNOWN(0x%04x)", wFormatTag
);
307 /* Allow 1% deviation for sample rates (some ES137x cards) */
308 static BOOL
NearMatch(int rate1
, int rate2
)
310 return (((100 * (rate1
- rate2
)) / rate1
) == 0);
313 static DWORD
bytes_to_mmtime(LPMMTIME lpTime
, DWORD position
,
314 WAVEFORMATPCMEX
* format
)
316 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
317 lpTime
->wType
, format
->Format
.wBitsPerSample
, format
->Format
.nSamplesPerSec
,
318 format
->Format
.nChannels
, format
->Format
.nAvgBytesPerSec
);
319 TRACE("Position in bytes=%lu\n", position
);
321 switch (lpTime
->wType
) {
323 lpTime
->u
.sample
= position
/ (format
->Format
.wBitsPerSample
/ 8 * format
->Format
.nChannels
);
324 TRACE("TIME_SAMPLES=%lu\n", lpTime
->u
.sample
);
327 lpTime
->u
.ms
= 1000.0 * position
/ (format
->Format
.wBitsPerSample
/ 8 * format
->Format
.nChannels
* format
->Format
.nSamplesPerSec
);
328 TRACE("TIME_MS=%lu\n", lpTime
->u
.ms
);
331 lpTime
->u
.smpte
.fps
= 30;
332 position
= position
/ (format
->Format
.wBitsPerSample
/ 8 * format
->Format
.nChannels
);
333 position
+= (format
->Format
.nSamplesPerSec
/ lpTime
->u
.smpte
.fps
) - 1; /* round up */
334 lpTime
->u
.smpte
.sec
= position
/ format
->Format
.nSamplesPerSec
;
335 position
-= lpTime
->u
.smpte
.sec
* format
->Format
.nSamplesPerSec
;
336 lpTime
->u
.smpte
.min
= lpTime
->u
.smpte
.sec
/ 60;
337 lpTime
->u
.smpte
.sec
-= 60 * lpTime
->u
.smpte
.min
;
338 lpTime
->u
.smpte
.hour
= lpTime
->u
.smpte
.min
/ 60;
339 lpTime
->u
.smpte
.min
-= 60 * lpTime
->u
.smpte
.hour
;
340 lpTime
->u
.smpte
.fps
= 30;
341 lpTime
->u
.smpte
.frame
= position
* lpTime
->u
.smpte
.fps
/ format
->Format
.nSamplesPerSec
;
342 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
343 lpTime
->u
.smpte
.hour
, lpTime
->u
.smpte
.min
,
344 lpTime
->u
.smpte
.sec
, lpTime
->u
.smpte
.frame
);
347 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime
->wType
);
348 lpTime
->wType
= TIME_BYTES
;
351 lpTime
->u
.cb
= position
;
352 TRACE("TIME_BYTES=%lu\n", lpTime
->u
.cb
);
355 return MMSYSERR_NOERROR
;
358 static BOOL
supportedFormat(LPWAVEFORMATEX wf
)
362 if (wf
->nSamplesPerSec
<DSBFREQUENCY_MIN
||wf
->nSamplesPerSec
>DSBFREQUENCY_MAX
)
365 if (wf
->wFormatTag
== WAVE_FORMAT_PCM
) {
366 if (wf
->nChannels
==1||wf
->nChannels
==2) {
367 if (wf
->wBitsPerSample
==8||wf
->wBitsPerSample
==16)
370 } else if (wf
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) {
371 WAVEFORMATEXTENSIBLE
* wfex
= (WAVEFORMATEXTENSIBLE
*)wf
;
373 if (wf
->cbSize
== 22 &&
374 (IsEqualGUID(&wfex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
) ||
375 IsEqualGUID(&wfex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))) {
376 if (wf
->nChannels
>=1 && wf
->nChannels
<=6) {
377 if (wf
->wBitsPerSample
==wfex
->Samples
.wValidBitsPerSample
) {
378 if (wf
->wBitsPerSample
==8||wf
->wBitsPerSample
==16||
379 wf
->wBitsPerSample
==24||wf
->wBitsPerSample
==32) {
383 WARN("wBitsPerSample != wValidBitsPerSample not supported yet\n");
386 WARN("only KSDATAFORMAT_SUBTYPE_PCM and KSDATAFORMAT_SUBTYPE_IEEE_FLOAT "
388 } else if (wf
->wFormatTag
== WAVE_FORMAT_MULAW
|| wf
->wFormatTag
== WAVE_FORMAT_ALAW
) {
389 if (wf
->wBitsPerSample
==8)
392 ERR("WAVE_FORMAT_MULAW and WAVE_FORMAT_ALAW wBitsPerSample must = 8\n");
394 } else if (wf
->wFormatTag
== WAVE_FORMAT_ADPCM
) {
395 if (wf
->wBitsPerSample
==4)
398 ERR("WAVE_FORMAT_ADPCM wBitsPerSample must = 4\n");
400 WARN("only WAVE_FORMAT_PCM and WAVE_FORMAT_EXTENSIBLE supported\n");
405 static void copy_format(LPWAVEFORMATEX wf1
, LPWAVEFORMATPCMEX wf2
)
407 unsigned int iLength
;
409 ZeroMemory(wf2
, sizeof(wf2
));
410 if (wf1
->wFormatTag
== WAVE_FORMAT_PCM
)
411 iLength
= sizeof(PCMWAVEFORMAT
);
412 else if (wf1
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
413 iLength
= sizeof(WAVEFORMATPCMEX
);
415 iLength
= sizeof(WAVEFORMATEX
) + wf1
->cbSize
;
416 if (iLength
> sizeof(WAVEFORMATPCMEX
)) {
417 ERR("calculated %u bytes, capping to %u bytes\n", iLength
, sizeof(WAVEFORMATPCMEX
));
418 iLength
= sizeof(WAVEFORMATPCMEX
);
420 memcpy(wf2
, wf1
, iLength
);
423 /*----------------------------------------------------------------------------
425 ** Retrieve a string from a registry key
427 static int ALSA_RegGetString(HKEY key
, const char *value
, char **bufp
)
434 rc
= RegQueryValueExA(key
, value
, NULL
, &type
, NULL
, &bufsize
);
435 if (rc
!= ERROR_SUCCESS
)
441 *bufp
= HeapAlloc(GetProcessHeap(), 0, bufsize
);
445 rc
= RegQueryValueExA(key
, value
, NULL
, NULL
, (LPBYTE
)*bufp
, &bufsize
);
449 /*----------------------------------------------------------------------------
450 ** ALSA_RegGetBoolean
451 ** Get a string and interpret it as a boolean
453 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
454 static int ALSA_RegGetBoolean(HKEY key
, const char *value
, BOOL
*answer
)
459 rc
= ALSA_RegGetString(key
, value
, &buf
);
463 if (IS_OPTION_TRUE(*buf
))
466 HeapFree(GetProcessHeap(), 0, buf
);
472 /*----------------------------------------------------------------------------
473 ** ALSA_RegGetBoolean
474 ** Get a string and interpret it as a DWORD
476 static int ALSA_RegGetInt(HKEY key
, const char *value
, DWORD
*answer
)
481 rc
= ALSA_RegGetString(key
, value
, &buf
);
485 HeapFree(GetProcessHeap(), 0, buf
);
491 /*======================================================================*
492 * Low level WAVE implementation *
493 *======================================================================*/
495 /*----------------------------------------------------------------------------
496 ** ALSA_TestDeviceForWine
498 ** Test to see if a given device is sufficient for Wine.
500 static int ALSA_TestDeviceForWine(int card
, int device
, snd_pcm_stream_t streamtype
)
502 snd_pcm_t
*pcm
= NULL
;
505 snd_pcm_hw_params_t
*hwparams
;
510 /* Note that the plug: device masks out a lot of info, we want to avoid that */
511 sprintf(pcmname
, "hw:%d,%d", card
, device
);
512 retcode
= snd_pcm_open(&pcm
, pcmname
, streamtype
, SND_PCM_NONBLOCK
);
515 /* Note that a busy device isn't automatically disqualified */
516 if (retcode
== (-1 * EBUSY
))
521 snd_pcm_hw_params_alloca(&hwparams
);
523 retcode
= snd_pcm_hw_params_any(pcm
, hwparams
);
526 reason
= "Could not retrieve hw_params";
530 /* set the count of channels */
531 retcode
= snd_pcm_hw_params_set_channels(pcm
, hwparams
, 2);
534 reason
= "Could not set channels";
539 retcode
= snd_pcm_hw_params_set_rate_near(pcm
, hwparams
, &rrate
, 0);
542 reason
= "Could not set rate";
548 reason
= "Rate came back as 0";
552 /* write the parameters to device */
553 retcode
= snd_pcm_hw_params(pcm
, hwparams
);
556 reason
= "Could not set hwparams";
566 if (retcode
!= 0 && retcode
!= (-1 * ENOENT
))
567 TRACE("Discarding card %d/device %d: %s [%d(%s)]\n", card
, device
, reason
, retcode
, snd_strerror(retcode
));
573 /**************************************************************************
574 * ALSA_CheckSetVolume [internal]
576 * Helper function for Alsa volume queries. This tries to simplify
577 * the process of managing the volume. All parameters are optional
578 * (pass NULL to ignore or not use).
579 * Return values are MMSYSERR_NOERROR on success, or !0 on failure;
580 * error codes are normalized into the possible documented return
581 * values from waveOutGetVolume.
583 static int ALSA_CheckSetVolume(snd_hctl_t
*hctl
, int *out_left
, int *out_right
,
584 int *out_min
, int *out_max
, int *out_step
,
585 int *new_left
, int *new_right
)
587 int rc
= MMSYSERR_NOERROR
;
589 snd_hctl_elem_t
* elem
= NULL
;
590 snd_ctl_elem_info_t
* eleminfop
= NULL
;
591 snd_ctl_elem_value_t
* elemvaluep
= NULL
;
592 snd_ctl_elem_id_t
* elemidp
= NULL
;
595 #define EXIT_ON_ERROR(f,txt,exitcode) do \
598 if ( (err = (f) ) < 0) \
600 ERR(txt " failed: %s\n", snd_strerror(err)); \
607 return MMSYSERR_NOTSUPPORTED
;
609 /* Allocate areas to return information about the volume */
610 EXIT_ON_ERROR(snd_ctl_elem_id_malloc(&elemidp
), "snd_ctl_elem_id_malloc", MMSYSERR_NOMEM
);
611 EXIT_ON_ERROR(snd_ctl_elem_value_malloc (&elemvaluep
), "snd_ctl_elem_value_malloc", MMSYSERR_NOMEM
);
612 EXIT_ON_ERROR(snd_ctl_elem_info_malloc (&eleminfop
), "snd_ctl_elem_info_malloc", MMSYSERR_NOMEM
);
613 snd_ctl_elem_id_clear(elemidp
);
614 snd_ctl_elem_value_clear(elemvaluep
);
615 snd_ctl_elem_info_clear(eleminfop
);
617 /* Setup and find an element id that exactly matches the characteristic we want
618 ** FIXME: It is probably short sighted to hard code and fixate on PCM Playback Volume */
619 snd_ctl_elem_id_set_name(elemidp
, "PCM Playback Volume");
620 snd_ctl_elem_id_set_interface(elemidp
, SND_CTL_ELEM_IFACE_MIXER
);
621 elem
= snd_hctl_find_elem(hctl
, elemidp
);
624 /* Read and return volume information */
625 EXIT_ON_ERROR(snd_hctl_elem_info(elem
, eleminfop
), "snd_hctl_elem_info", MMSYSERR_NOTSUPPORTED
);
626 value_count
= snd_ctl_elem_info_get_count(eleminfop
);
627 if (out_min
|| out_max
|| out_step
)
629 if (!snd_ctl_elem_info_is_readable(eleminfop
))
631 ERR("snd_ctl_elem_info_is_readable returned false; cannot return info\n");
632 rc
= MMSYSERR_NOTSUPPORTED
;
637 *out_min
= snd_ctl_elem_info_get_min(eleminfop
);
640 *out_max
= snd_ctl_elem_info_get_max(eleminfop
);
643 *out_step
= snd_ctl_elem_info_get_step(eleminfop
);
646 if (out_left
|| out_right
)
648 EXIT_ON_ERROR(snd_hctl_elem_read(elem
, elemvaluep
), "snd_hctl_elem_read", MMSYSERR_NOTSUPPORTED
);
651 *out_left
= snd_ctl_elem_value_get_integer(elemvaluep
, 0);
655 if (value_count
== 1)
656 *out_right
= snd_ctl_elem_value_get_integer(elemvaluep
, 0);
657 else if (value_count
== 2)
658 *out_right
= snd_ctl_elem_value_get_integer(elemvaluep
, 1);
661 ERR("Unexpected value count %d from snd_ctl_elem_info_get_count while getting volume info\n", value_count
);
669 if (new_left
|| new_right
)
671 EXIT_ON_ERROR(snd_hctl_elem_read(elem
, elemvaluep
), "snd_hctl_elem_read", MMSYSERR_NOTSUPPORTED
);
673 snd_ctl_elem_value_set_integer(elemvaluep
, 0, *new_left
);
676 if (value_count
== 1)
677 snd_ctl_elem_value_set_integer(elemvaluep
, 0, *new_right
);
678 else if (value_count
== 2)
679 snd_ctl_elem_value_set_integer(elemvaluep
, 1, *new_right
);
682 ERR("Unexpected value count %d from snd_ctl_elem_info_get_count while setting volume info\n", value_count
);
688 EXIT_ON_ERROR(snd_hctl_elem_write(elem
, elemvaluep
), "snd_hctl_elem_write", MMSYSERR_NOTSUPPORTED
);
693 ERR("Could not find 'PCM Playback Volume' element\n");
694 rc
= MMSYSERR_NOTSUPPORTED
;
703 snd_ctl_elem_value_free(elemvaluep
);
705 snd_ctl_elem_info_free(eleminfop
);
707 snd_ctl_elem_id_free(elemidp
);
713 /**************************************************************************
714 * ALSA_XRUNRecovery [internal]
716 * used to recovery from XRUN errors (buffer underflow/overflow)
718 static int ALSA_XRUNRecovery(WINE_WAVEDEV
* wwo
, int err
)
720 if (err
== -EPIPE
) { /* under-run */
721 err
= snd_pcm_prepare(wwo
->pcm
);
723 ERR( "underrun recovery failed. prepare failed: %s\n", snd_strerror(err
));
725 } else if (err
== -ESTRPIPE
) {
726 while ((err
= snd_pcm_resume(wwo
->pcm
)) == -EAGAIN
)
727 sleep(1); /* wait until the suspend flag is released */
729 err
= snd_pcm_prepare(wwo
->pcm
);
731 ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err
));
738 /**************************************************************************
739 * ALSA_TraceParameters [internal]
741 * used to trace format changes, hw and sw parameters
743 static void ALSA_TraceParameters(snd_pcm_hw_params_t
* hw_params
, snd_pcm_sw_params_t
* sw
, int full
)
746 snd_pcm_format_t format
;
747 snd_pcm_access_t access
;
748 err
= snd_pcm_hw_params_get_access(hw_params
, &access
);
749 err
= snd_pcm_hw_params_get_format(hw_params
, &format
);
751 #define X(x) ((x)? "true" : "false")
753 TRACE("FLAGS: sampleres=%s overrng=%s pause=%s resume=%s syncstart=%s batch=%s block=%s double=%s "
754 "halfd=%s joint=%s\n",
755 X(snd_pcm_hw_params_can_mmap_sample_resolution(hw_params
)),
756 X(snd_pcm_hw_params_can_overrange(hw_params
)),
757 X(snd_pcm_hw_params_can_pause(hw_params
)),
758 X(snd_pcm_hw_params_can_resume(hw_params
)),
759 X(snd_pcm_hw_params_can_sync_start(hw_params
)),
760 X(snd_pcm_hw_params_is_batch(hw_params
)),
761 X(snd_pcm_hw_params_is_block_transfer(hw_params
)),
762 X(snd_pcm_hw_params_is_double(hw_params
)),
763 X(snd_pcm_hw_params_is_half_duplex(hw_params
)),
764 X(snd_pcm_hw_params_is_joint_duplex(hw_params
)));
768 TRACE("access=%s\n", snd_pcm_access_name(access
));
771 snd_pcm_access_mask_t
* acmask
;
772 snd_pcm_access_mask_alloca(&acmask
);
773 snd_pcm_hw_params_get_access_mask(hw_params
, acmask
);
774 for ( access
= SND_PCM_ACCESS_MMAP_INTERLEAVED
; access
<= SND_PCM_ACCESS_LAST
; access
++)
775 if (snd_pcm_access_mask_test(acmask
, access
))
776 TRACE("access=%s\n", snd_pcm_access_name(access
));
781 TRACE("format=%s\n", snd_pcm_format_name(format
));
786 snd_pcm_format_mask_t
* fmask
;
788 snd_pcm_format_mask_alloca(&fmask
);
789 snd_pcm_hw_params_get_format_mask(hw_params
, fmask
);
790 for ( format
= SND_PCM_FORMAT_S8
; format
<= SND_PCM_FORMAT_LAST
; format
++)
791 if ( snd_pcm_format_mask_test(fmask
, format
) )
792 TRACE("format=%s\n", snd_pcm_format_name(format
));
798 err
= snd_pcm_hw_params_get_channels(hw_params
, &val
);
800 unsigned int min
= 0;
801 unsigned int max
= 0;
802 err
= snd_pcm_hw_params_get_channels_min(hw_params
, &min
),
803 err
= snd_pcm_hw_params_get_channels_max(hw_params
, &max
);
804 TRACE("channels_min=%u, channels_min_max=%u\n", min
, max
);
806 TRACE("channels=%d\n", val
);
811 snd_pcm_uframes_t val
=0;
812 err
= snd_pcm_hw_params_get_buffer_size(hw_params
, &val
);
814 snd_pcm_uframes_t min
= 0;
815 snd_pcm_uframes_t max
= 0;
816 err
= snd_pcm_hw_params_get_buffer_size_min(hw_params
, &min
),
817 err
= snd_pcm_hw_params_get_buffer_size_max(hw_params
, &max
);
818 TRACE("buffer_size_min=%lu, buffer_size_min_max=%lu\n", min
, max
);
820 TRACE("buffer_size=%lu\n", val
);
827 unsigned int val=0; \
828 err = snd_pcm_hw_params_get_##x(hw_params,&val, &dir); \
830 unsigned int min = 0; \
831 unsigned int max = 0; \
832 err = snd_pcm_hw_params_get_##x##_min(hw_params, &min, &dir); \
833 err = snd_pcm_hw_params_get_##x##_max(hw_params, &max, &dir); \
834 TRACE(#x "_min=%u " #x "_max=%u\n", min, max); \
836 TRACE(#x "=%d\n", val); \
845 snd_pcm_uframes_t val
=0;
846 err
= snd_pcm_hw_params_get_period_size(hw_params
, &val
, &dir
);
848 snd_pcm_uframes_t min
= 0;
849 snd_pcm_uframes_t max
= 0;
850 err
= snd_pcm_hw_params_get_period_size_min(hw_params
, &min
, &dir
),
851 err
= snd_pcm_hw_params_get_period_size_max(hw_params
, &max
, &dir
);
852 TRACE("period_size_min=%lu, period_size_min_max=%lu\n", min
, max
);
854 TRACE("period_size=%lu\n", val
);
866 /* return a string duplicated on the win32 process heap, free with HeapFree */
867 static char* ALSA_strdup(const char *s
) {
868 char *result
= HeapAlloc(GetProcessHeap(), 0, strlen(s
)+1);
875 #define ALSA_RETURN_ONFAIL(mycall) \
881 ERR("%s failed: %s(%d)\n", #mycall, snd_strerror(rc), rc); \
886 /*----------------------------------------------------------------------------
889 ** Given an ALSA PCM, figure out our HW CAPS structure info.
890 ** ctl can be null, pcm is required, as is all output parms.
893 static int ALSA_ComputeCaps(snd_ctl_t
*ctl
, snd_pcm_t
*pcm
,
894 WORD
*channels
, DWORD
*flags
, DWORD
*formats
, DWORD
*supports
)
896 snd_pcm_hw_params_t
*hw_params
;
897 snd_pcm_format_mask_t
*fmask
;
898 snd_pcm_access_mask_t
*acmask
;
899 unsigned int ratemin
= 0;
900 unsigned int ratemax
= 0;
901 unsigned int chmin
= 0;
902 unsigned int chmax
= 0;
905 snd_pcm_hw_params_alloca(&hw_params
);
906 ALSA_RETURN_ONFAIL(snd_pcm_hw_params_any(pcm
, hw_params
));
908 snd_pcm_format_mask_alloca(&fmask
);
909 snd_pcm_hw_params_get_format_mask(hw_params
, fmask
);
911 snd_pcm_access_mask_alloca(&acmask
);
912 ALSA_RETURN_ONFAIL(snd_pcm_hw_params_get_access_mask(hw_params
, acmask
));
914 ALSA_RETURN_ONFAIL(snd_pcm_hw_params_get_rate_min(hw_params
, &ratemin
, &dir
));
915 ALSA_RETURN_ONFAIL(snd_pcm_hw_params_get_rate_max(hw_params
, &ratemax
, &dir
));
916 ALSA_RETURN_ONFAIL(snd_pcm_hw_params_get_channels_min(hw_params
, &chmin
));
917 ALSA_RETURN_ONFAIL(snd_pcm_hw_params_get_channels_max(hw_params
, &chmax
));
920 if ( (r) >= ratemin && ( (r) <= ratemax || ratemax == -1) ) \
922 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_U8)) \
924 if (chmin <= 1 && 1 <= chmax) \
925 *formats |= WAVE_FORMAT_##v##M08; \
926 if (chmin <= 2 && 2 <= chmax) \
927 *formats |= WAVE_FORMAT_##v##S08; \
929 if (snd_pcm_format_mask_test( fmask, SND_PCM_FORMAT_S16_LE)) \
931 if (chmin <= 1 && 1 <= chmax) \
932 *formats |= WAVE_FORMAT_##v##M16; \
933 if (chmin <= 2 && 2 <= chmax) \
934 *formats |= WAVE_FORMAT_##v##S16; \
945 FIXME("Device has a minimum of %d channels\n", chmin
);
948 /* FIXME: is sample accurate always true ?
949 ** Can we do WAVECAPS_PITCH, WAVECAPS_SYNC, or WAVECAPS_PLAYBACKRATE? */
950 *supports
|= WAVECAPS_SAMPLEACCURATE
;
952 /* FIXME: NONITERLEAVED and COMPLEX are not supported right now */
953 if ( snd_pcm_access_mask_test( acmask
, SND_PCM_ACCESS_MMAP_INTERLEAVED
) )
954 *supports
|= WAVECAPS_DIRECTSOUND
;
956 /* check for volume control support */
958 *supports
|= WAVECAPS_VOLUME
;
960 if (chmin
<= 2 && 2 <= chmax
)
961 *supports
|= WAVECAPS_LRVOLUME
;
964 if (*formats
& (WAVE_FORMAT_1M08
| WAVE_FORMAT_2M08
|
965 WAVE_FORMAT_4M08
| WAVE_FORMAT_48M08
|
966 WAVE_FORMAT_96M08
| WAVE_FORMAT_1M16
|
967 WAVE_FORMAT_2M16
| WAVE_FORMAT_4M16
|
968 WAVE_FORMAT_48M16
| WAVE_FORMAT_96M16
) )
969 *flags
|= DSCAPS_PRIMARYMONO
;
971 if (*formats
& (WAVE_FORMAT_1S08
| WAVE_FORMAT_2S08
|
972 WAVE_FORMAT_4S08
| WAVE_FORMAT_48S08
|
973 WAVE_FORMAT_96S08
| WAVE_FORMAT_1S16
|
974 WAVE_FORMAT_2S16
| WAVE_FORMAT_4S16
|
975 WAVE_FORMAT_48S16
| WAVE_FORMAT_96S16
) )
976 *flags
|= DSCAPS_PRIMARYSTEREO
;
978 if (*formats
& (WAVE_FORMAT_1M08
| WAVE_FORMAT_2M08
|
979 WAVE_FORMAT_4M08
| WAVE_FORMAT_48M08
|
980 WAVE_FORMAT_96M08
| WAVE_FORMAT_1S08
|
981 WAVE_FORMAT_2S08
| WAVE_FORMAT_4S08
|
982 WAVE_FORMAT_48S08
| WAVE_FORMAT_96S08
) )
983 *flags
|= DSCAPS_PRIMARY8BIT
;
985 if (*formats
& (WAVE_FORMAT_1M16
| WAVE_FORMAT_2M16
|
986 WAVE_FORMAT_4M16
| WAVE_FORMAT_48M16
|
987 WAVE_FORMAT_96M16
| WAVE_FORMAT_1S16
|
988 WAVE_FORMAT_2S16
| WAVE_FORMAT_4S16
|
989 WAVE_FORMAT_48S16
| WAVE_FORMAT_96S16
) )
990 *flags
|= DSCAPS_PRIMARY16BIT
;
995 /*----------------------------------------------------------------------------
996 ** ALSA_AddCommonDevice
998 ** Perform Alsa initialization common to both capture and playback
1000 ** Side Effect: ww->pcname and ww->ctlname may need to be freed.
1002 ** Note: this was originally coded by using snd_pcm_name(pcm), until
1003 ** I discovered that with at least one version of alsa lib,
1004 ** the use of a pcm named default:0 would cause snd_pcm_name() to fail.
1005 ** So passing the name in is logically extraneous. Sigh.
1007 static int ALSA_AddCommonDevice(snd_ctl_t
*ctl
, snd_pcm_t
*pcm
, const char *pcmname
, WINE_WAVEDEV
*ww
)
1009 snd_pcm_info_t
*infop
;
1011 snd_pcm_info_alloca(&infop
);
1012 ALSA_RETURN_ONFAIL(snd_pcm_info(pcm
, infop
));
1015 ww
->pcmname
= ALSA_strdup(pcmname
);
1019 if (ctl
&& snd_ctl_name(ctl
))
1020 ww
->ctlname
= ALSA_strdup(snd_ctl_name(ctl
));
1022 strcpy(ww
->interface_name
, "winealsa: ");
1023 memcpy(ww
->interface_name
+ strlen(ww
->interface_name
),
1025 min(strlen(ww
->pcmname
), sizeof(ww
->interface_name
) - strlen("winealsa: ")));
1027 strcpy(ww
->ds_desc
.szDrvname
, "winealsa.drv");
1029 memcpy(ww
->ds_desc
.szDesc
, snd_pcm_info_get_name(infop
),
1030 min( (sizeof(ww
->ds_desc
.szDesc
) - 1), strlen(snd_pcm_info_get_name(infop
))) );
1032 ww
->ds_caps
.dwMinSecondarySampleRate
= DSBFREQUENCY_MIN
;
1033 ww
->ds_caps
.dwMaxSecondarySampleRate
= DSBFREQUENCY_MAX
;
1034 ww
->ds_caps
.dwPrimaryBuffers
= 1;
1039 /*----------------------------------------------------------------------------
1042 static void ALSA_FreeDevice(WINE_WAVEDEV
*ww
)
1044 HeapFree(GetProcessHeap(), 0, ww
->pcmname
);
1047 HeapFree(GetProcessHeap(), 0, ww
->ctlname
);
1051 /*----------------------------------------------------------------------------
1052 ** ALSA_AddDeviceToArray
1054 ** Dynamically size one of the wavein or waveout arrays of devices,
1055 ** and add a fully configured device node to the array.
1058 static int ALSA_AddDeviceToArray(WINE_WAVEDEV
*ww
, WINE_WAVEDEV
**array
,
1059 DWORD
*count
, DWORD
*alloced
, int isdefault
)
1063 if (*count
>= *alloced
)
1065 (*alloced
) += WAVEDEV_ALLOC_EXTENT_SIZE
;
1067 *array
= HeapAlloc(GetProcessHeap(), 0, sizeof(*ww
) * (*alloced
));
1069 *array
= HeapReAlloc(GetProcessHeap(), 0, *array
, sizeof(*ww
) * (*alloced
));
1077 /* If this is the default, arrange for it to be the first element */
1078 if (isdefault
&& i
> 0)
1080 (*array
)[*count
] = (*array
)[0];
1090 /*----------------------------------------------------------------------------
1091 ** ALSA_AddPlaybackDevice
1093 ** Add a given Alsa device to Wine's internal list of Playback
1096 static int ALSA_AddPlaybackDevice(snd_ctl_t
*ctl
, snd_pcm_t
*pcm
, const char *pcmname
, int isdefault
)
1101 memset(&wwo
, '\0', sizeof(wwo
));
1103 rc
= ALSA_AddCommonDevice(ctl
, pcm
, pcmname
, &wwo
);
1107 MultiByteToWideChar(CP_ACP
, 0, wwo
.ds_desc
.szDesc
, -1,
1108 wwo
.outcaps
.szPname
, sizeof(wwo
.outcaps
.szPname
)/sizeof(WCHAR
));
1109 wwo
.outcaps
.szPname
[sizeof(wwo
.outcaps
.szPname
)/sizeof(WCHAR
) - 1] = '\0';
1111 wwo
.outcaps
.wMid
= MM_CREATIVE
;
1112 wwo
.outcaps
.wPid
= MM_CREATIVE_SBP16_WAVEOUT
;
1113 wwo
.outcaps
.vDriverVersion
= 0x0100;
1115 rc
= ALSA_ComputeCaps(ctl
, pcm
, &wwo
.outcaps
.wChannels
, &wwo
.ds_caps
.dwFlags
,
1116 &wwo
.outcaps
.dwFormats
, &wwo
.outcaps
.dwSupport
);
1119 WARN("Error calculating device caps for pcm [%s]\n", wwo
.pcmname
);
1120 ALSA_FreeDevice(&wwo
);
1124 rc
= ALSA_AddDeviceToArray(&wwo
, &WOutDev
, &ALSA_WodNumDevs
, &ALSA_WodNumMallocedDevs
, isdefault
);
1126 ALSA_FreeDevice(&wwo
);
1130 /*----------------------------------------------------------------------------
1131 ** ALSA_AddCaptureDevice
1133 ** Add a given Alsa device to Wine's internal list of Capture
1136 static int ALSA_AddCaptureDevice(snd_ctl_t
*ctl
, snd_pcm_t
*pcm
, const char *pcmname
, int isdefault
)
1141 memset(&wwi
, '\0', sizeof(wwi
));
1143 rc
= ALSA_AddCommonDevice(ctl
, pcm
, pcmname
, &wwi
);
1147 MultiByteToWideChar(CP_ACP
, 0, wwi
.ds_desc
.szDesc
, -1,
1148 wwi
.incaps
.szPname
, sizeof(wwi
.incaps
.szPname
) / sizeof(WCHAR
));
1149 wwi
.incaps
.szPname
[sizeof(wwi
.incaps
.szPname
)/sizeof(WCHAR
) - 1] = '\0';
1151 wwi
.incaps
.wMid
= MM_CREATIVE
;
1152 wwi
.incaps
.wPid
= MM_CREATIVE_SBP16_WAVEOUT
;
1153 wwi
.incaps
.vDriverVersion
= 0x0100;
1155 rc
= ALSA_ComputeCaps(ctl
, pcm
, &wwi
.incaps
.wChannels
, &wwi
.ds_caps
.dwFlags
,
1156 &wwi
.incaps
.dwFormats
, &wwi
.dwSupport
);
1159 WARN("Error calculating device caps for pcm [%s]\n", wwi
.pcmname
);
1160 ALSA_FreeDevice(&wwi
);
1164 rc
= ALSA_AddDeviceToArray(&wwi
, &WInDev
, &ALSA_WidNumDevs
, &ALSA_WidNumMallocedDevs
, isdefault
);
1166 ALSA_FreeDevice(&wwi
);
1170 /*----------------------------------------------------------------------------
1171 ** ALSA_CheckEnvironment
1173 ** Given an Alsa style configuration node, scan its subitems
1174 ** for environment variable names, and use them to find an override,
1176 ** This is essentially a long and convolunted way of doing:
1177 ** getenv("ALSA_CARD")
1178 ** getenv("ALSA_CTL_CARD")
1179 ** getenv("ALSA_PCM_CARD")
1180 ** getenv("ALSA_PCM_DEVICE")
1182 ** The output value is set with the atoi() of the first environment
1183 ** variable found to be set, if any; otherwise, it is left alone
1185 static void ALSA_CheckEnvironment(snd_config_t
*node
, int *outvalue
)
1187 snd_config_iterator_t iter
;
1189 for (iter
= snd_config_iterator_first(node
);
1190 iter
!= snd_config_iterator_end(node
);
1191 iter
= snd_config_iterator_next(iter
))
1193 snd_config_t
*leaf
= snd_config_iterator_entry(iter
);
1194 if (snd_config_get_type(leaf
) == SND_CONFIG_TYPE_STRING
)
1197 if (snd_config_get_string(leaf
, &value
) >= 0)
1199 char *p
= getenv(value
);
1202 *outvalue
= atoi(p
);
1210 /*----------------------------------------------------------------------------
1211 ** ALSA_DefaultDevices
1213 ** Jump through Alsa style hoops to (hopefully) properly determine
1214 ** Alsa defaults for CTL Card #, as well as for PCM Card + Device #.
1215 ** We'll also find out if the user has set any of the environment
1216 ** variables that specify we're to use a specific card or device.
1219 ** directhw Whether to use a direct hardware device or not;
1220 ** essentially switches the pcm device name from
1221 ** one of 'default:X' or 'plughw:X' to "hw:X"
1222 ** defctlcard If !NULL, will hold the ctl card number given
1223 ** by the ALSA config as the default
1224 ** defpcmcard If !NULL, default pcm card #
1225 ** defpcmdev If !NULL, default pcm device #
1226 ** fixedctlcard If !NULL, and the user set the appropriate
1227 ** environment variable, we'll set to the
1228 ** card the user specified.
1229 ** fixedpcmcard If !NULL, and the user set the appropriate
1230 ** environment variable, we'll set to the
1231 ** card the user specified.
1232 ** fixedpcmdev If !NULL, and the user set the appropriate
1233 ** environment variable, we'll set to the
1234 ** device the user specified.
1236 ** Returns: 0 on success, < 0 on failiure
1238 static int ALSA_DefaultDevices(int directhw
,
1240 long *defpcmcard
, long *defpcmdev
,
1242 int *fixedpcmcard
, int *fixedpcmdev
)
1244 snd_config_t
*configp
;
1245 char pcmsearch
[256];
1247 ALSA_RETURN_ONFAIL(snd_config_update());
1250 if (snd_config_search(snd_config
, "defaults.ctl.card", &configp
) >= 0)
1251 snd_config_get_integer(configp
, defctlcard
);
1254 if (snd_config_search(snd_config
, "defaults.pcm.card", &configp
) >= 0)
1255 snd_config_get_integer(configp
, defpcmcard
);
1258 if (snd_config_search(snd_config
, "defaults.pcm.device", &configp
) >= 0)
1259 snd_config_get_integer(configp
, defpcmdev
);
1264 if (snd_config_search(snd_config
, "ctl.hw.@args.CARD.default.vars", &configp
) >= 0)
1265 ALSA_CheckEnvironment(configp
, fixedctlcard
);
1270 sprintf(pcmsearch
, "pcm.%s.@args.CARD.default.vars", directhw
? "hw" : "plughw");
1271 if (snd_config_search(snd_config
, pcmsearch
, &configp
) >= 0)
1272 ALSA_CheckEnvironment(configp
, fixedpcmcard
);
1277 sprintf(pcmsearch
, "pcm.%s.@args.DEV.default.vars", directhw
? "hw" : "plughw");
1278 if (snd_config_search(snd_config
, pcmsearch
, &configp
) >= 0)
1279 ALSA_CheckEnvironment(configp
, fixedpcmdev
);
1286 /*----------------------------------------------------------------------------
1289 ** Iterate through all discoverable ALSA cards, searching
1290 ** for usable PCM devices.
1293 ** directhw Whether to use a direct hardware device or not;
1294 ** essentially switches the pcm device name from
1295 ** one of 'default:X' or 'plughw:X' to "hw:X"
1296 ** defctlcard Alsa's notion of the default ctl card.
1297 ** defpcmcard . pcm card
1298 ** defpcmdev . pcm device
1299 ** fixedctlcard If not -1, then gives the value of ALSA_CTL_CARD
1300 ** or equivalent environment variable
1301 ** fixedpcmcard If not -1, then gives the value of ALSA_PCM_CARD
1302 ** or equivalent environment variable
1303 ** fixedpcmdev If not -1, then gives the value of ALSA_PCM_DEVICE
1304 ** or equivalent environment variable
1306 ** Returns: 0 on success, < 0 on failiure
1308 static int ALSA_ScanDevices(int directhw
,
1309 long defctlcard
, long defpcmcard
, long defpcmdev
,
1310 int fixedctlcard
, int fixedpcmcard
, int fixedpcmdev
)
1312 int card
= fixedpcmcard
;
1313 int scan_devices
= (fixedpcmdev
== -1);
1315 /*------------------------------------------------------------------------
1316 ** Loop through all available cards
1317 **----------------------------------------------------------------------*/
1319 snd_card_next(&card
);
1321 for (; card
!= -1; snd_card_next(&card
))
1328 /*--------------------------------------------------------------------
1329 ** Try to open a ctl handle; Wine doesn't absolutely require one,
1330 ** but it does allow for volume control and for device scanning
1331 **------------------------------------------------------------------*/
1332 sprintf(ctlname
, "default:%d", fixedctlcard
== -1 ? card
: fixedctlcard
);
1333 rc
= snd_ctl_open(&ctl
, ctlname
, SND_CTL_NONBLOCK
);
1336 sprintf(ctlname
, "hw:%d", fixedctlcard
== -1 ? card
: fixedctlcard
);
1337 rc
= snd_ctl_open(&ctl
, ctlname
, SND_CTL_NONBLOCK
);
1342 WARN("Unable to open an alsa ctl for [%s] (pcm card %d): %s; not scanning devices\n",
1343 ctlname
, card
, snd_strerror(rc
));
1344 if (fixedpcmdev
== -1)
1348 /*--------------------------------------------------------------------
1349 ** Loop through all available devices on this card
1350 **------------------------------------------------------------------*/
1351 device
= fixedpcmdev
;
1353 snd_ctl_pcm_next_device(ctl
, &device
);
1355 for (; device
!= -1; snd_ctl_pcm_next_device(ctl
, &device
))
1357 char defaultpcmname
[256];
1358 char plugpcmname
[256];
1359 char hwpcmname
[256];
1360 char *pcmname
= NULL
;
1363 sprintf(defaultpcmname
, "default:%d", card
);
1364 sprintf(plugpcmname
, "plughw:%d,%d", card
, device
);
1365 sprintf(hwpcmname
, "hw:%d,%d", card
, device
);
1367 /*----------------------------------------------------------------
1368 ** See if it's a valid playback device
1369 **--------------------------------------------------------------*/
1370 if (ALSA_TestDeviceForWine(card
, device
, SND_PCM_STREAM_PLAYBACK
) == 0)
1372 /* If we can, try the default:X device name first */
1373 if (! scan_devices
&& ! directhw
)
1375 pcmname
= defaultpcmname
;
1376 rc
= snd_pcm_open(&pcm
, pcmname
, SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
);
1383 pcmname
= directhw
? hwpcmname
: plugpcmname
;
1384 rc
= snd_pcm_open(&pcm
, pcmname
, SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
);
1389 if (defctlcard
== card
&& defpcmcard
== card
&& defpcmdev
== device
)
1390 ALSA_AddPlaybackDevice(ctl
, pcm
, pcmname
, TRUE
);
1392 ALSA_AddPlaybackDevice(ctl
, pcm
, pcmname
, FALSE
);
1397 TRACE("Device [%s/%s] failed to open for playback: %s\n",
1398 directhw
|| scan_devices
? "(N/A)" : defaultpcmname
,
1399 directhw
? hwpcmname
: plugpcmname
,
1404 /*----------------------------------------------------------------
1405 ** See if it's a valid capture device
1406 **--------------------------------------------------------------*/
1407 if (ALSA_TestDeviceForWine(card
, device
, SND_PCM_STREAM_CAPTURE
) == 0)
1409 /* If we can, try the default:X device name first */
1410 if (! scan_devices
&& ! directhw
)
1412 pcmname
= defaultpcmname
;
1413 rc
= snd_pcm_open(&pcm
, pcmname
, SND_PCM_STREAM_CAPTURE
, SND_PCM_NONBLOCK
);
1420 pcmname
= directhw
? hwpcmname
: plugpcmname
;
1421 rc
= snd_pcm_open(&pcm
, pcmname
, SND_PCM_STREAM_CAPTURE
, SND_PCM_NONBLOCK
);
1426 if (defctlcard
== card
&& defpcmcard
== card
&& defpcmdev
== device
)
1427 ALSA_AddCaptureDevice(ctl
, pcm
, pcmname
, TRUE
);
1429 ALSA_AddCaptureDevice(ctl
, pcm
, pcmname
, FALSE
);
1435 TRACE("Device [%s/%s] failed to open for capture: %s\n",
1436 directhw
|| scan_devices
? "(N/A)" : defaultpcmname
,
1437 directhw
? hwpcmname
: plugpcmname
,
1449 /*--------------------------------------------------------------------
1450 ** If the user has set env variables such that we're pegged to
1451 ** a specific card, then break after we've examined it
1452 **------------------------------------------------------------------*/
1453 if (fixedpcmcard
!= -1)
1461 /*----------------------------------------------------------------------------
1462 ** ALSA_PerformDefaultScan
1463 ** Perform the basic default scanning for devices within ALSA.
1464 ** The hope is that this routine implements a 'correct'
1465 ** scanning algorithm from the Alsalib point of view.
1467 ** Note that Wine, overall, has other mechanisms to
1468 ** override and specify exact CTL and PCM device names,
1469 ** but this routine is imagined as the default that
1470 ** 99% of users will use.
1472 ** The basic algorithm is simple:
1473 ** Use snd_card_next to iterate cards; within cards, use
1474 ** snd_ctl_pcm_next_device to iterate through devices.
1476 ** We add a little complexity by taking into consideration
1477 ** environment variables such as ALSA_CARD (et all), and by
1478 ** detecting when a given device matches the default specified
1482 ** directhw If !0, indicates we should use the hw:X
1483 ** PCM interface, rather than first try
1484 ** the 'default' device followed by the plughw
1485 ** device. (default and plughw do fancy mixing
1486 ** and audio scaling, if they are available).
1487 ** devscan If TRUE, we should scan all devices, not
1488 ** juse use device 0 on each card
1494 ** Invokes the ALSA_AddXXXDevice functions on valid
1497 static int ALSA_PerformDefaultScan(int directhw
, BOOL devscan
)
1499 long defctlcard
= -1, defpcmcard
= -1, defpcmdev
= -1;
1500 int fixedctlcard
= -1, fixedpcmcard
= -1, fixedpcmdev
= -1;
1503 /* FIXME: We should dlsym the new snd_names_list/snd_names_list_free 1.0.9 apis,
1504 ** and use them instead of this scan mechanism if they are present */
1506 rc
= ALSA_DefaultDevices(directhw
, &defctlcard
, &defpcmcard
, &defpcmdev
,
1507 &fixedctlcard
, &fixedpcmcard
, &fixedpcmdev
);
1511 if (fixedpcmdev
== -1 && ! devscan
)
1514 return(ALSA_ScanDevices(directhw
, defctlcard
, defpcmcard
, defpcmdev
, fixedctlcard
, fixedpcmcard
, fixedpcmdev
));
1518 /*----------------------------------------------------------------------------
1519 ** ALSA_AddUserSpecifiedDevice
1520 ** Add a device given from the registry
1522 static int ALSA_AddUserSpecifiedDevice(const char *ctlname
, const char *pcmname
)
1526 snd_ctl_t
*ctl
= NULL
;
1527 snd_pcm_t
*pcm
= NULL
;
1531 rc
= snd_ctl_open(&ctl
, ctlname
, SND_CTL_NONBLOCK
);
1536 rc
= snd_pcm_open(&pcm
, pcmname
, SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
);
1539 ALSA_AddPlaybackDevice(ctl
, pcm
, pcmname
, FALSE
);
1544 rc
= snd_pcm_open(&pcm
, pcmname
, SND_PCM_STREAM_CAPTURE
, SND_PCM_NONBLOCK
);
1547 ALSA_AddCaptureDevice(ctl
, pcm
, pcmname
, FALSE
);
1559 /*----------------------------------------------------------------------------
1561 ** Initialize the Wine Alsa sub system.
1562 ** The main task is to probe for and store a list of all appropriate playback
1563 ** and capture devices.
1564 ** Key control points are from the registry key:
1565 ** [Software\Wine\Alsa Driver]
1566 ** AutoScanCards Whether or not to scan all known sound cards
1567 ** and add them to Wine's list (default yes)
1568 ** AutoScanDevices Whether or not to scan all known PCM devices
1569 ** on each card (default no)
1570 ** UseDirectHW Whether or not to use the hw:X device,
1571 ** instead of the fancy default:X or plughw:X device.
1572 ** The hw:X device goes straight to the hardware
1573 ** without any fancy mixing or audio scaling in between.
1574 ** DeviceCount If present, specifies the number of hard coded
1575 ** Alsa devices to add to Wine's list; default 0
1576 ** DevicePCMn Specifies the Alsa PCM devices to open for
1577 ** Device n (where n goes from 1 to DeviceCount)
1578 ** DeviceCTLn Specifies the Alsa control devices to open for
1579 ** Device n (where n goes from 1 to DeviceCount)
1581 ** Using AutoScanCards no, and then Devicexxx info
1582 ** is a way to exactly specify the devices used by Wine.
1585 LONG
ALSA_WaveInit(void)
1588 BOOL AutoScanCards
= TRUE
;
1589 BOOL AutoScanDevices
= FALSE
;
1590 BOOL UseDirectHW
= FALSE
;
1591 DWORD DeviceCount
= 0;
1595 if (!wine_dlopen("libasound.so.2", RTLD_LAZY
|RTLD_GLOBAL
, NULL
, 0))
1597 ERR("Error: ALSA lib needs to be loaded with flags RTLD_LAZY and RTLD_GLOBAL.\n");
1601 /* @@ Wine registry key: HKCU\Software\Wine\Alsa Driver */
1602 rc
= RegOpenKeyExA(HKEY_CURRENT_USER
, "Software\\Wine\\Alsa Driver", 0, KEY_QUERY_VALUE
, &key
);
1603 if (rc
== ERROR_SUCCESS
)
1605 ALSA_RegGetBoolean(key
, "AutoScanCards", &AutoScanCards
);
1606 ALSA_RegGetBoolean(key
, "AutoScanDevices", &AutoScanDevices
);
1607 ALSA_RegGetBoolean(key
, "UseDirectHW", &UseDirectHW
);
1608 ALSA_RegGetInt(key
, "DeviceCount", &DeviceCount
);
1612 rc
= ALSA_PerformDefaultScan(UseDirectHW
, AutoScanDevices
);
1614 for (i
= 0; i
< DeviceCount
; i
++)
1616 char *ctl_name
= NULL
;
1617 char *pcm_name
= NULL
;
1620 sprintf(value
, "DevicePCM%d", i
+ 1);
1621 if (ALSA_RegGetString(key
, value
, &pcm_name
) == ERROR_SUCCESS
)
1623 sprintf(value
, "DeviceCTL%d", i
+ 1);
1624 ALSA_RegGetString(key
, value
, &ctl_name
);
1625 ALSA_AddUserSpecifiedDevice(ctl_name
, pcm_name
);
1628 HeapFree(GetProcessHeap(), 0, ctl_name
);
1629 HeapFree(GetProcessHeap(), 0, pcm_name
);
1638 /******************************************************************
1639 * ALSA_InitRingMessage
1641 * Initialize the ring of messages for passing between driver's caller and playback/record
1644 static int ALSA_InitRingMessage(ALSA_MSG_RING
* omr
)
1647 omr
->msg_tosave
= 0;
1648 #ifdef USE_PIPE_SYNC
1649 if (pipe(omr
->msg_pipe
) < 0) {
1650 omr
->msg_pipe
[0] = -1;
1651 omr
->msg_pipe
[1] = -1;
1652 ERR("could not create pipe, error=%s\n", strerror(errno
));
1655 omr
->msg_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1657 omr
->ring_buffer_size
= ALSA_RING_BUFFER_INCREMENT
;
1658 omr
->messages
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,omr
->ring_buffer_size
* sizeof(ALSA_MSG
));
1660 InitializeCriticalSection(&omr
->msg_crst
);
1661 omr
->msg_crst
.DebugInfo
->Spare
[0] = (DWORD_PTR
)"WINEALSA_msg_crst";
1665 /******************************************************************
1666 * ALSA_DestroyRingMessage
1669 static int ALSA_DestroyRingMessage(ALSA_MSG_RING
* omr
)
1671 #ifdef USE_PIPE_SYNC
1672 close(omr
->msg_pipe
[0]);
1673 close(omr
->msg_pipe
[1]);
1675 CloseHandle(omr
->msg_event
);
1677 HeapFree(GetProcessHeap(),0,omr
->messages
);
1678 omr
->ring_buffer_size
= 0;
1679 omr
->msg_crst
.DebugInfo
->Spare
[0] = 0;
1680 DeleteCriticalSection(&omr
->msg_crst
);
1684 /******************************************************************
1685 * ALSA_AddRingMessage
1687 * Inserts a new message into the ring (should be called from DriverProc derivated routines)
1689 static int ALSA_AddRingMessage(ALSA_MSG_RING
* omr
, enum win_wm_message msg
, DWORD param
, BOOL wait
)
1691 HANDLE hEvent
= INVALID_HANDLE_VALUE
;
1693 EnterCriticalSection(&omr
->msg_crst
);
1694 if ((omr
->msg_toget
== ((omr
->msg_tosave
+ 1) % omr
->ring_buffer_size
)))
1696 int old_ring_buffer_size
= omr
->ring_buffer_size
;
1697 omr
->ring_buffer_size
+= ALSA_RING_BUFFER_INCREMENT
;
1698 TRACE("omr->ring_buffer_size=%d\n",omr
->ring_buffer_size
);
1699 omr
->messages
= HeapReAlloc(GetProcessHeap(),0,omr
->messages
, omr
->ring_buffer_size
* sizeof(ALSA_MSG
));
1700 /* Now we need to rearrange the ring buffer so that the new
1701 buffers just allocated are in between omr->msg_tosave and
1704 if (omr
->msg_tosave
< omr
->msg_toget
)
1706 memmove(&(omr
->messages
[omr
->msg_toget
+ ALSA_RING_BUFFER_INCREMENT
]),
1707 &(omr
->messages
[omr
->msg_toget
]),
1708 sizeof(ALSA_MSG
)*(old_ring_buffer_size
- omr
->msg_toget
)
1710 omr
->msg_toget
+= ALSA_RING_BUFFER_INCREMENT
;
1715 hEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1716 if (hEvent
== INVALID_HANDLE_VALUE
)
1718 ERR("can't create event !?\n");
1719 LeaveCriticalSection(&omr
->msg_crst
);
1722 if (omr
->msg_toget
!= omr
->msg_tosave
&& omr
->messages
[omr
->msg_toget
].msg
!= WINE_WM_HEADER
)
1723 FIXME("two fast messages in the queue!!!! toget = %d(%s), tosave=%d(%s)\n",
1724 omr
->msg_toget
,getCmdString(omr
->messages
[omr
->msg_toget
].msg
),
1725 omr
->msg_tosave
,getCmdString(omr
->messages
[omr
->msg_tosave
].msg
));
1727 /* fast messages have to be added at the start of the queue */
1728 omr
->msg_toget
= (omr
->msg_toget
+ omr
->ring_buffer_size
- 1) % omr
->ring_buffer_size
;
1730 omr
->messages
[omr
->msg_toget
].msg
= msg
;
1731 omr
->messages
[omr
->msg_toget
].param
= param
;
1732 omr
->messages
[omr
->msg_toget
].hEvent
= hEvent
;
1736 omr
->messages
[omr
->msg_tosave
].msg
= msg
;
1737 omr
->messages
[omr
->msg_tosave
].param
= param
;
1738 omr
->messages
[omr
->msg_tosave
].hEvent
= INVALID_HANDLE_VALUE
;
1739 omr
->msg_tosave
= (omr
->msg_tosave
+ 1) % omr
->ring_buffer_size
;
1741 LeaveCriticalSection(&omr
->msg_crst
);
1742 /* signal a new message */
1746 /* wait for playback/record thread to have processed the message */
1747 WaitForSingleObject(hEvent
, INFINITE
);
1748 CloseHandle(hEvent
);
1753 /******************************************************************
1754 * ALSA_RetrieveRingMessage
1756 * Get a message from the ring. Should be called by the playback/record thread.
1758 static int ALSA_RetrieveRingMessage(ALSA_MSG_RING
* omr
,
1759 enum win_wm_message
*msg
, DWORD
*param
, HANDLE
*hEvent
)
1761 EnterCriticalSection(&omr
->msg_crst
);
1763 if (omr
->msg_toget
== omr
->msg_tosave
) /* buffer empty ? */
1765 LeaveCriticalSection(&omr
->msg_crst
);
1769 *msg
= omr
->messages
[omr
->msg_toget
].msg
;
1770 omr
->messages
[omr
->msg_toget
].msg
= 0;
1771 *param
= omr
->messages
[omr
->msg_toget
].param
;
1772 *hEvent
= omr
->messages
[omr
->msg_toget
].hEvent
;
1773 omr
->msg_toget
= (omr
->msg_toget
+ 1) % omr
->ring_buffer_size
;
1775 LeaveCriticalSection(&omr
->msg_crst
);
1779 /******************************************************************
1780 * ALSA_PeekRingMessage
1782 * Peek at a message from the ring but do not remove it.
1783 * Should be called by the playback/record thread.
1785 static int ALSA_PeekRingMessage(ALSA_MSG_RING
* omr
,
1786 enum win_wm_message
*msg
,
1787 DWORD
*param
, HANDLE
*hEvent
)
1789 EnterCriticalSection(&omr
->msg_crst
);
1791 if (omr
->msg_toget
== omr
->msg_tosave
) /* buffer empty ? */
1793 LeaveCriticalSection(&omr
->msg_crst
);
1797 *msg
= omr
->messages
[omr
->msg_toget
].msg
;
1798 *param
= omr
->messages
[omr
->msg_toget
].param
;
1799 *hEvent
= omr
->messages
[omr
->msg_toget
].hEvent
;
1800 LeaveCriticalSection(&omr
->msg_crst
);
1804 /*======================================================================*
1805 * Low level WAVE OUT implementation *
1806 *======================================================================*/
1808 /**************************************************************************
1809 * wodNotifyClient [internal]
1811 static DWORD
wodNotifyClient(WINE_WAVEDEV
* wwo
, WORD wMsg
, DWORD dwParam1
, DWORD dwParam2
)
1813 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg
, dwParam1
, dwParam2
);
1819 if (wwo
->wFlags
!= DCB_NULL
&&
1820 !DriverCallback(wwo
->waveDesc
.dwCallback
, wwo
->wFlags
, (HDRVR
)wwo
->waveDesc
.hWave
,
1821 wMsg
, wwo
->waveDesc
.dwInstance
, dwParam1
, dwParam2
)) {
1822 WARN("can't notify client !\n");
1823 return MMSYSERR_ERROR
;
1827 FIXME("Unknown callback message %u\n", wMsg
);
1828 return MMSYSERR_INVALPARAM
;
1830 return MMSYSERR_NOERROR
;
1833 /**************************************************************************
1834 * wodUpdatePlayedTotal [internal]
1837 static BOOL
wodUpdatePlayedTotal(WINE_WAVEDEV
* wwo
, snd_pcm_status_t
* ps
)
1839 snd_pcm_sframes_t delay
= 0;
1840 snd_pcm_state_t state
;
1842 state
= snd_pcm_state(wwo
->pcm
);
1843 snd_pcm_delay(wwo
->pcm
, &delay
);
1845 /* A delay < 0 indicates an underrun; for our purposes that's 0. */
1846 if ( (state
!= SND_PCM_STATE_RUNNING
&& state
!= SND_PCM_STATE_PREPARED
) || (delay
< 0))
1848 WARN("Unexpected state (%d) or delay (%ld) while updating Total Played, resetting\n", state
, delay
);
1851 wwo
->dwPlayedTotal
= wwo
->dwWrittenTotal
- snd_pcm_frames_to_bytes(wwo
->pcm
, delay
);
1855 /**************************************************************************
1856 * wodPlayer_BeginWaveHdr [internal]
1858 * Makes the specified lpWaveHdr the currently playing wave header.
1859 * If the specified wave header is a begin loop and we're not already in
1860 * a loop, setup the loop.
1862 static void wodPlayer_BeginWaveHdr(WINE_WAVEDEV
* wwo
, LPWAVEHDR lpWaveHdr
)
1864 wwo
->lpPlayPtr
= lpWaveHdr
;
1866 if (!lpWaveHdr
) return;
1868 if (lpWaveHdr
->dwFlags
& WHDR_BEGINLOOP
) {
1869 if (wwo
->lpLoopPtr
) {
1870 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr
);
1872 TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr
->dwLoops
, lpWaveHdr
);
1873 wwo
->lpLoopPtr
= lpWaveHdr
;
1874 /* Windows does not touch WAVEHDR.dwLoops,
1875 * so we need to make an internal copy */
1876 wwo
->dwLoops
= lpWaveHdr
->dwLoops
;
1879 wwo
->dwPartialOffset
= 0;
1882 /**************************************************************************
1883 * wodPlayer_PlayPtrNext [internal]
1885 * Advance the play pointer to the next waveheader, looping if required.
1887 static LPWAVEHDR
wodPlayer_PlayPtrNext(WINE_WAVEDEV
* wwo
)
1889 LPWAVEHDR lpWaveHdr
= wwo
->lpPlayPtr
;
1891 wwo
->dwPartialOffset
= 0;
1892 if ((lpWaveHdr
->dwFlags
& WHDR_ENDLOOP
) && wwo
->lpLoopPtr
) {
1893 /* We're at the end of a loop, loop if required */
1894 if (--wwo
->dwLoops
> 0) {
1895 wwo
->lpPlayPtr
= wwo
->lpLoopPtr
;
1897 /* Handle overlapping loops correctly */
1898 if (wwo
->lpLoopPtr
!= lpWaveHdr
&& (lpWaveHdr
->dwFlags
& WHDR_BEGINLOOP
)) {
1899 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
1900 /* shall we consider the END flag for the closing loop or for
1901 * the opening one or for both ???
1902 * code assumes for closing loop only
1905 lpWaveHdr
= lpWaveHdr
->lpNext
;
1907 wwo
->lpLoopPtr
= NULL
;
1908 wodPlayer_BeginWaveHdr(wwo
, lpWaveHdr
);
1911 /* We're not in a loop. Advance to the next wave header */
1912 wodPlayer_BeginWaveHdr(wwo
, lpWaveHdr
= lpWaveHdr
->lpNext
);
1918 /**************************************************************************
1919 * wodPlayer_DSPWait [internal]
1920 * Returns the number of milliseconds to wait for the DSP buffer to play a
1923 static DWORD
wodPlayer_DSPWait(const WINE_WAVEDEV
*wwo
)
1925 /* time for one period to be played */
1929 err
= snd_pcm_hw_params_get_period_time(wwo
->hw_params
, &val
, &dir
);
1933 /**************************************************************************
1934 * wodPlayer_NotifyWait [internal]
1935 * Returns the number of milliseconds to wait before attempting to notify
1936 * completion of the specified wavehdr.
1937 * This is based on the number of bytes remaining to be written in the
1940 static DWORD
wodPlayer_NotifyWait(const WINE_WAVEDEV
* wwo
, LPWAVEHDR lpWaveHdr
)
1944 if (lpWaveHdr
->reserved
< wwo
->dwPlayedTotal
) {
1947 dwMillis
= (lpWaveHdr
->reserved
- wwo
->dwPlayedTotal
) * 1000 / wwo
->format
.Format
.nAvgBytesPerSec
;
1948 if (!dwMillis
) dwMillis
= 1;
1955 /**************************************************************************
1956 * wodPlayer_WriteMaxFrags [internal]
1957 * Writes the maximum number of frames possible to the DSP and returns
1958 * the number of frames written.
1960 static int wodPlayer_WriteMaxFrags(WINE_WAVEDEV
* wwo
, DWORD
* frames
)
1962 /* Only attempt to write to free frames */
1963 LPWAVEHDR lpWaveHdr
= wwo
->lpPlayPtr
;
1964 DWORD dwLength
= snd_pcm_bytes_to_frames(wwo
->pcm
, lpWaveHdr
->dwBufferLength
- wwo
->dwPartialOffset
);
1965 int toWrite
= min(dwLength
, *frames
);
1968 TRACE("Writing wavehdr %p.%lu[%lu]\n", lpWaveHdr
, wwo
->dwPartialOffset
, lpWaveHdr
->dwBufferLength
);
1971 written
= (wwo
->write
)(wwo
->pcm
, lpWaveHdr
->lpData
+ wwo
->dwPartialOffset
, toWrite
);
1973 /* XRUN occurred. let's try to recover */
1974 ALSA_XRUNRecovery(wwo
, written
);
1975 written
= (wwo
->write
)(wwo
->pcm
, lpWaveHdr
->lpData
+ wwo
->dwPartialOffset
, toWrite
);
1978 /* still in error */
1979 ERR("Error in writing wavehdr. Reason: %s\n", snd_strerror(written
));
1985 wwo
->dwPartialOffset
+= snd_pcm_frames_to_bytes(wwo
->pcm
, written
);
1986 if ( wwo
->dwPartialOffset
>= lpWaveHdr
->dwBufferLength
) {
1987 /* this will be used to check if the given wave header has been fully played or not... */
1988 wwo
->dwPartialOffset
= lpWaveHdr
->dwBufferLength
;
1989 /* If we wrote all current wavehdr, skip to the next one */
1990 wodPlayer_PlayPtrNext(wwo
);
1993 wwo
->dwWrittenTotal
+= snd_pcm_frames_to_bytes(wwo
->pcm
, written
);
1994 TRACE("dwWrittenTotal=%lu\n", wwo
->dwWrittenTotal
);
2000 /**************************************************************************
2001 * wodPlayer_NotifyCompletions [internal]
2003 * Notifies and remove from queue all wavehdrs which have been played to
2004 * the speaker (ie. they have cleared the ALSA buffer). If force is true,
2005 * we notify all wavehdrs and remove them all from the queue even if they
2006 * are unplayed or part of a loop.
2008 static DWORD
wodPlayer_NotifyCompletions(WINE_WAVEDEV
* wwo
, BOOL force
)
2010 LPWAVEHDR lpWaveHdr
;
2012 /* Start from lpQueuePtr and keep notifying until:
2013 * - we hit an unwritten wavehdr
2014 * - we hit the beginning of a running loop
2015 * - we hit a wavehdr which hasn't finished playing
2018 while ((lpWaveHdr
= wwo
->lpQueuePtr
) &&
2020 (lpWaveHdr
!= wwo
->lpPlayPtr
&&
2021 lpWaveHdr
!= wwo
->lpLoopPtr
&&
2022 lpWaveHdr
->reserved
<= wwo
->dwPlayedTotal
))) {
2024 wwo
->lpQueuePtr
= lpWaveHdr
->lpNext
;
2026 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
2027 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
2029 wodNotifyClient(wwo
, WOM_DONE
, (DWORD
)lpWaveHdr
, 0);
2034 lpWaveHdr
= wwo
->lpQueuePtr
;
2035 if (!lpWaveHdr
) {TRACE("Empty queue\n"); break;}
2038 if (lpWaveHdr
== wwo
->lpPlayPtr
) {TRACE("play %p\n", lpWaveHdr
); break;}
2039 if (lpWaveHdr
== wwo
->lpLoopPtr
) {TRACE("loop %p\n", lpWaveHdr
); break;}
2040 if (lpWaveHdr
->reserved
> wwo
->dwPlayedTotal
){TRACE("still playing %p (%lu/%lu)\n", lpWaveHdr
, lpWaveHdr
->reserved
, wwo
->dwPlayedTotal
);break;}
2042 wwo
->lpQueuePtr
= lpWaveHdr
->lpNext
;
2044 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
2045 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
2047 wodNotifyClient(wwo
, WOM_DONE
, (DWORD
)lpWaveHdr
, 0);
2050 return (lpWaveHdr
&& lpWaveHdr
!= wwo
->lpPlayPtr
&& lpWaveHdr
!= wwo
->lpLoopPtr
) ?
2051 wodPlayer_NotifyWait(wwo
, lpWaveHdr
) : INFINITE
;
2055 /**************************************************************************
2056 * wodPlayer_Reset [internal]
2058 * wodPlayer helper. Resets current output stream.
2060 static void wodPlayer_Reset(WINE_WAVEDEV
* wwo
, BOOL reset
)
2063 TRACE("(%p)\n", wwo
);
2065 /* flush all possible output */
2066 snd_pcm_drain(wwo
->pcm
);
2068 wodUpdatePlayedTotal(wwo
, NULL
);
2069 /* updates current notify list */
2070 wodPlayer_NotifyCompletions(wwo
, FALSE
);
2072 if ( (err
= snd_pcm_drop(wwo
->pcm
)) < 0) {
2073 FIXME("flush: %s\n", snd_strerror(err
));
2075 wwo
->state
= WINE_WS_STOPPED
;
2078 if ( (err
= snd_pcm_prepare(wwo
->pcm
)) < 0 )
2079 ERR("pcm prepare failed: %s\n", snd_strerror(err
));
2082 enum win_wm_message msg
;
2086 /* remove any buffer */
2087 wodPlayer_NotifyCompletions(wwo
, TRUE
);
2089 wwo
->lpPlayPtr
= wwo
->lpQueuePtr
= wwo
->lpLoopPtr
= NULL
;
2090 wwo
->state
= WINE_WS_STOPPED
;
2091 wwo
->dwPlayedTotal
= wwo
->dwWrittenTotal
= 0;
2092 /* Clear partial wavehdr */
2093 wwo
->dwPartialOffset
= 0;
2095 /* remove any existing message in the ring */
2096 EnterCriticalSection(&wwo
->msgRing
.msg_crst
);
2097 /* return all pending headers in queue */
2098 while (ALSA_RetrieveRingMessage(&wwo
->msgRing
, &msg
, ¶m
, &ev
))
2100 if (msg
!= WINE_WM_HEADER
)
2102 FIXME("shouldn't have headers left\n");
2106 ((LPWAVEHDR
)param
)->dwFlags
&= ~WHDR_INQUEUE
;
2107 ((LPWAVEHDR
)param
)->dwFlags
|= WHDR_DONE
;
2109 wodNotifyClient(wwo
, WOM_DONE
, param
, 0);
2111 RESET_OMR(&wwo
->msgRing
);
2112 LeaveCriticalSection(&wwo
->msgRing
.msg_crst
);
2114 if (wwo
->lpLoopPtr
) {
2115 /* complicated case, not handled yet (could imply modifying the loop counter */
2116 FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
2117 wwo
->lpPlayPtr
= wwo
->lpLoopPtr
;
2118 wwo
->dwPartialOffset
= 0;
2119 wwo
->dwWrittenTotal
= wwo
->dwPlayedTotal
; /* this is wrong !!! */
2122 DWORD sz
= wwo
->dwPartialOffset
;
2124 /* reset all the data as if we had written only up to lpPlayedTotal bytes */
2125 /* compute the max size playable from lpQueuePtr */
2126 for (ptr
= wwo
->lpQueuePtr
; ptr
!= wwo
->lpPlayPtr
; ptr
= ptr
->lpNext
) {
2127 sz
+= ptr
->dwBufferLength
;
2129 /* because the reset lpPlayPtr will be lpQueuePtr */
2130 if (wwo
->dwWrittenTotal
> wwo
->dwPlayedTotal
+ sz
) ERR("grin\n");
2131 wwo
->dwPartialOffset
= sz
- (wwo
->dwWrittenTotal
- wwo
->dwPlayedTotal
);
2132 wwo
->dwWrittenTotal
= wwo
->dwPlayedTotal
;
2133 wwo
->lpPlayPtr
= wwo
->lpQueuePtr
;
2135 wwo
->state
= WINE_WS_PAUSED
;
2139 /**************************************************************************
2140 * wodPlayer_ProcessMessages [internal]
2142 static void wodPlayer_ProcessMessages(WINE_WAVEDEV
* wwo
)
2144 LPWAVEHDR lpWaveHdr
;
2145 enum win_wm_message msg
;
2150 while (ALSA_RetrieveRingMessage(&wwo
->msgRing
, &msg
, ¶m
, &ev
)) {
2151 TRACE("Received %s %lx\n", getCmdString(msg
), param
);
2154 case WINE_WM_PAUSING
:
2155 if ( snd_pcm_state(wwo
->pcm
) == SND_PCM_STATE_RUNNING
)
2157 if ( snd_pcm_hw_params_can_pause(wwo
->hw_params
) )
2159 err
= snd_pcm_pause(wwo
->pcm
, 1);
2161 ERR("pcm_pause failed: %s\n", snd_strerror(err
));
2162 wwo
->state
= WINE_WS_PAUSED
;
2166 wodPlayer_Reset(wwo
,FALSE
);
2171 case WINE_WM_RESTARTING
:
2172 if (wwo
->state
== WINE_WS_PAUSED
)
2174 if ( snd_pcm_state(wwo
->pcm
) == SND_PCM_STATE_PAUSED
)
2176 err
= snd_pcm_pause(wwo
->pcm
, 0);
2178 ERR("pcm_pause failed: %s\n", snd_strerror(err
));
2180 wwo
->state
= WINE_WS_PLAYING
;
2184 case WINE_WM_HEADER
:
2185 lpWaveHdr
= (LPWAVEHDR
)param
;
2187 /* insert buffer at the end of queue */
2190 for (wh
= &(wwo
->lpQueuePtr
); *wh
; wh
= &((*wh
)->lpNext
));
2193 if (!wwo
->lpPlayPtr
)
2194 wodPlayer_BeginWaveHdr(wwo
,lpWaveHdr
);
2195 if (wwo
->state
== WINE_WS_STOPPED
)
2196 wwo
->state
= WINE_WS_PLAYING
;
2198 case WINE_WM_RESETTING
:
2199 wodPlayer_Reset(wwo
,TRUE
);
2202 case WINE_WM_UPDATE
:
2203 wodUpdatePlayedTotal(wwo
, NULL
);
2206 case WINE_WM_BREAKLOOP
:
2207 if (wwo
->state
== WINE_WS_PLAYING
&& wwo
->lpLoopPtr
!= NULL
) {
2208 /* ensure exit at end of current loop */
2213 case WINE_WM_CLOSING
:
2214 /* sanity check: this should not happen since the device must have been reset before */
2215 if (wwo
->lpQueuePtr
|| wwo
->lpPlayPtr
) ERR("out of sync\n");
2217 wwo
->state
= WINE_WS_CLOSED
;
2220 /* shouldn't go here */
2222 FIXME("unknown message %d\n", msg
);
2228 /**************************************************************************
2229 * wodPlayer_FeedDSP [internal]
2230 * Feed as much sound data as we can into the DSP and return the number of
2231 * milliseconds before it will be necessary to feed the DSP again.
2233 static DWORD
wodPlayer_FeedDSP(WINE_WAVEDEV
* wwo
)
2237 wodUpdatePlayedTotal(wwo
, NULL
);
2238 availInQ
= snd_pcm_avail_update(wwo
->pcm
);
2241 /* input queue empty and output buffer with less than one fragment to play */
2242 if (!wwo
->lpPlayPtr
&& wwo
->dwBufferSize
< availInQ
+ wwo
->dwFragmentSize
) {
2243 TRACE("Run out of wavehdr:s...\n");
2247 /* no more room... no need to try to feed */
2249 /* Feed from partial wavehdr */
2250 if (wwo
->lpPlayPtr
&& wwo
->dwPartialOffset
!= 0) {
2251 wodPlayer_WriteMaxFrags(wwo
, &availInQ
);
2254 /* Feed wavehdrs until we run out of wavehdrs or DSP space */
2255 if (wwo
->dwPartialOffset
== 0 && wwo
->lpPlayPtr
) {
2257 TRACE("Setting time to elapse for %p to %lu\n",
2258 wwo
->lpPlayPtr
, wwo
->dwWrittenTotal
+ wwo
->lpPlayPtr
->dwBufferLength
);
2259 /* note the value that dwPlayedTotal will return when this wave finishes playing */
2260 wwo
->lpPlayPtr
->reserved
= wwo
->dwWrittenTotal
+ wwo
->lpPlayPtr
->dwBufferLength
;
2261 } while (wodPlayer_WriteMaxFrags(wwo
, &availInQ
) && wwo
->lpPlayPtr
&& availInQ
> 0);
2265 return wodPlayer_DSPWait(wwo
);
2268 /**************************************************************************
2269 * wodPlayer [internal]
2271 static DWORD CALLBACK
wodPlayer(LPVOID pmt
)
2273 WORD uDevID
= (DWORD
)pmt
;
2274 WINE_WAVEDEV
* wwo
= (WINE_WAVEDEV
*)&WOutDev
[uDevID
];
2275 DWORD dwNextFeedTime
= INFINITE
; /* Time before DSP needs feeding */
2276 DWORD dwNextNotifyTime
= INFINITE
; /* Time before next wave completion */
2279 wwo
->state
= WINE_WS_STOPPED
;
2280 SetEvent(wwo
->hStartUpEvent
);
2283 /** Wait for the shortest time before an action is required. If there
2284 * are no pending actions, wait forever for a command.
2286 dwSleepTime
= min(dwNextFeedTime
, dwNextNotifyTime
);
2287 TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime
, dwNextFeedTime
, dwNextNotifyTime
);
2288 WAIT_OMR(&wwo
->msgRing
, dwSleepTime
);
2289 wodPlayer_ProcessMessages(wwo
);
2290 if (wwo
->state
== WINE_WS_PLAYING
) {
2291 dwNextFeedTime
= wodPlayer_FeedDSP(wwo
);
2292 dwNextNotifyTime
= wodPlayer_NotifyCompletions(wwo
, FALSE
);
2293 if (dwNextFeedTime
== INFINITE
) {
2294 /* FeedDSP ran out of data, but before giving up, */
2295 /* check that a notification didn't give us more */
2296 wodPlayer_ProcessMessages(wwo
);
2297 if (wwo
->lpPlayPtr
) {
2298 TRACE("recovering\n");
2299 dwNextFeedTime
= wodPlayer_FeedDSP(wwo
);
2303 dwNextFeedTime
= dwNextNotifyTime
= INFINITE
;
2308 /**************************************************************************
2309 * wodGetDevCaps [internal]
2311 static DWORD
wodGetDevCaps(WORD wDevID
, LPWAVEOUTCAPSW lpCaps
, DWORD dwSize
)
2313 TRACE("(%u, %p, %lu);\n", wDevID
, lpCaps
, dwSize
);
2315 if (lpCaps
== NULL
) return MMSYSERR_NOTENABLED
;
2317 if (wDevID
>= ALSA_WodNumDevs
) {
2318 TRACE("Asked for device %d, but only %ld known!\n", wDevID
, ALSA_WodNumDevs
);
2319 return MMSYSERR_BADDEVICEID
;
2322 memcpy(lpCaps
, &WOutDev
[wDevID
].outcaps
, min(dwSize
, sizeof(*lpCaps
)));
2323 return MMSYSERR_NOERROR
;
2326 /**************************************************************************
2327 * wodOpen [internal]
2329 static DWORD
wodOpen(WORD wDevID
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
2332 snd_pcm_t
* pcm
= NULL
;
2333 snd_hctl_t
* hctl
= NULL
;
2334 snd_pcm_hw_params_t
* hw_params
= NULL
;
2335 snd_pcm_sw_params_t
* sw_params
;
2336 snd_pcm_access_t access
;
2337 snd_pcm_format_t format
= -1;
2339 unsigned int buffer_time
= 500000;
2340 unsigned int period_time
= 10000;
2341 snd_pcm_uframes_t buffer_size
;
2342 snd_pcm_uframes_t period_size
;
2348 snd_pcm_sw_params_alloca(&sw_params
);
2350 TRACE("(%u, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
2351 if (lpDesc
== NULL
) {
2352 WARN("Invalid Parameter !\n");
2353 return MMSYSERR_INVALPARAM
;
2355 if (wDevID
>= ALSA_WodNumDevs
) {
2356 TRACE("Asked for device %d, but only %ld known!\n", wDevID
, ALSA_WodNumDevs
);
2357 return MMSYSERR_BADDEVICEID
;
2360 /* only PCM format is supported so far... */
2361 if (!supportedFormat(lpDesc
->lpFormat
)) {
2362 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
2363 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
2364 lpDesc
->lpFormat
->nSamplesPerSec
);
2365 return WAVERR_BADFORMAT
;
2368 if (dwFlags
& WAVE_FORMAT_QUERY
) {
2369 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
2370 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
2371 lpDesc
->lpFormat
->nSamplesPerSec
);
2372 return MMSYSERR_NOERROR
;
2375 wwo
= &WOutDev
[wDevID
];
2377 if (wwo
->pcm
!= NULL
) {
2378 WARN("%d already allocated\n", wDevID
);
2379 return MMSYSERR_ALLOCATED
;
2382 if ((dwFlags
& WAVE_DIRECTSOUND
) && !(wwo
->outcaps
.dwSupport
& WAVECAPS_DIRECTSOUND
))
2383 /* not supported, ignore it */
2384 dwFlags
&= ~WAVE_DIRECTSOUND
;
2386 flags
= SND_PCM_NONBLOCK
;
2388 /* FIXME - why is this ifdefed? */
2390 if ( dwFlags
& WAVE_DIRECTSOUND
)
2391 flags
|= SND_PCM_ASYNC
;
2394 if ( (err
= snd_pcm_open(&pcm
, wwo
->pcmname
, SND_PCM_STREAM_PLAYBACK
, flags
)) < 0)
2396 ERR("Error open: %s\n", snd_strerror(err
));
2397 return MMSYSERR_NOTENABLED
;
2402 err
= snd_hctl_open(&hctl
, wwo
->ctlname
, 0);
2405 snd_hctl_load(hctl
);
2409 WARN("Could not open hctl for [%s]: %s\n", wwo
->ctlname
, snd_strerror(err
));
2414 wwo
->wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
2416 memcpy(&wwo
->waveDesc
, lpDesc
, sizeof(WAVEOPENDESC
));
2417 copy_format(lpDesc
->lpFormat
, &wwo
->format
);
2419 TRACE("Requested this format: %ldx%dx%d %s\n",
2420 wwo
->format
.Format
.nSamplesPerSec
,
2421 wwo
->format
.Format
.wBitsPerSample
,
2422 wwo
->format
.Format
.nChannels
,
2423 getFormat(wwo
->format
.Format
.wFormatTag
));
2425 if (wwo
->format
.Format
.wBitsPerSample
== 0) {
2426 WARN("Resetting zeroed wBitsPerSample\n");
2427 wwo
->format
.Format
.wBitsPerSample
= 8 *
2428 (wwo
->format
.Format
.nAvgBytesPerSec
/
2429 wwo
->format
.Format
.nSamplesPerSec
) /
2430 wwo
->format
.Format
.nChannels
;
2433 #define EXIT_ON_ERROR(f,e,txt) do \
2436 if ( (err = (f) ) < 0) \
2438 WARN(txt ": %s\n", snd_strerror(err)); \
2444 snd_pcm_hw_params_malloc(&hw_params
);
2447 retcode
= MMSYSERR_NOMEM
;
2450 snd_pcm_hw_params_any(pcm
, hw_params
);
2452 access
= SND_PCM_ACCESS_MMAP_INTERLEAVED
;
2453 if ( ( err
= snd_pcm_hw_params_set_access(pcm
, hw_params
, access
) ) < 0) {
2454 WARN("mmap not available. switching to standard write.\n");
2455 access
= SND_PCM_ACCESS_RW_INTERLEAVED
;
2456 EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm
, hw_params
, access
), MMSYSERR_INVALPARAM
, "unable to set access for playback");
2457 wwo
->write
= snd_pcm_writei
;
2460 wwo
->write
= snd_pcm_mmap_writei
;
2462 if ((err
= snd_pcm_hw_params_set_channels(pcm
, hw_params
, wwo
->format
.Format
.nChannels
)) < 0) {
2463 WARN("unable to set required channels: %d\n", wwo
->format
.Format
.nChannels
);
2464 if (dwFlags
& WAVE_DIRECTSOUND
) {
2465 if (wwo
->format
.Format
.nChannels
> 2)
2466 wwo
->format
.Format
.nChannels
= 2;
2467 else if (wwo
->format
.Format
.nChannels
== 2)
2468 wwo
->format
.Format
.nChannels
= 1;
2469 else if (wwo
->format
.Format
.nChannels
== 1)
2470 wwo
->format
.Format
.nChannels
= 2;
2471 /* recalculate block align and bytes per second */
2472 wwo
->format
.Format
.nBlockAlign
= (wwo
->format
.Format
.wBitsPerSample
* wwo
->format
.Format
.nChannels
) / 8;
2473 wwo
->format
.Format
.nAvgBytesPerSec
= wwo
->format
.Format
.nSamplesPerSec
* wwo
->format
.Format
.nBlockAlign
;
2474 WARN("changed number of channels from %d to %d\n", lpDesc
->lpFormat
->nChannels
, wwo
->format
.Format
.nChannels
);
2476 EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm
, hw_params
, wwo
->format
.Format
.nChannels
), WAVERR_BADFORMAT
, "unable to set required channels" );
2479 if ((wwo
->format
.Format
.wFormatTag
== WAVE_FORMAT_PCM
) ||
2480 ((wwo
->format
.Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) &&
2481 IsEqualGUID(&wwo
->format
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))) {
2482 format
= (wwo
->format
.Format
.wBitsPerSample
== 8) ? SND_PCM_FORMAT_U8
:
2483 (wwo
->format
.Format
.wBitsPerSample
== 16) ? SND_PCM_FORMAT_S16_LE
:
2484 (wwo
->format
.Format
.wBitsPerSample
== 24) ? SND_PCM_FORMAT_S24_LE
:
2485 (wwo
->format
.Format
.wBitsPerSample
== 32) ? SND_PCM_FORMAT_S32_LE
: -1;
2486 } else if ((wwo
->format
.Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) &&
2487 IsEqualGUID(&wwo
->format
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)){
2488 format
= (wwo
->format
.Format
.wBitsPerSample
== 32) ? SND_PCM_FORMAT_FLOAT_LE
: -1;
2489 } else if (wwo
->format
.Format
.wFormatTag
== WAVE_FORMAT_MULAW
) {
2490 FIXME("unimplemented format: WAVE_FORMAT_MULAW\n");
2491 retcode
= WAVERR_BADFORMAT
;
2493 } else if (wwo
->format
.Format
.wFormatTag
== WAVE_FORMAT_ALAW
) {
2494 FIXME("unimplemented format: WAVE_FORMAT_ALAW\n");
2495 retcode
= WAVERR_BADFORMAT
;
2497 } else if (wwo
->format
.Format
.wFormatTag
== WAVE_FORMAT_ADPCM
) {
2498 FIXME("unimplemented format: WAVE_FORMAT_ADPCM\n");
2499 retcode
= WAVERR_BADFORMAT
;
2502 ERR("invalid format: %0x04x\n", wwo
->format
.Format
.wFormatTag
);
2503 retcode
= WAVERR_BADFORMAT
;
2507 if ((err
= snd_pcm_hw_params_set_format(pcm
, hw_params
, format
)) < 0) {
2508 WARN("unable to set required format: %s\n", snd_pcm_format_name(format
));
2509 if (dwFlags
& WAVE_DIRECTSOUND
) {
2510 if ((wwo
->format
.Format
.wFormatTag
== WAVE_FORMAT_PCM
) ||
2511 ((wwo
->format
.Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) &&
2512 IsEqualGUID(&wwo
->format
.SubFormat
, & KSDATAFORMAT_SUBTYPE_PCM
))) {
2513 if (wwo
->format
.Format
.wBitsPerSample
!= 16) {
2514 wwo
->format
.Format
.wBitsPerSample
= 16;
2515 format
= SND_PCM_FORMAT_S16_LE
;
2517 wwo
->format
.Format
.wBitsPerSample
= 8;
2518 format
= SND_PCM_FORMAT_U8
;
2520 /* recalculate block align and bytes per second */
2521 wwo
->format
.Format
.nBlockAlign
= (wwo
->format
.Format
.wBitsPerSample
* wwo
->format
.Format
.nChannels
) / 8;
2522 wwo
->format
.Format
.nAvgBytesPerSec
= wwo
->format
.Format
.nSamplesPerSec
* wwo
->format
.Format
.nBlockAlign
;
2523 WARN("changed bits per sample from %d to %d\n", lpDesc
->lpFormat
->wBitsPerSample
, wwo
->format
.Format
.wBitsPerSample
);
2526 EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm
, hw_params
, format
), WAVERR_BADFORMAT
, "unable to set required format" );
2529 rate
= wwo
->format
.Format
.nSamplesPerSec
;
2531 err
= snd_pcm_hw_params_set_rate_near(pcm
, hw_params
, &rate
, &dir
);
2533 WARN("Rate %ld Hz not available for playback: %s\n", wwo
->format
.Format
.nSamplesPerSec
, snd_strerror(rate
));
2534 retcode
= WAVERR_BADFORMAT
;
2537 if (!NearMatch(rate
, wwo
->format
.Format
.nSamplesPerSec
)) {
2538 if (dwFlags
& WAVE_DIRECTSOUND
) {
2539 WARN("changed sample rate from %ld Hz to %d Hz\n", wwo
->format
.Format
.nSamplesPerSec
, rate
);
2540 wwo
->format
.Format
.nSamplesPerSec
= rate
;
2541 /* recalculate bytes per second */
2542 wwo
->format
.Format
.nAvgBytesPerSec
= wwo
->format
.Format
.nSamplesPerSec
* wwo
->format
.Format
.nBlockAlign
;
2544 WARN("Rate doesn't match (requested %ld Hz, got %d Hz)\n", wwo
->format
.Format
.nSamplesPerSec
, rate
);
2545 retcode
= WAVERR_BADFORMAT
;
2550 /* give the new format back to direct sound */
2551 if (dwFlags
& WAVE_DIRECTSOUND
) {
2552 lpDesc
->lpFormat
->wFormatTag
= wwo
->format
.Format
.wFormatTag
;
2553 lpDesc
->lpFormat
->nChannels
= wwo
->format
.Format
.nChannels
;
2554 lpDesc
->lpFormat
->nSamplesPerSec
= wwo
->format
.Format
.nSamplesPerSec
;
2555 lpDesc
->lpFormat
->wBitsPerSample
= wwo
->format
.Format
.wBitsPerSample
;
2556 lpDesc
->lpFormat
->nBlockAlign
= wwo
->format
.Format
.nBlockAlign
;
2557 lpDesc
->lpFormat
->nAvgBytesPerSec
= wwo
->format
.Format
.nAvgBytesPerSec
;
2560 TRACE("Got this format: %ldx%dx%d %s\n",
2561 wwo
->format
.Format
.nSamplesPerSec
,
2562 wwo
->format
.Format
.wBitsPerSample
,
2563 wwo
->format
.Format
.nChannels
,
2564 getFormat(wwo
->format
.Format
.wFormatTag
));
2567 EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm
, hw_params
, &buffer_time
, &dir
), MMSYSERR_INVALPARAM
, "unable to set buffer time");
2569 EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm
, hw_params
, &period_time
, &dir
), MMSYSERR_INVALPARAM
, "unable to set period time");
2571 EXIT_ON_ERROR( snd_pcm_hw_params(pcm
, hw_params
), MMSYSERR_INVALPARAM
, "unable to set hw params for playback");
2573 err
= snd_pcm_hw_params_get_period_size(hw_params
, &period_size
, &dir
);
2574 err
= snd_pcm_hw_params_get_buffer_size(hw_params
, &buffer_size
);
2576 snd_pcm_sw_params_current(pcm
, sw_params
);
2577 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");
2578 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm
, sw_params
, 0), MMSYSERR_ERROR
, "unable to set silence size");
2579 EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm
, sw_params
, period_size
), MMSYSERR_ERROR
, "unable to set avail min");
2580 EXIT_ON_ERROR( snd_pcm_sw_params_set_xfer_align(pcm
, sw_params
, 1), MMSYSERR_ERROR
, "unable to set xfer align");
2581 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm
, sw_params
, 0), MMSYSERR_ERROR
, "unable to set silence threshold");
2582 EXIT_ON_ERROR( snd_pcm_sw_params_set_xrun_mode(pcm
, sw_params
, SND_PCM_XRUN_NONE
), MMSYSERR_ERROR
, "unable to set xrun mode");
2583 EXIT_ON_ERROR( snd_pcm_sw_params(pcm
, sw_params
), MMSYSERR_ERROR
, "unable to set sw params for playback");
2584 #undef EXIT_ON_ERROR
2586 snd_pcm_prepare(pcm
);
2589 ALSA_TraceParameters(hw_params
, sw_params
, FALSE
);
2591 /* now, we can save all required data for later use... */
2593 wwo
->dwBufferSize
= snd_pcm_frames_to_bytes(pcm
, buffer_size
);
2594 wwo
->lpQueuePtr
= wwo
->lpPlayPtr
= wwo
->lpLoopPtr
= NULL
;
2595 wwo
->dwPlayedTotal
= wwo
->dwWrittenTotal
= 0;
2596 wwo
->dwPartialOffset
= 0;
2598 ALSA_InitRingMessage(&wwo
->msgRing
);
2600 if (!(dwFlags
& WAVE_DIRECTSOUND
)) {
2601 wwo
->hStartUpEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
2602 wwo
->hThread
= CreateThread(NULL
, 0, wodPlayer
, (LPVOID
)(DWORD
)wDevID
, 0, &(wwo
->dwThreadID
));
2604 SetThreadPriority(wwo
->hThread
, THREAD_PRIORITY_TIME_CRITICAL
);
2607 ERR("Thread creation for the wodPlayer failed!\n");
2608 CloseHandle(wwo
->hStartUpEvent
);
2609 retcode
= MMSYSERR_NOMEM
;
2612 WaitForSingleObject(wwo
->hStartUpEvent
, INFINITE
);
2613 CloseHandle(wwo
->hStartUpEvent
);
2615 wwo
->hThread
= INVALID_HANDLE_VALUE
;
2616 wwo
->dwThreadID
= 0;
2618 wwo
->hStartUpEvent
= INVALID_HANDLE_VALUE
;
2620 TRACE("handle=%p\n", pcm
);
2621 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
2622 wwo
->format
.Format
.wBitsPerSample
, wwo
->format
.Format
.nAvgBytesPerSec
,
2623 wwo
->format
.Format
.nSamplesPerSec
, wwo
->format
.Format
.nChannels
,
2624 wwo
->format
.Format
.nBlockAlign
);
2628 if ( wwo
->hw_params
)
2629 snd_pcm_hw_params_free(wwo
->hw_params
);
2630 wwo
->hw_params
= hw_params
;
2633 return wodNotifyClient(wwo
, WOM_OPEN
, 0L, 0L);
2641 snd_hctl_free(hctl
);
2642 snd_hctl_close(hctl
);
2646 snd_pcm_hw_params_free(hw_params
);
2648 if (wwo
->msgRing
.ring_buffer_size
> 0)
2649 ALSA_DestroyRingMessage(&wwo
->msgRing
);
2655 /**************************************************************************
2656 * wodClose [internal]
2658 static DWORD
wodClose(WORD wDevID
)
2660 DWORD ret
= MMSYSERR_NOERROR
;
2663 TRACE("(%u);\n", wDevID
);
2665 if (wDevID
>= ALSA_WodNumDevs
) {
2666 TRACE("Asked for device %d, but only %ld known!\n", wDevID
, ALSA_WodNumDevs
);
2667 return MMSYSERR_BADDEVICEID
;
2670 if (WOutDev
[wDevID
].pcm
== NULL
) {
2671 WARN("Requested to close already closed device %d!\n", wDevID
);
2672 return MMSYSERR_BADDEVICEID
;
2675 wwo
= &WOutDev
[wDevID
];
2676 if (wwo
->lpQueuePtr
) {
2677 WARN("buffers still playing !\n");
2678 ret
= WAVERR_STILLPLAYING
;
2680 if (wwo
->hThread
!= INVALID_HANDLE_VALUE
) {
2681 ALSA_AddRingMessage(&wwo
->msgRing
, WINE_WM_CLOSING
, 0, TRUE
);
2683 ALSA_DestroyRingMessage(&wwo
->msgRing
);
2686 snd_pcm_hw_params_free(wwo
->hw_params
);
2687 wwo
->hw_params
= NULL
;
2690 snd_pcm_close(wwo
->pcm
);
2695 snd_hctl_free(wwo
->hctl
);
2696 snd_hctl_close(wwo
->hctl
);
2700 ret
= wodNotifyClient(wwo
, WOM_CLOSE
, 0L, 0L);
2707 /**************************************************************************
2708 * wodWrite [internal]
2711 static DWORD
wodWrite(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
2713 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
2715 if (wDevID
>= ALSA_WodNumDevs
) {
2716 TRACE("Asked for device %d, but only %ld known!\n", wDevID
, ALSA_WodNumDevs
);
2717 return MMSYSERR_BADDEVICEID
;
2720 if (WOutDev
[wDevID
].pcm
== NULL
) {
2721 WARN("Requested to write to closed device %d!\n", wDevID
);
2722 return MMSYSERR_BADDEVICEID
;
2725 if (lpWaveHdr
->lpData
== NULL
|| !(lpWaveHdr
->dwFlags
& WHDR_PREPARED
))
2726 return WAVERR_UNPREPARED
;
2728 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
2729 return WAVERR_STILLPLAYING
;
2731 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
2732 lpWaveHdr
->dwFlags
|= WHDR_INQUEUE
;
2733 lpWaveHdr
->lpNext
= 0;
2735 ALSA_AddRingMessage(&WOutDev
[wDevID
].msgRing
, WINE_WM_HEADER
, (DWORD
)lpWaveHdr
, FALSE
);
2737 return MMSYSERR_NOERROR
;
2740 /**************************************************************************
2741 * wodPause [internal]
2743 static DWORD
wodPause(WORD wDevID
)
2745 TRACE("(%u);!\n", wDevID
);
2747 if (wDevID
>= ALSA_WodNumDevs
) {
2748 TRACE("Asked for device %d, but only %ld known!\n", wDevID
, ALSA_WodNumDevs
);
2749 return MMSYSERR_BADDEVICEID
;
2752 if (WOutDev
[wDevID
].pcm
== NULL
) {
2753 WARN("Requested to pause closed device %d!\n", wDevID
);
2754 return MMSYSERR_BADDEVICEID
;
2757 ALSA_AddRingMessage(&WOutDev
[wDevID
].msgRing
, WINE_WM_PAUSING
, 0, TRUE
);
2759 return MMSYSERR_NOERROR
;
2762 /**************************************************************************
2763 * wodRestart [internal]
2765 static DWORD
wodRestart(WORD wDevID
)
2767 TRACE("(%u);\n", wDevID
);
2769 if (wDevID
>= ALSA_WodNumDevs
) {
2770 TRACE("Asked for device %d, but only %ld known!\n", wDevID
, ALSA_WodNumDevs
);
2771 return MMSYSERR_BADDEVICEID
;
2774 if (WOutDev
[wDevID
].pcm
== NULL
) {
2775 WARN("Requested to restart closed device %d!\n", wDevID
);
2776 return MMSYSERR_BADDEVICEID
;
2779 if (WOutDev
[wDevID
].state
== WINE_WS_PAUSED
) {
2780 ALSA_AddRingMessage(&WOutDev
[wDevID
].msgRing
, WINE_WM_RESTARTING
, 0, TRUE
);
2783 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
2784 /* FIXME: Myst crashes with this ... hmm -MM
2785 return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
2788 return MMSYSERR_NOERROR
;
2791 /**************************************************************************
2792 * wodReset [internal]
2794 static DWORD
wodReset(WORD wDevID
)
2796 TRACE("(%u);\n", wDevID
);
2798 if (wDevID
>= ALSA_WodNumDevs
) {
2799 TRACE("Asked for device %d, but only %ld known!\n", wDevID
, ALSA_WodNumDevs
);
2800 return MMSYSERR_BADDEVICEID
;
2803 if (WOutDev
[wDevID
].pcm
== NULL
) {
2804 WARN("Requested to reset closed device %d!\n", wDevID
);
2805 return MMSYSERR_BADDEVICEID
;
2808 ALSA_AddRingMessage(&WOutDev
[wDevID
].msgRing
, WINE_WM_RESETTING
, 0, TRUE
);
2810 return MMSYSERR_NOERROR
;
2813 /**************************************************************************
2814 * wodGetPosition [internal]
2816 static DWORD
wodGetPosition(WORD wDevID
, LPMMTIME lpTime
, DWORD uSize
)
2820 TRACE("(%u, %p, %lu);\n", wDevID
, lpTime
, uSize
);
2822 if (wDevID
>= ALSA_WodNumDevs
) {
2823 TRACE("Asked for device %d, but only %ld known!\n", wDevID
, ALSA_WodNumDevs
);
2824 return MMSYSERR_BADDEVICEID
;
2827 if (WOutDev
[wDevID
].pcm
== NULL
) {
2828 WARN("Requested to get position of closed device %d!\n", wDevID
);
2829 return MMSYSERR_BADDEVICEID
;
2832 if (lpTime
== NULL
) return MMSYSERR_INVALPARAM
;
2834 wwo
= &WOutDev
[wDevID
];
2835 ALSA_AddRingMessage(&wwo
->msgRing
, WINE_WM_UPDATE
, 0, TRUE
);
2837 return bytes_to_mmtime(lpTime
, wwo
->dwPlayedTotal
, &wwo
->format
);
2840 /**************************************************************************
2841 * wodBreakLoop [internal]
2843 static DWORD
wodBreakLoop(WORD wDevID
)
2845 TRACE("(%u);\n", wDevID
);
2847 if (wDevID
>= ALSA_WodNumDevs
) {
2848 TRACE("Asked for device %d, but only %ld known!\n", wDevID
, ALSA_WodNumDevs
);
2849 return MMSYSERR_BADDEVICEID
;
2852 if (WOutDev
[wDevID
].pcm
== NULL
) {
2853 WARN("Requested to breakloop of closed device %d!\n", wDevID
);
2854 return MMSYSERR_BADDEVICEID
;
2857 ALSA_AddRingMessage(&WOutDev
[wDevID
].msgRing
, WINE_WM_BREAKLOOP
, 0, TRUE
);
2858 return MMSYSERR_NOERROR
;
2861 /**************************************************************************
2862 * wodGetVolume [internal]
2864 static DWORD
wodGetVolume(WORD wDevID
, LPDWORD lpdwVol
)
2872 TRACE("(%u, %p);\n", wDevID
, lpdwVol
);
2873 if (wDevID
>= ALSA_WodNumDevs
) {
2874 TRACE("Asked for device %d, but only %ld known!\n", wDevID
, ALSA_WodNumDevs
);
2875 return MMSYSERR_BADDEVICEID
;
2878 if (lpdwVol
== NULL
)
2879 return MMSYSERR_NOTENABLED
;
2881 wwo
= &WOutDev
[wDevID
];
2883 if (lpdwVol
== NULL
)
2884 return MMSYSERR_NOTENABLED
;
2886 rc
= ALSA_CheckSetVolume(wwo
->hctl
, &left
, &right
, &min
, &max
, NULL
, NULL
, NULL
);
2887 if (rc
== MMSYSERR_NOERROR
)
2889 #define VOLUME_ALSA_TO_WIN(x) ( ( (((x)-min) * 65535) + (max-min)/2 ) /(max-min))
2890 wleft
= VOLUME_ALSA_TO_WIN(left
);
2891 wright
= VOLUME_ALSA_TO_WIN(right
);
2892 #undef VOLUME_ALSA_TO_WIN
2893 TRACE("left=%d,right=%d,converted to windows left %d, right %d\n", left
, right
, wleft
, wright
);
2894 *lpdwVol
= MAKELONG( wleft
, wright
);
2897 TRACE("CheckSetVolume failed; rc %ld\n", rc
);
2902 /**************************************************************************
2903 * wodSetVolume [internal]
2905 static DWORD
wodSetVolume(WORD wDevID
, DWORD dwParam
)
2913 TRACE("(%u, %08lX);\n", wDevID
, dwParam
);
2914 if (wDevID
>= ALSA_WodNumDevs
) {
2915 TRACE("Asked for device %d, but only %ld known!\n", wDevID
, ALSA_WodNumDevs
);
2916 return MMSYSERR_BADDEVICEID
;
2919 wwo
= &WOutDev
[wDevID
];
2921 rc
= ALSA_CheckSetVolume(wwo
->hctl
, NULL
, NULL
, &min
, &max
, NULL
, NULL
, NULL
);
2922 if (rc
== MMSYSERR_NOERROR
)
2924 wleft
= LOWORD(dwParam
);
2925 wright
= HIWORD(dwParam
);
2926 #define VOLUME_WIN_TO_ALSA(x) ( ( ( ((x) * (max-min)) + 32767) / 65535) + min )
2927 left
= VOLUME_WIN_TO_ALSA(wleft
);
2928 right
= VOLUME_WIN_TO_ALSA(wright
);
2929 #undef VOLUME_WIN_TO_ALSA
2930 rc
= ALSA_CheckSetVolume(wwo
->hctl
, NULL
, NULL
, NULL
, NULL
, NULL
, &left
, &right
);
2931 if (rc
== MMSYSERR_NOERROR
)
2932 TRACE("set volume: wleft=%d, wright=%d, converted to alsa left %d, right %d\n", wleft
, wright
, left
, right
);
2934 TRACE("SetVolume failed; rc %ld\n", rc
);
2940 /**************************************************************************
2941 * wodGetNumDevs [internal]
2943 static DWORD
wodGetNumDevs(void)
2945 return ALSA_WodNumDevs
;
2948 /**************************************************************************
2949 * wodDevInterfaceSize [internal]
2951 static DWORD
wodDevInterfaceSize(UINT wDevID
, LPDWORD dwParam1
)
2953 TRACE("(%u, %p)\n", wDevID
, dwParam1
);
2955 *dwParam1
= MultiByteToWideChar(CP_ACP
, 0, WOutDev
[wDevID
].interface_name
, -1,
2956 NULL
, 0 ) * sizeof(WCHAR
);
2957 return MMSYSERR_NOERROR
;
2960 /**************************************************************************
2961 * wodDevInterface [internal]
2963 static DWORD
wodDevInterface(UINT wDevID
, PWCHAR dwParam1
, DWORD dwParam2
)
2965 if (dwParam2
>= MultiByteToWideChar(CP_ACP
, 0, WOutDev
[wDevID
].interface_name
, -1,
2966 NULL
, 0 ) * sizeof(WCHAR
))
2968 MultiByteToWideChar(CP_ACP
, 0, WOutDev
[wDevID
].interface_name
, -1,
2969 dwParam1
, dwParam2
/ sizeof(WCHAR
));
2970 return MMSYSERR_NOERROR
;
2972 return MMSYSERR_INVALPARAM
;
2975 /**************************************************************************
2976 * wodMessage (WINEALSA.@)
2978 DWORD WINAPI
ALSA_wodMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
2979 DWORD dwParam1
, DWORD dwParam2
)
2981 TRACE("(%u, %s, %08lX, %08lX, %08lX);\n",
2982 wDevID
, getMessage(wMsg
), dwUser
, dwParam1
, dwParam2
);
2989 /* FIXME: Pretend this is supported */
2991 case WODM_OPEN
: return wodOpen (wDevID
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
2992 case WODM_CLOSE
: return wodClose (wDevID
);
2993 case WODM_GETDEVCAPS
: return wodGetDevCaps (wDevID
, (LPWAVEOUTCAPSW
)dwParam1
, dwParam2
);
2994 case WODM_GETNUMDEVS
: return wodGetNumDevs ();
2995 case WODM_GETPITCH
: return MMSYSERR_NOTSUPPORTED
;
2996 case WODM_SETPITCH
: return MMSYSERR_NOTSUPPORTED
;
2997 case WODM_GETPLAYBACKRATE
: return MMSYSERR_NOTSUPPORTED
;
2998 case WODM_SETPLAYBACKRATE
: return MMSYSERR_NOTSUPPORTED
;
2999 case WODM_WRITE
: return wodWrite (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
3000 case WODM_PAUSE
: return wodPause (wDevID
);
3001 case WODM_GETPOS
: return wodGetPosition (wDevID
, (LPMMTIME
)dwParam1
, dwParam2
);
3002 case WODM_BREAKLOOP
: return wodBreakLoop (wDevID
);
3003 case WODM_PREPARE
: return MMSYSERR_NOTSUPPORTED
;
3004 case WODM_UNPREPARE
: return MMSYSERR_NOTSUPPORTED
;
3005 case WODM_GETVOLUME
: return wodGetVolume (wDevID
, (LPDWORD
)dwParam1
);
3006 case WODM_SETVOLUME
: return wodSetVolume (wDevID
, dwParam1
);
3007 case WODM_RESTART
: return wodRestart (wDevID
);
3008 case WODM_RESET
: return wodReset (wDevID
);
3009 case DRV_QUERYDEVICEINTERFACESIZE
: return wodDevInterfaceSize (wDevID
, (LPDWORD
)dwParam1
);
3010 case DRV_QUERYDEVICEINTERFACE
: return wodDevInterface (wDevID
, (PWCHAR
)dwParam1
, dwParam2
);
3011 case DRV_QUERYDSOUNDIFACE
: return wodDsCreate (wDevID
, (PIDSDRIVER
*)dwParam1
);
3012 case DRV_QUERYDSOUNDDESC
: return wodDsDesc (wDevID
, (PDSDRIVERDESC
)dwParam1
);
3015 FIXME("unknown message %d!\n", wMsg
);
3017 return MMSYSERR_NOTSUPPORTED
;
3020 /*======================================================================*
3021 * Low level DSOUND implementation *
3022 *======================================================================*/
3024 typedef struct IDsDriverImpl IDsDriverImpl
;
3025 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl
;
3027 struct IDsDriverImpl
3029 /* IUnknown fields */
3030 const IDsDriverVtbl
*lpVtbl
;
3032 /* IDsDriverImpl fields */
3034 IDsDriverBufferImpl
*primary
;
3037 struct IDsDriverBufferImpl
3039 /* IUnknown fields */
3040 const IDsDriverBufferVtbl
*lpVtbl
;
3042 /* IDsDriverBufferImpl fields */
3045 CRITICAL_SECTION mmap_crst
;
3047 DWORD mmap_buflen_bytes
;
3048 snd_pcm_uframes_t mmap_buflen_frames
;
3049 snd_pcm_channel_area_t
* mmap_areas
;
3050 snd_async_handler_t
* mmap_async_handler
;
3051 snd_pcm_uframes_t mmap_ppos
; /* play position */
3053 /* Do we have a direct hardware buffer - SND_PCM_TYPE_HW? */
3057 static void DSDB_CheckXRUN(IDsDriverBufferImpl
* pdbi
)
3059 WINE_WAVEDEV
* wwo
= &(WOutDev
[pdbi
->drv
->wDevID
]);
3060 snd_pcm_state_t state
= snd_pcm_state(wwo
->pcm
);
3062 if ( state
== SND_PCM_STATE_XRUN
)
3064 int err
= snd_pcm_prepare(wwo
->pcm
);
3065 TRACE("xrun occurred\n");
3067 ERR("recovery from xrun failed, prepare failed: %s\n", snd_strerror(err
));
3069 else if ( state
== SND_PCM_STATE_SUSPENDED
)
3071 int err
= snd_pcm_resume(wwo
->pcm
);
3072 TRACE("recovery from suspension occurred\n");
3073 if (err
< 0 && err
!= -EAGAIN
){
3074 err
= snd_pcm_prepare(wwo
->pcm
);
3076 ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err
));
3081 static void DSDB_MMAPCopy(IDsDriverBufferImpl
* pdbi
, int mul
)
3083 WINE_WAVEDEV
* wwo
= &(WOutDev
[pdbi
->drv
->wDevID
]);
3084 snd_pcm_uframes_t period_size
;
3085 snd_pcm_sframes_t avail
;
3089 const snd_pcm_channel_area_t
*areas
;
3090 snd_pcm_uframes_t ofs
;
3091 snd_pcm_uframes_t frames
;
3092 snd_pcm_uframes_t wanted
;
3094 if ( !pdbi
->mmap_buffer
|| !wwo
->hw_params
|| !wwo
->pcm
)
3097 err
= snd_pcm_hw_params_get_period_size(wwo
->hw_params
, &period_size
, &dir
);
3098 avail
= snd_pcm_avail_update(wwo
->pcm
);
3100 DSDB_CheckXRUN(pdbi
);
3102 TRACE("avail=%d, mul=%d\n", (int)avail
, mul
);
3104 frames
= pdbi
->mmap_buflen_frames
;
3106 EnterCriticalSection(&pdbi
->mmap_crst
);
3108 /* we want to commit the given number of periods, or the whole lot */
3109 wanted
= mul
== 0 ? frames
: period_size
* 2;
3111 snd_pcm_mmap_begin(wwo
->pcm
, &areas
, &ofs
, &frames
);
3112 if (areas
!= pdbi
->mmap_areas
|| areas
->addr
!= pdbi
->mmap_areas
->addr
)
3113 FIXME("Can't access sound driver's buffer directly.\n");
3115 /* mark our current play position */
3116 pdbi
->mmap_ppos
= ofs
;
3118 if (frames
> wanted
)
3121 err
= snd_pcm_mmap_commit(wwo
->pcm
, ofs
, frames
);
3123 /* Check to make sure we committed all we want to commit. ALSA
3124 * only gives a contiguous linear region, so we need to check this
3125 * in case we've reached the end of the buffer, in which case we
3126 * can wrap around back to the beginning. */
3127 if (frames
< wanted
) {
3128 frames
= wanted
-= frames
;
3129 snd_pcm_mmap_begin(wwo
->pcm
, &areas
, &ofs
, &frames
);
3130 snd_pcm_mmap_commit(wwo
->pcm
, ofs
, frames
);
3133 LeaveCriticalSection(&pdbi
->mmap_crst
);
3136 static void DSDB_PCMCallback(snd_async_handler_t
*ahandler
)
3139 /* snd_pcm_t * handle = snd_async_handler_get_pcm(ahandler); */
3140 IDsDriverBufferImpl
* pdbi
= snd_async_handler_get_callback_private(ahandler
);
3141 TRACE("callback called\n");
3143 /* Commit another block (the entire buffer if it's a direct hw buffer) */
3144 periods
= pdbi
->mmap_mode
== SND_PCM_TYPE_HW
? 0 : 1;
3145 DSDB_MMAPCopy(pdbi
, periods
);
3149 * Allocate the memory-mapped buffer for direct sound, and set up the
3152 static int DSDB_CreateMMAP(IDsDriverBufferImpl
* pdbi
)
3154 WINE_WAVEDEV
* wwo
= &(WOutDev
[pdbi
->drv
->wDevID
]);
3155 snd_pcm_format_t format
;
3156 snd_pcm_uframes_t frames
;
3157 snd_pcm_uframes_t ofs
;
3158 snd_pcm_uframes_t avail
;
3159 unsigned int channels
;
3160 unsigned int bits_per_sample
;
3161 unsigned int bits_per_frame
;
3164 err
= snd_pcm_hw_params_get_format(wwo
->hw_params
, &format
);
3165 err
= snd_pcm_hw_params_get_buffer_size(wwo
->hw_params
, &frames
);
3166 err
= snd_pcm_hw_params_get_channels(wwo
->hw_params
, &channels
);
3167 bits_per_sample
= snd_pcm_format_physical_width(format
);
3168 bits_per_frame
= bits_per_sample
* channels
;
3169 pdbi
->mmap_mode
= snd_pcm_type(wwo
->pcm
);
3171 if (pdbi
->mmap_mode
== SND_PCM_TYPE_HW
) {
3172 TRACE("mmap'd buffer is a hardware buffer.\n");
3175 TRACE("mmap'd buffer is an ALSA emulation of hardware buffer.\n");
3179 ALSA_TraceParameters(wwo
->hw_params
, NULL
, FALSE
);
3181 TRACE("format=%s frames=%ld channels=%d bits_per_sample=%d bits_per_frame=%d\n",
3182 snd_pcm_format_name(format
), frames
, channels
, bits_per_sample
, bits_per_frame
);
3184 pdbi
->mmap_buflen_frames
= frames
;
3185 pdbi
->mmap_buflen_bytes
= snd_pcm_frames_to_bytes( wwo
->pcm
, frames
);
3187 avail
= snd_pcm_avail_update(wwo
->pcm
);
3190 ERR("No buffer is available: %s.", snd_strerror(avail
));
3191 return DSERR_GENERIC
;
3193 err
= snd_pcm_mmap_begin(wwo
->pcm
, (const snd_pcm_channel_area_t
**)&pdbi
->mmap_areas
, &ofs
, &avail
);
3196 ERR("Can't map sound device for direct access: %s\n", snd_strerror(err
));
3197 return DSERR_GENERIC
;
3199 avail
= 0;/* We don't have any data to commit yet */
3200 err
= snd_pcm_mmap_commit(wwo
->pcm
, ofs
, avail
);
3202 err
= snd_pcm_rewind(wwo
->pcm
, ofs
);
3203 pdbi
->mmap_buffer
= pdbi
->mmap_areas
->addr
;
3205 snd_pcm_format_set_silence(format
, pdbi
->mmap_buffer
, frames
);
3207 TRACE("created mmap buffer of %ld frames (%ld bytes) at %p\n",
3208 frames
, pdbi
->mmap_buflen_bytes
, pdbi
->mmap_buffer
);
3210 InitializeCriticalSection(&pdbi
->mmap_crst
);
3211 pdbi
->mmap_crst
.DebugInfo
->Spare
[0] = (DWORD_PTR
)"WINEALSA_mmap_crst";
3213 err
= snd_async_add_pcm_handler(&pdbi
->mmap_async_handler
, wwo
->pcm
, DSDB_PCMCallback
, pdbi
);
3216 ERR("add_pcm_handler failed. reason: %s\n", snd_strerror(err
));
3217 return DSERR_GENERIC
;
3223 static void DSDB_DestroyMMAP(IDsDriverBufferImpl
* pdbi
)
3225 TRACE("mmap buffer %p destroyed\n", pdbi
->mmap_buffer
);
3226 pdbi
->mmap_areas
= NULL
;
3227 pdbi
->mmap_buffer
= NULL
;
3228 pdbi
->mmap_crst
.DebugInfo
->Spare
[0] = 0;
3229 DeleteCriticalSection(&pdbi
->mmap_crst
);
3233 static HRESULT WINAPI
IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface
, REFIID riid
, LPVOID
*ppobj
)
3235 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
3236 FIXME("(): stub!\n");
3237 return DSERR_UNSUPPORTED
;
3240 static ULONG WINAPI
IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface
)
3242 IDsDriverBufferImpl
*This
= (IDsDriverBufferImpl
*)iface
;
3243 ULONG refCount
= InterlockedIncrement(&This
->ref
);
3245 TRACE("(%p)->(ref before=%lu)\n",This
, refCount
- 1);
3250 static ULONG WINAPI
IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface
)
3252 IDsDriverBufferImpl
*This
= (IDsDriverBufferImpl
*)iface
;
3253 ULONG refCount
= InterlockedDecrement(&This
->ref
);
3255 TRACE("(%p)->(ref before=%lu)\n",This
, refCount
+ 1);
3259 if (This
== This
->drv
->primary
)
3260 This
->drv
->primary
= NULL
;
3261 DSDB_DestroyMMAP(This
);
3262 HeapFree(GetProcessHeap(), 0, This
);
3266 static HRESULT WINAPI
IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface
,
3267 LPVOID
*ppvAudio1
,LPDWORD pdwLen1
,
3268 LPVOID
*ppvAudio2
,LPDWORD pdwLen2
,
3269 DWORD dwWritePosition
,DWORD dwWriteLen
,
3272 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
3273 TRACE("(%p)\n",iface
);
3274 return DSERR_UNSUPPORTED
;
3277 static HRESULT WINAPI
IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface
,
3278 LPVOID pvAudio1
,DWORD dwLen1
,
3279 LPVOID pvAudio2
,DWORD dwLen2
)
3281 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
3282 TRACE("(%p)\n",iface
);
3283 return DSERR_UNSUPPORTED
;
3286 static HRESULT WINAPI
IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface
,
3287 LPWAVEFORMATEX pwfx
)
3289 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
3290 TRACE("(%p,%p)\n",iface
,pwfx
);
3291 return DSERR_BUFFERLOST
;
3294 static HRESULT WINAPI
IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface
, DWORD dwFreq
)
3296 /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
3297 TRACE("(%p,%ld): stub\n",iface
,dwFreq
);
3298 return DSERR_UNSUPPORTED
;
3301 static HRESULT WINAPI
IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface
, PDSVOLUMEPAN pVolPan
)
3304 IDsDriverBufferImpl
*This
= (IDsDriverBufferImpl
*)iface
;
3305 TRACE("(%p,%p)\n",iface
,pVolPan
);
3306 vol
= pVolPan
->dwTotalLeftAmpFactor
| (pVolPan
->dwTotalRightAmpFactor
<< 16);
3308 if (wodSetVolume(This
->drv
->wDevID
, vol
) != MMSYSERR_NOERROR
) {
3309 WARN("wodSetVolume failed\n");
3310 return DSERR_INVALIDPARAM
;
3316 static HRESULT WINAPI
IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface
, DWORD dwNewPos
)
3318 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
3319 TRACE("(%p,%ld): stub\n",iface
,dwNewPos
);
3320 return DSERR_UNSUPPORTED
;
3323 static HRESULT WINAPI
IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface
,
3324 LPDWORD lpdwPlay
, LPDWORD lpdwWrite
)
3326 IDsDriverBufferImpl
*This
= (IDsDriverBufferImpl
*)iface
;
3327 WINE_WAVEDEV
* wwo
= &(WOutDev
[This
->drv
->wDevID
]);
3328 snd_pcm_uframes_t hw_ptr
;
3329 snd_pcm_uframes_t period_size
;
3330 snd_pcm_state_t state
;
3334 if (wwo
->hw_params
== NULL
) return DSERR_GENERIC
;
3337 err
= snd_pcm_hw_params_get_period_size(wwo
->hw_params
, &period_size
, &dir
);
3339 if (wwo
->pcm
== NULL
) return DSERR_GENERIC
;
3340 /** we need to track down buffer underruns */
3341 DSDB_CheckXRUN(This
);
3343 EnterCriticalSection(&This
->mmap_crst
);
3344 hw_ptr
= This
->mmap_ppos
;
3346 state
= snd_pcm_state(wwo
->pcm
);
3347 if (state
!= SND_PCM_STATE_RUNNING
)
3351 *lpdwPlay
= snd_pcm_frames_to_bytes(wwo
->pcm
, hw_ptr
) % This
->mmap_buflen_bytes
;
3353 *lpdwWrite
= snd_pcm_frames_to_bytes(wwo
->pcm
, hw_ptr
+ period_size
* 2) % This
->mmap_buflen_bytes
;
3354 LeaveCriticalSection(&This
->mmap_crst
);
3356 TRACE("hw_ptr=0x%08x, playpos=%ld, writepos=%ld\n", (unsigned int)hw_ptr
, lpdwPlay
?*lpdwPlay
:-1, lpdwWrite
?*lpdwWrite
:-1);
3360 static HRESULT WINAPI
IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface
, DWORD dwRes1
, DWORD dwRes2
, DWORD dwFlags
)
3362 IDsDriverBufferImpl
*This
= (IDsDriverBufferImpl
*)iface
;
3363 WINE_WAVEDEV
* wwo
= &(WOutDev
[This
->drv
->wDevID
]);
3364 snd_pcm_state_t state
;
3367 TRACE("(%p,%lx,%lx,%lx)\n",iface
,dwRes1
,dwRes2
,dwFlags
);
3369 if (wwo
->pcm
== NULL
) return DSERR_GENERIC
;
3371 state
= snd_pcm_state(wwo
->pcm
);
3372 if ( state
== SND_PCM_STATE_SETUP
)
3374 err
= snd_pcm_prepare(wwo
->pcm
);
3375 state
= snd_pcm_state(wwo
->pcm
);
3377 if ( state
== SND_PCM_STATE_PREPARED
)
3379 /* If we have a direct hardware buffer, we can commit the whole lot
3380 * immediately (periods = 0), otherwise we prime the queue with only
3383 * Why 2? We want a small number so that we don't get ahead of the
3384 * DirectSound mixer. But we don't want to ever let the buffer get
3385 * completely empty - having 2 periods gives us time to commit another
3386 * period when the first expires.
3388 * The potential for buffer underrun is high, but that's the reality
3389 * of using a translated buffer (the whole point of DirectSound is
3390 * to provide direct access to the hardware).
3392 * A better implementation would use the buffer Lock() and Unlock()
3393 * methods to determine how far ahead we can commit, and to rewind if
3396 int periods
= This
->mmap_mode
== SND_PCM_TYPE_HW
? 0 : 2;
3398 DSDB_MMAPCopy(This
, periods
);
3399 err
= snd_pcm_start(wwo
->pcm
);
3404 static HRESULT WINAPI
IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface
)
3406 IDsDriverBufferImpl
*This
= (IDsDriverBufferImpl
*)iface
;
3407 WINE_WAVEDEV
* wwo
= &(WOutDev
[This
->drv
->wDevID
]);
3412 TRACE("(%p)\n",iface
);
3414 if (wwo
->pcm
== NULL
) return DSERR_GENERIC
;
3416 /* ring buffer wrap up detection */
3417 IDsDriverBufferImpl_GetPosition(iface
, &play
, &write
);
3420 TRACE("writepos wrapper up\n");
3424 if ( ( err
= snd_pcm_drop(wwo
->pcm
)) < 0 )
3426 ERR("error while stopping pcm: %s\n", snd_strerror(err
));
3427 return DSERR_GENERIC
;
3432 static const IDsDriverBufferVtbl dsdbvt
=
3434 IDsDriverBufferImpl_QueryInterface
,
3435 IDsDriverBufferImpl_AddRef
,
3436 IDsDriverBufferImpl_Release
,
3437 IDsDriverBufferImpl_Lock
,
3438 IDsDriverBufferImpl_Unlock
,
3439 IDsDriverBufferImpl_SetFormat
,
3440 IDsDriverBufferImpl_SetFrequency
,
3441 IDsDriverBufferImpl_SetVolumePan
,
3442 IDsDriverBufferImpl_SetPosition
,
3443 IDsDriverBufferImpl_GetPosition
,
3444 IDsDriverBufferImpl_Play
,
3445 IDsDriverBufferImpl_Stop
3448 static HRESULT WINAPI
IDsDriverImpl_QueryInterface(PIDSDRIVER iface
, REFIID riid
, LPVOID
*ppobj
)
3450 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
3451 FIXME("(%p): stub!\n",iface
);
3452 return DSERR_UNSUPPORTED
;
3455 static ULONG WINAPI
IDsDriverImpl_AddRef(PIDSDRIVER iface
)
3457 IDsDriverImpl
*This
= (IDsDriverImpl
*)iface
;
3458 ULONG refCount
= InterlockedIncrement(&This
->ref
);
3460 TRACE("(%p)->(ref before=%lu)\n",This
, refCount
- 1);
3465 static ULONG WINAPI
IDsDriverImpl_Release(PIDSDRIVER iface
)
3467 IDsDriverImpl
*This
= (IDsDriverImpl
*)iface
;
3468 ULONG refCount
= InterlockedDecrement(&This
->ref
);
3470 TRACE("(%p)->(ref before=%lu)\n",This
, refCount
+ 1);
3474 HeapFree(GetProcessHeap(),0,This
);
3478 static HRESULT WINAPI
IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface
, PDSDRIVERDESC pDesc
)
3480 IDsDriverImpl
*This
= (IDsDriverImpl
*)iface
;
3481 TRACE("(%p,%p)\n",iface
,pDesc
);
3482 memcpy(pDesc
, &(WOutDev
[This
->wDevID
].ds_desc
), sizeof(DSDRIVERDESC
));
3483 pDesc
->dwFlags
= DSDDESC_DOMMSYSTEMOPEN
| DSDDESC_DOMMSYSTEMSETFORMAT
|
3484 DSDDESC_USESYSTEMMEMORY
| DSDDESC_DONTNEEDPRIMARYLOCK
;
3485 pDesc
->dnDevNode
= WOutDev
[This
->wDevID
].waveDesc
.dnDevNode
;
3487 pDesc
->wReserved
= 0;
3488 pDesc
->ulDeviceNum
= This
->wDevID
;
3489 pDesc
->dwHeapType
= DSDHEAP_NOHEAP
;
3490 pDesc
->pvDirectDrawHeap
= NULL
;
3491 pDesc
->dwMemStartAddress
= 0;
3492 pDesc
->dwMemEndAddress
= 0;
3493 pDesc
->dwMemAllocExtra
= 0;
3494 pDesc
->pvReserved1
= NULL
;
3495 pDesc
->pvReserved2
= NULL
;
3499 static HRESULT WINAPI
IDsDriverImpl_Open(PIDSDRIVER iface
)
3501 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
3502 TRACE("(%p)\n",iface
);
3506 static HRESULT WINAPI
IDsDriverImpl_Close(PIDSDRIVER iface
)
3508 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
3509 TRACE("(%p)\n",iface
);
3513 static HRESULT WINAPI
IDsDriverImpl_GetCaps(PIDSDRIVER iface
, PDSDRIVERCAPS pCaps
)
3515 IDsDriverImpl
*This
= (IDsDriverImpl
*)iface
;
3516 TRACE("(%p,%p)\n",iface
,pCaps
);
3517 memcpy(pCaps
, &(WOutDev
[This
->wDevID
].ds_caps
), sizeof(DSDRIVERCAPS
));
3521 static HRESULT WINAPI
IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface
,
3522 LPWAVEFORMATEX pwfx
,
3523 DWORD dwFlags
, DWORD dwCardAddress
,
3524 LPDWORD pdwcbBufferSize
,
3528 IDsDriverImpl
*This
= (IDsDriverImpl
*)iface
;
3529 IDsDriverBufferImpl
** ippdsdb
= (IDsDriverBufferImpl
**)ppvObj
;
3532 TRACE("(%p,%p,%lx,%lx)\n",iface
,pwfx
,dwFlags
,dwCardAddress
);
3533 /* we only support primary buffers */
3534 if (!(dwFlags
& DSBCAPS_PRIMARYBUFFER
))
3535 return DSERR_UNSUPPORTED
;
3537 return DSERR_ALLOCATED
;
3538 if (dwFlags
& (DSBCAPS_CTRLFREQUENCY
| DSBCAPS_CTRLPAN
))
3539 return DSERR_CONTROLUNAVAIL
;
3541 *ippdsdb
= HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverBufferImpl
));
3542 if (*ippdsdb
== NULL
)
3543 return DSERR_OUTOFMEMORY
;
3544 (*ippdsdb
)->lpVtbl
= &dsdbvt
;
3545 (*ippdsdb
)->ref
= 1;
3546 (*ippdsdb
)->drv
= This
;
3548 err
= DSDB_CreateMMAP((*ippdsdb
));
3551 HeapFree(GetProcessHeap(), 0, *ippdsdb
);
3555 *ppbBuffer
= (*ippdsdb
)->mmap_buffer
;
3556 *pdwcbBufferSize
= (*ippdsdb
)->mmap_buflen_bytes
;
3558 This
->primary
= *ippdsdb
;
3560 /* buffer is ready to go */
3561 TRACE("buffer created at %p\n", *ippdsdb
);
3565 static HRESULT WINAPI
IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface
,
3566 PIDSDRIVERBUFFER pBuffer
,
3569 /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
3570 TRACE("(%p,%p): stub\n",iface
,pBuffer
);
3571 return DSERR_INVALIDCALL
;
3574 static const IDsDriverVtbl dsdvt
=
3576 IDsDriverImpl_QueryInterface
,
3577 IDsDriverImpl_AddRef
,
3578 IDsDriverImpl_Release
,
3579 IDsDriverImpl_GetDriverDesc
,
3581 IDsDriverImpl_Close
,
3582 IDsDriverImpl_GetCaps
,
3583 IDsDriverImpl_CreateSoundBuffer
,
3584 IDsDriverImpl_DuplicateSoundBuffer
3587 static DWORD
wodDsCreate(UINT wDevID
, PIDSDRIVER
* drv
)
3589 IDsDriverImpl
** idrv
= (IDsDriverImpl
**)drv
;
3591 TRACE("driver created\n");
3593 /* the HAL isn't much better than the HEL if we can't do mmap() */
3594 if (!(WOutDev
[wDevID
].outcaps
.dwSupport
& WAVECAPS_DIRECTSOUND
)) {
3595 ERR("DirectSound flag not set\n");
3596 MESSAGE("This sound card's driver does not support direct access\n");
3597 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
3598 return MMSYSERR_NOTSUPPORTED
;
3601 *idrv
= HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverImpl
));
3603 return MMSYSERR_NOMEM
;
3604 (*idrv
)->lpVtbl
= &dsdvt
;
3607 (*idrv
)->wDevID
= wDevID
;
3608 (*idrv
)->primary
= NULL
;
3609 return MMSYSERR_NOERROR
;
3612 static DWORD
wodDsDesc(UINT wDevID
, PDSDRIVERDESC desc
)
3614 memcpy(desc
, &(WOutDev
[wDevID
].ds_desc
), sizeof(DSDRIVERDESC
));
3615 return MMSYSERR_NOERROR
;
3618 /*======================================================================*
3619 * Low level WAVE IN implementation *
3620 *======================================================================*/
3622 /**************************************************************************
3623 * widNotifyClient [internal]
3625 static DWORD
widNotifyClient(WINE_WAVEDEV
* wwi
, WORD wMsg
, DWORD dwParam1
, DWORD dwParam2
)
3627 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg
, dwParam1
, dwParam2
);
3633 if (wwi
->wFlags
!= DCB_NULL
&&
3634 !DriverCallback(wwi
->waveDesc
.dwCallback
, wwi
->wFlags
, (HDRVR
)wwi
->waveDesc
.hWave
,
3635 wMsg
, wwi
->waveDesc
.dwInstance
, dwParam1
, dwParam2
)) {
3636 WARN("can't notify client !\n");
3637 return MMSYSERR_ERROR
;
3641 FIXME("Unknown callback message %u\n", wMsg
);
3642 return MMSYSERR_INVALPARAM
;
3644 return MMSYSERR_NOERROR
;
3647 /**************************************************************************
3648 * widGetDevCaps [internal]
3650 static DWORD
widGetDevCaps(WORD wDevID
, LPWAVEOUTCAPSW lpCaps
, DWORD dwSize
)
3652 TRACE("(%u, %p, %lu);\n", wDevID
, lpCaps
, dwSize
);
3654 if (lpCaps
== NULL
) return MMSYSERR_NOTENABLED
;
3656 if (wDevID
>= ALSA_WidNumDevs
) {
3657 TRACE("Requested device %d, but only %ld are known!\n", wDevID
, ALSA_WidNumDevs
);
3658 return MMSYSERR_BADDEVICEID
;
3661 memcpy(lpCaps
, &WInDev
[wDevID
].incaps
, min(dwSize
, sizeof(*lpCaps
)));
3662 return MMSYSERR_NOERROR
;
3665 /**************************************************************************
3666 * widRecorder_ReadHeaders [internal]
3668 static void widRecorder_ReadHeaders(WINE_WAVEDEV
* wwi
)
3670 enum win_wm_message tmp_msg
;
3675 while (ALSA_RetrieveRingMessage(&wwi
->msgRing
, &tmp_msg
, &tmp_param
, &tmp_ev
)) {
3676 if (tmp_msg
== WINE_WM_HEADER
) {
3678 lpWaveHdr
= (LPWAVEHDR
)tmp_param
;
3679 lpWaveHdr
->lpNext
= 0;
3681 if (wwi
->lpQueuePtr
== 0)
3682 wwi
->lpQueuePtr
= lpWaveHdr
;
3684 for (wh
= &(wwi
->lpQueuePtr
); *wh
; wh
= &((*wh
)->lpNext
));
3688 ERR("should only have headers left\n");
3693 /**************************************************************************
3694 * widRecorder [internal]
3696 static DWORD CALLBACK
widRecorder(LPVOID pmt
)
3698 WORD uDevID
= (DWORD
)pmt
;
3699 WINE_WAVEDEV
* wwi
= (WINE_WAVEDEV
*)&WInDev
[uDevID
];
3703 LPVOID buffer
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, wwi
->dwPeriodSize
);
3704 char *pOffset
= buffer
;
3705 enum win_wm_message msg
;
3708 DWORD frames_per_period
;
3710 wwi
->state
= WINE_WS_STOPPED
;
3711 wwi
->dwTotalRecorded
= 0;
3712 wwi
->lpQueuePtr
= NULL
;
3714 SetEvent(wwi
->hStartUpEvent
);
3716 /* make sleep time to be # of ms to output a period */
3717 dwSleepTime
= (1024/*wwi-dwPeriodSize => overrun!*/ * 1000) / wwi
->format
.Format
.nAvgBytesPerSec
;
3718 frames_per_period
= snd_pcm_bytes_to_frames(wwi
->pcm
, wwi
->dwPeriodSize
);
3719 TRACE("sleeptime=%ld ms\n", dwSleepTime
);
3722 /* wait for dwSleepTime or an event in thread's queue */
3723 /* FIXME: could improve wait time depending on queue state,
3724 * ie, number of queued fragments
3726 if (wwi
->lpQueuePtr
!= NULL
&& wwi
->state
== WINE_WS_PLAYING
)
3733 lpWaveHdr
= wwi
->lpQueuePtr
;
3734 /* read all the fragments accumulated so far */
3735 frames
= snd_pcm_avail_update(wwi
->pcm
);
3736 bytes
= snd_pcm_frames_to_bytes(wwi
->pcm
, frames
);
3737 TRACE("frames = %ld bytes = %ld\n", frames
, bytes
);
3738 periods
= bytes
/ wwi
->dwPeriodSize
;
3739 while ((periods
> 0) && (wwi
->lpQueuePtr
))
3742 bytes
= wwi
->dwPeriodSize
;
3743 TRACE("bytes = %ld\n",bytes
);
3744 if (lpWaveHdr
->dwBufferLength
- lpWaveHdr
->dwBytesRecorded
>= wwi
->dwPeriodSize
)
3746 /* directly read fragment in wavehdr */
3747 read
= wwi
->read(wwi
->pcm
, lpWaveHdr
->lpData
+ lpWaveHdr
->dwBytesRecorded
, frames_per_period
);
3748 bytesRead
= snd_pcm_frames_to_bytes(wwi
->pcm
, read
);
3750 TRACE("bytesRead=%ld (direct)\n", bytesRead
);
3751 if (bytesRead
!= (DWORD
) -1)
3753 /* update number of bytes recorded in current buffer and by this device */
3754 lpWaveHdr
->dwBytesRecorded
+= bytesRead
;
3755 wwi
->dwTotalRecorded
+= bytesRead
;
3757 /* buffer is full. notify client */
3758 if (lpWaveHdr
->dwBytesRecorded
== lpWaveHdr
->dwBufferLength
)
3760 /* must copy the value of next waveHdr, because we have no idea of what
3761 * will be done with the content of lpWaveHdr in callback
3763 LPWAVEHDR lpNext
= lpWaveHdr
->lpNext
;
3765 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
3766 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
3768 wwi
->lpQueuePtr
= lpNext
;
3769 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
3773 TRACE("read(%s, %p, %ld) failed (%s)\n", wwi
->pcmname
,
3774 lpWaveHdr
->lpData
+ lpWaveHdr
->dwBytesRecorded
,
3775 frames_per_period
, strerror(errno
));
3780 /* read the fragment in a local buffer */
3781 read
= wwi
->read(wwi
->pcm
, buffer
, frames_per_period
);
3782 bytesRead
= snd_pcm_frames_to_bytes(wwi
->pcm
, read
);
3785 TRACE("bytesRead=%ld (local)\n", bytesRead
);
3787 if (bytesRead
== (DWORD
) -1) {
3788 TRACE("read(%s, %p, %ld) failed (%s)\n", wwi
->pcmname
,
3789 buffer
, frames_per_period
, strerror(errno
));
3793 /* copy data in client buffers */
3794 while (bytesRead
!= (DWORD
) -1 && bytesRead
> 0)
3796 DWORD dwToCopy
= min (bytesRead
, lpWaveHdr
->dwBufferLength
- lpWaveHdr
->dwBytesRecorded
);
3798 memcpy(lpWaveHdr
->lpData
+ lpWaveHdr
->dwBytesRecorded
,
3802 /* update number of bytes recorded in current buffer and by this device */
3803 lpWaveHdr
->dwBytesRecorded
+= dwToCopy
;
3804 wwi
->dwTotalRecorded
+= dwToCopy
;
3805 bytesRead
-= dwToCopy
;
3806 pOffset
+= dwToCopy
;
3808 /* client buffer is full. notify client */
3809 if (lpWaveHdr
->dwBytesRecorded
== lpWaveHdr
->dwBufferLength
)
3811 /* must copy the value of next waveHdr, because we have no idea of what
3812 * will be done with the content of lpWaveHdr in callback
3814 LPWAVEHDR lpNext
= lpWaveHdr
->lpNext
;
3815 TRACE("lpNext=%p\n", lpNext
);
3817 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
3818 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
3820 wwi
->lpQueuePtr
= lpNext
;
3821 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
3824 if (!lpNext
&& bytesRead
) {
3825 /* before we give up, check for more header messages */
3826 while (ALSA_PeekRingMessage(&wwi
->msgRing
, &msg
, ¶m
, &ev
))
3828 if (msg
== WINE_WM_HEADER
) {
3830 ALSA_RetrieveRingMessage(&wwi
->msgRing
, &msg
, ¶m
, &ev
);
3831 hdr
= ((LPWAVEHDR
)param
);
3832 TRACE("msg = %s, hdr = %p, ev = %p\n", getCmdString(msg
), hdr
, ev
);
3834 if (lpWaveHdr
== 0) {
3835 /* new head of queue */
3836 wwi
->lpQueuePtr
= lpWaveHdr
= hdr
;
3838 /* insert buffer at the end of queue */
3840 for (wh
= &(wwi
->lpQueuePtr
); *wh
; wh
= &((*wh
)->lpNext
));
3847 if (lpWaveHdr
== 0) {
3848 /* no more buffer to copy data to, but we did read more.
3849 * what hasn't been copied will be dropped
3851 WARN("buffer under run! %lu bytes dropped.\n", bytesRead
);
3852 wwi
->lpQueuePtr
= NULL
;
3862 WAIT_OMR(&wwi
->msgRing
, dwSleepTime
);
3864 while (ALSA_RetrieveRingMessage(&wwi
->msgRing
, &msg
, ¶m
, &ev
))
3866 TRACE("msg=%s param=0x%lx\n", getCmdString(msg
), param
);
3868 case WINE_WM_PAUSING
:
3869 wwi
->state
= WINE_WS_PAUSED
;
3870 /*FIXME("Device should stop recording\n");*/
3873 case WINE_WM_STARTING
:
3874 wwi
->state
= WINE_WS_PLAYING
;
3875 snd_pcm_start(wwi
->pcm
);
3878 case WINE_WM_HEADER
:
3879 lpWaveHdr
= (LPWAVEHDR
)param
;
3880 lpWaveHdr
->lpNext
= 0;
3882 /* insert buffer at the end of queue */
3885 for (wh
= &(wwi
->lpQueuePtr
); *wh
; wh
= &((*wh
)->lpNext
));
3889 case WINE_WM_STOPPING
:
3890 if (wwi
->state
!= WINE_WS_STOPPED
)
3892 snd_pcm_drain(wwi
->pcm
);
3894 /* read any headers in queue */
3895 widRecorder_ReadHeaders(wwi
);
3897 /* return current buffer to app */
3898 lpWaveHdr
= wwi
->lpQueuePtr
;
3901 LPWAVEHDR lpNext
= lpWaveHdr
->lpNext
;
3902 TRACE("stop %p %p\n", lpWaveHdr
, lpWaveHdr
->lpNext
);
3903 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
3904 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
3905 wwi
->lpQueuePtr
= lpNext
;
3906 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
3909 wwi
->state
= WINE_WS_STOPPED
;
3912 case WINE_WM_RESETTING
:
3913 if (wwi
->state
!= WINE_WS_STOPPED
)
3915 snd_pcm_drain(wwi
->pcm
);
3917 wwi
->state
= WINE_WS_STOPPED
;
3918 wwi
->dwTotalRecorded
= 0;
3920 /* read any headers in queue */
3921 widRecorder_ReadHeaders(wwi
);
3923 /* return all buffers to the app */
3924 for (lpWaveHdr
= wwi
->lpQueuePtr
; lpWaveHdr
; lpWaveHdr
= lpWaveHdr
->lpNext
) {
3925 TRACE("reset %p %p\n", lpWaveHdr
, lpWaveHdr
->lpNext
);
3926 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
3927 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
3928 wwi
->lpQueuePtr
= lpWaveHdr
->lpNext
;
3929 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
3932 wwi
->lpQueuePtr
= NULL
;
3935 case WINE_WM_CLOSING
:
3937 wwi
->state
= WINE_WS_CLOSED
;
3939 HeapFree(GetProcessHeap(), 0, buffer
);
3941 /* shouldn't go here */
3942 case WINE_WM_UPDATE
:
3947 FIXME("unknown message %d\n", msg
);
3953 /* just for not generating compilation warnings... should never be executed */
3957 /**************************************************************************
3958 * widOpen [internal]
3960 static DWORD
widOpen(WORD wDevID
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
3963 snd_pcm_hw_params_t
* hw_params
;
3964 snd_pcm_sw_params_t
* sw_params
;
3965 snd_pcm_access_t access
;
3966 snd_pcm_format_t format
;
3968 unsigned int buffer_time
= 500000;
3969 unsigned int period_time
= 10000;
3970 snd_pcm_uframes_t buffer_size
;
3971 snd_pcm_uframes_t period_size
;
3977 snd_pcm_hw_params_alloca(&hw_params
);
3978 snd_pcm_sw_params_alloca(&sw_params
);
3980 /* JPW TODO - review this code */
3981 TRACE("(%u, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
3982 if (lpDesc
== NULL
) {
3983 WARN("Invalid Parameter !\n");
3984 return MMSYSERR_INVALPARAM
;
3986 if (wDevID
>= ALSA_WidNumDevs
) {
3987 TRACE("Requested device %d, but only %ld are known!\n", wDevID
, ALSA_WidNumDevs
);
3988 return MMSYSERR_BADDEVICEID
;
3991 /* only PCM format is supported so far... */
3992 if (!supportedFormat(lpDesc
->lpFormat
)) {
3993 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3994 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
3995 lpDesc
->lpFormat
->nSamplesPerSec
);
3996 return WAVERR_BADFORMAT
;
3999 if (dwFlags
& WAVE_FORMAT_QUERY
) {
4000 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
4001 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
4002 lpDesc
->lpFormat
->nSamplesPerSec
);
4003 return MMSYSERR_NOERROR
;
4006 wwi
= &WInDev
[wDevID
];
4008 if (wwi
->pcm
!= NULL
) {
4009 WARN("already allocated\n");
4010 return MMSYSERR_ALLOCATED
;
4013 if ((dwFlags
& WAVE_DIRECTSOUND
) && !(wwi
->dwSupport
& WAVECAPS_DIRECTSOUND
))
4014 /* not supported, ignore it */
4015 dwFlags
&= ~WAVE_DIRECTSOUND
;
4018 flags
= SND_PCM_NONBLOCK
;
4020 if ( dwFlags
& WAVE_DIRECTSOUND
)
4021 flags
|= SND_PCM_ASYNC
;
4024 if ( (err
=snd_pcm_open(&pcm
, wwi
->pcmname
, SND_PCM_STREAM_CAPTURE
, flags
)) < 0 )
4026 ERR("Error open: %s\n", snd_strerror(err
));
4027 return MMSYSERR_NOTENABLED
;
4030 wwi
->wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
4032 memcpy(&wwi
->waveDesc
, lpDesc
, sizeof(WAVEOPENDESC
));
4033 copy_format(lpDesc
->lpFormat
, &wwi
->format
);
4035 if (wwi
->format
.Format
.wBitsPerSample
== 0) {
4036 WARN("Resetting zeroed wBitsPerSample\n");
4037 wwi
->format
.Format
.wBitsPerSample
= 8 *
4038 (wwi
->format
.Format
.nAvgBytesPerSec
/
4039 wwi
->format
.Format
.nSamplesPerSec
) /
4040 wwi
->format
.Format
.nChannels
;
4043 snd_pcm_hw_params_any(pcm
, hw_params
);
4045 #define EXIT_ON_ERROR(f,e,txt) do \
4048 if ( (err = (f) ) < 0) \
4050 WARN(txt ": %s\n", snd_strerror(err)); \
4051 snd_pcm_close(pcm); \
4056 access
= SND_PCM_ACCESS_MMAP_INTERLEAVED
;
4057 if ( ( err
= snd_pcm_hw_params_set_access(pcm
, hw_params
, access
) ) < 0) {
4058 WARN("mmap not available. switching to standard write.\n");
4059 access
= SND_PCM_ACCESS_RW_INTERLEAVED
;
4060 EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm
, hw_params
, access
), MMSYSERR_INVALPARAM
, "unable to set access for playback");
4061 wwi
->read
= snd_pcm_readi
;
4064 wwi
->read
= snd_pcm_mmap_readi
;
4066 EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm
, hw_params
, wwi
->format
.Format
.nChannels
), WAVERR_BADFORMAT
, "unable to set required channels");
4068 if ((wwi
->format
.Format
.wFormatTag
== WAVE_FORMAT_PCM
) ||
4069 ((wwi
->format
.Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) &&
4070 IsEqualGUID(&wwi
->format
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))) {
4071 format
= (wwi
->format
.Format
.wBitsPerSample
== 8) ? SND_PCM_FORMAT_U8
:
4072 (wwi
->format
.Format
.wBitsPerSample
== 16) ? SND_PCM_FORMAT_S16_LE
:
4073 (wwi
->format
.Format
.wBitsPerSample
== 24) ? SND_PCM_FORMAT_S24_LE
:
4074 (wwi
->format
.Format
.wBitsPerSample
== 32) ? SND_PCM_FORMAT_S32_LE
: -1;
4075 } else if ((wwi
->format
.Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) &&
4076 IsEqualGUID(&wwi
->format
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)){
4077 format
= (wwi
->format
.Format
.wBitsPerSample
== 32) ? SND_PCM_FORMAT_FLOAT_LE
: -1;
4078 } else if (wwi
->format
.Format
.wFormatTag
== WAVE_FORMAT_MULAW
) {
4079 FIXME("unimplemented format: WAVE_FORMAT_MULAW\n");
4081 return WAVERR_BADFORMAT
;
4082 } else if (wwi
->format
.Format
.wFormatTag
== WAVE_FORMAT_ALAW
) {
4083 FIXME("unimplemented format: WAVE_FORMAT_ALAW\n");
4085 return WAVERR_BADFORMAT
;
4086 } else if (wwi
->format
.Format
.wFormatTag
== WAVE_FORMAT_ADPCM
) {
4087 FIXME("unimplemented format: WAVE_FORMAT_ADPCM\n");
4089 return WAVERR_BADFORMAT
;
4091 ERR("invalid format: %0x04x\n", wwi
->format
.Format
.wFormatTag
);
4093 return WAVERR_BADFORMAT
;
4096 EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm
, hw_params
, format
), WAVERR_BADFORMAT
, "unable to set required format");
4098 rate
= wwi
->format
.Format
.nSamplesPerSec
;
4100 err
= snd_pcm_hw_params_set_rate_near(pcm
, hw_params
, &rate
, &dir
);
4102 WARN("Rate %ld Hz not available for playback: %s\n", wwi
->format
.Format
.nSamplesPerSec
, snd_strerror(rate
));
4104 return WAVERR_BADFORMAT
;
4106 if (!NearMatch(rate
, wwi
->format
.Format
.nSamplesPerSec
)) {
4107 WARN("Rate doesn't match (requested %ld Hz, got %d Hz)\n", wwi
->format
.Format
.nSamplesPerSec
, rate
);
4109 return WAVERR_BADFORMAT
;
4113 EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm
, hw_params
, &buffer_time
, &dir
), MMSYSERR_INVALPARAM
, "unable to set buffer time");
4115 EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm
, hw_params
, &period_time
, &dir
), MMSYSERR_INVALPARAM
, "unable to set period time");
4117 EXIT_ON_ERROR( snd_pcm_hw_params(pcm
, hw_params
), MMSYSERR_INVALPARAM
, "unable to set hw params for playback");
4120 err
= snd_pcm_hw_params_get_period_size(hw_params
, &period_size
, &dir
);
4121 err
= snd_pcm_hw_params_get_buffer_size(hw_params
, &buffer_size
);
4123 snd_pcm_sw_params_current(pcm
, sw_params
);
4124 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");
4125 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm
, sw_params
, 0), MMSYSERR_ERROR
, "unable to set silence size");
4126 EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm
, sw_params
, period_size
), MMSYSERR_ERROR
, "unable to set avail min");
4127 EXIT_ON_ERROR( snd_pcm_sw_params_set_xfer_align(pcm
, sw_params
, 1), MMSYSERR_ERROR
, "unable to set xfer align");
4128 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm
, sw_params
, 0), MMSYSERR_ERROR
, "unable to set silence threshold");
4129 EXIT_ON_ERROR( snd_pcm_sw_params(pcm
, sw_params
), MMSYSERR_ERROR
, "unable to set sw params for playback");
4130 #undef EXIT_ON_ERROR
4132 snd_pcm_prepare(pcm
);
4135 ALSA_TraceParameters(hw_params
, sw_params
, FALSE
);
4137 /* now, we can save all required data for later use... */
4138 if ( wwi
->hw_params
)
4139 snd_pcm_hw_params_free(wwi
->hw_params
);
4140 snd_pcm_hw_params_malloc(&(wwi
->hw_params
));
4141 snd_pcm_hw_params_copy(wwi
->hw_params
, hw_params
);
4143 wwi
->dwBufferSize
= snd_pcm_frames_to_bytes(pcm
, buffer_size
);
4144 wwi
->lpQueuePtr
= wwi
->lpPlayPtr
= wwi
->lpLoopPtr
= NULL
;
4147 ALSA_InitRingMessage(&wwi
->msgRing
);
4149 wwi
->dwPeriodSize
= period_size
;
4150 /*if (wwi->dwFragmentSize % wwi->format.Format.nBlockAlign)
4151 ERR("Fragment doesn't contain an integral number of data blocks\n");
4153 TRACE("dwPeriodSize=%lu\n", wwi
->dwPeriodSize
);
4154 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
4155 wwi
->format
.Format
.wBitsPerSample
, wwi
->format
.Format
.nAvgBytesPerSec
,
4156 wwi
->format
.Format
.nSamplesPerSec
, wwi
->format
.Format
.nChannels
,
4157 wwi
->format
.Format
.nBlockAlign
);
4159 if (!(dwFlags
& WAVE_DIRECTSOUND
)) {
4160 wwi
->hStartUpEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
4161 wwi
->hThread
= CreateThread(NULL
, 0, widRecorder
, (LPVOID
)(DWORD
)wDevID
, 0, &(wwi
->dwThreadID
));
4163 SetThreadPriority(wwi
->hThread
, THREAD_PRIORITY_TIME_CRITICAL
);
4164 WaitForSingleObject(wwi
->hStartUpEvent
, INFINITE
);
4165 CloseHandle(wwi
->hStartUpEvent
);
4167 wwi
->hThread
= INVALID_HANDLE_VALUE
;
4168 wwi
->dwThreadID
= 0;
4170 wwi
->hStartUpEvent
= INVALID_HANDLE_VALUE
;
4172 return widNotifyClient(wwi
, WIM_OPEN
, 0L, 0L);
4176 /**************************************************************************
4177 * widClose [internal]
4179 static DWORD
widClose(WORD wDevID
)
4181 DWORD ret
= MMSYSERR_NOERROR
;
4184 TRACE("(%u);\n", wDevID
);
4186 if (wDevID
>= ALSA_WidNumDevs
) {
4187 TRACE("Requested device %d, but only %ld are known!\n", wDevID
, ALSA_WidNumDevs
);
4188 return MMSYSERR_BADDEVICEID
;
4191 if (WInDev
[wDevID
].pcm
== NULL
) {
4192 WARN("Requested to close already closed device %d!\n", wDevID
);
4193 return MMSYSERR_BADDEVICEID
;
4196 wwi
= &WInDev
[wDevID
];
4197 if (wwi
->lpQueuePtr
) {
4198 WARN("buffers still playing !\n");
4199 ret
= WAVERR_STILLPLAYING
;
4201 if (wwi
->hThread
!= INVALID_HANDLE_VALUE
) {
4202 ALSA_AddRingMessage(&wwi
->msgRing
, WINE_WM_CLOSING
, 0, TRUE
);
4204 ALSA_DestroyRingMessage(&wwi
->msgRing
);
4206 snd_pcm_hw_params_free(wwi
->hw_params
);
4207 wwi
->hw_params
= NULL
;
4209 snd_pcm_close(wwi
->pcm
);
4212 ret
= widNotifyClient(wwi
, WIM_CLOSE
, 0L, 0L);
4218 /**************************************************************************
4219 * widAddBuffer [internal]
4222 static DWORD
widAddBuffer(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
4224 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
4226 /* first, do the sanity checks... */
4227 if (wDevID
>= ALSA_WidNumDevs
) {
4228 TRACE("Requested device %d, but only %ld are known!\n", wDevID
, ALSA_WidNumDevs
);
4229 return MMSYSERR_BADDEVICEID
;
4232 if (WInDev
[wDevID
].pcm
== NULL
) {
4233 WARN("Requested to add buffer to already closed device %d!\n", wDevID
);
4234 return MMSYSERR_BADDEVICEID
;
4237 if (lpWaveHdr
->lpData
== NULL
|| !(lpWaveHdr
->dwFlags
& WHDR_PREPARED
))
4238 return WAVERR_UNPREPARED
;
4240 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
4241 return WAVERR_STILLPLAYING
;
4243 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
4244 lpWaveHdr
->dwFlags
|= WHDR_INQUEUE
;
4245 lpWaveHdr
->lpNext
= 0;
4247 ALSA_AddRingMessage(&WInDev
[wDevID
].msgRing
, WINE_WM_HEADER
, (DWORD
)lpWaveHdr
, FALSE
);
4249 return MMSYSERR_NOERROR
;
4252 /**************************************************************************
4253 * widStart [internal]
4256 static DWORD
widStart(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
4258 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
4260 /* first, do the sanity checks... */
4261 if (wDevID
>= ALSA_WidNumDevs
) {
4262 TRACE("Requested device %d, but only %ld are known!\n", wDevID
, ALSA_WidNumDevs
);
4263 return MMSYSERR_BADDEVICEID
;
4266 if (WInDev
[wDevID
].pcm
== NULL
) {
4267 WARN("Requested to start closed device %d!\n", wDevID
);
4268 return MMSYSERR_BADDEVICEID
;
4271 ALSA_AddRingMessage(&WInDev
[wDevID
].msgRing
, WINE_WM_STARTING
, 0, TRUE
);
4273 return MMSYSERR_NOERROR
;
4276 /**************************************************************************
4277 * widStop [internal]
4280 static DWORD
widStop(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
4282 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
4284 /* first, do the sanity checks... */
4285 if (wDevID
>= ALSA_WidNumDevs
) {
4286 TRACE("Requested device %d, but only %ld are known!\n", wDevID
, ALSA_WidNumDevs
);
4287 return MMSYSERR_BADDEVICEID
;
4290 if (WInDev
[wDevID
].pcm
== NULL
) {
4291 WARN("Requested to stop closed device %d!\n", wDevID
);
4292 return MMSYSERR_BADDEVICEID
;
4295 ALSA_AddRingMessage(&WInDev
[wDevID
].msgRing
, WINE_WM_STOPPING
, 0, TRUE
);
4297 return MMSYSERR_NOERROR
;
4300 /**************************************************************************
4301 * widReset [internal]
4303 static DWORD
widReset(WORD wDevID
)
4305 TRACE("(%u);\n", wDevID
);
4306 if (wDevID
>= ALSA_WidNumDevs
) {
4307 TRACE("Requested device %d, but only %ld are known!\n", wDevID
, ALSA_WidNumDevs
);
4308 return MMSYSERR_BADDEVICEID
;
4311 if (WInDev
[wDevID
].pcm
== NULL
) {
4312 WARN("Requested to reset closed device %d!\n", wDevID
);
4313 return MMSYSERR_BADDEVICEID
;
4316 ALSA_AddRingMessage(&WInDev
[wDevID
].msgRing
, WINE_WM_RESETTING
, 0, TRUE
);
4317 return MMSYSERR_NOERROR
;
4320 /**************************************************************************
4321 * widGetPosition [internal]
4323 static DWORD
widGetPosition(WORD wDevID
, LPMMTIME lpTime
, DWORD uSize
)
4327 TRACE("(%u, %p, %lu);\n", wDevID
, lpTime
, uSize
);
4329 if (wDevID
>= ALSA_WidNumDevs
) {
4330 TRACE("Requested device %d, but only %ld are known!\n", wDevID
, ALSA_WidNumDevs
);
4331 return MMSYSERR_BADDEVICEID
;
4334 if (WInDev
[wDevID
].state
== WINE_WS_CLOSED
) {
4335 WARN("Requested position of closed device %d!\n", wDevID
);
4336 return MMSYSERR_BADDEVICEID
;
4339 if (lpTime
== NULL
) {
4340 WARN("invalid parameter: lpTime = NULL\n");
4341 return MMSYSERR_INVALPARAM
;
4344 wwi
= &WInDev
[wDevID
];
4345 ALSA_AddRingMessage(&wwi
->msgRing
, WINE_WM_UPDATE
, 0, TRUE
);
4347 return bytes_to_mmtime(lpTime
, wwi
->dwTotalRecorded
, &wwi
->format
);
4350 /**************************************************************************
4351 * widGetNumDevs [internal]
4353 static DWORD
widGetNumDevs(void)
4355 return ALSA_WidNumDevs
;
4358 /**************************************************************************
4359 * widDevInterfaceSize [internal]
4361 static DWORD
widDevInterfaceSize(UINT wDevID
, LPDWORD dwParam1
)
4363 TRACE("(%u, %p)\n", wDevID
, dwParam1
);
4365 *dwParam1
= MultiByteToWideChar(CP_ACP
, 0, WInDev
[wDevID
].interface_name
, -1,
4366 NULL
, 0 ) * sizeof(WCHAR
);
4367 return MMSYSERR_NOERROR
;
4370 /**************************************************************************
4371 * widDevInterface [internal]
4373 static DWORD
widDevInterface(UINT wDevID
, PWCHAR dwParam1
, DWORD dwParam2
)
4375 if (dwParam2
>= MultiByteToWideChar(CP_ACP
, 0, WInDev
[wDevID
].interface_name
, -1,
4376 NULL
, 0 ) * sizeof(WCHAR
))
4378 MultiByteToWideChar(CP_ACP
, 0, WInDev
[wDevID
].interface_name
, -1,
4379 dwParam1
, dwParam2
/ sizeof(WCHAR
));
4380 return MMSYSERR_NOERROR
;
4382 return MMSYSERR_INVALPARAM
;
4385 /**************************************************************************
4386 * widDsCreate [internal]
4388 static DWORD
widDsCreate(UINT wDevID
, PIDSCDRIVER
* drv
)
4390 TRACE("(%d,%p)\n",wDevID
,drv
);
4392 /* the HAL isn't much better than the HEL if we can't do mmap() */
4393 FIXME("DirectSoundCapture not implemented\n");
4394 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
4395 return MMSYSERR_NOTSUPPORTED
;
4398 /**************************************************************************
4399 * widDsDesc [internal]
4401 static DWORD
widDsDesc(UINT wDevID
, PDSDRIVERDESC desc
)
4403 memcpy(desc
, &(WInDev
[wDevID
].ds_desc
), sizeof(DSDRIVERDESC
));
4404 return MMSYSERR_NOERROR
;
4407 /**************************************************************************
4408 * widMessage (WINEALSA.@)
4410 DWORD WINAPI
ALSA_widMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
4411 DWORD dwParam1
, DWORD dwParam2
)
4413 TRACE("(%u, %s, %08lX, %08lX, %08lX);\n",
4414 wDevID
, getMessage(wMsg
), dwUser
, dwParam1
, dwParam2
);
4421 /* FIXME: Pretend this is supported */
4423 case WIDM_OPEN
: return widOpen (wDevID
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
4424 case WIDM_CLOSE
: return widClose (wDevID
);
4425 case WIDM_ADDBUFFER
: return widAddBuffer (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
4426 case WIDM_PREPARE
: return MMSYSERR_NOTSUPPORTED
;
4427 case WIDM_UNPREPARE
: return MMSYSERR_NOTSUPPORTED
;
4428 case WIDM_GETDEVCAPS
: return widGetDevCaps (wDevID
, (LPWAVEOUTCAPSW
)dwParam1
, dwParam2
);
4429 case WIDM_GETNUMDEVS
: return widGetNumDevs ();
4430 case WIDM_GETPOS
: return widGetPosition (wDevID
, (LPMMTIME
)dwParam1
, dwParam2
);
4431 case WIDM_RESET
: return widReset (wDevID
);
4432 case WIDM_START
: return widStart (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
4433 case WIDM_STOP
: return widStop (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
4434 case DRV_QUERYDEVICEINTERFACESIZE
: return widDevInterfaceSize (wDevID
, (LPDWORD
)dwParam1
);
4435 case DRV_QUERYDEVICEINTERFACE
: return widDevInterface (wDevID
, (PWCHAR
)dwParam1
, dwParam2
);
4436 case DRV_QUERYDSOUNDIFACE
: return widDsCreate (wDevID
, (PIDSCDRIVER
*)dwParam1
);
4437 case DRV_QUERYDSOUNDDESC
: return widDsDesc (wDevID
, (PDSDRIVERDESC
)dwParam1
);
4439 FIXME("unknown message %d!\n", wMsg
);
4441 return MMSYSERR_NOTSUPPORTED
;
4446 /**************************************************************************
4447 * widMessage (WINEALSA.@)
4449 DWORD WINAPI
ALSA_widMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
4450 DWORD dwParam1
, DWORD dwParam2
)
4452 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
4453 return MMSYSERR_NOTENABLED
;
4456 /**************************************************************************
4457 * wodMessage (WINEALSA.@)
4459 DWORD WINAPI
ALSA_wodMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
4460 DWORD dwParam1
, DWORD dwParam2
)
4462 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
4463 return MMSYSERR_NOTENABLED
;
4466 #endif /* HAVE_ALSA */