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 REFERENCE_TIME def_period
= 100000;
88 static const REFERENCE_TIME min_period
= 50000;
90 static const WCHAR drv_keyW
[] = {'S','o','f','t','w','a','r','e','\\',
91 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
92 'w','i','n','e','a','l','s','a','.','d','r','v'};
94 static ULONG_PTR zero_bits
= 0;
96 static NTSTATUS
alsa_not_implemented(void *args
)
98 return STATUS_SUCCESS
;
101 static inline void ascii_to_unicode( WCHAR
*dst
, const char *src
, size_t len
)
103 while (len
--) *dst
++ = (unsigned char)*src
++;
106 static HKEY
reg_open_key( HKEY root
, const WCHAR
*name
, ULONG name_len
)
108 UNICODE_STRING nameW
= { name_len
, name_len
, (WCHAR
*)name
};
109 OBJECT_ATTRIBUTES attr
;
112 attr
.Length
= sizeof(attr
);
113 attr
.RootDirectory
= root
;
114 attr
.ObjectName
= &nameW
;
116 attr
.SecurityDescriptor
= NULL
;
117 attr
.SecurityQualityOfService
= NULL
;
119 if (NtOpenKeyEx( &ret
, MAXIMUM_ALLOWED
, &attr
, 0 )) return 0;
123 static HKEY
open_hkcu(void)
127 DWORD_PTR sid_data
[(sizeof(TOKEN_USER
) + SECURITY_MAX_SID_SIZE
) / sizeof(DWORD_PTR
)];
128 DWORD i
, len
= sizeof(sid_data
);
131 if (NtQueryInformationToken( GetCurrentThreadEffectiveToken(), TokenUser
, sid_data
, len
, &len
))
134 sid
= ((TOKEN_USER
*)sid_data
)->User
.Sid
;
135 len
= sprintf( buffer
, "\\Registry\\User\\S-%u-%u", sid
->Revision
,
136 (unsigned)MAKELONG( MAKEWORD( sid
->IdentifierAuthority
.Value
[5], sid
->IdentifierAuthority
.Value
[4] ),
137 MAKEWORD( sid
->IdentifierAuthority
.Value
[3], sid
->IdentifierAuthority
.Value
[2] )));
138 for (i
= 0; i
< sid
->SubAuthorityCount
; i
++)
139 len
+= sprintf( buffer
+ len
, "-%u", (unsigned)sid
->SubAuthority
[i
] );
140 ascii_to_unicode( bufferW
, buffer
, len
+ 1 );
142 return reg_open_key( NULL
, bufferW
, len
* sizeof(WCHAR
) );
145 static HKEY
reg_open_hkcu_key( const WCHAR
*name
, ULONG name_len
)
147 HKEY hkcu
= open_hkcu(), key
;
149 key
= reg_open_key( hkcu
, name
, name_len
);
155 static ULONG
reg_query_value( HKEY hkey
, const WCHAR
*name
,
156 KEY_VALUE_PARTIAL_INFORMATION
*info
, ULONG size
)
158 unsigned int name_size
= name
? wcslen( name
) * sizeof(WCHAR
) : 0;
159 UNICODE_STRING nameW
= { name_size
, name_size
, (WCHAR
*)name
};
161 if (NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
,
165 return size
- FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
168 static snd_pcm_stream_t
alsa_get_direction(EDataFlow flow
)
170 return (flow
== eRender
) ? SND_PCM_STREAM_PLAYBACK
: SND_PCM_STREAM_CAPTURE
;
173 static WCHAR
*strdupAtoW(const char *str
)
178 if(!str
) return NULL
;
180 len
= strlen(str
) + 1;
181 ret
= malloc(len
* sizeof(WCHAR
));
182 if(ret
) ntdll_umbstowcs(str
, len
, ret
, len
);
186 /* copied from kernelbase */
187 static int muldiv( int a
, int b
, int c
)
193 /* We want to deal with a positive divisor to simplify the logic. */
200 /* If the result is positive, we "add" to round. else, we subtract to round. */
201 if ((a
< 0 && b
< 0) || (a
>= 0 && b
>= 0))
202 ret
= (((LONGLONG
)a
* b
) + (c
/ 2)) / c
;
204 ret
= (((LONGLONG
)a
* b
) - (c
/ 2)) / c
;
206 if (ret
> 2147483647 || ret
< -2147483647) return -1;
210 static void alsa_lock(struct alsa_stream
*stream
)
212 pthread_mutex_lock(&stream
->lock
);
215 static void alsa_unlock(struct alsa_stream
*stream
)
217 pthread_mutex_unlock(&stream
->lock
);
220 static NTSTATUS
alsa_unlock_result(struct alsa_stream
*stream
,
221 HRESULT
*result
, HRESULT value
)
225 return STATUS_SUCCESS
;
228 static struct alsa_stream
*handle_get_stream(stream_handle h
)
230 return (struct alsa_stream
*)(UINT_PTR
)h
;
233 static BOOL
alsa_try_open(const char *devnode
, EDataFlow flow
)
238 TRACE("devnode: %s, flow: %d\n", devnode
, flow
);
240 if((err
= snd_pcm_open(&handle
, devnode
, alsa_get_direction(flow
), SND_PCM_NONBLOCK
)) < 0){
241 WARN("The device \"%s\" failed to open: %d (%s).\n", devnode
, err
, snd_strerror(err
));
245 snd_pcm_close(handle
);
249 static WCHAR
*construct_device_id(EDataFlow flow
, const WCHAR
*chunk1
, const WCHAR
*chunk2
)
253 size_t len_wchars
= 0, chunk1_len
= 0, chunk2_len
= 0, copied
= 0, prefix_len
;
255 static const WCHAR dashW
[] = {' ','-',' ',0};
256 static const size_t dashW_len
= ARRAY_SIZE(dashW
) - 1;
257 static const WCHAR outW
[] = {'O','u','t',':',' ',0};
258 static const WCHAR inW
[] = {'I','n',':',' ',0};
262 prefix_len
= ARRAY_SIZE(outW
) - 1;
263 len_wchars
+= prefix_len
;
266 prefix_len
= ARRAY_SIZE(inW
) - 1;
267 len_wchars
+= prefix_len
;
270 chunk1_len
= wcslen(chunk1
);
271 len_wchars
+= chunk1_len
;
274 len_wchars
+= dashW_len
;
276 chunk2_len
= wcslen(chunk2
);
277 len_wchars
+= chunk2_len
;
279 len_wchars
+= 1; /* NULL byte */
281 ret
= malloc(len_wchars
* sizeof(WCHAR
));
283 memcpy(ret
, prefix
, prefix_len
* sizeof(WCHAR
));
284 copied
+= prefix_len
;
286 memcpy(ret
+ copied
, chunk1
, chunk1_len
* sizeof(WCHAR
));
287 copied
+= chunk1_len
;
289 if(chunk1
&& chunk2
){
290 memcpy(ret
+ copied
, dashW
, dashW_len
* sizeof(WCHAR
));
294 memcpy(ret
+ copied
, chunk2
, chunk2_len
* sizeof(WCHAR
));
295 copied
+= chunk2_len
;
299 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret
));
310 struct endpoints_info
312 unsigned int num
, size
;
313 struct endpt
*endpoints
;
316 static void endpoints_add(struct endpoints_info
*endpoints
, WCHAR
*name
, char *device
)
318 if(endpoints
->num
>= endpoints
->size
){
319 if (!endpoints
->size
) endpoints
->size
= 16;
320 else endpoints
->size
*= 2;
321 endpoints
->endpoints
= realloc(endpoints
->endpoints
, endpoints
->size
* sizeof(*endpoints
->endpoints
));
324 endpoints
->endpoints
[endpoints
->num
].name
= name
;
325 endpoints
->endpoints
[endpoints
->num
++].device
= device
;
328 static HRESULT
alsa_get_card_devices(EDataFlow flow
, struct endpoints_info
*endpoints_info
,
329 snd_ctl_t
*ctl
, int card
, const WCHAR
*cardname
)
332 snd_pcm_info_t
*info
;
334 info
= calloc(1, snd_pcm_info_sizeof());
336 return E_OUTOFMEMORY
;
338 snd_pcm_info_set_subdevice(info
, 0);
339 snd_pcm_info_set_stream(info
, alsa_get_direction(flow
));
342 for(err
= snd_ctl_pcm_next_device(ctl
, &device
); device
!= -1 && err
>= 0;
343 err
= snd_ctl_pcm_next_device(ctl
, &device
)){
347 snd_pcm_info_set_device(info
, device
);
349 if((err
= snd_ctl_pcm_info(ctl
, info
)) < 0){
351 /* This device doesn't have the right stream direction */
354 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
355 card
, device
, err
, snd_strerror(err
));
359 sprintf(devnode
, "plughw:%d,%d", card
, device
);
360 if(!alsa_try_open(devnode
, flow
))
363 devname
= strdupAtoW(snd_pcm_info_get_name(info
));
365 WARN("Unable to get device name for card %d, device %d\n", card
, device
);
369 endpoints_add(endpoints_info
, construct_device_id(flow
, cardname
, devname
), strdup(devnode
));
376 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
377 card
, err
, snd_strerror(err
));
382 static void get_reg_devices(EDataFlow flow
, struct endpoints_info
*endpoints_info
)
384 static const WCHAR ALSAOutputDevices
[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
385 static const WCHAR ALSAInputDevices
[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
387 KEY_VALUE_PARTIAL_INFORMATION
*key_info
= (void *)buffer
;
390 const WCHAR
*value_name
= (flow
== eRender
) ? ALSAOutputDevices
: ALSAInputDevices
;
392 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
393 if((key
= reg_open_hkcu_key(drv_keyW
, sizeof(drv_keyW
)))){
394 if((size
= reg_query_value(key
, value_name
, key_info
, sizeof(buffer
)))){
395 WCHAR
*p
= (WCHAR
*)key_info
->Data
;
397 if(key_info
->Type
!= REG_MULTI_SZ
){
398 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
405 char *devname
= malloc(len
* 3 + 1);
407 ntdll_wcstoumbs(p
, len
+ 1, devname
, len
* 3 + 1, FALSE
);
409 if(alsa_try_open(devname
, flow
))
410 endpoints_add(endpoints_info
, construct_device_id(flow
, p
, NULL
), strdup(devname
));
423 int first_card_number
;
427 static struct list card_types
= LIST_INIT(card_types
);
429 static BOOL
need_card_number(int card
, const char *string
)
431 struct card_type
*cptr
;
433 LIST_FOR_EACH_ENTRY(cptr
, &card_types
, struct card_type
, entry
)
435 if(!strcmp(string
, cptr
->string
))
436 return card
!= cptr
->first_card_number
;
439 /* this is the first instance of string */
440 cptr
= malloc(sizeof(struct card_type
) + strlen(string
));
442 /* Default to displaying card number if we can't track cards */
445 cptr
->first_card_number
= card
;
446 strcpy(cptr
->string
, string
);
447 list_add_head(&card_types
, &cptr
->entry
);
451 static WCHAR
*alsa_get_card_name(int card
)
457 if((err
= snd_card_get_name(card
, &cardname
)) < 0){
458 /* FIXME: Should be localized */
459 WARN("Unable to get card name for ALSA device %d: %d (%s)\n", card
, err
, snd_strerror(err
));
460 cardname
= strdup("Unknown soundcard");
463 if(need_card_number(card
, cardname
)){
466 * For identical card names, second and subsequent instances get
467 * card number prefix to distinguish them (like Windows).
469 if(asprintf(&cardnameN
, "%u-%s", card
, cardname
) > 0){
471 cardname
= cardnameN
;
475 ret
= strdupAtoW(cardname
);
481 static NTSTATUS
alsa_process_attach(void *args
)
484 if (NtCurrentTeb()->WowTebOffset
)
486 SYSTEM_BASIC_INFORMATION info
;
488 NtQuerySystemInformation(SystemEmulationBasicInformation
, &info
, sizeof(info
), NULL
);
489 zero_bits
= (ULONG_PTR
)info
.HighestUserAddress
| 0x7fffffff;
492 return STATUS_SUCCESS
;
495 static NTSTATUS
alsa_main_loop(void *args
)
497 struct main_loop_params
*params
= args
;
498 NtSetEvent(params
->event
, NULL
);
499 return STATUS_SUCCESS
;
502 static NTSTATUS
alsa_get_endpoint_ids(void *args
)
504 static const WCHAR defaultW
[] = {'d','e','f','a','u','l','t',0};
505 struct get_endpoint_ids_params
*params
= args
;
506 struct endpoints_info endpoints_info
;
507 unsigned int i
, needed
, name_len
, device_len
, offset
;
508 struct endpoint
*endpoint
;
513 endpoints_info
.num
= endpoints_info
.size
= 0;
514 endpoints_info
.endpoints
= NULL
;
516 if(alsa_try_open("default", params
->flow
))
517 endpoints_add(&endpoints_info
, construct_device_id(params
->flow
, defaultW
, NULL
), strdup("default"));
519 get_reg_devices(params
->flow
, &endpoints_info
);
521 for(err
= snd_card_next(&card
); card
!= -1 && err
>= 0; err
= snd_card_next(&card
)){
526 sprintf(cardpath
, "hw:%u", card
);
528 if((err
= snd_ctl_open(&ctl
, cardpath
, 0)) < 0){
529 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath
,
530 err
, snd_strerror(err
));
534 cardname
= alsa_get_card_name(card
);
535 alsa_get_card_devices(params
->flow
, &endpoints_info
, ctl
, card
, cardname
);
542 WARN("Got a failure during card enumeration: %d (%s)\n", err
, snd_strerror(err
));
544 offset
= needed
= endpoints_info
.num
* sizeof(*params
->endpoints
);
545 endpoint
= params
->endpoints
;
547 for(i
= 0; i
< endpoints_info
.num
; i
++){
548 name_len
= wcslen(endpoints_info
.endpoints
[i
].name
) + 1;
549 device_len
= strlen(endpoints_info
.endpoints
[i
].device
) + 1;
550 needed
+= name_len
* sizeof(WCHAR
) + ((device_len
+ 1) & ~1);
552 if(needed
<= params
->size
){
553 endpoint
->name
= offset
;
554 memcpy((char *)params
->endpoints
+ offset
, endpoints_info
.endpoints
[i
].name
, name_len
* sizeof(WCHAR
));
555 offset
+= name_len
* sizeof(WCHAR
);
556 endpoint
->device
= offset
;
557 memcpy((char *)params
->endpoints
+ offset
, endpoints_info
.endpoints
[i
].device
, device_len
);
558 offset
+= (device_len
+ 1) & ~1;
561 free(endpoints_info
.endpoints
[i
].name
);
562 free(endpoints_info
.endpoints
[i
].device
);
564 free(endpoints_info
.endpoints
);
566 params
->num
= endpoints_info
.num
;
567 params
->default_idx
= 0;
569 if(needed
> params
->size
){
570 params
->size
= needed
;
571 params
->result
= HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER
);
573 params
->result
= S_OK
;
575 return STATUS_SUCCESS
;
578 static WAVEFORMATEXTENSIBLE
*clone_format(const WAVEFORMATEX
*fmt
)
580 WAVEFORMATEXTENSIBLE
*ret
;
583 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
584 size
= sizeof(WAVEFORMATEXTENSIBLE
);
586 size
= sizeof(WAVEFORMATEX
);
592 memcpy(ret
, fmt
, size
);
594 ret
->Format
.cbSize
= size
- sizeof(WAVEFORMATEX
);
599 static HRESULT
alsa_open_device(const char *alsa_name
, EDataFlow flow
, snd_pcm_t
**pcm_handle
,
600 snd_pcm_hw_params_t
**hw_params
)
602 snd_pcm_stream_t pcm_stream
;
606 pcm_stream
= SND_PCM_STREAM_PLAYBACK
;
607 else if(flow
== eCapture
)
608 pcm_stream
= SND_PCM_STREAM_CAPTURE
;
612 err
= snd_pcm_open(pcm_handle
, alsa_name
, pcm_stream
, SND_PCM_NONBLOCK
);
614 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name
, err
, snd_strerror(err
));
617 return AUDCLNT_E_DEVICE_IN_USE
;
619 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
623 *hw_params
= malloc(snd_pcm_hw_params_sizeof());
625 snd_pcm_close(*pcm_handle
);
626 return E_OUTOFMEMORY
;
632 static snd_pcm_format_t
alsa_format(const WAVEFORMATEX
*fmt
)
634 snd_pcm_format_t format
= SND_PCM_FORMAT_UNKNOWN
;
635 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
637 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
638 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
639 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
640 if(fmt
->wBitsPerSample
== 8)
641 format
= SND_PCM_FORMAT_U8
;
642 else if(fmt
->wBitsPerSample
== 16)
643 format
= SND_PCM_FORMAT_S16_LE
;
644 else if(fmt
->wBitsPerSample
== 24)
645 format
= SND_PCM_FORMAT_S24_3LE
;
646 else if(fmt
->wBitsPerSample
== 32)
647 format
= SND_PCM_FORMAT_S32_LE
;
649 WARN("Unsupported bit depth: %u\n", fmt
->wBitsPerSample
);
650 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
651 fmt
->wBitsPerSample
!= fmtex
->Samples
.wValidBitsPerSample
){
652 if(fmtex
->Samples
.wValidBitsPerSample
== 20 && fmt
->wBitsPerSample
== 24)
653 format
= SND_PCM_FORMAT_S20_3LE
;
655 WARN("Unsupported ValidBits: %u\n", fmtex
->Samples
.wValidBitsPerSample
);
657 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
658 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
659 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
660 if(fmt
->wBitsPerSample
== 32)
661 format
= SND_PCM_FORMAT_FLOAT_LE
;
662 else if(fmt
->wBitsPerSample
== 64)
663 format
= SND_PCM_FORMAT_FLOAT64_LE
;
665 WARN("Unsupported float size: %u\n", fmt
->wBitsPerSample
);
667 WARN("Unknown wave format: %04x\n", fmt
->wFormatTag
);
671 static int alsa_channel_index(UINT flag
)
674 case SPEAKER_FRONT_LEFT
:
676 case SPEAKER_FRONT_RIGHT
:
678 case SPEAKER_BACK_LEFT
:
680 case SPEAKER_BACK_RIGHT
:
682 case SPEAKER_FRONT_CENTER
:
684 case SPEAKER_LOW_FREQUENCY
:
686 case SPEAKER_SIDE_LEFT
:
688 case SPEAKER_SIDE_RIGHT
:
694 static BOOL
need_remapping(const WAVEFORMATEX
*fmt
, int *map
)
697 for(i
= 0; i
< fmt
->nChannels
; ++i
){
704 static DWORD
get_channel_mask(unsigned int channels
)
710 return KSAUDIO_SPEAKER_MONO
;
712 return KSAUDIO_SPEAKER_STEREO
;
714 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
716 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
718 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
720 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
722 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
724 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
726 FIXME("Unknown speaker configuration: %u\n", channels
);
730 static HRESULT
map_channels(EDataFlow flow
, const WAVEFORMATEX
*fmt
, int *alsa_channels
, int *map
)
734 if(flow
!= eCapture
&& (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
|| fmt
->nChannels
> 2) ){
735 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
736 UINT mask
, flag
= SPEAKER_FRONT_LEFT
;
739 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
740 fmtex
->dwChannelMask
!= 0)
741 mask
= fmtex
->dwChannelMask
;
743 mask
= get_channel_mask(fmt
->nChannels
);
747 while(i
< fmt
->nChannels
&& !(flag
& SPEAKER_RESERVED
)){
749 map
[i
] = alsa_channel_index(flag
);
750 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
752 if(map
[i
] >= *alsa_channels
)
753 *alsa_channels
= map
[i
] + 1;
759 while(i
< fmt
->nChannels
){
760 map
[i
] = *alsa_channels
;
761 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
767 for(i
= 0; i
< fmt
->nChannels
; ++i
){
769 map
[i
] = *alsa_channels
;
771 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
776 need_remap
= need_remapping(fmt
, map
);
778 *alsa_channels
= fmt
->nChannels
;
783 TRACE("need_remapping: %u, alsa_channels: %d\n", need_remap
, *alsa_channels
);
785 return need_remap
? S_OK
: S_FALSE
;
788 static void silence_buffer(struct alsa_stream
*stream
, BYTE
*buffer
, UINT32 frames
)
790 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)stream
->fmt
;
791 if((stream
->fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
792 (stream
->fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
793 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))) &&
794 stream
->fmt
->wBitsPerSample
== 8)
795 memset(buffer
, 128, frames
* stream
->fmt
->nBlockAlign
);
797 memset(buffer
, 0, frames
* stream
->fmt
->nBlockAlign
);
800 static NTSTATUS
alsa_create_stream(void *args
)
802 struct create_stream_params
*params
= args
;
803 struct alsa_stream
*stream
;
804 snd_pcm_sw_params_t
*sw_params
= NULL
;
805 snd_pcm_format_t format
;
806 unsigned int rate
, alsa_period_us
, i
;
807 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)params
->fmt
;
811 params
->result
= S_OK
;
813 if (params
->share
== AUDCLNT_SHAREMODE_SHARED
) {
814 params
->period
= def_period
;
815 if (params
->duration
< 3 * params
->period
)
816 params
->duration
= 3 * params
->period
;
818 if (fmtex
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
819 (fmtex
->dwChannelMask
== 0 || fmtex
->dwChannelMask
& SPEAKER_RESERVED
))
820 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
823 params
->period
= def_period
;
824 if (params
->period
< min_period
|| params
->period
> 5000000)
825 params
->result
= AUDCLNT_E_INVALID_DEVICE_PERIOD
;
826 else if (params
->duration
> 20000000) /* The smaller the period, the lower this limit. */
827 params
->result
= AUDCLNT_E_BUFFER_SIZE_ERROR
;
828 else if (params
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) {
829 if (params
->duration
!= params
->period
)
830 params
->result
= AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL
;
832 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
834 params
->result
= AUDCLNT_E_DEVICE_IN_USE
;
835 } else if (params
->duration
< 8 * params
->period
)
836 params
->duration
= 8 * params
->period
; /* May grow above 2s. */
840 if (FAILED(params
->result
))
841 return STATUS_SUCCESS
;
843 stream
= calloc(1, sizeof(*stream
));
845 params
->result
= E_OUTOFMEMORY
;
846 return STATUS_SUCCESS
;
849 params
->result
= alsa_open_device(params
->device
, params
->flow
, &stream
->pcm_handle
, &stream
->hw_params
);
850 if(FAILED(params
->result
)){
852 return STATUS_SUCCESS
;
855 stream
->need_remapping
= map_channels(params
->flow
, params
->fmt
, &stream
->alsa_channels
, stream
->alsa_channel_map
) == S_OK
;
857 if((err
= snd_pcm_hw_params_any(stream
->pcm_handle
, stream
->hw_params
)) < 0){
858 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
859 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
863 if((err
= snd_pcm_hw_params_set_access(stream
->pcm_handle
, stream
->hw_params
,
864 SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0){
865 WARN("Unable to set access: %d (%s)\n", err
, snd_strerror(err
));
866 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
870 format
= alsa_format(params
->fmt
);
871 if (format
== SND_PCM_FORMAT_UNKNOWN
){
872 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
876 if((err
= snd_pcm_hw_params_set_format(stream
->pcm_handle
, stream
->hw_params
,
878 WARN("Unable to set ALSA format to %u: %d (%s)\n", format
, err
,
880 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
884 stream
->alsa_format
= format
;
885 stream
->flow
= params
->flow
;
887 rate
= params
->fmt
->nSamplesPerSec
;
888 if((err
= snd_pcm_hw_params_set_rate_near(stream
->pcm_handle
, stream
->hw_params
,
890 WARN("Unable to set rate to %u: %d (%s)\n", rate
, err
,
892 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
896 if((err
= snd_pcm_hw_params_set_channels(stream
->pcm_handle
, stream
->hw_params
,
897 stream
->alsa_channels
)) < 0){
898 WARN("Unable to set channels to %u: %d (%s)\n", params
->fmt
->nChannels
, err
,
900 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
904 stream
->mmdev_period_rt
= params
->period
;
905 alsa_period_us
= stream
->mmdev_period_rt
/ 10;
906 if((err
= snd_pcm_hw_params_set_period_time_near(stream
->pcm_handle
,
907 stream
->hw_params
, &alsa_period_us
, NULL
)) < 0)
908 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us
,
909 err
, snd_strerror(err
));
910 /* ALSA updates the output variable alsa_period_us */
912 stream
->mmdev_period_frames
= muldiv(params
->fmt
->nSamplesPerSec
,
913 stream
->mmdev_period_rt
, 10000000);
915 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
916 stream
->alsa_bufsize_frames
= stream
->mmdev_period_frames
* 4;
917 if(err
< 0 || alsa_period_us
< params
->period
/ 10)
918 err
= snd_pcm_hw_params_set_buffer_size_near(stream
->pcm_handle
,
919 stream
->hw_params
, &stream
->alsa_bufsize_frames
);
921 unsigned int periods
= 4;
922 err
= snd_pcm_hw_params_set_periods_near(stream
->pcm_handle
, stream
->hw_params
, &periods
, NULL
);
925 WARN("Unable to set buffer size: %d (%s)\n", err
, snd_strerror(err
));
927 if((err
= snd_pcm_hw_params(stream
->pcm_handle
, stream
->hw_params
)) < 0){
928 WARN("Unable to set hw params: %d (%s)\n", err
, snd_strerror(err
));
929 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
933 if((err
= snd_pcm_hw_params_get_period_size(stream
->hw_params
,
934 &stream
->alsa_period_frames
, NULL
)) < 0){
935 WARN("Unable to get period size: %d (%s)\n", err
, snd_strerror(err
));
936 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
940 if((err
= snd_pcm_hw_params_get_buffer_size(stream
->hw_params
,
941 &stream
->alsa_bufsize_frames
)) < 0){
942 WARN("Unable to get buffer size: %d (%s)\n", err
, snd_strerror(err
));
943 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
947 sw_params
= calloc(1, snd_pcm_sw_params_sizeof());
949 params
->result
= E_OUTOFMEMORY
;
953 if((err
= snd_pcm_sw_params_current(stream
->pcm_handle
, sw_params
)) < 0){
954 WARN("Unable to get sw_params: %d (%s)\n", err
, snd_strerror(err
));
955 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
959 if((err
= snd_pcm_sw_params_set_start_threshold(stream
->pcm_handle
,
961 WARN("Unable set start threshold to 1: %d (%s)\n", err
, snd_strerror(err
));
962 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
966 if((err
= snd_pcm_sw_params_set_stop_threshold(stream
->pcm_handle
,
967 sw_params
, stream
->alsa_bufsize_frames
)) < 0){
968 WARN("Unable set stop threshold to %lu: %d (%s)\n",
969 stream
->alsa_bufsize_frames
, err
, snd_strerror(err
));
970 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
974 if((err
= snd_pcm_sw_params(stream
->pcm_handle
, sw_params
)) < 0){
975 WARN("Unable to set sw params: %d (%s)\n", err
, snd_strerror(err
));
976 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
980 if((err
= snd_pcm_prepare(stream
->pcm_handle
)) < 0){
981 WARN("Unable to prepare device: %d (%s)\n", err
, snd_strerror(err
));
982 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
986 /* Bear in mind weird situations where
987 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
988 * or surprising rounding as seen with 22050x8x1 with Pulse:
989 * ALSA period 220 vs. 221 frames in mmdevapi and
990 * buffer 883 vs. 2205 frames in mmdevapi! */
991 stream
->bufsize_frames
= muldiv(params
->duration
, params
->fmt
->nSamplesPerSec
, 10000000);
992 if(params
->share
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
993 stream
->bufsize_frames
-= stream
->bufsize_frames
% stream
->mmdev_period_frames
;
994 stream
->hidden_frames
= stream
->alsa_period_frames
+ stream
->mmdev_period_frames
+
995 muldiv(params
->fmt
->nSamplesPerSec
, EXTRA_SAFE_RT
, 10000000);
996 /* leave no less than about 1.33ms or 256 bytes of data after a rewind */
997 stream
->safe_rewind_frames
= max(256 / params
->fmt
->nBlockAlign
, muldiv(133, params
->fmt
->nSamplesPerSec
, 100000));
999 /* Check if the ALSA buffer is so small that it will run out before
1000 * the next MMDevAPI period tick occurs. Allow a little wiggle room
1001 * with 120% of the period time. */
1002 if(stream
->alsa_bufsize_frames
< 1.2 * stream
->mmdev_period_frames
)
1003 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
1004 stream
->alsa_bufsize_frames
, stream
->mmdev_period_frames
);
1006 fmtex
= clone_format(params
->fmt
);
1008 params
->result
= E_OUTOFMEMORY
;
1011 stream
->fmt
= &fmtex
->Format
;
1013 size
= stream
->bufsize_frames
* params
->fmt
->nBlockAlign
;
1014 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, zero_bits
, &size
,
1015 MEM_COMMIT
, PAGE_READWRITE
)){
1016 params
->result
= E_OUTOFMEMORY
;
1019 silence_buffer(stream
, stream
->local_buffer
, stream
->bufsize_frames
);
1021 stream
->silence_buf
= malloc(stream
->alsa_period_frames
* stream
->fmt
->nBlockAlign
);
1022 if(!stream
->silence_buf
){
1023 params
->result
= E_OUTOFMEMORY
;
1026 silence_buffer(stream
, stream
->silence_buf
, stream
->alsa_period_frames
);
1028 stream
->vols
= malloc(params
->fmt
->nChannels
* sizeof(float));
1030 params
->result
= E_OUTOFMEMORY
;
1033 for(i
= 0; i
< params
->fmt
->nChannels
; ++i
)
1034 stream
->vols
[i
] = 1.f
;
1036 stream
->share
= params
->share
;
1037 stream
->flags
= params
->flags
;
1039 pthread_mutex_init(&stream
->lock
, NULL
);
1041 TRACE("ALSA period: %lu frames\n", stream
->alsa_period_frames
);
1042 TRACE("ALSA buffer: %lu frames\n", stream
->alsa_bufsize_frames
);
1043 TRACE("MMDevice period: %u frames\n", stream
->mmdev_period_frames
);
1044 TRACE("MMDevice buffer: %u frames\n", stream
->bufsize_frames
);
1048 if(FAILED(params
->result
)){
1049 snd_pcm_close(stream
->pcm_handle
);
1050 if(stream
->local_buffer
){
1052 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, &size
, MEM_RELEASE
);
1054 free(stream
->silence_buf
);
1055 free(stream
->hw_params
);
1060 *params
->channel_count
= params
->fmt
->nChannels
;
1061 *params
->stream
= (stream_handle
)(UINT_PTR
)stream
;
1064 return STATUS_SUCCESS
;
1067 static NTSTATUS
alsa_release_stream(void *args
)
1069 struct release_stream_params
*params
= args
;
1070 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1073 if(params
->timer_thread
){
1074 stream
->please_quit
= TRUE
;
1075 NtWaitForSingleObject(params
->timer_thread
, FALSE
, NULL
);
1076 NtClose(params
->timer_thread
);
1079 snd_pcm_drop(stream
->pcm_handle
);
1080 snd_pcm_close(stream
->pcm_handle
);
1081 if(stream
->local_buffer
){
1083 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, &size
, MEM_RELEASE
);
1085 if(stream
->tmp_buffer
){
1087 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
1089 free(stream
->remapping_buf
);
1090 free(stream
->silence_buf
);
1091 free(stream
->hw_params
);
1094 pthread_mutex_destroy(&stream
->lock
);
1097 params
->result
= S_OK
;
1098 return STATUS_SUCCESS
;
1101 static BYTE
*remap_channels(struct alsa_stream
*stream
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1103 snd_pcm_uframes_t i
;
1105 UINT bytes_per_sample
= stream
->fmt
->wBitsPerSample
/ 8;
1107 if(!stream
->need_remapping
)
1110 if(stream
->remapping_buf_frames
< frames
){
1111 stream
->remapping_buf
= realloc(stream
->remapping_buf
,
1112 bytes_per_sample
* stream
->alsa_channels
* frames
);
1113 stream
->remapping_buf_frames
= frames
;
1116 snd_pcm_format_set_silence(stream
->alsa_format
, stream
->remapping_buf
,
1117 frames
* stream
->alsa_channels
);
1119 switch(stream
->fmt
->wBitsPerSample
){
1121 UINT8
*tgt_buf
, *src_buf
;
1122 tgt_buf
= stream
->remapping_buf
;
1124 for(i
= 0; i
< frames
; ++i
){
1125 for(c
= 0; c
< stream
->fmt
->nChannels
; ++c
)
1126 tgt_buf
[stream
->alsa_channel_map
[c
]] = src_buf
[c
];
1127 tgt_buf
+= stream
->alsa_channels
;
1128 src_buf
+= stream
->fmt
->nChannels
;
1133 UINT16
*tgt_buf
, *src_buf
;
1134 tgt_buf
= (UINT16
*)stream
->remapping_buf
;
1135 src_buf
= (UINT16
*)buf
;
1136 for(i
= 0; i
< frames
; ++i
){
1137 for(c
= 0; c
< stream
->fmt
->nChannels
; ++c
)
1138 tgt_buf
[stream
->alsa_channel_map
[c
]] = src_buf
[c
];
1139 tgt_buf
+= stream
->alsa_channels
;
1140 src_buf
+= stream
->fmt
->nChannels
;
1145 UINT32
*tgt_buf
, *src_buf
;
1146 tgt_buf
= (UINT32
*)stream
->remapping_buf
;
1147 src_buf
= (UINT32
*)buf
;
1148 for(i
= 0; i
< frames
; ++i
){
1149 for(c
= 0; c
< stream
->fmt
->nChannels
; ++c
)
1150 tgt_buf
[stream
->alsa_channel_map
[c
]] = src_buf
[c
];
1151 tgt_buf
+= stream
->alsa_channels
;
1152 src_buf
+= stream
->fmt
->nChannels
;
1157 BYTE
*tgt_buf
, *src_buf
;
1158 tgt_buf
= stream
->remapping_buf
;
1160 for(i
= 0; i
< frames
; ++i
){
1161 for(c
= 0; c
< stream
->fmt
->nChannels
; ++c
)
1162 memcpy(&tgt_buf
[stream
->alsa_channel_map
[c
] * bytes_per_sample
],
1163 &src_buf
[c
* bytes_per_sample
], bytes_per_sample
);
1164 tgt_buf
+= stream
->alsa_channels
* bytes_per_sample
;
1165 src_buf
+= stream
->fmt
->nChannels
* bytes_per_sample
;
1171 return stream
->remapping_buf
;
1174 static void adjust_buffer_volume(const struct alsa_stream
*stream
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1176 BOOL adjust
= FALSE
;
1177 UINT32 i
, channels
, mute
= 0;
1180 if (stream
->vol_adjusted_frames
>= frames
)
1182 channels
= stream
->fmt
->nChannels
;
1184 /* Adjust the buffer based on the volume for each channel */
1185 for (i
= 0; i
< channels
; i
++)
1187 adjust
|= stream
->vols
[i
] != 1.0f
;
1188 if (stream
->vols
[i
] == 0.0f
)
1192 if (mute
== channels
)
1194 int err
= snd_pcm_format_set_silence(stream
->alsa_format
, buf
, frames
* channels
);
1196 WARN("Setting buffer to silence failed: %d (%s)\n", err
, snd_strerror(err
));
1199 if (!adjust
) return;
1201 /* Skip the frames we've already adjusted before */
1202 end
= buf
+ frames
* stream
->fmt
->nBlockAlign
;
1203 buf
+= stream
->vol_adjusted_frames
* stream
->fmt
->nBlockAlign
;
1205 switch (stream
->alsa_format
)
1207 #ifndef WORDS_BIGENDIAN
1208 #define PROCESS_BUFFER(type) do \
1210 type *p = (type*)buf; \
1213 for (i = 0; i < channels; i++) \
1214 p[i] = p[i] * stream->vols[i]; \
1216 } while ((BYTE*)p != end); \
1218 case SND_PCM_FORMAT_S16_LE
:
1219 PROCESS_BUFFER(INT16
);
1221 case SND_PCM_FORMAT_S32_LE
:
1222 PROCESS_BUFFER(INT32
);
1224 case SND_PCM_FORMAT_FLOAT_LE
:
1225 PROCESS_BUFFER(float);
1227 case SND_PCM_FORMAT_FLOAT64_LE
:
1228 PROCESS_BUFFER(double);
1230 #undef PROCESS_BUFFER
1231 case SND_PCM_FORMAT_S20_3LE
:
1232 case SND_PCM_FORMAT_S24_3LE
:
1234 /* Do it 12 bytes at a time until it is no longer possible */
1235 UINT32
*q
= (UINT32
*)buf
, mask
= ~0xff;
1238 /* After we adjust the volume, we need to mask out low bits */
1239 if (stream
->alsa_format
== SND_PCM_FORMAT_S20_3LE
)
1243 while (end
- (BYTE
*)q
>= 12)
1247 v
[1] = q
[1] << 16 | (q
[0] >> 16 & ~0xff);
1248 v
[2] = q
[2] << 24 | (q
[1] >> 8 & ~0xff);
1249 v
[3] = q
[2] & ~0xff;
1250 for (k
= 0; k
< 4; k
++)
1252 v
[k
] = (INT32
)((INT32
)v
[k
] * stream
->vols
[i
]);
1254 if (++i
== channels
) i
= 0;
1256 *q
++ = v
[0] >> 8 | v
[1] << 16;
1257 *q
++ = v
[1] >> 16 | v
[2] << 8;
1258 *q
++ = v
[2] >> 24 | v
[3];
1263 UINT32 v
= (INT32
)((INT32
)(p
[0] << 8 | p
[1] << 16 | p
[2] << 24) * stream
->vols
[i
]);
1265 *p
++ = v
>> 8 & 0xff;
1266 *p
++ = v
>> 16 & 0xff;
1268 if (++i
== channels
) i
= 0;
1273 case SND_PCM_FORMAT_U8
:
1275 UINT8
*p
= (UINT8
*)buf
;
1278 for (i
= 0; i
< channels
; i
++)
1279 p
[i
] = (int)((p
[i
] - 128) * stream
->vols
[i
]) + 128;
1281 } while ((BYTE
*)p
!= end
);
1285 TRACE("Unhandled format %i, not adjusting volume.\n", stream
->alsa_format
);
1290 static snd_pcm_sframes_t
alsa_write_best_effort(struct alsa_stream
*stream
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1292 snd_pcm_sframes_t written
;
1294 adjust_buffer_volume(stream
, buf
, frames
);
1296 /* Mark the frames we've already adjusted */
1297 if (stream
->vol_adjusted_frames
< frames
)
1298 stream
->vol_adjusted_frames
= frames
;
1300 buf
= remap_channels(stream
, buf
, frames
);
1302 written
= snd_pcm_writei(stream
->pcm_handle
, buf
, frames
);
1306 if(written
== -EAGAIN
)
1310 WARN("writei failed, recovering: %ld (%s)\n", written
,
1311 snd_strerror(written
));
1313 ret
= snd_pcm_recover(stream
->pcm_handle
, written
, 0);
1315 WARN("Could not recover: %d (%s)\n", ret
, snd_strerror(ret
));
1319 written
= snd_pcm_writei(stream
->pcm_handle
, buf
, frames
);
1323 stream
->vol_adjusted_frames
-= written
;
1327 static snd_pcm_sframes_t
alsa_write_buffer_wrap(struct alsa_stream
*stream
, BYTE
*buf
,
1328 snd_pcm_uframes_t buflen
, snd_pcm_uframes_t offs
,
1329 snd_pcm_uframes_t to_write
)
1331 snd_pcm_sframes_t ret
= 0;
1334 snd_pcm_uframes_t chunk
;
1335 snd_pcm_sframes_t tmp
;
1337 if(offs
+ to_write
> buflen
)
1338 chunk
= buflen
- offs
;
1342 tmp
= alsa_write_best_effort(stream
, buf
+ offs
* stream
->fmt
->nBlockAlign
, chunk
);
1357 static UINT
buf_ptr_diff(UINT left
, UINT right
, UINT bufsize
)
1360 return right
- left
;
1361 return bufsize
- (left
- right
);
1364 static UINT
data_not_in_alsa(struct alsa_stream
*stream
)
1368 diff
= buf_ptr_diff(stream
->lcl_offs_frames
, stream
->wri_offs_frames
, stream
->bufsize_frames
);
1372 return stream
->held_frames
- stream
->data_in_alsa_frames
;
1375 /* Here's the buffer setup:
1377 * vvvvvvvv sent to HW already
1378 * vvvvvvvv in ALSA buffer but rewindable
1379 * [dddddddddddddddd] ALSA buffer
1380 * [dddddddddddddddd--------] mmdevapi buffer
1381 * ^^^^^^^^ data_in_alsa_frames
1382 * ^^^^^^^^^^^^^^^^ held_frames
1386 * GetCurrentPadding is held_frames
1388 * During period callback, we decrement held_frames, fill ALSA buffer, and move
1391 * During Stop, we rewind the ALSA buffer
1393 static void alsa_write_data(struct alsa_stream
*stream
)
1395 snd_pcm_sframes_t written
;
1396 snd_pcm_uframes_t avail
, max_copy_frames
, data_frames_played
;
1399 /* this call seems to be required to get an accurate snd_pcm_state() */
1400 avail
= snd_pcm_avail_update(stream
->pcm_handle
);
1402 if(snd_pcm_state(stream
->pcm_handle
) == SND_PCM_STATE_XRUN
){
1403 TRACE("XRun state, recovering\n");
1405 avail
= stream
->alsa_bufsize_frames
;
1407 if((err
= snd_pcm_recover(stream
->pcm_handle
, -EPIPE
, 1)) < 0)
1408 WARN("snd_pcm_recover failed: %d (%s)\n", err
, snd_strerror(err
));
1410 if((err
= snd_pcm_reset(stream
->pcm_handle
)) < 0)
1411 WARN("snd_pcm_reset failed: %d (%s)\n", err
, snd_strerror(err
));
1413 if((err
= snd_pcm_prepare(stream
->pcm_handle
)) < 0)
1414 WARN("snd_pcm_prepare failed: %d (%s)\n", err
, snd_strerror(err
));
1417 TRACE("avail: %ld\n", avail
);
1419 /* Add a lead-in when starting with too few frames to ensure
1420 * continuous rendering. Additional benefit: Force ALSA to start. */
1421 if(stream
->data_in_alsa_frames
== 0 && stream
->held_frames
< stream
->alsa_period_frames
)
1423 alsa_write_best_effort(stream
, stream
->silence_buf
,
1424 stream
->alsa_period_frames
- stream
->held_frames
);
1425 stream
->vol_adjusted_frames
= 0;
1429 max_copy_frames
= data_not_in_alsa(stream
);
1431 max_copy_frames
= 0;
1433 data_frames_played
= min(stream
->data_in_alsa_frames
, avail
);
1434 stream
->data_in_alsa_frames
-= data_frames_played
;
1436 if(stream
->held_frames
> data_frames_played
){
1438 stream
->held_frames
-= data_frames_played
;
1440 stream
->held_frames
= 0;
1442 while(avail
&& max_copy_frames
){
1443 snd_pcm_uframes_t to_write
;
1445 to_write
= min(avail
, max_copy_frames
);
1447 written
= alsa_write_buffer_wrap(stream
, stream
->local_buffer
,
1448 stream
->bufsize_frames
, stream
->lcl_offs_frames
, to_write
);
1453 stream
->lcl_offs_frames
+= written
;
1454 stream
->lcl_offs_frames
%= stream
->bufsize_frames
;
1455 stream
->data_in_alsa_frames
+= written
;
1456 max_copy_frames
-= written
;
1460 NtSetEvent(stream
->event
, NULL
);
1463 static void alsa_read_data(struct alsa_stream
*stream
)
1465 snd_pcm_sframes_t nread
;
1466 UINT32 pos
= stream
->wri_offs_frames
, limit
= stream
->held_frames
;
1469 if(!stream
->started
)
1472 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
1473 * How to count overrun frames and report them as position increase? */
1474 limit
= stream
->bufsize_frames
- max(limit
, pos
);
1476 nread
= snd_pcm_readi(stream
->pcm_handle
,
1477 stream
->local_buffer
+ pos
* stream
->fmt
->nBlockAlign
, limit
);
1478 TRACE("read %ld from %u limit %u\n", nread
, pos
, limit
);
1482 if(nread
== -EAGAIN
) /* no data yet */
1485 WARN("read failed, recovering: %ld (%s)\n", nread
, snd_strerror(nread
));
1487 ret
= snd_pcm_recover(stream
->pcm_handle
, nread
, 0);
1489 WARN("Recover failed: %d (%s)\n", ret
, snd_strerror(ret
));
1493 nread
= snd_pcm_readi(stream
->pcm_handle
,
1494 stream
->local_buffer
+ pos
* stream
->fmt
->nBlockAlign
, limit
);
1496 WARN("read failed: %ld (%s)\n", nread
, snd_strerror(nread
));
1501 for(i
= 0; i
< stream
->fmt
->nChannels
; i
++)
1502 if(stream
->vols
[i
] != 0.0f
)
1504 if(i
== stream
->fmt
->nChannels
){ /* mute */
1506 if((err
= snd_pcm_format_set_silence(stream
->alsa_format
,
1507 stream
->local_buffer
+ pos
* stream
->fmt
->nBlockAlign
,
1509 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
1513 stream
->wri_offs_frames
+= nread
;
1514 stream
->wri_offs_frames
%= stream
->bufsize_frames
;
1515 stream
->held_frames
+= nread
;
1519 NtSetEvent(stream
->event
, NULL
);
1522 static snd_pcm_uframes_t
interp_elapsed_frames(struct alsa_stream
*stream
)
1524 LARGE_INTEGER time_freq
, current_time
, time_diff
;
1526 NtQueryPerformanceCounter(¤t_time
, &time_freq
);
1527 time_diff
.QuadPart
= current_time
.QuadPart
- stream
->last_period_time
.QuadPart
;
1528 return muldiv(time_diff
.QuadPart
, stream
->fmt
->nSamplesPerSec
, time_freq
.QuadPart
);
1531 static int alsa_rewind_best_effort(struct alsa_stream
*stream
)
1533 snd_pcm_uframes_t len
, leave
;
1535 /* we can't use snd_pcm_rewindable, some PCM devices crash. so follow
1536 * PulseAudio's example and rewind as much data as we believe is in the
1537 * buffer, minus 1.33ms for safety. */
1539 /* amount of data to leave in ALSA buffer */
1540 leave
= interp_elapsed_frames(stream
) + stream
->safe_rewind_frames
;
1542 if(stream
->held_frames
< leave
)
1543 stream
->held_frames
= 0;
1545 stream
->held_frames
-= leave
;
1547 if(stream
->data_in_alsa_frames
< leave
)
1550 len
= stream
->data_in_alsa_frames
- leave
;
1552 TRACE("rewinding %lu frames, now held %u\n", len
, stream
->held_frames
);
1555 /* snd_pcm_rewind return value is often broken, assume it succeeded */
1556 snd_pcm_rewind(stream
->pcm_handle
, len
);
1558 stream
->data_in_alsa_frames
= 0;
1563 static NTSTATUS
alsa_start(void *args
)
1565 struct start_params
*params
= args
;
1566 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1570 if((stream
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !stream
->event
)
1571 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_EVENTHANDLE_NOT_SET
);
1574 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_NOT_STOPPED
);
1576 if(stream
->flow
== eCapture
){
1577 /* dump any data that might be leftover in the ALSA capture buffer */
1578 snd_pcm_readi(stream
->pcm_handle
, stream
->local_buffer
,
1579 stream
->bufsize_frames
);
1581 snd_pcm_sframes_t avail
, written
;
1582 snd_pcm_uframes_t offs
;
1584 avail
= snd_pcm_avail_update(stream
->pcm_handle
);
1585 avail
= min(avail
, stream
->held_frames
);
1587 if(stream
->wri_offs_frames
< stream
->held_frames
)
1588 offs
= stream
->bufsize_frames
- stream
->held_frames
+ stream
->wri_offs_frames
;
1590 offs
= stream
->wri_offs_frames
- stream
->held_frames
;
1592 /* fill it with data */
1593 written
= alsa_write_buffer_wrap(stream
, stream
->local_buffer
,
1594 stream
->bufsize_frames
, offs
, avail
);
1597 stream
->lcl_offs_frames
= (offs
+ written
) % stream
->bufsize_frames
;
1598 stream
->data_in_alsa_frames
= written
;
1600 stream
->lcl_offs_frames
= offs
;
1601 stream
->data_in_alsa_frames
= 0;
1604 stream
->started
= TRUE
;
1606 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1609 static NTSTATUS
alsa_stop(void *args
)
1611 struct stop_params
*params
= args
;
1612 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1616 if(!stream
->started
)
1617 return alsa_unlock_result(stream
, ¶ms
->result
, S_FALSE
);
1619 if(stream
->flow
== eRender
)
1620 alsa_rewind_best_effort(stream
);
1622 stream
->started
= FALSE
;
1624 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1627 static NTSTATUS
alsa_reset(void *args
)
1629 struct reset_params
*params
= args
;
1630 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1635 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_NOT_STOPPED
);
1637 if(stream
->getbuf_last
)
1638 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_BUFFER_OPERATION_PENDING
);
1640 if(snd_pcm_drop(stream
->pcm_handle
) < 0)
1641 WARN("snd_pcm_drop failed\n");
1643 if(snd_pcm_reset(stream
->pcm_handle
) < 0)
1644 WARN("snd_pcm_reset failed\n");
1646 if(snd_pcm_prepare(stream
->pcm_handle
) < 0)
1647 WARN("snd_pcm_prepare failed\n");
1649 if(stream
->flow
== eRender
){
1650 stream
->written_frames
= 0;
1651 stream
->last_pos_frames
= 0;
1653 stream
->written_frames
+= stream
->held_frames
;
1655 stream
->held_frames
= 0;
1656 stream
->lcl_offs_frames
= 0;
1657 stream
->wri_offs_frames
= 0;
1659 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1662 static NTSTATUS
alsa_timer_loop(void *args
)
1664 struct timer_loop_params
*params
= args
;
1665 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1666 LARGE_INTEGER delay
, next
;
1671 delay
.QuadPart
= -stream
->mmdev_period_rt
;
1672 NtQueryPerformanceCounter(&stream
->last_period_time
, NULL
);
1673 next
.QuadPart
= stream
->last_period_time
.QuadPart
+ stream
->mmdev_period_rt
;
1675 while(!stream
->please_quit
){
1676 if(stream
->flow
== eRender
)
1677 alsa_write_data(stream
);
1678 else if(stream
->flow
== eCapture
)
1679 alsa_read_data(stream
);
1680 alsa_unlock(stream
);
1682 NtDelayExecution(FALSE
, &delay
);
1685 NtQueryPerformanceCounter(&stream
->last_period_time
, NULL
);
1686 adjust
= next
.QuadPart
- stream
->last_period_time
.QuadPart
;
1687 if(adjust
> stream
->mmdev_period_rt
/ 2)
1688 adjust
= stream
->mmdev_period_rt
/ 2;
1689 else if(adjust
< -stream
->mmdev_period_rt
/ 2)
1690 adjust
= -stream
->mmdev_period_rt
/ 2;
1691 delay
.QuadPart
= -(stream
->mmdev_period_rt
+ adjust
);
1692 next
.QuadPart
+= stream
->mmdev_period_rt
;
1695 alsa_unlock(stream
);
1697 return STATUS_SUCCESS
;
1700 static NTSTATUS
alsa_get_render_buffer(void *args
)
1702 struct get_render_buffer_params
*params
= args
;
1703 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1704 UINT32 write_pos
, frames
= params
->frames
;
1709 if(stream
->getbuf_last
)
1710 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1713 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1715 /* held_frames == GetCurrentPadding_nolock(); */
1716 if(stream
->held_frames
+ frames
> stream
->bufsize_frames
)
1717 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_BUFFER_TOO_LARGE
);
1719 write_pos
= stream
->wri_offs_frames
;
1720 if(write_pos
+ frames
> stream
->bufsize_frames
){
1721 if(stream
->tmp_buffer_frames
< frames
){
1722 if(stream
->tmp_buffer
){
1724 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
1725 stream
->tmp_buffer
= NULL
;
1727 size
= frames
* stream
->fmt
->nBlockAlign
;
1728 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, zero_bits
, &size
,
1729 MEM_COMMIT
, PAGE_READWRITE
)){
1730 stream
->tmp_buffer_frames
= 0;
1731 return alsa_unlock_result(stream
, ¶ms
->result
, E_OUTOFMEMORY
);
1733 stream
->tmp_buffer_frames
= frames
;
1735 *params
->data
= stream
->tmp_buffer
;
1736 stream
->getbuf_last
= -frames
;
1738 *params
->data
= stream
->local_buffer
+ write_pos
* stream
->fmt
->nBlockAlign
;
1739 stream
->getbuf_last
= frames
;
1742 silence_buffer(stream
, *params
->data
, frames
);
1744 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1747 static void alsa_wrap_buffer(struct alsa_stream
*stream
, BYTE
*buffer
, UINT32 written_frames
)
1749 snd_pcm_uframes_t write_offs_frames
= stream
->wri_offs_frames
;
1750 UINT32 write_offs_bytes
= write_offs_frames
* stream
->fmt
->nBlockAlign
;
1751 snd_pcm_uframes_t chunk_frames
= stream
->bufsize_frames
- write_offs_frames
;
1752 UINT32 chunk_bytes
= chunk_frames
* stream
->fmt
->nBlockAlign
;
1753 UINT32 written_bytes
= written_frames
* stream
->fmt
->nBlockAlign
;
1755 if(written_bytes
<= chunk_bytes
){
1756 memcpy(stream
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
1758 memcpy(stream
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
1759 memcpy(stream
->local_buffer
, buffer
+ chunk_bytes
,
1760 written_bytes
- chunk_bytes
);
1764 static NTSTATUS
alsa_release_render_buffer(void *args
)
1766 struct release_render_buffer_params
*params
= args
;
1767 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1768 UINT32 written_frames
= params
->written_frames
;
1773 if(!written_frames
){
1774 stream
->getbuf_last
= 0;
1775 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1778 if(!stream
->getbuf_last
)
1779 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1781 if(written_frames
> (stream
->getbuf_last
>= 0 ? stream
->getbuf_last
: -stream
->getbuf_last
))
1782 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_INVALID_SIZE
);
1784 if(stream
->getbuf_last
>= 0)
1785 buffer
= stream
->local_buffer
+ stream
->wri_offs_frames
* stream
->fmt
->nBlockAlign
;
1787 buffer
= stream
->tmp_buffer
;
1789 if(params
->flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
1790 silence_buffer(stream
, buffer
, written_frames
);
1792 if(stream
->getbuf_last
< 0)
1793 alsa_wrap_buffer(stream
, buffer
, written_frames
);
1795 stream
->wri_offs_frames
+= written_frames
;
1796 stream
->wri_offs_frames
%= stream
->bufsize_frames
;
1797 stream
->held_frames
+= written_frames
;
1798 stream
->written_frames
+= written_frames
;
1799 stream
->getbuf_last
= 0;
1801 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1804 static NTSTATUS
alsa_get_capture_buffer(void *args
)
1806 struct get_capture_buffer_params
*params
= args
;
1807 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1808 UINT32
*frames
= params
->frames
;
1813 if(stream
->getbuf_last
)
1814 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1816 if(stream
->held_frames
< stream
->mmdev_period_frames
){
1818 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_S_BUFFER_EMPTY
);
1820 *frames
= stream
->mmdev_period_frames
;
1822 if(stream
->lcl_offs_frames
+ *frames
> stream
->bufsize_frames
){
1823 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
1824 if(stream
->tmp_buffer_frames
< *frames
){
1825 if(stream
->tmp_buffer
){
1827 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
1828 stream
->tmp_buffer
= NULL
;
1830 size
= *frames
* stream
->fmt
->nBlockAlign
;
1831 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, zero_bits
, &size
,
1832 MEM_COMMIT
, PAGE_READWRITE
)){
1833 stream
->tmp_buffer_frames
= 0;
1834 return alsa_unlock_result(stream
, ¶ms
->result
, E_OUTOFMEMORY
);
1836 stream
->tmp_buffer_frames
= *frames
;
1839 *params
->data
= stream
->tmp_buffer
;
1840 chunk_bytes
= (stream
->bufsize_frames
- stream
->lcl_offs_frames
) *
1841 stream
->fmt
->nBlockAlign
;
1842 offs_bytes
= stream
->lcl_offs_frames
* stream
->fmt
->nBlockAlign
;
1843 frames_bytes
= *frames
* stream
->fmt
->nBlockAlign
;
1844 memcpy(stream
->tmp_buffer
, stream
->local_buffer
+ offs_bytes
, chunk_bytes
);
1845 memcpy(stream
->tmp_buffer
+ chunk_bytes
, stream
->local_buffer
,
1846 frames_bytes
- chunk_bytes
);
1848 *params
->data
= stream
->local_buffer
+
1849 stream
->lcl_offs_frames
* stream
->fmt
->nBlockAlign
;
1851 stream
->getbuf_last
= *frames
;
1855 *params
->devpos
= stream
->written_frames
;
1856 if(params
->qpcpos
){ /* fixme: qpc of recording time */
1857 LARGE_INTEGER stamp
, freq
;
1858 NtQueryPerformanceCounter(&stamp
, &freq
);
1859 *params
->qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
1862 return alsa_unlock_result(stream
, ¶ms
->result
, *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
);
1865 static NTSTATUS
alsa_release_capture_buffer(void *args
)
1867 struct release_capture_buffer_params
*params
= args
;
1868 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1869 UINT32 done
= params
->done
;
1874 stream
->getbuf_last
= 0;
1875 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1878 if(!stream
->getbuf_last
)
1879 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1881 if(stream
->getbuf_last
!= done
)
1882 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_INVALID_SIZE
);
1884 stream
->written_frames
+= done
;
1885 stream
->held_frames
-= done
;
1886 stream
->lcl_offs_frames
+= done
;
1887 stream
->lcl_offs_frames
%= stream
->bufsize_frames
;
1888 stream
->getbuf_last
= 0;
1890 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1893 static NTSTATUS
alsa_is_format_supported(void *args
)
1895 struct is_format_supported_params
*params
= args
;
1896 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)params
->fmt_in
;
1897 snd_pcm_t
*pcm_handle
;
1898 snd_pcm_hw_params_t
*hw_params
;
1899 snd_pcm_format_mask_t
*formats
= NULL
;
1900 snd_pcm_format_t format
;
1901 WAVEFORMATEXTENSIBLE
*closest
= NULL
;
1902 unsigned int max
= 0, min
= 0;
1904 int alsa_channels
, alsa_channel_map
[32];
1906 params
->result
= S_OK
;
1908 if(!params
->fmt_in
|| (params
->share
== AUDCLNT_SHAREMODE_SHARED
&& !params
->fmt_out
))
1909 params
->result
= E_POINTER
;
1910 else if(params
->share
!= AUDCLNT_SHAREMODE_SHARED
&& params
->share
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1911 params
->result
= E_INVALIDARG
;
1912 else if(params
->fmt_in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1913 if(params
->fmt_in
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1914 params
->result
= E_INVALIDARG
;
1915 else if(params
->fmt_in
->nAvgBytesPerSec
== 0 || params
->fmt_in
->nBlockAlign
== 0 ||
1916 (fmtex
->Samples
.wValidBitsPerSample
> params
->fmt_in
->wBitsPerSample
))
1917 params
->result
= E_INVALIDARG
;
1919 if(FAILED(params
->result
))
1920 return STATUS_SUCCESS
;
1922 if(params
->fmt_in
->nChannels
== 0){
1923 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1924 return STATUS_SUCCESS
;
1927 params
->result
= alsa_open_device(params
->device
, params
->flow
, &pcm_handle
, &hw_params
);
1928 if(FAILED(params
->result
))
1929 return STATUS_SUCCESS
;
1931 if((err
= snd_pcm_hw_params_any(pcm_handle
, hw_params
)) < 0){
1932 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1936 formats
= calloc(1, snd_pcm_format_mask_sizeof());
1938 params
->result
= E_OUTOFMEMORY
;
1942 snd_pcm_hw_params_get_format_mask(hw_params
, formats
);
1943 format
= alsa_format(params
->fmt_in
);
1944 if (format
== SND_PCM_FORMAT_UNKNOWN
||
1945 !snd_pcm_format_mask_test(formats
, format
)){
1946 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1950 closest
= clone_format(params
->fmt_in
);
1952 params
->result
= E_OUTOFMEMORY
;
1956 if((err
= snd_pcm_hw_params_get_rate_min(hw_params
, &min
, NULL
)) < 0){
1957 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1958 WARN("Unable to get min rate: %d (%s)\n", err
, snd_strerror(err
));
1962 if((err
= snd_pcm_hw_params_get_rate_max(hw_params
, &max
, NULL
)) < 0){
1963 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1964 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1968 if(params
->fmt_in
->nSamplesPerSec
< min
|| params
->fmt_in
->nSamplesPerSec
> max
){
1969 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1973 if((err
= snd_pcm_hw_params_get_channels_min(hw_params
, &min
)) < 0){
1974 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1975 WARN("Unable to get min channels: %d (%s)\n", err
, snd_strerror(err
));
1979 if((err
= snd_pcm_hw_params_get_channels_max(hw_params
, &max
)) < 0){
1980 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1981 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1984 if(params
->fmt_in
->nChannels
> max
){
1985 params
->result
= S_FALSE
;
1986 closest
->Format
.nChannels
= max
;
1987 }else if(params
->fmt_in
->nChannels
< min
){
1988 params
->result
= S_FALSE
;
1989 closest
->Format
.nChannels
= min
;
1992 map_channels(params
->flow
, params
->fmt_in
, &alsa_channels
, alsa_channel_map
);
1994 if(alsa_channels
> max
){
1995 params
->result
= S_FALSE
;
1996 closest
->Format
.nChannels
= max
;
1999 if(closest
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
2000 closest
->dwChannelMask
= get_channel_mask(closest
->Format
.nChannels
);
2002 if(params
->fmt_in
->nBlockAlign
!= params
->fmt_in
->nChannels
* params
->fmt_in
->wBitsPerSample
/ 8 ||
2003 params
->fmt_in
->nAvgBytesPerSec
!= params
->fmt_in
->nBlockAlign
* params
->fmt_in
->nSamplesPerSec
||
2004 (params
->fmt_in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
2005 fmtex
->Samples
.wValidBitsPerSample
< params
->fmt_in
->wBitsPerSample
))
2006 params
->result
= S_FALSE
;
2008 if(params
->share
== AUDCLNT_SHAREMODE_EXCLUSIVE
&& params
->fmt_in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
2009 if(fmtex
->dwChannelMask
== 0 || fmtex
->dwChannelMask
& SPEAKER_RESERVED
)
2010 params
->result
= S_FALSE
;
2014 if(params
->result
== S_FALSE
&& !params
->fmt_out
)
2015 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
2017 if(params
->result
== S_FALSE
&& params
->fmt_out
) {
2018 closest
->Format
.nBlockAlign
= closest
->Format
.nChannels
* closest
->Format
.wBitsPerSample
/ 8;
2019 closest
->Format
.nAvgBytesPerSec
= closest
->Format
.nBlockAlign
* closest
->Format
.nSamplesPerSec
;
2020 if(closest
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
2021 closest
->Samples
.wValidBitsPerSample
= closest
->Format
.wBitsPerSample
;
2022 memcpy(params
->fmt_out
, closest
, closest
->Format
.cbSize
);
2027 snd_pcm_close(pcm_handle
);
2029 return STATUS_SUCCESS
;
2032 static NTSTATUS
alsa_get_mix_format(void *args
)
2034 struct get_mix_format_params
*params
= args
;
2035 WAVEFORMATEXTENSIBLE
*fmt
= params
->fmt
;
2036 snd_pcm_t
*pcm_handle
;
2037 snd_pcm_hw_params_t
*hw_params
;
2038 snd_pcm_format_mask_t
*formats
;
2039 unsigned int max_rate
, max_channels
;
2042 params
->result
= alsa_open_device(params
->device
, params
->flow
, &pcm_handle
, &hw_params
);
2043 if(FAILED(params
->result
))
2044 return STATUS_SUCCESS
;
2046 formats
= calloc(1, snd_pcm_format_mask_sizeof());
2049 snd_pcm_close(pcm_handle
);
2050 params
->result
= E_OUTOFMEMORY
;
2051 return STATUS_SUCCESS
;
2054 if((err
= snd_pcm_hw_params_any(pcm_handle
, hw_params
)) < 0){
2055 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
2056 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2060 snd_pcm_hw_params_get_format_mask(hw_params
, formats
);
2062 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
2063 if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
2064 fmt
->Format
.wBitsPerSample
= 32;
2065 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
2066 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
2067 fmt
->Format
.wBitsPerSample
= 16;
2068 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
2069 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
2070 fmt
->Format
.wBitsPerSample
= 8;
2071 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
2072 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
2073 fmt
->Format
.wBitsPerSample
= 32;
2074 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
2075 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
2076 fmt
->Format
.wBitsPerSample
= 24;
2077 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
2079 ERR("Didn't recognize any available ALSA formats\n");
2080 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2084 if((err
= snd_pcm_hw_params_get_channels_max(hw_params
, &max_channels
)) < 0){
2085 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
2086 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2090 if(max_channels
> 6)
2091 fmt
->Format
.nChannels
= 2;
2093 fmt
->Format
.nChannels
= max_channels
;
2095 if(fmt
->Format
.nChannels
> 1 && (fmt
->Format
.nChannels
& 0x1)){
2096 /* For most hardware on Windows, users must choose a configuration with an even
2097 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
2098 * channels, but those channels are still reported to applications from
2099 * GetMixFormat! Some applications behave badly if given an odd number of
2100 * channels (e.g. 2.1). */
2102 if(fmt
->Format
.nChannels
< max_channels
)
2103 fmt
->Format
.nChannels
+= 1;
2105 /* We could "fake" more channels and downmix the emulated channels,
2106 * but at that point you really ought to tweak your ALSA setup or
2107 * just use PulseAudio. */
2108 WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt
->Format
.nChannels
);
2111 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
2113 if((err
= snd_pcm_hw_params_get_rate_max(hw_params
, &max_rate
, NULL
)) < 0){
2114 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
2115 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2119 if(max_rate
>= 48000)
2120 fmt
->Format
.nSamplesPerSec
= 48000;
2121 else if(max_rate
>= 44100)
2122 fmt
->Format
.nSamplesPerSec
= 44100;
2123 else if(max_rate
>= 22050)
2124 fmt
->Format
.nSamplesPerSec
= 22050;
2125 else if(max_rate
>= 11025)
2126 fmt
->Format
.nSamplesPerSec
= 11025;
2127 else if(max_rate
>= 8000)
2128 fmt
->Format
.nSamplesPerSec
= 8000;
2130 ERR("Unknown max rate: %u\n", max_rate
);
2131 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2135 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
* fmt
->Format
.nChannels
) / 8;
2136 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
* fmt
->Format
.nBlockAlign
;
2138 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
2139 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
2144 snd_pcm_close(pcm_handle
);
2146 return STATUS_SUCCESS
;
2149 static NTSTATUS
alsa_get_device_period(void *args
)
2151 struct get_device_period_params
*params
= args
;
2153 if (params
->def_period
)
2154 *params
->def_period
= def_period
;
2155 if (params
->min_period
)
2156 *params
->min_period
= def_period
;
2158 params
->result
= S_OK
;
2160 return STATUS_SUCCESS
;
2163 static NTSTATUS
alsa_get_buffer_size(void *args
)
2165 struct get_buffer_size_params
*params
= args
;
2166 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2170 *params
->frames
= stream
->bufsize_frames
;
2172 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2175 static NTSTATUS
alsa_get_latency(void *args
)
2177 struct get_latency_params
*params
= args
;
2178 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2182 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
2183 * yet have enough data left to play (as if it were in native's mixer). Add:
2184 * + mmdevapi_period such that at the end of it, ALSA still has data;
2185 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
2186 * + alsa_period such that ALSA always has at least one period to play. */
2187 if(stream
->flow
== eRender
)
2188 *params
->latency
= muldiv(stream
->hidden_frames
, 10000000, stream
->fmt
->nSamplesPerSec
);
2190 *params
->latency
= muldiv(stream
->alsa_period_frames
, 10000000, stream
->fmt
->nSamplesPerSec
)
2191 + stream
->mmdev_period_rt
;
2193 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2196 static NTSTATUS
alsa_get_current_padding(void *args
)
2198 struct get_current_padding_params
*params
= args
;
2199 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2203 /* padding is solely updated at callback time in shared mode */
2204 *params
->padding
= stream
->held_frames
;
2206 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2209 static NTSTATUS
alsa_get_next_packet_size(void *args
)
2211 struct get_next_packet_size_params
*params
= args
;
2212 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2216 *params
->frames
= stream
->held_frames
< stream
->mmdev_period_frames
? 0 : stream
->mmdev_period_frames
;
2218 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2221 static NTSTATUS
alsa_get_frequency(void *args
)
2223 struct get_frequency_params
*params
= args
;
2224 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2225 UINT64
*freq
= params
->freq
;
2229 if(stream
->share
== AUDCLNT_SHAREMODE_SHARED
)
2230 *freq
= (UINT64
)stream
->fmt
->nSamplesPerSec
* stream
->fmt
->nBlockAlign
;
2232 *freq
= stream
->fmt
->nSamplesPerSec
;
2234 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2237 static NTSTATUS
alsa_get_position(void *args
)
2239 struct get_position_params
*params
= args
;
2240 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2242 snd_pcm_state_t alsa_state
;
2244 if (params
->device
) {
2245 FIXME("Device position reporting not implemented\n");
2246 params
->result
= E_NOTIMPL
;
2247 return STATUS_SUCCESS
;
2252 /* avail_update required to get accurate snd_pcm_state() */
2253 snd_pcm_avail_update(stream
->pcm_handle
);
2254 alsa_state
= snd_pcm_state(stream
->pcm_handle
);
2256 if(stream
->flow
== eRender
){
2257 position
= stream
->written_frames
- stream
->held_frames
;
2259 if(stream
->started
&& alsa_state
== SND_PCM_STATE_RUNNING
&& stream
->held_frames
)
2260 /* we should be using snd_pcm_delay here, but it is broken
2261 * especially during ALSA device underrun. instead, let's just
2262 * interpolate between periods with the system timer. */
2263 position
+= interp_elapsed_frames(stream
);
2265 position
= min(position
, stream
->written_frames
- stream
->held_frames
+ stream
->mmdev_period_frames
);
2267 position
= min(position
, stream
->written_frames
);
2269 position
= stream
->written_frames
+ stream
->held_frames
;
2271 /* ensure monotic growth */
2272 if(position
< stream
->last_pos_frames
)
2273 position
= stream
->last_pos_frames
;
2275 stream
->last_pos_frames
= position
;
2277 TRACE("frames written: %u, held: %u, state: 0x%x, position: %u\n",
2278 (UINT32
)(stream
->written_frames
%1000000000), stream
->held_frames
,
2279 alsa_state
, (UINT32
)(position
%1000000000));
2281 if(stream
->share
== AUDCLNT_SHAREMODE_SHARED
)
2282 *params
->pos
= position
* stream
->fmt
->nBlockAlign
;
2284 *params
->pos
= position
;
2286 if(params
->qpctime
){
2287 LARGE_INTEGER stamp
, freq
;
2288 NtQueryPerformanceCounter(&stamp
, &freq
);
2289 *params
->qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2292 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2295 static NTSTATUS
alsa_set_volumes(void *args
)
2297 struct set_volumes_params
*params
= args
;
2298 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2301 for(i
= 0; i
< stream
->fmt
->nChannels
; i
++)
2302 stream
->vols
[i
] = params
->volumes
[i
] * params
->session_volumes
[i
] * params
->master_volume
;
2304 return STATUS_SUCCESS
;
2307 static NTSTATUS
alsa_set_event_handle(void *args
)
2309 struct set_event_handle_params
*params
= args
;
2310 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2314 if(!(stream
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
))
2315 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
);
2318 FIXME("called twice\n");
2319 return alsa_unlock_result(stream
, ¶ms
->result
, HRESULT_FROM_WIN32(ERROR_INVALID_NAME
));
2322 stream
->event
= params
->event
;
2324 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2327 static NTSTATUS
alsa_is_started(void *args
)
2329 struct is_started_params
*params
= args
;
2330 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2334 return alsa_unlock_result(stream
, ¶ms
->result
, stream
->started
? S_OK
: S_FALSE
);
2337 static unsigned int alsa_probe_num_speakers(char *name
)
2340 snd_pcm_hw_params_t
*params
;
2342 unsigned int max_channels
= 0;
2344 if ((err
= snd_pcm_open(&handle
, name
, SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
)) < 0) {
2345 WARN("The device \"%s\" failed to open: %d (%s).\n",
2346 name
, err
, snd_strerror(err
));
2350 params
= malloc(snd_pcm_hw_params_sizeof());
2352 WARN("Out of memory.\n");
2353 snd_pcm_close(handle
);
2357 if ((err
= snd_pcm_hw_params_any(handle
, params
)) < 0) {
2358 WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
2359 name
, err
, snd_strerror(err
));
2363 if ((err
= snd_pcm_hw_params_get_channels_max(params
,
2364 &max_channels
)) < 0){
2365 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
2371 snd_pcm_close(handle
);
2373 return max_channels
;
2376 enum AudioDeviceConnectionType
{
2377 AudioDeviceConnectionType_Unknown
= 0,
2378 AudioDeviceConnectionType_PCI
,
2379 AudioDeviceConnectionType_USB
2382 static NTSTATUS
alsa_get_prop_value(void *args
)
2384 struct get_prop_value_params
*params
= args
;
2385 const char *name
= params
->device
;
2386 EDataFlow flow
= params
->flow
;
2387 const GUID
*guid
= params
->guid
;
2388 const PROPERTYKEY
*prop
= params
->prop
;
2389 PROPVARIANT
*out
= params
->value
;
2390 static const PROPERTYKEY devicepath_key
= { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
2391 {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
2394 if(IsEqualPropertyKey(*prop
, devicepath_key
))
2396 enum AudioDeviceConnectionType connection
= AudioDeviceConnectionType_Unknown
;
2397 USHORT vendor_id
= 0, product_id
= 0;
2398 char uevent
[MAX_PATH
];
2399 FILE *fuevent
= NULL
;
2405 if(sscanf(name
, "plughw:%u,%u", &card
, &device
)){
2406 sprintf(uevent
, "/sys/class/sound/card%u/device/uevent", card
);
2407 fuevent
= fopen(uevent
, "r");
2413 while (fgets(line
, sizeof(line
), fuevent
)) {
2417 if((val
= strchr(line
, '='))) {
2421 val_len
= strlen(val
);
2422 if(val_len
> 0 && val
[val_len
- 1] == '\n') { val
[val_len
- 1] = 0; }
2424 if(!strcmp(line
, "PCI_ID")){
2425 connection
= AudioDeviceConnectionType_PCI
;
2426 if(sscanf(val
, "%hX:%hX", &vendor_id
, &product_id
)<2){
2427 WARN("Unexpected input when reading PCI_ID in uevent file.\n");
2428 connection
= AudioDeviceConnectionType_Unknown
;
2431 }else if(!strcmp(line
, "DEVTYPE") && !strcmp(val
,"usb_interface"))
2432 connection
= AudioDeviceConnectionType_USB
;
2433 else if(!strcmp(line
, "PRODUCT"))
2434 if(sscanf(val
, "%hx/%hx/", &vendor_id
, &product_id
)<2){
2435 WARN("Unexpected input when reading PRODUCT in uevent file.\n");
2436 connection
= AudioDeviceConnectionType_Unknown
;
2445 /* As hardly any audio devices have serial numbers, Windows instead
2446 appears to use a persistent random number. We emulate this here
2447 by instead using the last 8 hex digits of the GUID. */
2448 serial_number
= (guid
->Data4
[4] << 24) | (guid
->Data4
[5] << 16) | (guid
->Data4
[6] << 8) | guid
->Data4
[7];
2450 if(connection
== AudioDeviceConnectionType_USB
)
2451 sprintf(buf
, "{1}.USB\\VID_%04X&PID_%04X\\%u&%08X",
2452 vendor_id
, product_id
, device
, serial_number
);
2453 else if (connection
== AudioDeviceConnectionType_PCI
)
2454 sprintf(buf
, "{1}.HDAUDIO\\FUNC_01&VEN_%04X&DEV_%04X\\%u&%08X",
2455 vendor_id
, product_id
, device
, serial_number
);
2457 sprintf(buf
, "{1}.ROOT\\MEDIA\\%04u", serial_number
& 0x1FF);
2459 len
= strlen(buf
) + 1;
2460 if(*params
->buffer_size
< len
* sizeof(WCHAR
)){
2461 params
->result
= E_NOT_SUFFICIENT_BUFFER
;
2462 *params
->buffer_size
= len
* sizeof(WCHAR
);
2463 return STATUS_SUCCESS
;
2465 out
->vt
= VT_LPWSTR
;
2466 out
->pwszVal
= params
->buffer
;
2467 ntdll_umbstowcs(buf
, len
, out
->pwszVal
, len
);
2468 params
->result
= S_OK
;
2469 return STATUS_SUCCESS
;
2470 } else if (flow
!= eCapture
&& IsEqualPropertyKey(*prop
, PKEY_AudioEndpoint_PhysicalSpeakers
)) {
2471 unsigned int num_speakers
, card
, device
;
2474 if (sscanf(name
, "plughw:%u,%u", &card
, &device
))
2475 sprintf(hwname
, "hw:%u,%u", card
, device
); /* must be hw rather than plughw to work */
2477 strcpy(hwname
, name
);
2479 num_speakers
= alsa_probe_num_speakers(hwname
);
2480 if (num_speakers
== 0){
2481 params
->result
= E_FAIL
;
2482 return STATUS_SUCCESS
;
2486 if (num_speakers
> 6)
2487 out
->ulVal
= KSAUDIO_SPEAKER_STEREO
;
2488 else if (num_speakers
== 6)
2489 out
->ulVal
= KSAUDIO_SPEAKER_5POINT1
;
2490 else if (num_speakers
>= 4)
2491 out
->ulVal
= KSAUDIO_SPEAKER_QUAD
;
2492 else if (num_speakers
>= 2)
2493 out
->ulVal
= KSAUDIO_SPEAKER_STEREO
;
2494 else if (num_speakers
== 1)
2495 out
->ulVal
= KSAUDIO_SPEAKER_MONO
;
2497 params
->result
= S_OK
;
2498 return STATUS_SUCCESS
;
2501 TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop
->fmtid
), (unsigned)prop
->pid
);
2503 params
->result
= E_NOTIMPL
;
2504 return STATUS_SUCCESS
;
2507 const unixlib_entry_t __wine_unix_call_funcs
[] =
2509 alsa_process_attach
,
2510 alsa_not_implemented
,
2512 alsa_get_endpoint_ids
,
2514 alsa_release_stream
,
2519 alsa_get_render_buffer
,
2520 alsa_release_render_buffer
,
2521 alsa_get_capture_buffer
,
2522 alsa_release_capture_buffer
,
2523 alsa_is_format_supported
,
2524 alsa_get_mix_format
,
2525 alsa_get_device_period
,
2526 alsa_get_buffer_size
,
2528 alsa_get_current_padding
,
2529 alsa_get_next_packet_size
,
2533 alsa_set_event_handle
,
2534 alsa_not_implemented
,
2536 alsa_get_prop_value
,
2537 alsa_not_implemented
,
2539 alsa_midi_out_message
,
2540 alsa_midi_in_message
,
2541 alsa_midi_notify_wait
,
2542 alsa_not_implemented
,
2545 C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs
) == funcs_count
);
2551 static NTSTATUS
alsa_wow64_main_loop(void *args
)
2557 struct main_loop_params params
=
2559 .event
= ULongToHandle(params32
->event
)
2561 return alsa_main_loop(¶ms
);
2564 static NTSTATUS
alsa_wow64_get_endpoint_ids(void *args
)
2573 unsigned int default_idx
;
2575 struct get_endpoint_ids_params params
=
2577 .flow
= params32
->flow
,
2578 .endpoints
= ULongToPtr(params32
->endpoints
),
2579 .size
= params32
->size
2581 alsa_get_endpoint_ids(¶ms
);
2582 params32
->size
= params
.size
;
2583 params32
->result
= params
.result
;
2584 params32
->num
= params
.num
;
2585 params32
->default_idx
= params
.default_idx
;
2586 return STATUS_SUCCESS
;
2589 static NTSTATUS
alsa_wow64_create_stream(void *args
)
2596 AUDCLNT_SHAREMODE share
;
2598 REFERENCE_TIME duration
;
2599 REFERENCE_TIME period
;
2602 PTR32 channel_count
;
2605 struct create_stream_params params
=
2607 .name
= ULongToPtr(params32
->name
),
2608 .device
= ULongToPtr(params32
->device
),
2609 .flow
= params32
->flow
,
2610 .share
= params32
->share
,
2611 .flags
= params32
->flags
,
2612 .duration
= params32
->duration
,
2613 .period
= params32
->period
,
2614 .fmt
= ULongToPtr(params32
->fmt
),
2615 .channel_count
= ULongToPtr(params32
->channel_count
),
2616 .stream
= ULongToPtr(params32
->stream
)
2618 alsa_create_stream(¶ms
);
2619 params32
->result
= params
.result
;
2620 return STATUS_SUCCESS
;
2623 static NTSTATUS
alsa_wow64_release_stream(void *args
)
2627 stream_handle stream
;
2631 struct release_stream_params params
=
2633 .stream
= params32
->stream
,
2634 .timer_thread
= ULongToHandle(params32
->timer_thread
)
2636 alsa_release_stream(¶ms
);
2637 params32
->result
= params
.result
;
2638 return STATUS_SUCCESS
;
2641 static NTSTATUS
alsa_wow64_get_render_buffer(void *args
)
2645 stream_handle stream
;
2651 struct get_render_buffer_params params
=
2653 .stream
= params32
->stream
,
2654 .frames
= params32
->frames
,
2657 alsa_get_render_buffer(¶ms
);
2658 params32
->result
= params
.result
;
2659 *(unsigned int *)ULongToPtr(params32
->data
) = PtrToUlong(data
);
2660 return STATUS_SUCCESS
;
2663 static NTSTATUS
alsa_wow64_get_capture_buffer(void *args
)
2667 stream_handle stream
;
2676 struct get_capture_buffer_params params
=
2678 .stream
= params32
->stream
,
2680 .frames
= ULongToPtr(params32
->frames
),
2681 .flags
= ULongToPtr(params32
->flags
),
2682 .devpos
= ULongToPtr(params32
->devpos
),
2683 .qpcpos
= ULongToPtr(params32
->qpcpos
)
2685 alsa_get_capture_buffer(¶ms
);
2686 params32
->result
= params
.result
;
2687 *(unsigned int *)ULongToPtr(params32
->data
) = PtrToUlong(data
);
2688 return STATUS_SUCCESS
;
2691 static NTSTATUS
alsa_wow64_is_format_supported(void *args
)
2697 AUDCLNT_SHAREMODE share
;
2702 struct is_format_supported_params params
=
2704 .device
= ULongToPtr(params32
->device
),
2705 .flow
= params32
->flow
,
2706 .share
= params32
->share
,
2707 .fmt_in
= ULongToPtr(params32
->fmt_in
),
2708 .fmt_out
= ULongToPtr(params32
->fmt_out
)
2710 alsa_is_format_supported(¶ms
);
2711 params32
->result
= params
.result
;
2712 return STATUS_SUCCESS
;
2715 static NTSTATUS
alsa_wow64_get_mix_format(void *args
)
2724 struct get_mix_format_params params
=
2726 .device
= ULongToPtr(params32
->device
),
2727 .flow
= params32
->flow
,
2728 .fmt
= ULongToPtr(params32
->fmt
)
2730 alsa_get_mix_format(¶ms
);
2731 params32
->result
= params
.result
;
2732 return STATUS_SUCCESS
;
2735 static NTSTATUS
alsa_wow64_get_device_period(void *args
)
2745 struct get_device_period_params params
=
2747 .device
= ULongToPtr(params32
->device
),
2748 .flow
= params32
->flow
,
2749 .def_period
= ULongToPtr(params32
->def_period
),
2750 .min_period
= ULongToPtr(params32
->min_period
),
2752 alsa_get_device_period(¶ms
);
2753 params32
->result
= params
.result
;
2754 return STATUS_SUCCESS
;
2757 static NTSTATUS
alsa_wow64_get_buffer_size(void *args
)
2761 stream_handle stream
;
2765 struct get_buffer_size_params params
=
2767 .stream
= params32
->stream
,
2768 .frames
= ULongToPtr(params32
->frames
)
2770 alsa_get_buffer_size(¶ms
);
2771 params32
->result
= params
.result
;
2772 return STATUS_SUCCESS
;
2775 static NTSTATUS
alsa_wow64_get_latency(void *args
)
2779 stream_handle stream
;
2783 struct get_latency_params params
=
2785 .stream
= params32
->stream
,
2786 .latency
= ULongToPtr(params32
->latency
)
2788 alsa_get_latency(¶ms
);
2789 params32
->result
= params
.result
;
2790 return STATUS_SUCCESS
;
2793 static NTSTATUS
alsa_wow64_get_current_padding(void *args
)
2797 stream_handle stream
;
2801 struct get_current_padding_params params
=
2803 .stream
= params32
->stream
,
2804 .padding
= ULongToPtr(params32
->padding
)
2806 alsa_get_current_padding(¶ms
);
2807 params32
->result
= params
.result
;
2808 return STATUS_SUCCESS
;
2811 static NTSTATUS
alsa_wow64_get_next_packet_size(void *args
)
2815 stream_handle stream
;
2819 struct get_next_packet_size_params params
=
2821 .stream
= params32
->stream
,
2822 .frames
= ULongToPtr(params32
->frames
)
2824 alsa_get_next_packet_size(¶ms
);
2825 params32
->result
= params
.result
;
2826 return STATUS_SUCCESS
;
2829 static NTSTATUS
alsa_wow64_get_frequency(void *args
)
2833 stream_handle stream
;
2837 struct get_frequency_params params
=
2839 .stream
= params32
->stream
,
2840 .freq
= ULongToPtr(params32
->freq
)
2842 alsa_get_frequency(¶ms
);
2843 params32
->result
= params
.result
;
2844 return STATUS_SUCCESS
;
2847 static NTSTATUS
alsa_wow64_get_position(void *args
)
2851 stream_handle stream
;
2857 struct get_position_params params
=
2859 .stream
= params32
->stream
,
2860 .device
= params32
->device
,
2861 .pos
= ULongToPtr(params32
->pos
),
2862 .qpctime
= ULongToPtr(params32
->qpctime
)
2864 alsa_get_position(¶ms
);
2865 params32
->result
= params
.result
;
2866 return STATUS_SUCCESS
;
2869 static NTSTATUS
alsa_wow64_set_volumes(void *args
)
2873 stream_handle stream
;
2874 float master_volume
;
2876 PTR32 session_volumes
;
2878 struct set_volumes_params params
=
2880 .stream
= params32
->stream
,
2881 .master_volume
= params32
->master_volume
,
2882 .volumes
= ULongToPtr(params32
->volumes
),
2883 .session_volumes
= ULongToPtr(params32
->session_volumes
),
2885 return alsa_set_volumes(¶ms
);
2888 static NTSTATUS
alsa_wow64_set_event_handle(void *args
)
2892 stream_handle stream
;
2896 struct set_event_handle_params params
=
2898 .stream
= params32
->stream
,
2899 .event
= ULongToHandle(params32
->event
)
2902 alsa_set_event_handle(¶ms
);
2903 params32
->result
= params
.result
;
2904 return STATUS_SUCCESS
;
2907 static NTSTATUS
alsa_wow64_get_prop_value(void *args
)
2909 struct propvariant32
2912 WORD pad1
, pad2
, pad3
;
2917 ULARGE_INTEGER uhVal
;
2928 PTR32 buffer
; /* caller allocated buffer to hold value's strings */
2932 struct get_prop_value_params params
=
2934 .device
= ULongToPtr(params32
->device
),
2935 .flow
= params32
->flow
,
2936 .guid
= ULongToPtr(params32
->guid
),
2937 .prop
= ULongToPtr(params32
->prop
),
2939 .buffer
= ULongToPtr(params32
->buffer
),
2940 .buffer_size
= ULongToPtr(params32
->buffer_size
)
2942 alsa_get_prop_value(¶ms
);
2943 params32
->result
= params
.result
;
2944 if (SUCCEEDED(params
.result
))
2946 value32
= UlongToPtr(params32
->value
);
2947 value32
->vt
= value
.vt
;
2951 value32
->ulVal
= value
.ulVal
;
2954 value32
->ptr
= params32
->buffer
;
2957 FIXME("Unhandled vt %04x\n", value
.vt
);
2960 return STATUS_SUCCESS
;
2963 const unixlib_entry_t __wine_unix_call_wow64_funcs
[] =
2965 alsa_process_attach
,
2966 alsa_not_implemented
,
2967 alsa_wow64_main_loop
,
2968 alsa_wow64_get_endpoint_ids
,
2969 alsa_wow64_create_stream
,
2970 alsa_wow64_release_stream
,
2975 alsa_wow64_get_render_buffer
,
2976 alsa_release_render_buffer
,
2977 alsa_wow64_get_capture_buffer
,
2978 alsa_release_capture_buffer
,
2979 alsa_wow64_is_format_supported
,
2980 alsa_wow64_get_mix_format
,
2981 alsa_wow64_get_device_period
,
2982 alsa_wow64_get_buffer_size
,
2983 alsa_wow64_get_latency
,
2984 alsa_wow64_get_current_padding
,
2985 alsa_wow64_get_next_packet_size
,
2986 alsa_wow64_get_frequency
,
2987 alsa_wow64_get_position
,
2988 alsa_wow64_set_volumes
,
2989 alsa_wow64_set_event_handle
,
2990 alsa_not_implemented
,
2992 alsa_wow64_get_prop_value
,
2993 alsa_not_implemented
,
2995 alsa_wow64_midi_out_message
,
2996 alsa_wow64_midi_in_message
,
2997 alsa_wow64_midi_notify_wait
,
2998 alsa_not_implemented
,
3001 C_ASSERT(ARRAYSIZE(__wine_unix_call_wow64_funcs
) == funcs_count
);