2 * Copyright 2010 Maarten Lankhorst for CodeWeavers
3 * Copyright 2011 Andrew Eikum for CodeWeavers
4 * Copyright 2022 Huw Davies
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include <alsa/asoundlib.h>
33 #define WIN32_NO_STATUS
38 #include "mmdeviceapi.h"
40 #include "wine/debug.h"
41 #include "wine/list.h"
42 #include "wine/unixlib.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(alsa
);
50 snd_pcm_t
*pcm_handle
;
51 snd_pcm_uframes_t alsa_bufsize_frames
, alsa_period_frames
, safe_rewind_frames
;
52 snd_pcm_hw_params_t
*hw_params
; /* does not hold state between calls */
53 snd_pcm_format_t alsa_format
;
55 LARGE_INTEGER last_period_time
;
59 AUDCLNT_SHAREMODE share
;
65 int alsa_channel_map
[32];
67 BOOL started
, please_quit
;
68 REFERENCE_TIME mmdev_period_rt
;
69 UINT64 written_frames
, last_pos_frames
;
70 UINT32 bufsize_frames
, held_frames
, tmp_buffer_frames
, mmdev_period_frames
;
71 snd_pcm_uframes_t remapping_buf_frames
;
72 UINT32 lcl_offs_frames
; /* offs into local_buffer where valid data starts */
73 UINT32 wri_offs_frames
; /* where to write fresh data in local_buffer */
74 UINT32 hidden_frames
; /* ALSA reserve to ensure continuous rendering */
75 UINT32 vol_adjusted_frames
; /* Frames we've already adjusted the volume of but didn't write yet */
76 UINT32 data_in_alsa_frames
;
78 BYTE
*local_buffer
, *tmp_buffer
, *remapping_buf
, *silence_buf
;
79 LONG32 getbuf_last
; /* <0 when using tmp_buffer */
85 #define EXTRA_SAFE_RT 40000
87 static const WCHAR drv_keyW
[] = {'S','o','f','t','w','a','r','e','\\',
88 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
89 'w','i','n','e','a','l','s','a','.','d','r','v'};
91 static NTSTATUS
alsa_not_implemented(void *args
)
93 return STATUS_SUCCESS
;
96 static inline void ascii_to_unicode( WCHAR
*dst
, const char *src
, size_t len
)
98 while (len
--) *dst
++ = (unsigned char)*src
++;
101 static HKEY
reg_open_key( HKEY root
, const WCHAR
*name
, ULONG name_len
)
103 UNICODE_STRING nameW
= { name_len
, name_len
, (WCHAR
*)name
};
104 OBJECT_ATTRIBUTES attr
;
107 attr
.Length
= sizeof(attr
);
108 attr
.RootDirectory
= root
;
109 attr
.ObjectName
= &nameW
;
111 attr
.SecurityDescriptor
= NULL
;
112 attr
.SecurityQualityOfService
= NULL
;
114 if (NtOpenKeyEx( &ret
, MAXIMUM_ALLOWED
, &attr
, 0 )) return 0;
118 static HKEY
open_hkcu(void)
122 DWORD_PTR sid_data
[(sizeof(TOKEN_USER
) + SECURITY_MAX_SID_SIZE
) / sizeof(DWORD_PTR
)];
123 DWORD i
, len
= sizeof(sid_data
);
126 if (NtQueryInformationToken( GetCurrentThreadEffectiveToken(), TokenUser
, sid_data
, len
, &len
))
129 sid
= ((TOKEN_USER
*)sid_data
)->User
.Sid
;
130 len
= sprintf( buffer
, "\\Registry\\User\\S-%u-%u", sid
->Revision
,
131 (unsigned)MAKELONG( MAKEWORD( sid
->IdentifierAuthority
.Value
[5], sid
->IdentifierAuthority
.Value
[4] ),
132 MAKEWORD( sid
->IdentifierAuthority
.Value
[3], sid
->IdentifierAuthority
.Value
[2] )));
133 for (i
= 0; i
< sid
->SubAuthorityCount
; i
++)
134 len
+= sprintf( buffer
+ len
, "-%u", (unsigned)sid
->SubAuthority
[i
] );
135 ascii_to_unicode( bufferW
, buffer
, len
+ 1 );
137 return reg_open_key( NULL
, bufferW
, len
* sizeof(WCHAR
) );
140 static HKEY
reg_open_hkcu_key( const WCHAR
*name
, ULONG name_len
)
142 HKEY hkcu
= open_hkcu(), key
;
144 key
= reg_open_key( hkcu
, name
, name_len
);
150 static ULONG
reg_query_value( HKEY hkey
, const WCHAR
*name
,
151 KEY_VALUE_PARTIAL_INFORMATION
*info
, ULONG size
)
153 unsigned int name_size
= name
? wcslen( name
) * sizeof(WCHAR
) : 0;
154 UNICODE_STRING nameW
= { name_size
, name_size
, (WCHAR
*)name
};
156 if (NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
,
160 return size
- FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
163 static snd_pcm_stream_t
alsa_get_direction(EDataFlow flow
)
165 return (flow
== eRender
) ? SND_PCM_STREAM_PLAYBACK
: SND_PCM_STREAM_CAPTURE
;
168 static WCHAR
*strdupAtoW(const char *str
)
173 if(!str
) return NULL
;
175 len
= strlen(str
) + 1;
176 ret
= malloc(len
* sizeof(WCHAR
));
177 if(ret
) ntdll_umbstowcs(str
, len
, ret
, len
);
181 /* copied from kernelbase */
182 static int muldiv( int a
, int b
, int c
)
188 /* We want to deal with a positive divisor to simplify the logic. */
195 /* If the result is positive, we "add" to round. else, we subtract to round. */
196 if ((a
< 0 && b
< 0) || (a
>= 0 && b
>= 0))
197 ret
= (((LONGLONG
)a
* b
) + (c
/ 2)) / c
;
199 ret
= (((LONGLONG
)a
* b
) - (c
/ 2)) / c
;
201 if (ret
> 2147483647 || ret
< -2147483647) return -1;
205 static void alsa_lock(struct alsa_stream
*stream
)
207 pthread_mutex_lock(&stream
->lock
);
210 static void alsa_unlock(struct alsa_stream
*stream
)
212 pthread_mutex_unlock(&stream
->lock
);
215 static NTSTATUS
alsa_unlock_result(struct alsa_stream
*stream
,
216 HRESULT
*result
, HRESULT value
)
220 return STATUS_SUCCESS
;
223 static struct alsa_stream
*handle_get_stream(stream_handle h
)
225 return (struct alsa_stream
*)(UINT_PTR
)h
;
228 static BOOL
alsa_try_open(const char *devnode
, EDataFlow flow
)
233 TRACE("devnode: %s, flow: %d\n", devnode
, flow
);
235 if((err
= snd_pcm_open(&handle
, devnode
, alsa_get_direction(flow
), SND_PCM_NONBLOCK
)) < 0){
236 WARN("The device \"%s\" failed to open: %d (%s).\n", devnode
, err
, snd_strerror(err
));
240 snd_pcm_close(handle
);
244 static WCHAR
*construct_device_id(EDataFlow flow
, const WCHAR
*chunk1
, const WCHAR
*chunk2
)
248 size_t len_wchars
= 0, chunk1_len
= 0, chunk2_len
= 0, copied
= 0, prefix_len
;
250 static const WCHAR dashW
[] = {' ','-',' ',0};
251 static const size_t dashW_len
= ARRAY_SIZE(dashW
) - 1;
252 static const WCHAR outW
[] = {'O','u','t',':',' ',0};
253 static const WCHAR inW
[] = {'I','n',':',' ',0};
257 prefix_len
= ARRAY_SIZE(outW
) - 1;
258 len_wchars
+= prefix_len
;
261 prefix_len
= ARRAY_SIZE(inW
) - 1;
262 len_wchars
+= prefix_len
;
265 chunk1_len
= wcslen(chunk1
);
266 len_wchars
+= chunk1_len
;
269 len_wchars
+= dashW_len
;
271 chunk2_len
= wcslen(chunk2
);
272 len_wchars
+= chunk2_len
;
274 len_wchars
+= 1; /* NULL byte */
276 ret
= malloc(len_wchars
* sizeof(WCHAR
));
278 memcpy(ret
, prefix
, prefix_len
* sizeof(WCHAR
));
279 copied
+= prefix_len
;
281 memcpy(ret
+ copied
, chunk1
, chunk1_len
* sizeof(WCHAR
));
282 copied
+= chunk1_len
;
284 if(chunk1
&& chunk2
){
285 memcpy(ret
+ copied
, dashW
, dashW_len
* sizeof(WCHAR
));
289 memcpy(ret
+ copied
, chunk2
, chunk2_len
* sizeof(WCHAR
));
290 copied
+= chunk2_len
;
294 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret
));
305 struct endpoints_info
307 unsigned int num
, size
;
308 struct endpt
*endpoints
;
311 static void endpoints_add(struct endpoints_info
*endpoints
, WCHAR
*name
, char *device
)
313 if(endpoints
->num
>= endpoints
->size
){
314 if (!endpoints
->size
) endpoints
->size
= 16;
315 else endpoints
->size
*= 2;
316 endpoints
->endpoints
= realloc(endpoints
->endpoints
, endpoints
->size
* sizeof(*endpoints
->endpoints
));
319 endpoints
->endpoints
[endpoints
->num
].name
= name
;
320 endpoints
->endpoints
[endpoints
->num
++].device
= device
;
323 static HRESULT
alsa_get_card_devices(EDataFlow flow
, struct endpoints_info
*endpoints_info
,
324 snd_ctl_t
*ctl
, int card
, const WCHAR
*cardname
)
327 snd_pcm_info_t
*info
;
329 info
= calloc(1, snd_pcm_info_sizeof());
331 return E_OUTOFMEMORY
;
333 snd_pcm_info_set_subdevice(info
, 0);
334 snd_pcm_info_set_stream(info
, alsa_get_direction(flow
));
337 for(err
= snd_ctl_pcm_next_device(ctl
, &device
); device
!= -1 && err
>= 0;
338 err
= snd_ctl_pcm_next_device(ctl
, &device
)){
342 snd_pcm_info_set_device(info
, device
);
344 if((err
= snd_ctl_pcm_info(ctl
, info
)) < 0){
346 /* This device doesn't have the right stream direction */
349 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
350 card
, device
, err
, snd_strerror(err
));
354 sprintf(devnode
, "plughw:%d,%d", card
, device
);
355 if(!alsa_try_open(devnode
, flow
))
358 devname
= strdupAtoW(snd_pcm_info_get_name(info
));
360 WARN("Unable to get device name for card %d, device %d\n", card
, device
);
364 endpoints_add(endpoints_info
, construct_device_id(flow
, cardname
, devname
), strdup(devnode
));
371 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
372 card
, err
, snd_strerror(err
));
377 static void get_reg_devices(EDataFlow flow
, struct endpoints_info
*endpoints_info
)
379 static const WCHAR ALSAOutputDevices
[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
380 static const WCHAR ALSAInputDevices
[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
382 KEY_VALUE_PARTIAL_INFORMATION
*key_info
= (void *)buffer
;
385 const WCHAR
*value_name
= (flow
== eRender
) ? ALSAOutputDevices
: ALSAInputDevices
;
387 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
388 if((key
= reg_open_hkcu_key(drv_keyW
, sizeof(drv_keyW
)))){
389 if((size
= reg_query_value(key
, value_name
, key_info
, sizeof(buffer
)))){
390 WCHAR
*p
= (WCHAR
*)key_info
->Data
;
392 if(key_info
->Type
!= REG_MULTI_SZ
){
393 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
400 char *devname
= malloc(len
* 3 + 1);
402 ntdll_wcstoumbs(p
, len
+ 1, devname
, len
* 3 + 1, FALSE
);
404 if(alsa_try_open(devname
, flow
))
405 endpoints_add(endpoints_info
, construct_device_id(flow
, p
, NULL
), strdup(devname
));
418 int first_card_number
;
422 static struct list card_types
= LIST_INIT(card_types
);
424 static BOOL
need_card_number(int card
, const char *string
)
426 struct card_type
*cptr
;
428 LIST_FOR_EACH_ENTRY(cptr
, &card_types
, struct card_type
, entry
)
430 if(!strcmp(string
, cptr
->string
))
431 return card
!= cptr
->first_card_number
;
434 /* this is the first instance of string */
435 cptr
= malloc(sizeof(struct card_type
) + strlen(string
));
437 /* Default to displaying card number if we can't track cards */
440 cptr
->first_card_number
= card
;
441 strcpy(cptr
->string
, string
);
442 list_add_head(&card_types
, &cptr
->entry
);
446 static WCHAR
*alsa_get_card_name(int card
)
452 if((err
= snd_card_get_name(card
, &cardname
)) < 0){
453 /* FIXME: Should be localized */
454 WARN("Unable to get card name for ALSA device %d: %d (%s)\n", card
, err
, snd_strerror(err
));
455 cardname
= strdup("Unknown soundcard");
458 if(need_card_number(card
, cardname
)){
461 * For identical card names, second and subsequent instances get
462 * card number prefix to distinguish them (like Windows).
464 if(asprintf(&cardnameN
, "%u-%s", card
, cardname
) > 0){
466 cardname
= cardnameN
;
470 ret
= strdupAtoW(cardname
);
476 static NTSTATUS
alsa_get_endpoint_ids(void *args
)
478 static const WCHAR defaultW
[] = {'d','e','f','a','u','l','t',0};
479 struct get_endpoint_ids_params
*params
= args
;
480 struct endpoints_info endpoints_info
;
481 unsigned int i
, needed
, name_len
, device_len
, offset
;
482 struct endpoint
*endpoint
;
487 endpoints_info
.num
= endpoints_info
.size
= 0;
488 endpoints_info
.endpoints
= NULL
;
490 if(alsa_try_open("default", params
->flow
))
491 endpoints_add(&endpoints_info
, construct_device_id(params
->flow
, defaultW
, NULL
), strdup("default"));
493 get_reg_devices(params
->flow
, &endpoints_info
);
495 for(err
= snd_card_next(&card
); card
!= -1 && err
>= 0; err
= snd_card_next(&card
)){
500 sprintf(cardpath
, "hw:%u", card
);
502 if((err
= snd_ctl_open(&ctl
, cardpath
, 0)) < 0){
503 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath
,
504 err
, snd_strerror(err
));
508 cardname
= alsa_get_card_name(card
);
509 alsa_get_card_devices(params
->flow
, &endpoints_info
, ctl
, card
, cardname
);
516 WARN("Got a failure during card enumeration: %d (%s)\n", err
, snd_strerror(err
));
518 offset
= needed
= endpoints_info
.num
* sizeof(*params
->endpoints
);
519 endpoint
= params
->endpoints
;
521 for(i
= 0; i
< endpoints_info
.num
; i
++){
522 name_len
= wcslen(endpoints_info
.endpoints
[i
].name
) + 1;
523 device_len
= strlen(endpoints_info
.endpoints
[i
].device
) + 1;
524 needed
+= name_len
* sizeof(WCHAR
) + ((device_len
+ 1) & ~1);
526 if(needed
<= params
->size
){
527 endpoint
->name
= offset
;
528 memcpy((char *)params
->endpoints
+ offset
, endpoints_info
.endpoints
[i
].name
, name_len
* sizeof(WCHAR
));
529 offset
+= name_len
* sizeof(WCHAR
);
530 endpoint
->device
= offset
;
531 memcpy((char *)params
->endpoints
+ offset
, endpoints_info
.endpoints
[i
].device
, device_len
);
532 offset
+= (device_len
+ 1) & ~1;
535 free(endpoints_info
.endpoints
[i
].name
);
536 free(endpoints_info
.endpoints
[i
].device
);
538 free(endpoints_info
.endpoints
);
540 params
->num
= endpoints_info
.num
;
541 params
->default_idx
= 0;
543 if(needed
> params
->size
){
544 params
->size
= needed
;
545 params
->result
= HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER
);
547 params
->result
= S_OK
;
549 return STATUS_SUCCESS
;
552 static WAVEFORMATEXTENSIBLE
*clone_format(const WAVEFORMATEX
*fmt
)
554 WAVEFORMATEXTENSIBLE
*ret
;
557 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
558 size
= sizeof(WAVEFORMATEXTENSIBLE
);
560 size
= sizeof(WAVEFORMATEX
);
566 memcpy(ret
, fmt
, size
);
568 ret
->Format
.cbSize
= size
- sizeof(WAVEFORMATEX
);
573 static HRESULT
alsa_open_device(const char *alsa_name
, EDataFlow flow
, snd_pcm_t
**pcm_handle
,
574 snd_pcm_hw_params_t
**hw_params
)
576 snd_pcm_stream_t pcm_stream
;
580 pcm_stream
= SND_PCM_STREAM_PLAYBACK
;
581 else if(flow
== eCapture
)
582 pcm_stream
= SND_PCM_STREAM_CAPTURE
;
586 err
= snd_pcm_open(pcm_handle
, alsa_name
, pcm_stream
, SND_PCM_NONBLOCK
);
588 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name
, err
, snd_strerror(err
));
591 return AUDCLNT_E_DEVICE_IN_USE
;
593 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
597 *hw_params
= malloc(snd_pcm_hw_params_sizeof());
599 snd_pcm_close(*pcm_handle
);
600 return E_OUTOFMEMORY
;
606 static snd_pcm_format_t
alsa_format(const WAVEFORMATEX
*fmt
)
608 snd_pcm_format_t format
= SND_PCM_FORMAT_UNKNOWN
;
609 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
611 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
612 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
613 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
614 if(fmt
->wBitsPerSample
== 8)
615 format
= SND_PCM_FORMAT_U8
;
616 else if(fmt
->wBitsPerSample
== 16)
617 format
= SND_PCM_FORMAT_S16_LE
;
618 else if(fmt
->wBitsPerSample
== 24)
619 format
= SND_PCM_FORMAT_S24_3LE
;
620 else if(fmt
->wBitsPerSample
== 32)
621 format
= SND_PCM_FORMAT_S32_LE
;
623 WARN("Unsupported bit depth: %u\n", fmt
->wBitsPerSample
);
624 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
625 fmt
->wBitsPerSample
!= fmtex
->Samples
.wValidBitsPerSample
){
626 if(fmtex
->Samples
.wValidBitsPerSample
== 20 && fmt
->wBitsPerSample
== 24)
627 format
= SND_PCM_FORMAT_S20_3LE
;
629 WARN("Unsupported ValidBits: %u\n", fmtex
->Samples
.wValidBitsPerSample
);
631 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
632 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
633 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
634 if(fmt
->wBitsPerSample
== 32)
635 format
= SND_PCM_FORMAT_FLOAT_LE
;
636 else if(fmt
->wBitsPerSample
== 64)
637 format
= SND_PCM_FORMAT_FLOAT64_LE
;
639 WARN("Unsupported float size: %u\n", fmt
->wBitsPerSample
);
641 WARN("Unknown wave format: %04x\n", fmt
->wFormatTag
);
645 static int alsa_channel_index(UINT flag
)
648 case SPEAKER_FRONT_LEFT
:
650 case SPEAKER_FRONT_RIGHT
:
652 case SPEAKER_BACK_LEFT
:
654 case SPEAKER_BACK_RIGHT
:
656 case SPEAKER_FRONT_CENTER
:
658 case SPEAKER_LOW_FREQUENCY
:
660 case SPEAKER_SIDE_LEFT
:
662 case SPEAKER_SIDE_RIGHT
:
668 static BOOL
need_remapping(const WAVEFORMATEX
*fmt
, int *map
)
671 for(i
= 0; i
< fmt
->nChannels
; ++i
){
678 static DWORD
get_channel_mask(unsigned int channels
)
684 return KSAUDIO_SPEAKER_MONO
;
686 return KSAUDIO_SPEAKER_STEREO
;
688 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
690 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
692 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
694 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
696 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
698 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
700 FIXME("Unknown speaker configuration: %u\n", channels
);
704 static HRESULT
map_channels(EDataFlow flow
, const WAVEFORMATEX
*fmt
, int *alsa_channels
, int *map
)
708 if(flow
!= eCapture
&& (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
|| fmt
->nChannels
> 2) ){
709 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
710 UINT mask
, flag
= SPEAKER_FRONT_LEFT
;
713 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
714 fmtex
->dwChannelMask
!= 0)
715 mask
= fmtex
->dwChannelMask
;
717 mask
= get_channel_mask(fmt
->nChannels
);
721 while(i
< fmt
->nChannels
&& !(flag
& SPEAKER_RESERVED
)){
723 map
[i
] = alsa_channel_index(flag
);
724 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
726 if(map
[i
] >= *alsa_channels
)
727 *alsa_channels
= map
[i
] + 1;
733 while(i
< fmt
->nChannels
){
734 map
[i
] = *alsa_channels
;
735 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
741 for(i
= 0; i
< fmt
->nChannels
; ++i
){
743 map
[i
] = *alsa_channels
;
745 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
750 need_remap
= need_remapping(fmt
, map
);
752 *alsa_channels
= fmt
->nChannels
;
757 TRACE("need_remapping: %u, alsa_channels: %d\n", need_remap
, *alsa_channels
);
759 return need_remap
? S_OK
: S_FALSE
;
762 static void silence_buffer(struct alsa_stream
*stream
, BYTE
*buffer
, UINT32 frames
)
764 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)stream
->fmt
;
765 if((stream
->fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
766 (stream
->fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
767 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))) &&
768 stream
->fmt
->wBitsPerSample
== 8)
769 memset(buffer
, 128, frames
* stream
->fmt
->nBlockAlign
);
771 memset(buffer
, 0, frames
* stream
->fmt
->nBlockAlign
);
774 static ULONG_PTR
zero_bits(void)
777 return !NtCurrentTeb()->WowTebOffset
? 0 : 0x7fffffff;
783 static NTSTATUS
alsa_create_stream(void *args
)
785 struct create_stream_params
*params
= args
;
786 struct alsa_stream
*stream
;
787 snd_pcm_sw_params_t
*sw_params
= NULL
;
788 snd_pcm_format_t format
;
789 unsigned int rate
, alsa_period_us
, i
;
790 WAVEFORMATEXTENSIBLE
*fmtex
;
794 stream
= calloc(1, sizeof(*stream
));
796 params
->result
= E_OUTOFMEMORY
;
797 return STATUS_SUCCESS
;
800 params
->result
= alsa_open_device(params
->device
, params
->flow
, &stream
->pcm_handle
, &stream
->hw_params
);
801 if(FAILED(params
->result
)){
803 return STATUS_SUCCESS
;
806 stream
->need_remapping
= map_channels(params
->flow
, params
->fmt
, &stream
->alsa_channels
, stream
->alsa_channel_map
) == S_OK
;
808 if((err
= snd_pcm_hw_params_any(stream
->pcm_handle
, stream
->hw_params
)) < 0){
809 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
810 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
814 if((err
= snd_pcm_hw_params_set_access(stream
->pcm_handle
, stream
->hw_params
,
815 SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0){
816 WARN("Unable to set access: %d (%s)\n", err
, snd_strerror(err
));
817 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
821 format
= alsa_format(params
->fmt
);
822 if (format
== SND_PCM_FORMAT_UNKNOWN
){
823 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
827 if((err
= snd_pcm_hw_params_set_format(stream
->pcm_handle
, stream
->hw_params
,
829 WARN("Unable to set ALSA format to %u: %d (%s)\n", format
, err
,
831 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
835 stream
->alsa_format
= format
;
836 stream
->flow
= params
->flow
;
838 rate
= params
->fmt
->nSamplesPerSec
;
839 if((err
= snd_pcm_hw_params_set_rate_near(stream
->pcm_handle
, stream
->hw_params
,
841 WARN("Unable to set rate to %u: %d (%s)\n", rate
, err
,
843 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
847 if((err
= snd_pcm_hw_params_set_channels(stream
->pcm_handle
, stream
->hw_params
,
848 stream
->alsa_channels
)) < 0){
849 WARN("Unable to set channels to %u: %d (%s)\n", params
->fmt
->nChannels
, err
,
851 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
855 stream
->mmdev_period_rt
= params
->period
;
856 alsa_period_us
= stream
->mmdev_period_rt
/ 10;
857 if((err
= snd_pcm_hw_params_set_period_time_near(stream
->pcm_handle
,
858 stream
->hw_params
, &alsa_period_us
, NULL
)) < 0)
859 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us
,
860 err
, snd_strerror(err
));
861 /* ALSA updates the output variable alsa_period_us */
863 stream
->mmdev_period_frames
= muldiv(params
->fmt
->nSamplesPerSec
,
864 stream
->mmdev_period_rt
, 10000000);
866 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
867 stream
->alsa_bufsize_frames
= stream
->mmdev_period_frames
* 4;
868 if(err
< 0 || alsa_period_us
< params
->period
/ 10)
869 err
= snd_pcm_hw_params_set_buffer_size_near(stream
->pcm_handle
,
870 stream
->hw_params
, &stream
->alsa_bufsize_frames
);
872 unsigned int periods
= 4;
873 err
= snd_pcm_hw_params_set_periods_near(stream
->pcm_handle
, stream
->hw_params
, &periods
, NULL
);
876 WARN("Unable to set buffer size: %d (%s)\n", err
, snd_strerror(err
));
878 if((err
= snd_pcm_hw_params(stream
->pcm_handle
, stream
->hw_params
)) < 0){
879 WARN("Unable to set hw params: %d (%s)\n", err
, snd_strerror(err
));
880 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
884 if((err
= snd_pcm_hw_params_get_period_size(stream
->hw_params
,
885 &stream
->alsa_period_frames
, NULL
)) < 0){
886 WARN("Unable to get period size: %d (%s)\n", err
, snd_strerror(err
));
887 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
891 if((err
= snd_pcm_hw_params_get_buffer_size(stream
->hw_params
,
892 &stream
->alsa_bufsize_frames
)) < 0){
893 WARN("Unable to get buffer size: %d (%s)\n", err
, snd_strerror(err
));
894 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
898 sw_params
= calloc(1, snd_pcm_sw_params_sizeof());
900 params
->result
= E_OUTOFMEMORY
;
904 if((err
= snd_pcm_sw_params_current(stream
->pcm_handle
, sw_params
)) < 0){
905 WARN("Unable to get sw_params: %d (%s)\n", err
, snd_strerror(err
));
906 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
910 if((err
= snd_pcm_sw_params_set_start_threshold(stream
->pcm_handle
,
912 WARN("Unable set start threshold to 1: %d (%s)\n", err
, snd_strerror(err
));
913 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
917 if((err
= snd_pcm_sw_params_set_stop_threshold(stream
->pcm_handle
,
918 sw_params
, stream
->alsa_bufsize_frames
)) < 0){
919 WARN("Unable set stop threshold to %lu: %d (%s)\n",
920 stream
->alsa_bufsize_frames
, err
, snd_strerror(err
));
921 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
925 if((err
= snd_pcm_sw_params(stream
->pcm_handle
, sw_params
)) < 0){
926 WARN("Unable to set sw params: %d (%s)\n", err
, snd_strerror(err
));
927 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
931 if((err
= snd_pcm_prepare(stream
->pcm_handle
)) < 0){
932 WARN("Unable to prepare device: %d (%s)\n", err
, snd_strerror(err
));
933 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
937 /* Bear in mind weird situations where
938 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
939 * or surprising rounding as seen with 22050x8x1 with Pulse:
940 * ALSA period 220 vs. 221 frames in mmdevapi and
941 * buffer 883 vs. 2205 frames in mmdevapi! */
942 stream
->bufsize_frames
= muldiv(params
->duration
, params
->fmt
->nSamplesPerSec
, 10000000);
943 if(params
->share
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
944 stream
->bufsize_frames
-= stream
->bufsize_frames
% stream
->mmdev_period_frames
;
945 stream
->hidden_frames
= stream
->alsa_period_frames
+ stream
->mmdev_period_frames
+
946 muldiv(params
->fmt
->nSamplesPerSec
, EXTRA_SAFE_RT
, 10000000);
947 /* leave no less than about 1.33ms or 256 bytes of data after a rewind */
948 stream
->safe_rewind_frames
= max(256 / params
->fmt
->nBlockAlign
, muldiv(133, params
->fmt
->nSamplesPerSec
, 100000));
950 /* Check if the ALSA buffer is so small that it will run out before
951 * the next MMDevAPI period tick occurs. Allow a little wiggle room
952 * with 120% of the period time. */
953 if(stream
->alsa_bufsize_frames
< 1.2 * stream
->mmdev_period_frames
)
954 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
955 stream
->alsa_bufsize_frames
, stream
->mmdev_period_frames
);
957 fmtex
= clone_format(params
->fmt
);
959 params
->result
= E_OUTOFMEMORY
;
962 stream
->fmt
= &fmtex
->Format
;
964 size
= stream
->bufsize_frames
* params
->fmt
->nBlockAlign
;
965 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, zero_bits(), &size
,
966 MEM_COMMIT
, PAGE_READWRITE
)){
967 params
->result
= E_OUTOFMEMORY
;
970 silence_buffer(stream
, stream
->local_buffer
, stream
->bufsize_frames
);
972 stream
->silence_buf
= malloc(stream
->alsa_period_frames
* stream
->fmt
->nBlockAlign
);
973 if(!stream
->silence_buf
){
974 params
->result
= E_OUTOFMEMORY
;
977 silence_buffer(stream
, stream
->silence_buf
, stream
->alsa_period_frames
);
979 stream
->vols
= malloc(params
->fmt
->nChannels
* sizeof(float));
981 params
->result
= E_OUTOFMEMORY
;
984 for(i
= 0; i
< params
->fmt
->nChannels
; ++i
)
985 stream
->vols
[i
] = 1.f
;
987 stream
->share
= params
->share
;
988 stream
->flags
= params
->flags
;
990 pthread_mutex_init(&stream
->lock
, NULL
);
992 TRACE("ALSA period: %lu frames\n", stream
->alsa_period_frames
);
993 TRACE("ALSA buffer: %lu frames\n", stream
->alsa_bufsize_frames
);
994 TRACE("MMDevice period: %u frames\n", stream
->mmdev_period_frames
);
995 TRACE("MMDevice buffer: %u frames\n", stream
->bufsize_frames
);
999 if(FAILED(params
->result
)){
1000 snd_pcm_close(stream
->pcm_handle
);
1001 if(stream
->local_buffer
){
1003 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, &size
, MEM_RELEASE
);
1005 free(stream
->silence_buf
);
1006 free(stream
->hw_params
);
1011 *params
->stream
= (stream_handle
)(UINT_PTR
)stream
;
1014 return STATUS_SUCCESS
;
1017 static NTSTATUS
alsa_release_stream(void *args
)
1019 struct release_stream_params
*params
= args
;
1020 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1023 if(params
->timer_thread
){
1024 stream
->please_quit
= TRUE
;
1025 NtWaitForSingleObject(params
->timer_thread
, FALSE
, NULL
);
1026 NtClose(params
->timer_thread
);
1029 snd_pcm_drop(stream
->pcm_handle
);
1030 snd_pcm_close(stream
->pcm_handle
);
1031 if(stream
->local_buffer
){
1033 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, &size
, MEM_RELEASE
);
1035 if(stream
->tmp_buffer
){
1037 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
1039 free(stream
->remapping_buf
);
1040 free(stream
->silence_buf
);
1041 free(stream
->hw_params
);
1044 pthread_mutex_destroy(&stream
->lock
);
1047 params
->result
= S_OK
;
1048 return STATUS_SUCCESS
;
1051 static BYTE
*remap_channels(struct alsa_stream
*stream
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1053 snd_pcm_uframes_t i
;
1055 UINT bytes_per_sample
= stream
->fmt
->wBitsPerSample
/ 8;
1057 if(!stream
->need_remapping
)
1060 if(stream
->remapping_buf_frames
< frames
){
1061 stream
->remapping_buf
= realloc(stream
->remapping_buf
,
1062 bytes_per_sample
* stream
->alsa_channels
* frames
);
1063 stream
->remapping_buf_frames
= frames
;
1066 snd_pcm_format_set_silence(stream
->alsa_format
, stream
->remapping_buf
,
1067 frames
* stream
->alsa_channels
);
1069 switch(stream
->fmt
->wBitsPerSample
){
1071 UINT8
*tgt_buf
, *src_buf
;
1072 tgt_buf
= stream
->remapping_buf
;
1074 for(i
= 0; i
< frames
; ++i
){
1075 for(c
= 0; c
< stream
->fmt
->nChannels
; ++c
)
1076 tgt_buf
[stream
->alsa_channel_map
[c
]] = src_buf
[c
];
1077 tgt_buf
+= stream
->alsa_channels
;
1078 src_buf
+= stream
->fmt
->nChannels
;
1083 UINT16
*tgt_buf
, *src_buf
;
1084 tgt_buf
= (UINT16
*)stream
->remapping_buf
;
1085 src_buf
= (UINT16
*)buf
;
1086 for(i
= 0; i
< frames
; ++i
){
1087 for(c
= 0; c
< stream
->fmt
->nChannels
; ++c
)
1088 tgt_buf
[stream
->alsa_channel_map
[c
]] = src_buf
[c
];
1089 tgt_buf
+= stream
->alsa_channels
;
1090 src_buf
+= stream
->fmt
->nChannels
;
1095 UINT32
*tgt_buf
, *src_buf
;
1096 tgt_buf
= (UINT32
*)stream
->remapping_buf
;
1097 src_buf
= (UINT32
*)buf
;
1098 for(i
= 0; i
< frames
; ++i
){
1099 for(c
= 0; c
< stream
->fmt
->nChannels
; ++c
)
1100 tgt_buf
[stream
->alsa_channel_map
[c
]] = src_buf
[c
];
1101 tgt_buf
+= stream
->alsa_channels
;
1102 src_buf
+= stream
->fmt
->nChannels
;
1107 BYTE
*tgt_buf
, *src_buf
;
1108 tgt_buf
= stream
->remapping_buf
;
1110 for(i
= 0; i
< frames
; ++i
){
1111 for(c
= 0; c
< stream
->fmt
->nChannels
; ++c
)
1112 memcpy(&tgt_buf
[stream
->alsa_channel_map
[c
] * bytes_per_sample
],
1113 &src_buf
[c
* bytes_per_sample
], bytes_per_sample
);
1114 tgt_buf
+= stream
->alsa_channels
* bytes_per_sample
;
1115 src_buf
+= stream
->fmt
->nChannels
* bytes_per_sample
;
1121 return stream
->remapping_buf
;
1124 static void adjust_buffer_volume(const struct alsa_stream
*stream
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1126 BOOL adjust
= FALSE
;
1127 UINT32 i
, channels
, mute
= 0;
1130 if (stream
->vol_adjusted_frames
>= frames
)
1132 channels
= stream
->fmt
->nChannels
;
1134 /* Adjust the buffer based on the volume for each channel */
1135 for (i
= 0; i
< channels
; i
++)
1137 adjust
|= stream
->vols
[i
] != 1.0f
;
1138 if (stream
->vols
[i
] == 0.0f
)
1142 if (mute
== channels
)
1144 int err
= snd_pcm_format_set_silence(stream
->alsa_format
, buf
, frames
* channels
);
1146 WARN("Setting buffer to silence failed: %d (%s)\n", err
, snd_strerror(err
));
1149 if (!adjust
) return;
1151 /* Skip the frames we've already adjusted before */
1152 end
= buf
+ frames
* stream
->fmt
->nBlockAlign
;
1153 buf
+= stream
->vol_adjusted_frames
* stream
->fmt
->nBlockAlign
;
1155 switch (stream
->alsa_format
)
1157 #ifndef WORDS_BIGENDIAN
1158 #define PROCESS_BUFFER(type) do \
1160 type *p = (type*)buf; \
1163 for (i = 0; i < channels; i++) \
1164 p[i] = p[i] * stream->vols[i]; \
1166 } while ((BYTE*)p != end); \
1168 case SND_PCM_FORMAT_S16_LE
:
1169 PROCESS_BUFFER(INT16
);
1171 case SND_PCM_FORMAT_S32_LE
:
1172 PROCESS_BUFFER(INT32
);
1174 case SND_PCM_FORMAT_FLOAT_LE
:
1175 PROCESS_BUFFER(float);
1177 case SND_PCM_FORMAT_FLOAT64_LE
:
1178 PROCESS_BUFFER(double);
1180 #undef PROCESS_BUFFER
1181 case SND_PCM_FORMAT_S20_3LE
:
1182 case SND_PCM_FORMAT_S24_3LE
:
1184 /* Do it 12 bytes at a time until it is no longer possible */
1185 UINT32
*q
= (UINT32
*)buf
, mask
= ~0xff;
1188 /* After we adjust the volume, we need to mask out low bits */
1189 if (stream
->alsa_format
== SND_PCM_FORMAT_S20_3LE
)
1193 while (end
- (BYTE
*)q
>= 12)
1197 v
[1] = q
[1] << 16 | (q
[0] >> 16 & ~0xff);
1198 v
[2] = q
[2] << 24 | (q
[1] >> 8 & ~0xff);
1199 v
[3] = q
[2] & ~0xff;
1200 for (k
= 0; k
< 4; k
++)
1202 v
[k
] = (INT32
)((INT32
)v
[k
] * stream
->vols
[i
]);
1204 if (++i
== channels
) i
= 0;
1206 *q
++ = v
[0] >> 8 | v
[1] << 16;
1207 *q
++ = v
[1] >> 16 | v
[2] << 8;
1208 *q
++ = v
[2] >> 24 | v
[3];
1213 UINT32 v
= (INT32
)((INT32
)(p
[0] << 8 | p
[1] << 16 | p
[2] << 24) * stream
->vols
[i
]);
1215 *p
++ = v
>> 8 & 0xff;
1216 *p
++ = v
>> 16 & 0xff;
1218 if (++i
== channels
) i
= 0;
1223 case SND_PCM_FORMAT_U8
:
1225 UINT8
*p
= (UINT8
*)buf
;
1228 for (i
= 0; i
< channels
; i
++)
1229 p
[i
] = (int)((p
[i
] - 128) * stream
->vols
[i
]) + 128;
1231 } while ((BYTE
*)p
!= end
);
1235 TRACE("Unhandled format %i, not adjusting volume.\n", stream
->alsa_format
);
1240 static snd_pcm_sframes_t
alsa_write_best_effort(struct alsa_stream
*stream
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1242 snd_pcm_sframes_t written
;
1244 adjust_buffer_volume(stream
, buf
, frames
);
1246 /* Mark the frames we've already adjusted */
1247 if (stream
->vol_adjusted_frames
< frames
)
1248 stream
->vol_adjusted_frames
= frames
;
1250 buf
= remap_channels(stream
, buf
, frames
);
1252 written
= snd_pcm_writei(stream
->pcm_handle
, buf
, frames
);
1256 if(written
== -EAGAIN
)
1260 WARN("writei failed, recovering: %ld (%s)\n", written
,
1261 snd_strerror(written
));
1263 ret
= snd_pcm_recover(stream
->pcm_handle
, written
, 0);
1265 WARN("Could not recover: %d (%s)\n", ret
, snd_strerror(ret
));
1269 written
= snd_pcm_writei(stream
->pcm_handle
, buf
, frames
);
1273 stream
->vol_adjusted_frames
-= written
;
1277 static snd_pcm_sframes_t
alsa_write_buffer_wrap(struct alsa_stream
*stream
, BYTE
*buf
,
1278 snd_pcm_uframes_t buflen
, snd_pcm_uframes_t offs
,
1279 snd_pcm_uframes_t to_write
)
1281 snd_pcm_sframes_t ret
= 0;
1284 snd_pcm_uframes_t chunk
;
1285 snd_pcm_sframes_t tmp
;
1287 if(offs
+ to_write
> buflen
)
1288 chunk
= buflen
- offs
;
1292 tmp
= alsa_write_best_effort(stream
, buf
+ offs
* stream
->fmt
->nBlockAlign
, chunk
);
1307 static UINT
buf_ptr_diff(UINT left
, UINT right
, UINT bufsize
)
1310 return right
- left
;
1311 return bufsize
- (left
- right
);
1314 static UINT
data_not_in_alsa(struct alsa_stream
*stream
)
1318 diff
= buf_ptr_diff(stream
->lcl_offs_frames
, stream
->wri_offs_frames
, stream
->bufsize_frames
);
1322 return stream
->held_frames
- stream
->data_in_alsa_frames
;
1325 /* Here's the buffer setup:
1327 * vvvvvvvv sent to HW already
1328 * vvvvvvvv in ALSA buffer but rewindable
1329 * [dddddddddddddddd] ALSA buffer
1330 * [dddddddddddddddd--------] mmdevapi buffer
1331 * ^^^^^^^^ data_in_alsa_frames
1332 * ^^^^^^^^^^^^^^^^ held_frames
1336 * GetCurrentPadding is held_frames
1338 * During period callback, we decrement held_frames, fill ALSA buffer, and move
1341 * During Stop, we rewind the ALSA buffer
1343 static void alsa_write_data(struct alsa_stream
*stream
)
1345 snd_pcm_sframes_t written
;
1346 snd_pcm_uframes_t avail
, max_copy_frames
, data_frames_played
;
1349 /* this call seems to be required to get an accurate snd_pcm_state() */
1350 avail
= snd_pcm_avail_update(stream
->pcm_handle
);
1352 if(snd_pcm_state(stream
->pcm_handle
) == SND_PCM_STATE_XRUN
){
1353 TRACE("XRun state, recovering\n");
1355 avail
= stream
->alsa_bufsize_frames
;
1357 if((err
= snd_pcm_recover(stream
->pcm_handle
, -EPIPE
, 1)) < 0)
1358 WARN("snd_pcm_recover failed: %d (%s)\n", err
, snd_strerror(err
));
1360 if((err
= snd_pcm_reset(stream
->pcm_handle
)) < 0)
1361 WARN("snd_pcm_reset failed: %d (%s)\n", err
, snd_strerror(err
));
1363 if((err
= snd_pcm_prepare(stream
->pcm_handle
)) < 0)
1364 WARN("snd_pcm_prepare failed: %d (%s)\n", err
, snd_strerror(err
));
1367 TRACE("avail: %ld\n", avail
);
1369 /* Add a lead-in when starting with too few frames to ensure
1370 * continuous rendering. Additional benefit: Force ALSA to start. */
1371 if(stream
->data_in_alsa_frames
== 0 && stream
->held_frames
< stream
->alsa_period_frames
)
1373 alsa_write_best_effort(stream
, stream
->silence_buf
,
1374 stream
->alsa_period_frames
- stream
->held_frames
);
1375 stream
->vol_adjusted_frames
= 0;
1379 max_copy_frames
= data_not_in_alsa(stream
);
1381 max_copy_frames
= 0;
1383 data_frames_played
= min(stream
->data_in_alsa_frames
, avail
);
1384 stream
->data_in_alsa_frames
-= data_frames_played
;
1386 if(stream
->held_frames
> data_frames_played
){
1388 stream
->held_frames
-= data_frames_played
;
1390 stream
->held_frames
= 0;
1392 while(avail
&& max_copy_frames
){
1393 snd_pcm_uframes_t to_write
;
1395 to_write
= min(avail
, max_copy_frames
);
1397 written
= alsa_write_buffer_wrap(stream
, stream
->local_buffer
,
1398 stream
->bufsize_frames
, stream
->lcl_offs_frames
, to_write
);
1403 stream
->lcl_offs_frames
+= written
;
1404 stream
->lcl_offs_frames
%= stream
->bufsize_frames
;
1405 stream
->data_in_alsa_frames
+= written
;
1406 max_copy_frames
-= written
;
1410 NtSetEvent(stream
->event
, NULL
);
1413 static void alsa_read_data(struct alsa_stream
*stream
)
1415 snd_pcm_sframes_t nread
;
1416 UINT32 pos
= stream
->wri_offs_frames
, limit
= stream
->held_frames
;
1419 if(!stream
->started
)
1422 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
1423 * How to count overrun frames and report them as position increase? */
1424 limit
= stream
->bufsize_frames
- max(limit
, pos
);
1426 nread
= snd_pcm_readi(stream
->pcm_handle
,
1427 stream
->local_buffer
+ pos
* stream
->fmt
->nBlockAlign
, limit
);
1428 TRACE("read %ld from %u limit %u\n", nread
, pos
, limit
);
1432 if(nread
== -EAGAIN
) /* no data yet */
1435 WARN("read failed, recovering: %ld (%s)\n", nread
, snd_strerror(nread
));
1437 ret
= snd_pcm_recover(stream
->pcm_handle
, nread
, 0);
1439 WARN("Recover failed: %d (%s)\n", ret
, snd_strerror(ret
));
1443 nread
= snd_pcm_readi(stream
->pcm_handle
,
1444 stream
->local_buffer
+ pos
* stream
->fmt
->nBlockAlign
, limit
);
1446 WARN("read failed: %ld (%s)\n", nread
, snd_strerror(nread
));
1451 for(i
= 0; i
< stream
->fmt
->nChannels
; i
++)
1452 if(stream
->vols
[i
] != 0.0f
)
1454 if(i
== stream
->fmt
->nChannels
){ /* mute */
1456 if((err
= snd_pcm_format_set_silence(stream
->alsa_format
,
1457 stream
->local_buffer
+ pos
* stream
->fmt
->nBlockAlign
,
1459 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
1463 stream
->wri_offs_frames
+= nread
;
1464 stream
->wri_offs_frames
%= stream
->bufsize_frames
;
1465 stream
->held_frames
+= nread
;
1469 NtSetEvent(stream
->event
, NULL
);
1472 static snd_pcm_uframes_t
interp_elapsed_frames(struct alsa_stream
*stream
)
1474 LARGE_INTEGER time_freq
, current_time
, time_diff
;
1476 NtQueryPerformanceCounter(¤t_time
, &time_freq
);
1477 time_diff
.QuadPart
= current_time
.QuadPart
- stream
->last_period_time
.QuadPart
;
1478 return muldiv(time_diff
.QuadPart
, stream
->fmt
->nSamplesPerSec
, time_freq
.QuadPart
);
1481 static int alsa_rewind_best_effort(struct alsa_stream
*stream
)
1483 snd_pcm_uframes_t len
, leave
;
1485 /* we can't use snd_pcm_rewindable, some PCM devices crash. so follow
1486 * PulseAudio's example and rewind as much data as we believe is in the
1487 * buffer, minus 1.33ms for safety. */
1489 /* amount of data to leave in ALSA buffer */
1490 leave
= interp_elapsed_frames(stream
) + stream
->safe_rewind_frames
;
1492 if(stream
->held_frames
< leave
)
1493 stream
->held_frames
= 0;
1495 stream
->held_frames
-= leave
;
1497 if(stream
->data_in_alsa_frames
< leave
)
1500 len
= stream
->data_in_alsa_frames
- leave
;
1502 TRACE("rewinding %lu frames, now held %u\n", len
, stream
->held_frames
);
1505 /* snd_pcm_rewind return value is often broken, assume it succeeded */
1506 snd_pcm_rewind(stream
->pcm_handle
, len
);
1508 stream
->data_in_alsa_frames
= 0;
1513 static NTSTATUS
alsa_start(void *args
)
1515 struct start_params
*params
= args
;
1516 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1520 if((stream
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !stream
->event
)
1521 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_EVENTHANDLE_NOT_SET
);
1524 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_NOT_STOPPED
);
1526 if(stream
->flow
== eCapture
){
1527 /* dump any data that might be leftover in the ALSA capture buffer */
1528 snd_pcm_readi(stream
->pcm_handle
, stream
->local_buffer
,
1529 stream
->bufsize_frames
);
1531 snd_pcm_sframes_t avail
, written
;
1532 snd_pcm_uframes_t offs
;
1534 avail
= snd_pcm_avail_update(stream
->pcm_handle
);
1535 avail
= min(avail
, stream
->held_frames
);
1537 if(stream
->wri_offs_frames
< stream
->held_frames
)
1538 offs
= stream
->bufsize_frames
- stream
->held_frames
+ stream
->wri_offs_frames
;
1540 offs
= stream
->wri_offs_frames
- stream
->held_frames
;
1542 /* fill it with data */
1543 written
= alsa_write_buffer_wrap(stream
, stream
->local_buffer
,
1544 stream
->bufsize_frames
, offs
, avail
);
1547 stream
->lcl_offs_frames
= (offs
+ written
) % stream
->bufsize_frames
;
1548 stream
->data_in_alsa_frames
= written
;
1550 stream
->lcl_offs_frames
= offs
;
1551 stream
->data_in_alsa_frames
= 0;
1554 stream
->started
= TRUE
;
1556 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1559 static NTSTATUS
alsa_stop(void *args
)
1561 struct stop_params
*params
= args
;
1562 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1566 if(!stream
->started
)
1567 return alsa_unlock_result(stream
, ¶ms
->result
, S_FALSE
);
1569 if(stream
->flow
== eRender
)
1570 alsa_rewind_best_effort(stream
);
1572 stream
->started
= FALSE
;
1574 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1577 static NTSTATUS
alsa_reset(void *args
)
1579 struct reset_params
*params
= args
;
1580 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1585 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_NOT_STOPPED
);
1587 if(stream
->getbuf_last
)
1588 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_BUFFER_OPERATION_PENDING
);
1590 if(snd_pcm_drop(stream
->pcm_handle
) < 0)
1591 WARN("snd_pcm_drop failed\n");
1593 if(snd_pcm_reset(stream
->pcm_handle
) < 0)
1594 WARN("snd_pcm_reset failed\n");
1596 if(snd_pcm_prepare(stream
->pcm_handle
) < 0)
1597 WARN("snd_pcm_prepare failed\n");
1599 if(stream
->flow
== eRender
){
1600 stream
->written_frames
= 0;
1601 stream
->last_pos_frames
= 0;
1603 stream
->written_frames
+= stream
->held_frames
;
1605 stream
->held_frames
= 0;
1606 stream
->lcl_offs_frames
= 0;
1607 stream
->wri_offs_frames
= 0;
1609 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1612 static NTSTATUS
alsa_timer_loop(void *args
)
1614 struct timer_loop_params
*params
= args
;
1615 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1616 LARGE_INTEGER delay
, next
;
1621 delay
.QuadPart
= -stream
->mmdev_period_rt
;
1622 NtQueryPerformanceCounter(&stream
->last_period_time
, NULL
);
1623 next
.QuadPart
= stream
->last_period_time
.QuadPart
+ stream
->mmdev_period_rt
;
1625 while(!stream
->please_quit
){
1626 if(stream
->flow
== eRender
)
1627 alsa_write_data(stream
);
1628 else if(stream
->flow
== eCapture
)
1629 alsa_read_data(stream
);
1630 alsa_unlock(stream
);
1632 NtDelayExecution(FALSE
, &delay
);
1635 NtQueryPerformanceCounter(&stream
->last_period_time
, NULL
);
1636 adjust
= next
.QuadPart
- stream
->last_period_time
.QuadPart
;
1637 if(adjust
> stream
->mmdev_period_rt
/ 2)
1638 adjust
= stream
->mmdev_period_rt
/ 2;
1639 else if(adjust
< -stream
->mmdev_period_rt
/ 2)
1640 adjust
= -stream
->mmdev_period_rt
/ 2;
1641 delay
.QuadPart
= -(stream
->mmdev_period_rt
+ adjust
);
1642 next
.QuadPart
+= stream
->mmdev_period_rt
;
1645 alsa_unlock(stream
);
1647 return STATUS_SUCCESS
;
1650 static NTSTATUS
alsa_get_render_buffer(void *args
)
1652 struct get_render_buffer_params
*params
= args
;
1653 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1654 UINT32 write_pos
, frames
= params
->frames
;
1659 if(stream
->getbuf_last
)
1660 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1663 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1665 /* held_frames == GetCurrentPadding_nolock(); */
1666 if(stream
->held_frames
+ frames
> stream
->bufsize_frames
)
1667 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_BUFFER_TOO_LARGE
);
1669 write_pos
= stream
->wri_offs_frames
;
1670 if(write_pos
+ frames
> stream
->bufsize_frames
){
1671 if(stream
->tmp_buffer_frames
< frames
){
1672 if(stream
->tmp_buffer
){
1674 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
1675 stream
->tmp_buffer
= NULL
;
1677 size
= frames
* stream
->fmt
->nBlockAlign
;
1678 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, zero_bits(), &size
,
1679 MEM_COMMIT
, PAGE_READWRITE
)){
1680 stream
->tmp_buffer_frames
= 0;
1681 return alsa_unlock_result(stream
, ¶ms
->result
, E_OUTOFMEMORY
);
1683 stream
->tmp_buffer_frames
= frames
;
1685 *params
->data
= stream
->tmp_buffer
;
1686 stream
->getbuf_last
= -frames
;
1688 *params
->data
= stream
->local_buffer
+ write_pos
* stream
->fmt
->nBlockAlign
;
1689 stream
->getbuf_last
= frames
;
1692 silence_buffer(stream
, *params
->data
, frames
);
1694 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1697 static void alsa_wrap_buffer(struct alsa_stream
*stream
, BYTE
*buffer
, UINT32 written_frames
)
1699 snd_pcm_uframes_t write_offs_frames
= stream
->wri_offs_frames
;
1700 UINT32 write_offs_bytes
= write_offs_frames
* stream
->fmt
->nBlockAlign
;
1701 snd_pcm_uframes_t chunk_frames
= stream
->bufsize_frames
- write_offs_frames
;
1702 UINT32 chunk_bytes
= chunk_frames
* stream
->fmt
->nBlockAlign
;
1703 UINT32 written_bytes
= written_frames
* stream
->fmt
->nBlockAlign
;
1705 if(written_bytes
<= chunk_bytes
){
1706 memcpy(stream
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
1708 memcpy(stream
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
1709 memcpy(stream
->local_buffer
, buffer
+ chunk_bytes
,
1710 written_bytes
- chunk_bytes
);
1714 static NTSTATUS
alsa_release_render_buffer(void *args
)
1716 struct release_render_buffer_params
*params
= args
;
1717 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1718 UINT32 written_frames
= params
->written_frames
;
1723 if(!written_frames
){
1724 stream
->getbuf_last
= 0;
1725 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1728 if(!stream
->getbuf_last
)
1729 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1731 if(written_frames
> (stream
->getbuf_last
>= 0 ? stream
->getbuf_last
: -stream
->getbuf_last
))
1732 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_INVALID_SIZE
);
1734 if(stream
->getbuf_last
>= 0)
1735 buffer
= stream
->local_buffer
+ stream
->wri_offs_frames
* stream
->fmt
->nBlockAlign
;
1737 buffer
= stream
->tmp_buffer
;
1739 if(params
->flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
1740 silence_buffer(stream
, buffer
, written_frames
);
1742 if(stream
->getbuf_last
< 0)
1743 alsa_wrap_buffer(stream
, buffer
, written_frames
);
1745 stream
->wri_offs_frames
+= written_frames
;
1746 stream
->wri_offs_frames
%= stream
->bufsize_frames
;
1747 stream
->held_frames
+= written_frames
;
1748 stream
->written_frames
+= written_frames
;
1749 stream
->getbuf_last
= 0;
1751 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1754 static NTSTATUS
alsa_get_capture_buffer(void *args
)
1756 struct get_capture_buffer_params
*params
= args
;
1757 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1758 UINT32
*frames
= params
->frames
;
1763 if(stream
->getbuf_last
)
1764 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1766 if(stream
->held_frames
< stream
->mmdev_period_frames
){
1768 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_S_BUFFER_EMPTY
);
1770 *frames
= stream
->mmdev_period_frames
;
1772 if(stream
->lcl_offs_frames
+ *frames
> stream
->bufsize_frames
){
1773 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
1774 if(stream
->tmp_buffer_frames
< *frames
){
1775 if(stream
->tmp_buffer
){
1777 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
1778 stream
->tmp_buffer
= NULL
;
1780 size
= *frames
* stream
->fmt
->nBlockAlign
;
1781 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, zero_bits(), &size
,
1782 MEM_COMMIT
, PAGE_READWRITE
)){
1783 stream
->tmp_buffer_frames
= 0;
1784 return alsa_unlock_result(stream
, ¶ms
->result
, E_OUTOFMEMORY
);
1786 stream
->tmp_buffer_frames
= *frames
;
1789 *params
->data
= stream
->tmp_buffer
;
1790 chunk_bytes
= (stream
->bufsize_frames
- stream
->lcl_offs_frames
) *
1791 stream
->fmt
->nBlockAlign
;
1792 offs_bytes
= stream
->lcl_offs_frames
* stream
->fmt
->nBlockAlign
;
1793 frames_bytes
= *frames
* stream
->fmt
->nBlockAlign
;
1794 memcpy(stream
->tmp_buffer
, stream
->local_buffer
+ offs_bytes
, chunk_bytes
);
1795 memcpy(stream
->tmp_buffer
+ chunk_bytes
, stream
->local_buffer
,
1796 frames_bytes
- chunk_bytes
);
1798 *params
->data
= stream
->local_buffer
+
1799 stream
->lcl_offs_frames
* stream
->fmt
->nBlockAlign
;
1801 stream
->getbuf_last
= *frames
;
1805 *params
->devpos
= stream
->written_frames
;
1806 if(params
->qpcpos
){ /* fixme: qpc of recording time */
1807 LARGE_INTEGER stamp
, freq
;
1808 NtQueryPerformanceCounter(&stamp
, &freq
);
1809 *params
->qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
1812 return alsa_unlock_result(stream
, ¶ms
->result
, *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
);
1815 static NTSTATUS
alsa_release_capture_buffer(void *args
)
1817 struct release_capture_buffer_params
*params
= args
;
1818 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1819 UINT32 done
= params
->done
;
1824 stream
->getbuf_last
= 0;
1825 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1828 if(!stream
->getbuf_last
)
1829 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1831 if(stream
->getbuf_last
!= done
)
1832 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_INVALID_SIZE
);
1834 stream
->written_frames
+= done
;
1835 stream
->held_frames
-= done
;
1836 stream
->lcl_offs_frames
+= done
;
1837 stream
->lcl_offs_frames
%= stream
->bufsize_frames
;
1838 stream
->getbuf_last
= 0;
1840 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1843 static NTSTATUS
alsa_is_format_supported(void *args
)
1845 struct is_format_supported_params
*params
= args
;
1846 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)params
->fmt_in
;
1847 snd_pcm_t
*pcm_handle
;
1848 snd_pcm_hw_params_t
*hw_params
;
1849 snd_pcm_format_mask_t
*formats
= NULL
;
1850 snd_pcm_format_t format
;
1851 WAVEFORMATEXTENSIBLE
*closest
= NULL
;
1852 unsigned int max
= 0, min
= 0;
1854 int alsa_channels
, alsa_channel_map
[32];
1856 params
->result
= S_OK
;
1858 if(!params
->fmt_in
|| (params
->share
== AUDCLNT_SHAREMODE_SHARED
&& !params
->fmt_out
))
1859 params
->result
= E_POINTER
;
1860 else if(params
->share
!= AUDCLNT_SHAREMODE_SHARED
&& params
->share
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1861 params
->result
= E_INVALIDARG
;
1862 else if(params
->fmt_in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1863 if(params
->fmt_in
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1864 params
->result
= E_INVALIDARG
;
1865 else if(params
->fmt_in
->nAvgBytesPerSec
== 0 || params
->fmt_in
->nBlockAlign
== 0 ||
1866 (fmtex
->Samples
.wValidBitsPerSample
> params
->fmt_in
->wBitsPerSample
))
1867 params
->result
= E_INVALIDARG
;
1869 if(FAILED(params
->result
))
1870 return STATUS_SUCCESS
;
1872 if(params
->fmt_in
->nChannels
== 0){
1873 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1874 return STATUS_SUCCESS
;
1877 params
->result
= alsa_open_device(params
->device
, params
->flow
, &pcm_handle
, &hw_params
);
1878 if(FAILED(params
->result
))
1879 return STATUS_SUCCESS
;
1881 if((err
= snd_pcm_hw_params_any(pcm_handle
, hw_params
)) < 0){
1882 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1886 formats
= calloc(1, snd_pcm_format_mask_sizeof());
1888 params
->result
= E_OUTOFMEMORY
;
1892 snd_pcm_hw_params_get_format_mask(hw_params
, formats
);
1893 format
= alsa_format(params
->fmt_in
);
1894 if (format
== SND_PCM_FORMAT_UNKNOWN
||
1895 !snd_pcm_format_mask_test(formats
, format
)){
1896 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1900 closest
= clone_format(params
->fmt_in
);
1902 params
->result
= E_OUTOFMEMORY
;
1906 if((err
= snd_pcm_hw_params_get_rate_min(hw_params
, &min
, NULL
)) < 0){
1907 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1908 WARN("Unable to get min rate: %d (%s)\n", err
, snd_strerror(err
));
1912 if((err
= snd_pcm_hw_params_get_rate_max(hw_params
, &max
, NULL
)) < 0){
1913 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1914 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1918 if(params
->fmt_in
->nSamplesPerSec
< min
|| params
->fmt_in
->nSamplesPerSec
> max
){
1919 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1923 if((err
= snd_pcm_hw_params_get_channels_min(hw_params
, &min
)) < 0){
1924 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1925 WARN("Unable to get min channels: %d (%s)\n", err
, snd_strerror(err
));
1929 if((err
= snd_pcm_hw_params_get_channels_max(hw_params
, &max
)) < 0){
1930 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1931 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1934 if(params
->fmt_in
->nChannels
> max
){
1935 params
->result
= S_FALSE
;
1936 closest
->Format
.nChannels
= max
;
1937 }else if(params
->fmt_in
->nChannels
< min
){
1938 params
->result
= S_FALSE
;
1939 closest
->Format
.nChannels
= min
;
1942 map_channels(params
->flow
, params
->fmt_in
, &alsa_channels
, alsa_channel_map
);
1944 if(alsa_channels
> max
){
1945 params
->result
= S_FALSE
;
1946 closest
->Format
.nChannels
= max
;
1949 if(closest
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1950 closest
->dwChannelMask
= get_channel_mask(closest
->Format
.nChannels
);
1952 if(params
->fmt_in
->nBlockAlign
!= params
->fmt_in
->nChannels
* params
->fmt_in
->wBitsPerSample
/ 8 ||
1953 params
->fmt_in
->nAvgBytesPerSec
!= params
->fmt_in
->nBlockAlign
* params
->fmt_in
->nSamplesPerSec
||
1954 (params
->fmt_in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1955 fmtex
->Samples
.wValidBitsPerSample
< params
->fmt_in
->wBitsPerSample
))
1956 params
->result
= S_FALSE
;
1958 if(params
->share
== AUDCLNT_SHAREMODE_EXCLUSIVE
&& params
->fmt_in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1959 if(fmtex
->dwChannelMask
== 0 || fmtex
->dwChannelMask
& SPEAKER_RESERVED
)
1960 params
->result
= S_FALSE
;
1964 if(params
->result
== S_FALSE
&& !params
->fmt_out
)
1965 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1967 if(params
->result
== S_FALSE
&& params
->fmt_out
) {
1968 closest
->Format
.nBlockAlign
= closest
->Format
.nChannels
* closest
->Format
.wBitsPerSample
/ 8;
1969 closest
->Format
.nAvgBytesPerSec
= closest
->Format
.nBlockAlign
* closest
->Format
.nSamplesPerSec
;
1970 if(closest
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1971 closest
->Samples
.wValidBitsPerSample
= closest
->Format
.wBitsPerSample
;
1972 memcpy(params
->fmt_out
, closest
, closest
->Format
.cbSize
);
1977 snd_pcm_close(pcm_handle
);
1979 return STATUS_SUCCESS
;
1982 static NTSTATUS
alsa_get_mix_format(void *args
)
1984 struct get_mix_format_params
*params
= args
;
1985 WAVEFORMATEXTENSIBLE
*fmt
= params
->fmt
;
1986 snd_pcm_t
*pcm_handle
;
1987 snd_pcm_hw_params_t
*hw_params
;
1988 snd_pcm_format_mask_t
*formats
;
1989 unsigned int max_rate
, max_channels
;
1992 params
->result
= alsa_open_device(params
->device
, params
->flow
, &pcm_handle
, &hw_params
);
1993 if(FAILED(params
->result
))
1994 return STATUS_SUCCESS
;
1996 formats
= calloc(1, snd_pcm_format_mask_sizeof());
1999 snd_pcm_close(pcm_handle
);
2000 params
->result
= E_OUTOFMEMORY
;
2001 return STATUS_SUCCESS
;
2004 if((err
= snd_pcm_hw_params_any(pcm_handle
, hw_params
)) < 0){
2005 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
2006 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2010 snd_pcm_hw_params_get_format_mask(hw_params
, formats
);
2012 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
2013 if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
2014 fmt
->Format
.wBitsPerSample
= 32;
2015 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
2016 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
2017 fmt
->Format
.wBitsPerSample
= 16;
2018 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
2019 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
2020 fmt
->Format
.wBitsPerSample
= 8;
2021 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
2022 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
2023 fmt
->Format
.wBitsPerSample
= 32;
2024 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
2025 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
2026 fmt
->Format
.wBitsPerSample
= 24;
2027 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
2029 ERR("Didn't recognize any available ALSA formats\n");
2030 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2034 if((err
= snd_pcm_hw_params_get_channels_max(hw_params
, &max_channels
)) < 0){
2035 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
2036 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2040 if(max_channels
> 6)
2041 fmt
->Format
.nChannels
= 2;
2043 fmt
->Format
.nChannels
= max_channels
;
2045 if(fmt
->Format
.nChannels
> 1 && (fmt
->Format
.nChannels
& 0x1)){
2046 /* For most hardware on Windows, users must choose a configuration with an even
2047 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
2048 * channels, but those channels are still reported to applications from
2049 * GetMixFormat! Some applications behave badly if given an odd number of
2050 * channels (e.g. 2.1). */
2052 if(fmt
->Format
.nChannels
< max_channels
)
2053 fmt
->Format
.nChannels
+= 1;
2055 /* We could "fake" more channels and downmix the emulated channels,
2056 * but at that point you really ought to tweak your ALSA setup or
2057 * just use PulseAudio. */
2058 WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt
->Format
.nChannels
);
2061 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
2063 if((err
= snd_pcm_hw_params_get_rate_max(hw_params
, &max_rate
, NULL
)) < 0){
2064 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
2065 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2069 if(max_rate
>= 48000)
2070 fmt
->Format
.nSamplesPerSec
= 48000;
2071 else if(max_rate
>= 44100)
2072 fmt
->Format
.nSamplesPerSec
= 44100;
2073 else if(max_rate
>= 22050)
2074 fmt
->Format
.nSamplesPerSec
= 22050;
2075 else if(max_rate
>= 11025)
2076 fmt
->Format
.nSamplesPerSec
= 11025;
2077 else if(max_rate
>= 8000)
2078 fmt
->Format
.nSamplesPerSec
= 8000;
2080 ERR("Unknown max rate: %u\n", max_rate
);
2081 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2085 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
* fmt
->Format
.nChannels
) / 8;
2086 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
* fmt
->Format
.nBlockAlign
;
2088 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
2089 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
2094 snd_pcm_close(pcm_handle
);
2096 return STATUS_SUCCESS
;
2099 static NTSTATUS
alsa_get_buffer_size(void *args
)
2101 struct get_buffer_size_params
*params
= args
;
2102 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2106 *params
->frames
= stream
->bufsize_frames
;
2108 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2111 static NTSTATUS
alsa_get_latency(void *args
)
2113 struct get_latency_params
*params
= args
;
2114 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2118 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
2119 * yet have enough data left to play (as if it were in native's mixer). Add:
2120 * + mmdevapi_period such that at the end of it, ALSA still has data;
2121 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
2122 * + alsa_period such that ALSA always has at least one period to play. */
2123 if(stream
->flow
== eRender
)
2124 *params
->latency
= muldiv(stream
->hidden_frames
, 10000000, stream
->fmt
->nSamplesPerSec
);
2126 *params
->latency
= muldiv(stream
->alsa_period_frames
, 10000000, stream
->fmt
->nSamplesPerSec
)
2127 + stream
->mmdev_period_rt
;
2129 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2132 static NTSTATUS
alsa_get_current_padding(void *args
)
2134 struct get_current_padding_params
*params
= args
;
2135 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2139 /* padding is solely updated at callback time in shared mode */
2140 *params
->padding
= stream
->held_frames
;
2142 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2145 static NTSTATUS
alsa_get_next_packet_size(void *args
)
2147 struct get_next_packet_size_params
*params
= args
;
2148 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2152 *params
->frames
= stream
->held_frames
< stream
->mmdev_period_frames
? 0 : stream
->mmdev_period_frames
;
2154 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2157 static NTSTATUS
alsa_get_frequency(void *args
)
2159 struct get_frequency_params
*params
= args
;
2160 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2161 UINT64
*freq
= params
->freq
;
2165 if(stream
->share
== AUDCLNT_SHAREMODE_SHARED
)
2166 *freq
= (UINT64
)stream
->fmt
->nSamplesPerSec
* stream
->fmt
->nBlockAlign
;
2168 *freq
= stream
->fmt
->nSamplesPerSec
;
2170 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2173 static NTSTATUS
alsa_get_position(void *args
)
2175 struct get_position_params
*params
= args
;
2176 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2178 snd_pcm_state_t alsa_state
;
2182 /* avail_update required to get accurate snd_pcm_state() */
2183 snd_pcm_avail_update(stream
->pcm_handle
);
2184 alsa_state
= snd_pcm_state(stream
->pcm_handle
);
2186 if(stream
->flow
== eRender
){
2187 position
= stream
->written_frames
- stream
->held_frames
;
2189 if(stream
->started
&& alsa_state
== SND_PCM_STATE_RUNNING
&& stream
->held_frames
)
2190 /* we should be using snd_pcm_delay here, but it is broken
2191 * especially during ALSA device underrun. instead, let's just
2192 * interpolate between periods with the system timer. */
2193 position
+= interp_elapsed_frames(stream
);
2195 position
= min(position
, stream
->written_frames
- stream
->held_frames
+ stream
->mmdev_period_frames
);
2197 position
= min(position
, stream
->written_frames
);
2199 position
= stream
->written_frames
+ stream
->held_frames
;
2201 /* ensure monotic growth */
2202 if(position
< stream
->last_pos_frames
)
2203 position
= stream
->last_pos_frames
;
2205 stream
->last_pos_frames
= position
;
2207 TRACE("frames written: %u, held: %u, state: 0x%x, position: %u\n",
2208 (UINT32
)(stream
->written_frames
%1000000000), stream
->held_frames
,
2209 alsa_state
, (UINT32
)(position
%1000000000));
2211 if(stream
->share
== AUDCLNT_SHAREMODE_SHARED
)
2212 *params
->pos
= position
* stream
->fmt
->nBlockAlign
;
2214 *params
->pos
= position
;
2216 if(params
->qpctime
){
2217 LARGE_INTEGER stamp
, freq
;
2218 NtQueryPerformanceCounter(&stamp
, &freq
);
2219 *params
->qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2222 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2225 static NTSTATUS
alsa_set_volumes(void *args
)
2227 struct set_volumes_params
*params
= args
;
2228 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2231 for(i
= 0; i
< stream
->fmt
->nChannels
; i
++)
2232 stream
->vols
[i
] = params
->volumes
[i
] * params
->session_volumes
[i
] * params
->master_volume
;
2234 return STATUS_SUCCESS
;
2237 static NTSTATUS
alsa_set_event_handle(void *args
)
2239 struct set_event_handle_params
*params
= args
;
2240 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2244 if(!(stream
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
))
2245 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
);
2248 FIXME("called twice\n");
2249 return alsa_unlock_result(stream
, ¶ms
->result
, HRESULT_FROM_WIN32(ERROR_INVALID_NAME
));
2252 stream
->event
= params
->event
;
2254 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2257 static NTSTATUS
alsa_is_started(void *args
)
2259 struct is_started_params
*params
= args
;
2260 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2264 return alsa_unlock_result(stream
, ¶ms
->result
, stream
->started
? S_OK
: S_FALSE
);
2267 static unsigned int alsa_probe_num_speakers(char *name
)
2270 snd_pcm_hw_params_t
*params
;
2272 unsigned int max_channels
= 0;
2274 if ((err
= snd_pcm_open(&handle
, name
, SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
)) < 0) {
2275 WARN("The device \"%s\" failed to open: %d (%s).\n",
2276 name
, err
, snd_strerror(err
));
2280 params
= malloc(snd_pcm_hw_params_sizeof());
2282 WARN("Out of memory.\n");
2283 snd_pcm_close(handle
);
2287 if ((err
= snd_pcm_hw_params_any(handle
, params
)) < 0) {
2288 WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
2289 name
, err
, snd_strerror(err
));
2293 if ((err
= snd_pcm_hw_params_get_channels_max(params
,
2294 &max_channels
)) < 0){
2295 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
2301 snd_pcm_close(handle
);
2303 return max_channels
;
2306 enum AudioDeviceConnectionType
{
2307 AudioDeviceConnectionType_Unknown
= 0,
2308 AudioDeviceConnectionType_PCI
,
2309 AudioDeviceConnectionType_USB
2312 static NTSTATUS
alsa_get_prop_value(void *args
)
2314 struct get_prop_value_params
*params
= args
;
2315 const char *name
= params
->device
;
2316 EDataFlow flow
= params
->flow
;
2317 const GUID
*guid
= params
->guid
;
2318 const PROPERTYKEY
*prop
= params
->prop
;
2319 PROPVARIANT
*out
= params
->value
;
2320 static const PROPERTYKEY devicepath_key
= { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
2321 {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
2324 if(IsEqualPropertyKey(*prop
, devicepath_key
))
2326 enum AudioDeviceConnectionType connection
= AudioDeviceConnectionType_Unknown
;
2327 USHORT vendor_id
= 0, product_id
= 0;
2328 char uevent
[MAX_PATH
];
2329 FILE *fuevent
= NULL
;
2335 if(sscanf(name
, "plughw:%u,%u", &card
, &device
)){
2336 sprintf(uevent
, "/sys/class/sound/card%u/device/uevent", card
);
2337 fuevent
= fopen(uevent
, "r");
2343 while (fgets(line
, sizeof(line
), fuevent
)) {
2347 if((val
= strchr(line
, '='))) {
2351 val_len
= strlen(val
);
2352 if(val_len
> 0 && val
[val_len
- 1] == '\n') { val
[val_len
- 1] = 0; }
2354 if(!strcmp(line
, "PCI_ID")){
2355 connection
= AudioDeviceConnectionType_PCI
;
2356 if(sscanf(val
, "%hX:%hX", &vendor_id
, &product_id
)<2){
2357 WARN("Unexpected input when reading PCI_ID in uevent file.\n");
2358 connection
= AudioDeviceConnectionType_Unknown
;
2361 }else if(!strcmp(line
, "DEVTYPE") && !strcmp(val
,"usb_interface"))
2362 connection
= AudioDeviceConnectionType_USB
;
2363 else if(!strcmp(line
, "PRODUCT"))
2364 if(sscanf(val
, "%hx/%hx/", &vendor_id
, &product_id
)<2){
2365 WARN("Unexpected input when reading PRODUCT in uevent file.\n");
2366 connection
= AudioDeviceConnectionType_Unknown
;
2375 /* As hardly any audio devices have serial numbers, Windows instead
2376 appears to use a persistent random number. We emulate this here
2377 by instead using the last 8 hex digits of the GUID. */
2378 serial_number
= (guid
->Data4
[4] << 24) | (guid
->Data4
[5] << 16) | (guid
->Data4
[6] << 8) | guid
->Data4
[7];
2380 if(connection
== AudioDeviceConnectionType_USB
)
2381 sprintf(buf
, "{1}.USB\\VID_%04X&PID_%04X\\%u&%08X",
2382 vendor_id
, product_id
, device
, serial_number
);
2383 else if (connection
== AudioDeviceConnectionType_PCI
)
2384 sprintf(buf
, "{1}.HDAUDIO\\FUNC_01&VEN_%04X&DEV_%04X\\%u&%08X",
2385 vendor_id
, product_id
, device
, serial_number
);
2387 sprintf(buf
, "{1}.ROOT\\MEDIA\\%04u", serial_number
& 0x1FF);
2389 len
= strlen(buf
) + 1;
2390 if(*params
->buffer_size
< len
* sizeof(WCHAR
)){
2391 params
->result
= E_NOT_SUFFICIENT_BUFFER
;
2392 *params
->buffer_size
= len
* sizeof(WCHAR
);
2393 return STATUS_SUCCESS
;
2395 out
->vt
= VT_LPWSTR
;
2396 out
->pwszVal
= params
->buffer
;
2397 ntdll_umbstowcs(buf
, len
, out
->pwszVal
, len
);
2398 params
->result
= S_OK
;
2399 return STATUS_SUCCESS
;
2400 } else if (flow
!= eCapture
&& IsEqualPropertyKey(*prop
, PKEY_AudioEndpoint_PhysicalSpeakers
)) {
2401 unsigned int num_speakers
, card
, device
;
2404 if (sscanf(name
, "plughw:%u,%u", &card
, &device
))
2405 sprintf(hwname
, "hw:%u,%u", card
, device
); /* must be hw rather than plughw to work */
2407 strcpy(hwname
, name
);
2409 num_speakers
= alsa_probe_num_speakers(hwname
);
2410 if (num_speakers
== 0){
2411 params
->result
= E_FAIL
;
2412 return STATUS_SUCCESS
;
2416 if (num_speakers
> 6)
2417 out
->ulVal
= KSAUDIO_SPEAKER_STEREO
;
2418 else if (num_speakers
== 6)
2419 out
->ulVal
= KSAUDIO_SPEAKER_5POINT1
;
2420 else if (num_speakers
>= 4)
2421 out
->ulVal
= KSAUDIO_SPEAKER_QUAD
;
2422 else if (num_speakers
>= 2)
2423 out
->ulVal
= KSAUDIO_SPEAKER_STEREO
;
2424 else if (num_speakers
== 1)
2425 out
->ulVal
= KSAUDIO_SPEAKER_MONO
;
2427 params
->result
= S_OK
;
2428 return STATUS_SUCCESS
;
2431 TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop
->fmtid
), (unsigned)prop
->pid
);
2433 params
->result
= E_NOTIMPL
;
2434 return STATUS_SUCCESS
;
2437 unixlib_entry_t __wine_unix_call_funcs
[] =
2439 alsa_not_implemented
,
2440 alsa_not_implemented
,
2441 alsa_not_implemented
,
2442 alsa_get_endpoint_ids
,
2444 alsa_release_stream
,
2449 alsa_get_render_buffer
,
2450 alsa_release_render_buffer
,
2451 alsa_get_capture_buffer
,
2452 alsa_release_capture_buffer
,
2453 alsa_is_format_supported
,
2454 alsa_get_mix_format
,
2455 alsa_not_implemented
,
2456 alsa_get_buffer_size
,
2458 alsa_get_current_padding
,
2459 alsa_get_next_packet_size
,
2463 alsa_set_event_handle
,
2464 alsa_not_implemented
,
2466 alsa_get_prop_value
,
2467 alsa_not_implemented
,
2469 alsa_midi_out_message
,
2470 alsa_midi_in_message
,
2471 alsa_midi_notify_wait
,
2472 alsa_not_implemented
,
2479 static NTSTATUS
alsa_wow64_get_endpoint_ids(void *args
)
2488 unsigned int default_idx
;
2490 struct get_endpoint_ids_params params
=
2492 .flow
= params32
->flow
,
2493 .endpoints
= ULongToPtr(params32
->endpoints
),
2494 .size
= params32
->size
2496 alsa_get_endpoint_ids(¶ms
);
2497 params32
->size
= params
.size
;
2498 params32
->result
= params
.result
;
2499 params32
->num
= params
.num
;
2500 params32
->default_idx
= params
.default_idx
;
2501 return STATUS_SUCCESS
;
2504 static NTSTATUS
alsa_wow64_create_stream(void *args
)
2511 AUDCLNT_SHAREMODE share
;
2513 REFERENCE_TIME duration
;
2514 REFERENCE_TIME period
;
2517 PTR32 channel_count
;
2520 struct create_stream_params params
=
2522 .name
= ULongToPtr(params32
->name
),
2523 .device
= ULongToPtr(params32
->device
),
2524 .flow
= params32
->flow
,
2525 .share
= params32
->share
,
2526 .flags
= params32
->flags
,
2527 .duration
= params32
->duration
,
2528 .period
= params32
->period
,
2529 .fmt
= ULongToPtr(params32
->fmt
),
2530 .channel_count
= ULongToPtr(params32
->channel_count
),
2531 .stream
= ULongToPtr(params32
->stream
)
2533 alsa_create_stream(¶ms
);
2534 params32
->result
= params
.result
;
2535 return STATUS_SUCCESS
;
2538 static NTSTATUS
alsa_wow64_release_stream(void *args
)
2542 stream_handle stream
;
2546 struct release_stream_params params
=
2548 .stream
= params32
->stream
,
2549 .timer_thread
= ULongToHandle(params32
->timer_thread
)
2551 alsa_release_stream(¶ms
);
2552 params32
->result
= params
.result
;
2553 return STATUS_SUCCESS
;
2556 static NTSTATUS
alsa_wow64_get_render_buffer(void *args
)
2560 stream_handle stream
;
2566 struct get_render_buffer_params params
=
2568 .stream
= params32
->stream
,
2569 .frames
= params32
->frames
,
2572 alsa_get_render_buffer(¶ms
);
2573 params32
->result
= params
.result
;
2574 *(unsigned int *)ULongToPtr(params32
->data
) = PtrToUlong(data
);
2575 return STATUS_SUCCESS
;
2578 static NTSTATUS
alsa_wow64_get_capture_buffer(void *args
)
2582 stream_handle stream
;
2591 struct get_capture_buffer_params params
=
2593 .stream
= params32
->stream
,
2595 .frames
= ULongToPtr(params32
->frames
),
2596 .flags
= ULongToPtr(params32
->flags
),
2597 .devpos
= ULongToPtr(params32
->devpos
),
2598 .qpcpos
= ULongToPtr(params32
->qpcpos
)
2600 alsa_get_capture_buffer(¶ms
);
2601 params32
->result
= params
.result
;
2602 *(unsigned int *)ULongToPtr(params32
->data
) = PtrToUlong(data
);
2603 return STATUS_SUCCESS
;
2606 static NTSTATUS
alsa_wow64_is_format_supported(void *args
)
2612 AUDCLNT_SHAREMODE share
;
2617 struct is_format_supported_params params
=
2619 .device
= ULongToPtr(params32
->device
),
2620 .flow
= params32
->flow
,
2621 .share
= params32
->share
,
2622 .fmt_in
= ULongToPtr(params32
->fmt_in
),
2623 .fmt_out
= ULongToPtr(params32
->fmt_out
)
2625 alsa_is_format_supported(¶ms
);
2626 params32
->result
= params
.result
;
2627 return STATUS_SUCCESS
;
2630 static NTSTATUS
alsa_wow64_get_mix_format(void *args
)
2639 struct get_mix_format_params params
=
2641 .device
= ULongToPtr(params32
->device
),
2642 .flow
= params32
->flow
,
2643 .fmt
= ULongToPtr(params32
->fmt
)
2645 alsa_get_mix_format(¶ms
);
2646 params32
->result
= params
.result
;
2647 return STATUS_SUCCESS
;
2650 static NTSTATUS
alsa_wow64_get_buffer_size(void *args
)
2654 stream_handle stream
;
2658 struct get_buffer_size_params params
=
2660 .stream
= params32
->stream
,
2661 .frames
= ULongToPtr(params32
->frames
)
2663 alsa_get_buffer_size(¶ms
);
2664 params32
->result
= params
.result
;
2665 return STATUS_SUCCESS
;
2668 static NTSTATUS
alsa_wow64_get_latency(void *args
)
2672 stream_handle stream
;
2676 struct get_latency_params params
=
2678 .stream
= params32
->stream
,
2679 .latency
= ULongToPtr(params32
->latency
)
2681 alsa_get_latency(¶ms
);
2682 params32
->result
= params
.result
;
2683 return STATUS_SUCCESS
;
2686 static NTSTATUS
alsa_wow64_get_current_padding(void *args
)
2690 stream_handle stream
;
2694 struct get_current_padding_params params
=
2696 .stream
= params32
->stream
,
2697 .padding
= ULongToPtr(params32
->padding
)
2699 alsa_get_current_padding(¶ms
);
2700 params32
->result
= params
.result
;
2701 return STATUS_SUCCESS
;
2704 static NTSTATUS
alsa_wow64_get_next_packet_size(void *args
)
2708 stream_handle stream
;
2712 struct get_next_packet_size_params params
=
2714 .stream
= params32
->stream
,
2715 .frames
= ULongToPtr(params32
->frames
)
2717 alsa_get_next_packet_size(¶ms
);
2718 params32
->result
= params
.result
;
2719 return STATUS_SUCCESS
;
2722 static NTSTATUS
alsa_wow64_get_frequency(void *args
)
2726 stream_handle stream
;
2730 struct get_frequency_params params
=
2732 .stream
= params32
->stream
,
2733 .freq
= ULongToPtr(params32
->freq
)
2735 alsa_get_frequency(¶ms
);
2736 params32
->result
= params
.result
;
2737 return STATUS_SUCCESS
;
2740 static NTSTATUS
alsa_wow64_get_position(void *args
)
2744 stream_handle stream
;
2750 struct get_position_params params
=
2752 .stream
= params32
->stream
,
2753 .device
= params32
->device
,
2754 .pos
= ULongToPtr(params32
->pos
),
2755 .qpctime
= ULongToPtr(params32
->qpctime
)
2757 alsa_get_position(¶ms
);
2758 params32
->result
= params
.result
;
2759 return STATUS_SUCCESS
;
2762 static NTSTATUS
alsa_wow64_set_volumes(void *args
)
2766 stream_handle stream
;
2767 float master_volume
;
2769 PTR32 session_volumes
;
2771 struct set_volumes_params params
=
2773 .stream
= params32
->stream
,
2774 .master_volume
= params32
->master_volume
,
2775 .volumes
= ULongToPtr(params32
->volumes
),
2776 .session_volumes
= ULongToPtr(params32
->session_volumes
),
2778 return alsa_set_volumes(¶ms
);
2781 static NTSTATUS
alsa_wow64_set_event_handle(void *args
)
2785 stream_handle stream
;
2789 struct set_event_handle_params params
=
2791 .stream
= params32
->stream
,
2792 .event
= ULongToHandle(params32
->event
)
2795 alsa_set_event_handle(¶ms
);
2796 params32
->result
= params
.result
;
2797 return STATUS_SUCCESS
;
2800 static NTSTATUS
alsa_wow64_get_prop_value(void *args
)
2802 struct propvariant32
2805 WORD pad1
, pad2
, pad3
;
2810 ULARGE_INTEGER uhVal
;
2821 PTR32 buffer
; /* caller allocated buffer to hold value's strings */
2825 struct get_prop_value_params params
=
2827 .device
= ULongToPtr(params32
->device
),
2828 .flow
= params32
->flow
,
2829 .guid
= ULongToPtr(params32
->guid
),
2830 .prop
= ULongToPtr(params32
->prop
),
2832 .buffer
= ULongToPtr(params32
->buffer
),
2833 .buffer_size
= ULongToPtr(params32
->buffer_size
)
2835 alsa_get_prop_value(¶ms
);
2836 params32
->result
= params
.result
;
2837 if (SUCCEEDED(params
.result
))
2839 value32
= UlongToPtr(params32
->value
);
2840 value32
->vt
= value
.vt
;
2844 value32
->ulVal
= value
.ulVal
;
2847 value32
->ptr
= params32
->buffer
;
2850 FIXME("Unhandled vt %04x\n", value
.vt
);
2853 return STATUS_SUCCESS
;
2856 unixlib_entry_t __wine_unix_call_wow64_funcs
[] =
2858 alsa_not_implemented
,
2859 alsa_not_implemented
,
2860 alsa_not_implemented
,
2861 alsa_wow64_get_endpoint_ids
,
2862 alsa_wow64_create_stream
,
2863 alsa_wow64_release_stream
,
2868 alsa_wow64_get_render_buffer
,
2869 alsa_release_render_buffer
,
2870 alsa_wow64_get_capture_buffer
,
2871 alsa_release_capture_buffer
,
2872 alsa_wow64_is_format_supported
,
2873 alsa_wow64_get_mix_format
,
2874 alsa_not_implemented
,
2875 alsa_wow64_get_buffer_size
,
2876 alsa_wow64_get_latency
,
2877 alsa_wow64_get_current_padding
,
2878 alsa_wow64_get_next_packet_size
,
2879 alsa_wow64_get_frequency
,
2880 alsa_wow64_get_position
,
2881 alsa_wow64_set_volumes
,
2882 alsa_wow64_set_event_handle
,
2883 alsa_not_implemented
,
2885 alsa_wow64_get_prop_value
,
2886 alsa_not_implemented
,
2888 alsa_wow64_midi_out_message
,
2889 alsa_wow64_midi_in_message
,
2890 alsa_wow64_midi_notify_wait
,
2891 alsa_not_implemented
,