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 inline void ascii_to_unicode( WCHAR
*dst
, const char *src
, size_t len
)
93 while (len
--) *dst
++ = (unsigned char)*src
++;
96 static HKEY
reg_open_key( HKEY root
, const WCHAR
*name
, ULONG name_len
)
98 UNICODE_STRING nameW
= { name_len
, name_len
, (WCHAR
*)name
};
99 OBJECT_ATTRIBUTES attr
;
102 attr
.Length
= sizeof(attr
);
103 attr
.RootDirectory
= root
;
104 attr
.ObjectName
= &nameW
;
106 attr
.SecurityDescriptor
= NULL
;
107 attr
.SecurityQualityOfService
= NULL
;
109 if (NtOpenKeyEx( &ret
, MAXIMUM_ALLOWED
, &attr
, 0 )) return 0;
113 static HKEY
open_hkcu(void)
117 DWORD_PTR sid_data
[(sizeof(TOKEN_USER
) + SECURITY_MAX_SID_SIZE
) / sizeof(DWORD_PTR
)];
118 DWORD i
, len
= sizeof(sid_data
);
121 if (NtQueryInformationToken( GetCurrentThreadEffectiveToken(), TokenUser
, sid_data
, len
, &len
))
124 sid
= ((TOKEN_USER
*)sid_data
)->User
.Sid
;
125 len
= sprintf( buffer
, "\\Registry\\User\\S-%u-%u", sid
->Revision
,
126 (unsigned)MAKELONG( MAKEWORD( sid
->IdentifierAuthority
.Value
[5], sid
->IdentifierAuthority
.Value
[4] ),
127 MAKEWORD( sid
->IdentifierAuthority
.Value
[3], sid
->IdentifierAuthority
.Value
[2] )));
128 for (i
= 0; i
< sid
->SubAuthorityCount
; i
++)
129 len
+= sprintf( buffer
+ len
, "-%u", (unsigned)sid
->SubAuthority
[i
] );
130 ascii_to_unicode( bufferW
, buffer
, len
+ 1 );
132 return reg_open_key( NULL
, bufferW
, len
* sizeof(WCHAR
) );
135 static HKEY
reg_open_hkcu_key( const WCHAR
*name
, ULONG name_len
)
137 HKEY hkcu
= open_hkcu(), key
;
139 key
= reg_open_key( hkcu
, name
, name_len
);
145 static ULONG
reg_query_value( HKEY hkey
, const WCHAR
*name
,
146 KEY_VALUE_PARTIAL_INFORMATION
*info
, ULONG size
)
148 unsigned int name_size
= name
? wcslen( name
) * sizeof(WCHAR
) : 0;
149 UNICODE_STRING nameW
= { name_size
, name_size
, (WCHAR
*)name
};
151 if (NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
,
155 return size
- FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
);
158 static snd_pcm_stream_t
alsa_get_direction(EDataFlow flow
)
160 return (flow
== eRender
) ? SND_PCM_STREAM_PLAYBACK
: SND_PCM_STREAM_CAPTURE
;
163 static WCHAR
*strdupAtoW(const char *str
)
168 if(!str
) return NULL
;
170 len
= strlen(str
) + 1;
171 ret
= malloc(len
* sizeof(WCHAR
));
172 if(ret
) ntdll_umbstowcs(str
, len
, ret
, len
);
176 /* copied from kernelbase */
177 static int muldiv( int a
, int b
, int c
)
183 /* We want to deal with a positive divisor to simplify the logic. */
190 /* If the result is positive, we "add" to round. else, we subtract to round. */
191 if ((a
< 0 && b
< 0) || (a
>= 0 && b
>= 0))
192 ret
= (((LONGLONG
)a
* b
) + (c
/ 2)) / c
;
194 ret
= (((LONGLONG
)a
* b
) - (c
/ 2)) / c
;
196 if (ret
> 2147483647 || ret
< -2147483647) return -1;
200 static void alsa_lock(struct alsa_stream
*stream
)
202 pthread_mutex_lock(&stream
->lock
);
205 static void alsa_unlock(struct alsa_stream
*stream
)
207 pthread_mutex_unlock(&stream
->lock
);
210 static NTSTATUS
alsa_unlock_result(struct alsa_stream
*stream
,
211 HRESULT
*result
, HRESULT value
)
215 return STATUS_SUCCESS
;
218 static struct alsa_stream
*handle_get_stream(stream_handle h
)
220 return (struct alsa_stream
*)(UINT_PTR
)h
;
223 static BOOL
alsa_try_open(const char *devnode
, EDataFlow flow
)
228 TRACE("devnode: %s, flow: %d\n", devnode
, flow
);
230 if((err
= snd_pcm_open(&handle
, devnode
, alsa_get_direction(flow
), SND_PCM_NONBLOCK
)) < 0){
231 WARN("The device \"%s\" failed to open: %d (%s).\n", devnode
, err
, snd_strerror(err
));
235 snd_pcm_close(handle
);
239 static WCHAR
*construct_device_id(EDataFlow flow
, const WCHAR
*chunk1
, const WCHAR
*chunk2
)
243 size_t len_wchars
= 0, chunk1_len
= 0, chunk2_len
= 0, copied
= 0, prefix_len
;
245 static const WCHAR dashW
[] = {' ','-',' ',0};
246 static const size_t dashW_len
= ARRAY_SIZE(dashW
) - 1;
247 static const WCHAR outW
[] = {'O','u','t',':',' ',0};
248 static const WCHAR inW
[] = {'I','n',':',' ',0};
252 prefix_len
= ARRAY_SIZE(outW
) - 1;
253 len_wchars
+= prefix_len
;
256 prefix_len
= ARRAY_SIZE(inW
) - 1;
257 len_wchars
+= prefix_len
;
260 chunk1_len
= wcslen(chunk1
);
261 len_wchars
+= chunk1_len
;
264 len_wchars
+= dashW_len
;
266 chunk2_len
= wcslen(chunk2
);
267 len_wchars
+= chunk2_len
;
269 len_wchars
+= 1; /* NULL byte */
271 ret
= malloc(len_wchars
* sizeof(WCHAR
));
273 memcpy(ret
, prefix
, prefix_len
* sizeof(WCHAR
));
274 copied
+= prefix_len
;
276 memcpy(ret
+ copied
, chunk1
, chunk1_len
* sizeof(WCHAR
));
277 copied
+= chunk1_len
;
279 if(chunk1
&& chunk2
){
280 memcpy(ret
+ copied
, dashW
, dashW_len
* sizeof(WCHAR
));
284 memcpy(ret
+ copied
, chunk2
, chunk2_len
* sizeof(WCHAR
));
285 copied
+= chunk2_len
;
289 TRACE("Enumerated device: %s\n", wine_dbgstr_w(ret
));
300 struct endpoints_info
302 unsigned int num
, size
;
303 struct endpt
*endpoints
;
306 static void endpoints_add(struct endpoints_info
*endpoints
, WCHAR
*name
, char *device
)
308 if(endpoints
->num
>= endpoints
->size
){
309 if (!endpoints
->size
) endpoints
->size
= 16;
310 else endpoints
->size
*= 2;
311 endpoints
->endpoints
= realloc(endpoints
->endpoints
, endpoints
->size
* sizeof(*endpoints
->endpoints
));
314 endpoints
->endpoints
[endpoints
->num
].name
= name
;
315 endpoints
->endpoints
[endpoints
->num
++].device
= device
;
318 static HRESULT
alsa_get_card_devices(EDataFlow flow
, struct endpoints_info
*endpoints_info
,
319 snd_ctl_t
*ctl
, int card
, const WCHAR
*cardname
)
322 snd_pcm_info_t
*info
;
324 info
= calloc(1, snd_pcm_info_sizeof());
326 return E_OUTOFMEMORY
;
328 snd_pcm_info_set_subdevice(info
, 0);
329 snd_pcm_info_set_stream(info
, alsa_get_direction(flow
));
332 for(err
= snd_ctl_pcm_next_device(ctl
, &device
); device
!= -1 && err
>= 0;
333 err
= snd_ctl_pcm_next_device(ctl
, &device
)){
337 snd_pcm_info_set_device(info
, device
);
339 if((err
= snd_ctl_pcm_info(ctl
, info
)) < 0){
341 /* This device doesn't have the right stream direction */
344 WARN("Failed to get info for card %d, device %d: %d (%s)\n",
345 card
, device
, err
, snd_strerror(err
));
349 sprintf(devnode
, "plughw:%d,%d", card
, device
);
350 if(!alsa_try_open(devnode
, flow
))
353 devname
= strdupAtoW(snd_pcm_info_get_name(info
));
355 WARN("Unable to get device name for card %d, device %d\n", card
, device
);
359 endpoints_add(endpoints_info
, construct_device_id(flow
, cardname
, devname
), strdup(devnode
));
366 WARN("Got a failure during device enumeration on card %d: %d (%s)\n",
367 card
, err
, snd_strerror(err
));
372 static void get_reg_devices(EDataFlow flow
, struct endpoints_info
*endpoints_info
)
374 static const WCHAR ALSAOutputDevices
[] = {'A','L','S','A','O','u','t','p','u','t','D','e','v','i','c','e','s',0};
375 static const WCHAR ALSAInputDevices
[] = {'A','L','S','A','I','n','p','u','t','D','e','v','i','c','e','s',0};
377 KEY_VALUE_PARTIAL_INFORMATION
*key_info
= (void *)buffer
;
380 const WCHAR
*value_name
= (flow
== eRender
) ? ALSAOutputDevices
: ALSAInputDevices
;
382 /* @@ Wine registry key: HKCU\Software\Wine\Drivers\winealsa.drv */
383 if((key
= reg_open_hkcu_key(drv_keyW
, sizeof(drv_keyW
)))){
384 if((size
= reg_query_value(key
, value_name
, key_info
, sizeof(buffer
)))){
385 WCHAR
*p
= (WCHAR
*)key_info
->Data
;
387 if(key_info
->Type
!= REG_MULTI_SZ
){
388 ERR("Registry ALSA device list value type must be REG_MULTI_SZ\n");
395 char *devname
= malloc(len
* 3 + 1);
397 ntdll_wcstoumbs(p
, len
+ 1, devname
, len
* 3 + 1, FALSE
);
399 if(alsa_try_open(devname
, flow
))
400 endpoints_add(endpoints_info
, construct_device_id(flow
, p
, NULL
), strdup(devname
));
413 int first_card_number
;
417 static struct list card_types
= LIST_INIT(card_types
);
419 static BOOL
need_card_number(int card
, const char *string
)
421 struct card_type
*cptr
;
423 LIST_FOR_EACH_ENTRY(cptr
, &card_types
, struct card_type
, entry
)
425 if(!strcmp(string
, cptr
->string
))
426 return card
!= cptr
->first_card_number
;
429 /* this is the first instance of string */
430 cptr
= malloc(sizeof(struct card_type
) + strlen(string
));
432 /* Default to displaying card number if we can't track cards */
435 cptr
->first_card_number
= card
;
436 strcpy(cptr
->string
, string
);
437 list_add_head(&card_types
, &cptr
->entry
);
441 static WCHAR
*alsa_get_card_name(int card
)
447 if((err
= snd_card_get_name(card
, &cardname
)) < 0){
448 /* FIXME: Should be localized */
449 WARN("Unable to get card name for ALSA device %d: %d (%s)\n", card
, err
, snd_strerror(err
));
450 cardname
= strdup("Unknown soundcard");
453 if(need_card_number(card
, cardname
)){
456 * For identical card names, second and subsequent instances get
457 * card number prefix to distinguish them (like Windows).
459 if(asprintf(&cardnameN
, "%u-%s", card
, cardname
) > 0){
461 cardname
= cardnameN
;
465 ret
= strdupAtoW(cardname
);
471 static NTSTATUS
alsa_get_endpoint_ids(void *args
)
473 static const WCHAR defaultW
[] = {'d','e','f','a','u','l','t',0};
474 struct get_endpoint_ids_params
*params
= args
;
475 struct endpoints_info endpoints_info
;
476 unsigned int i
, needed
, name_len
, device_len
, offset
;
477 struct endpoint
*endpoint
;
482 endpoints_info
.num
= endpoints_info
.size
= 0;
483 endpoints_info
.endpoints
= NULL
;
485 if(alsa_try_open("default", params
->flow
))
486 endpoints_add(&endpoints_info
, construct_device_id(params
->flow
, defaultW
, NULL
), strdup("default"));
488 get_reg_devices(params
->flow
, &endpoints_info
);
490 for(err
= snd_card_next(&card
); card
!= -1 && err
>= 0; err
= snd_card_next(&card
)){
495 sprintf(cardpath
, "hw:%u", card
);
497 if((err
= snd_ctl_open(&ctl
, cardpath
, 0)) < 0){
498 WARN("Unable to open ctl for ALSA device %s: %d (%s)\n", cardpath
,
499 err
, snd_strerror(err
));
503 cardname
= alsa_get_card_name(card
);
504 alsa_get_card_devices(params
->flow
, &endpoints_info
, ctl
, card
, cardname
);
511 WARN("Got a failure during card enumeration: %d (%s)\n", err
, snd_strerror(err
));
513 offset
= needed
= endpoints_info
.num
* sizeof(*params
->endpoints
);
514 endpoint
= params
->endpoints
;
516 for(i
= 0; i
< endpoints_info
.num
; i
++){
517 name_len
= wcslen(endpoints_info
.endpoints
[i
].name
) + 1;
518 device_len
= strlen(endpoints_info
.endpoints
[i
].device
) + 1;
519 needed
+= name_len
* sizeof(WCHAR
) + ((device_len
+ 1) & ~1);
521 if(needed
<= params
->size
){
522 endpoint
->name
= offset
;
523 memcpy((char *)params
->endpoints
+ offset
, endpoints_info
.endpoints
[i
].name
, name_len
* sizeof(WCHAR
));
524 offset
+= name_len
* sizeof(WCHAR
);
525 endpoint
->device
= offset
;
526 memcpy((char *)params
->endpoints
+ offset
, endpoints_info
.endpoints
[i
].device
, device_len
);
527 offset
+= (device_len
+ 1) & ~1;
530 free(endpoints_info
.endpoints
[i
].name
);
531 free(endpoints_info
.endpoints
[i
].device
);
533 free(endpoints_info
.endpoints
);
535 params
->num
= endpoints_info
.num
;
536 params
->default_idx
= 0;
538 if(needed
> params
->size
){
539 params
->size
= needed
;
540 params
->result
= HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER
);
542 params
->result
= S_OK
;
544 return STATUS_SUCCESS
;
547 static WAVEFORMATEXTENSIBLE
*clone_format(const WAVEFORMATEX
*fmt
)
549 WAVEFORMATEXTENSIBLE
*ret
;
552 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
553 size
= sizeof(WAVEFORMATEXTENSIBLE
);
555 size
= sizeof(WAVEFORMATEX
);
561 memcpy(ret
, fmt
, size
);
563 ret
->Format
.cbSize
= size
- sizeof(WAVEFORMATEX
);
568 static HRESULT
alsa_open_device(const char *alsa_name
, EDataFlow flow
, snd_pcm_t
**pcm_handle
,
569 snd_pcm_hw_params_t
**hw_params
)
571 snd_pcm_stream_t pcm_stream
;
575 pcm_stream
= SND_PCM_STREAM_PLAYBACK
;
576 else if(flow
== eCapture
)
577 pcm_stream
= SND_PCM_STREAM_CAPTURE
;
581 err
= snd_pcm_open(pcm_handle
, alsa_name
, pcm_stream
, SND_PCM_NONBLOCK
);
583 WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name
, err
, snd_strerror(err
));
586 return AUDCLNT_E_DEVICE_IN_USE
;
588 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
592 *hw_params
= malloc(snd_pcm_hw_params_sizeof());
594 snd_pcm_close(*pcm_handle
);
595 return E_OUTOFMEMORY
;
601 static snd_pcm_format_t
alsa_format(const WAVEFORMATEX
*fmt
)
603 snd_pcm_format_t format
= SND_PCM_FORMAT_UNKNOWN
;
604 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
606 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
607 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
608 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
609 if(fmt
->wBitsPerSample
== 8)
610 format
= SND_PCM_FORMAT_U8
;
611 else if(fmt
->wBitsPerSample
== 16)
612 format
= SND_PCM_FORMAT_S16_LE
;
613 else if(fmt
->wBitsPerSample
== 24)
614 format
= SND_PCM_FORMAT_S24_3LE
;
615 else if(fmt
->wBitsPerSample
== 32)
616 format
= SND_PCM_FORMAT_S32_LE
;
618 WARN("Unsupported bit depth: %u\n", fmt
->wBitsPerSample
);
619 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
620 fmt
->wBitsPerSample
!= fmtex
->Samples
.wValidBitsPerSample
){
621 if(fmtex
->Samples
.wValidBitsPerSample
== 20 && fmt
->wBitsPerSample
== 24)
622 format
= SND_PCM_FORMAT_S20_3LE
;
624 WARN("Unsupported ValidBits: %u\n", fmtex
->Samples
.wValidBitsPerSample
);
626 }else if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
627 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
628 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
629 if(fmt
->wBitsPerSample
== 32)
630 format
= SND_PCM_FORMAT_FLOAT_LE
;
631 else if(fmt
->wBitsPerSample
== 64)
632 format
= SND_PCM_FORMAT_FLOAT64_LE
;
634 WARN("Unsupported float size: %u\n", fmt
->wBitsPerSample
);
636 WARN("Unknown wave format: %04x\n", fmt
->wFormatTag
);
640 static int alsa_channel_index(UINT flag
)
643 case SPEAKER_FRONT_LEFT
:
645 case SPEAKER_FRONT_RIGHT
:
647 case SPEAKER_BACK_LEFT
:
649 case SPEAKER_BACK_RIGHT
:
651 case SPEAKER_FRONT_CENTER
:
653 case SPEAKER_LOW_FREQUENCY
:
655 case SPEAKER_SIDE_LEFT
:
657 case SPEAKER_SIDE_RIGHT
:
663 static BOOL
need_remapping(const WAVEFORMATEX
*fmt
, int *map
)
666 for(i
= 0; i
< fmt
->nChannels
; ++i
){
673 static DWORD
get_channel_mask(unsigned int channels
)
679 return KSAUDIO_SPEAKER_MONO
;
681 return KSAUDIO_SPEAKER_STEREO
;
683 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
685 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
687 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
689 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
691 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
693 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
695 FIXME("Unknown speaker configuration: %u\n", channels
);
699 static HRESULT
map_channels(EDataFlow flow
, const WAVEFORMATEX
*fmt
, int *alsa_channels
, int *map
)
703 if(flow
!= eCapture
&& (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
|| fmt
->nChannels
> 2) ){
704 WAVEFORMATEXTENSIBLE
*fmtex
= (void*)fmt
;
705 UINT mask
, flag
= SPEAKER_FRONT_LEFT
;
708 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
709 fmtex
->dwChannelMask
!= 0)
710 mask
= fmtex
->dwChannelMask
;
712 mask
= get_channel_mask(fmt
->nChannels
);
716 while(i
< fmt
->nChannels
&& !(flag
& SPEAKER_RESERVED
)){
718 map
[i
] = alsa_channel_index(flag
);
719 TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
721 if(map
[i
] >= *alsa_channels
)
722 *alsa_channels
= map
[i
] + 1;
728 while(i
< fmt
->nChannels
){
729 map
[i
] = *alsa_channels
;
730 TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
736 for(i
= 0; i
< fmt
->nChannels
; ++i
){
738 map
[i
] = *alsa_channels
;
740 TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
745 need_remap
= need_remapping(fmt
, map
);
747 *alsa_channels
= fmt
->nChannels
;
752 TRACE("need_remapping: %u, alsa_channels: %d\n", need_remap
, *alsa_channels
);
754 return need_remap
? S_OK
: S_FALSE
;
757 static void silence_buffer(struct alsa_stream
*stream
, BYTE
*buffer
, UINT32 frames
)
759 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)stream
->fmt
;
760 if((stream
->fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
761 (stream
->fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
762 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))) &&
763 stream
->fmt
->wBitsPerSample
== 8)
764 memset(buffer
, 128, frames
* stream
->fmt
->nBlockAlign
);
766 memset(buffer
, 0, frames
* stream
->fmt
->nBlockAlign
);
769 static ULONG_PTR
zero_bits(void)
772 return !NtCurrentTeb()->WowTebOffset
? 0 : 0x7fffffff;
778 static NTSTATUS
alsa_create_stream(void *args
)
780 struct create_stream_params
*params
= args
;
781 struct alsa_stream
*stream
;
782 snd_pcm_sw_params_t
*sw_params
= NULL
;
783 snd_pcm_format_t format
;
784 unsigned int rate
, alsa_period_us
, i
;
785 WAVEFORMATEXTENSIBLE
*fmtex
;
789 stream
= calloc(1, sizeof(*stream
));
791 params
->result
= E_OUTOFMEMORY
;
792 return STATUS_SUCCESS
;
795 params
->result
= alsa_open_device(params
->device
, params
->flow
, &stream
->pcm_handle
, &stream
->hw_params
);
796 if(FAILED(params
->result
)){
798 return STATUS_SUCCESS
;
801 stream
->need_remapping
= map_channels(params
->flow
, params
->fmt
, &stream
->alsa_channels
, stream
->alsa_channel_map
) == S_OK
;
803 if((err
= snd_pcm_hw_params_any(stream
->pcm_handle
, stream
->hw_params
)) < 0){
804 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
805 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
809 if((err
= snd_pcm_hw_params_set_access(stream
->pcm_handle
, stream
->hw_params
,
810 SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0){
811 WARN("Unable to set access: %d (%s)\n", err
, snd_strerror(err
));
812 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
816 format
= alsa_format(params
->fmt
);
817 if (format
== SND_PCM_FORMAT_UNKNOWN
){
818 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
822 if((err
= snd_pcm_hw_params_set_format(stream
->pcm_handle
, stream
->hw_params
,
824 WARN("Unable to set ALSA format to %u: %d (%s)\n", format
, err
,
826 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
830 stream
->alsa_format
= format
;
831 stream
->flow
= params
->flow
;
833 rate
= params
->fmt
->nSamplesPerSec
;
834 if((err
= snd_pcm_hw_params_set_rate_near(stream
->pcm_handle
, stream
->hw_params
,
836 WARN("Unable to set rate to %u: %d (%s)\n", rate
, err
,
838 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
842 if((err
= snd_pcm_hw_params_set_channels(stream
->pcm_handle
, stream
->hw_params
,
843 stream
->alsa_channels
)) < 0){
844 WARN("Unable to set channels to %u: %d (%s)\n", params
->fmt
->nChannels
, err
,
846 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
850 stream
->mmdev_period_rt
= params
->period
;
851 alsa_period_us
= stream
->mmdev_period_rt
/ 10;
852 if((err
= snd_pcm_hw_params_set_period_time_near(stream
->pcm_handle
,
853 stream
->hw_params
, &alsa_period_us
, NULL
)) < 0)
854 WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us
,
855 err
, snd_strerror(err
));
856 /* ALSA updates the output variable alsa_period_us */
858 stream
->mmdev_period_frames
= muldiv(params
->fmt
->nSamplesPerSec
,
859 stream
->mmdev_period_rt
, 10000000);
861 /* Buffer 4 ALSA periods if large enough, else 4 mmdevapi periods */
862 stream
->alsa_bufsize_frames
= stream
->mmdev_period_frames
* 4;
863 if(err
< 0 || alsa_period_us
< params
->period
/ 10)
864 err
= snd_pcm_hw_params_set_buffer_size_near(stream
->pcm_handle
,
865 stream
->hw_params
, &stream
->alsa_bufsize_frames
);
867 unsigned int periods
= 4;
868 err
= snd_pcm_hw_params_set_periods_near(stream
->pcm_handle
, stream
->hw_params
, &periods
, NULL
);
871 WARN("Unable to set buffer size: %d (%s)\n", err
, snd_strerror(err
));
873 if((err
= snd_pcm_hw_params(stream
->pcm_handle
, stream
->hw_params
)) < 0){
874 WARN("Unable to set hw params: %d (%s)\n", err
, snd_strerror(err
));
875 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
879 if((err
= snd_pcm_hw_params_get_period_size(stream
->hw_params
,
880 &stream
->alsa_period_frames
, NULL
)) < 0){
881 WARN("Unable to get period size: %d (%s)\n", err
, snd_strerror(err
));
882 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
886 if((err
= snd_pcm_hw_params_get_buffer_size(stream
->hw_params
,
887 &stream
->alsa_bufsize_frames
)) < 0){
888 WARN("Unable to get buffer size: %d (%s)\n", err
, snd_strerror(err
));
889 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
893 sw_params
= calloc(1, snd_pcm_sw_params_sizeof());
895 params
->result
= E_OUTOFMEMORY
;
899 if((err
= snd_pcm_sw_params_current(stream
->pcm_handle
, sw_params
)) < 0){
900 WARN("Unable to get sw_params: %d (%s)\n", err
, snd_strerror(err
));
901 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
905 if((err
= snd_pcm_sw_params_set_start_threshold(stream
->pcm_handle
,
907 WARN("Unable set start threshold to 1: %d (%s)\n", err
, snd_strerror(err
));
908 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
912 if((err
= snd_pcm_sw_params_set_stop_threshold(stream
->pcm_handle
,
913 sw_params
, stream
->alsa_bufsize_frames
)) < 0){
914 WARN("Unable set stop threshold to %lu: %d (%s)\n",
915 stream
->alsa_bufsize_frames
, err
, snd_strerror(err
));
916 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
920 if((err
= snd_pcm_sw_params(stream
->pcm_handle
, sw_params
)) < 0){
921 WARN("Unable to set sw params: %d (%s)\n", err
, snd_strerror(err
));
922 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
926 if((err
= snd_pcm_prepare(stream
->pcm_handle
)) < 0){
927 WARN("Unable to prepare device: %d (%s)\n", err
, snd_strerror(err
));
928 params
->result
= AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
932 /* Bear in mind weird situations where
933 * ALSA period (50ms) > mmdevapi buffer (3x10ms)
934 * or surprising rounding as seen with 22050x8x1 with Pulse:
935 * ALSA period 220 vs. 221 frames in mmdevapi and
936 * buffer 883 vs. 2205 frames in mmdevapi! */
937 stream
->bufsize_frames
= muldiv(params
->duration
, params
->fmt
->nSamplesPerSec
, 10000000);
938 if(params
->share
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
939 stream
->bufsize_frames
-= stream
->bufsize_frames
% stream
->mmdev_period_frames
;
940 stream
->hidden_frames
= stream
->alsa_period_frames
+ stream
->mmdev_period_frames
+
941 muldiv(params
->fmt
->nSamplesPerSec
, EXTRA_SAFE_RT
, 10000000);
942 /* leave no less than about 1.33ms or 256 bytes of data after a rewind */
943 stream
->safe_rewind_frames
= max(256 / params
->fmt
->nBlockAlign
, muldiv(133, params
->fmt
->nSamplesPerSec
, 100000));
945 /* Check if the ALSA buffer is so small that it will run out before
946 * the next MMDevAPI period tick occurs. Allow a little wiggle room
947 * with 120% of the period time. */
948 if(stream
->alsa_bufsize_frames
< 1.2 * stream
->mmdev_period_frames
)
949 FIXME("ALSA buffer time is too small. Expect underruns. (%lu < %u * 1.2)\n",
950 stream
->alsa_bufsize_frames
, stream
->mmdev_period_frames
);
952 fmtex
= clone_format(params
->fmt
);
954 params
->result
= E_OUTOFMEMORY
;
957 stream
->fmt
= &fmtex
->Format
;
959 size
= stream
->bufsize_frames
* params
->fmt
->nBlockAlign
;
960 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, zero_bits(), &size
,
961 MEM_COMMIT
, PAGE_READWRITE
)){
962 params
->result
= E_OUTOFMEMORY
;
965 silence_buffer(stream
, stream
->local_buffer
, stream
->bufsize_frames
);
967 stream
->silence_buf
= malloc(stream
->alsa_period_frames
* stream
->fmt
->nBlockAlign
);
968 if(!stream
->silence_buf
){
969 params
->result
= E_OUTOFMEMORY
;
972 silence_buffer(stream
, stream
->silence_buf
, stream
->alsa_period_frames
);
974 stream
->vols
= malloc(params
->fmt
->nChannels
* sizeof(float));
976 params
->result
= E_OUTOFMEMORY
;
979 for(i
= 0; i
< params
->fmt
->nChannels
; ++i
)
980 stream
->vols
[i
] = 1.f
;
982 stream
->share
= params
->share
;
983 stream
->flags
= params
->flags
;
985 pthread_mutex_init(&stream
->lock
, NULL
);
987 TRACE("ALSA period: %lu frames\n", stream
->alsa_period_frames
);
988 TRACE("ALSA buffer: %lu frames\n", stream
->alsa_bufsize_frames
);
989 TRACE("MMDevice period: %u frames\n", stream
->mmdev_period_frames
);
990 TRACE("MMDevice buffer: %u frames\n", stream
->bufsize_frames
);
994 if(FAILED(params
->result
)){
995 snd_pcm_close(stream
->pcm_handle
);
996 if(stream
->local_buffer
){
998 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, &size
, MEM_RELEASE
);
1000 free(stream
->silence_buf
);
1001 free(stream
->hw_params
);
1006 *params
->stream
= (stream_handle
)(UINT_PTR
)stream
;
1009 return STATUS_SUCCESS
;
1012 static NTSTATUS
alsa_release_stream(void *args
)
1014 struct release_stream_params
*params
= args
;
1015 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1018 if(params
->timer_thread
){
1019 stream
->please_quit
= TRUE
;
1020 NtWaitForSingleObject(params
->timer_thread
, FALSE
, NULL
);
1021 NtClose(params
->timer_thread
);
1024 snd_pcm_drop(stream
->pcm_handle
);
1025 snd_pcm_close(stream
->pcm_handle
);
1026 if(stream
->local_buffer
){
1028 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, &size
, MEM_RELEASE
);
1030 if(stream
->tmp_buffer
){
1032 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
1034 free(stream
->remapping_buf
);
1035 free(stream
->silence_buf
);
1036 free(stream
->hw_params
);
1039 pthread_mutex_destroy(&stream
->lock
);
1042 params
->result
= S_OK
;
1043 return STATUS_SUCCESS
;
1046 static BYTE
*remap_channels(struct alsa_stream
*stream
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1048 snd_pcm_uframes_t i
;
1050 UINT bytes_per_sample
= stream
->fmt
->wBitsPerSample
/ 8;
1052 if(!stream
->need_remapping
)
1055 if(stream
->remapping_buf_frames
< frames
){
1056 stream
->remapping_buf
= realloc(stream
->remapping_buf
,
1057 bytes_per_sample
* stream
->alsa_channels
* frames
);
1058 stream
->remapping_buf_frames
= frames
;
1061 snd_pcm_format_set_silence(stream
->alsa_format
, stream
->remapping_buf
,
1062 frames
* stream
->alsa_channels
);
1064 switch(stream
->fmt
->wBitsPerSample
){
1066 UINT8
*tgt_buf
, *src_buf
;
1067 tgt_buf
= stream
->remapping_buf
;
1069 for(i
= 0; i
< frames
; ++i
){
1070 for(c
= 0; c
< stream
->fmt
->nChannels
; ++c
)
1071 tgt_buf
[stream
->alsa_channel_map
[c
]] = src_buf
[c
];
1072 tgt_buf
+= stream
->alsa_channels
;
1073 src_buf
+= stream
->fmt
->nChannels
;
1078 UINT16
*tgt_buf
, *src_buf
;
1079 tgt_buf
= (UINT16
*)stream
->remapping_buf
;
1080 src_buf
= (UINT16
*)buf
;
1081 for(i
= 0; i
< frames
; ++i
){
1082 for(c
= 0; c
< stream
->fmt
->nChannels
; ++c
)
1083 tgt_buf
[stream
->alsa_channel_map
[c
]] = src_buf
[c
];
1084 tgt_buf
+= stream
->alsa_channels
;
1085 src_buf
+= stream
->fmt
->nChannels
;
1090 UINT32
*tgt_buf
, *src_buf
;
1091 tgt_buf
= (UINT32
*)stream
->remapping_buf
;
1092 src_buf
= (UINT32
*)buf
;
1093 for(i
= 0; i
< frames
; ++i
){
1094 for(c
= 0; c
< stream
->fmt
->nChannels
; ++c
)
1095 tgt_buf
[stream
->alsa_channel_map
[c
]] = src_buf
[c
];
1096 tgt_buf
+= stream
->alsa_channels
;
1097 src_buf
+= stream
->fmt
->nChannels
;
1102 BYTE
*tgt_buf
, *src_buf
;
1103 tgt_buf
= stream
->remapping_buf
;
1105 for(i
= 0; i
< frames
; ++i
){
1106 for(c
= 0; c
< stream
->fmt
->nChannels
; ++c
)
1107 memcpy(&tgt_buf
[stream
->alsa_channel_map
[c
] * bytes_per_sample
],
1108 &src_buf
[c
* bytes_per_sample
], bytes_per_sample
);
1109 tgt_buf
+= stream
->alsa_channels
* bytes_per_sample
;
1110 src_buf
+= stream
->fmt
->nChannels
* bytes_per_sample
;
1116 return stream
->remapping_buf
;
1119 static void adjust_buffer_volume(const struct alsa_stream
*stream
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1121 BOOL adjust
= FALSE
;
1122 UINT32 i
, channels
, mute
= 0;
1125 if (stream
->vol_adjusted_frames
>= frames
)
1127 channels
= stream
->fmt
->nChannels
;
1129 /* Adjust the buffer based on the volume for each channel */
1130 for (i
= 0; i
< channels
; i
++)
1132 adjust
|= stream
->vols
[i
] != 1.0f
;
1133 if (stream
->vols
[i
] == 0.0f
)
1137 if (mute
== channels
)
1139 int err
= snd_pcm_format_set_silence(stream
->alsa_format
, buf
, frames
* channels
);
1141 WARN("Setting buffer to silence failed: %d (%s)\n", err
, snd_strerror(err
));
1144 if (!adjust
) return;
1146 /* Skip the frames we've already adjusted before */
1147 end
= buf
+ frames
* stream
->fmt
->nBlockAlign
;
1148 buf
+= stream
->vol_adjusted_frames
* stream
->fmt
->nBlockAlign
;
1150 switch (stream
->alsa_format
)
1152 #ifndef WORDS_BIGENDIAN
1153 #define PROCESS_BUFFER(type) do \
1155 type *p = (type*)buf; \
1158 for (i = 0; i < channels; i++) \
1159 p[i] = p[i] * stream->vols[i]; \
1161 } while ((BYTE*)p != end); \
1163 case SND_PCM_FORMAT_S16_LE
:
1164 PROCESS_BUFFER(INT16
);
1166 case SND_PCM_FORMAT_S32_LE
:
1167 PROCESS_BUFFER(INT32
);
1169 case SND_PCM_FORMAT_FLOAT_LE
:
1170 PROCESS_BUFFER(float);
1172 case SND_PCM_FORMAT_FLOAT64_LE
:
1173 PROCESS_BUFFER(double);
1175 #undef PROCESS_BUFFER
1176 case SND_PCM_FORMAT_S20_3LE
:
1177 case SND_PCM_FORMAT_S24_3LE
:
1179 /* Do it 12 bytes at a time until it is no longer possible */
1180 UINT32
*q
= (UINT32
*)buf
, mask
= ~0xff;
1183 /* After we adjust the volume, we need to mask out low bits */
1184 if (stream
->alsa_format
== SND_PCM_FORMAT_S20_3LE
)
1188 while (end
- (BYTE
*)q
>= 12)
1192 v
[1] = q
[1] << 16 | (q
[0] >> 16 & ~0xff);
1193 v
[2] = q
[2] << 24 | (q
[1] >> 8 & ~0xff);
1194 v
[3] = q
[2] & ~0xff;
1195 for (k
= 0; k
< 4; k
++)
1197 v
[k
] = (INT32
)((INT32
)v
[k
] * stream
->vols
[i
]);
1199 if (++i
== channels
) i
= 0;
1201 *q
++ = v
[0] >> 8 | v
[1] << 16;
1202 *q
++ = v
[1] >> 16 | v
[2] << 8;
1203 *q
++ = v
[2] >> 24 | v
[3];
1208 UINT32 v
= (INT32
)((INT32
)(p
[0] << 8 | p
[1] << 16 | p
[2] << 24) * stream
->vols
[i
]);
1210 *p
++ = v
>> 8 & 0xff;
1211 *p
++ = v
>> 16 & 0xff;
1213 if (++i
== channels
) i
= 0;
1218 case SND_PCM_FORMAT_U8
:
1220 UINT8
*p
= (UINT8
*)buf
;
1223 for (i
= 0; i
< channels
; i
++)
1224 p
[i
] = (int)((p
[i
] - 128) * stream
->vols
[i
]) + 128;
1226 } while ((BYTE
*)p
!= end
);
1230 TRACE("Unhandled format %i, not adjusting volume.\n", stream
->alsa_format
);
1235 static snd_pcm_sframes_t
alsa_write_best_effort(struct alsa_stream
*stream
, BYTE
*buf
, snd_pcm_uframes_t frames
)
1237 snd_pcm_sframes_t written
;
1239 adjust_buffer_volume(stream
, buf
, frames
);
1241 /* Mark the frames we've already adjusted */
1242 if (stream
->vol_adjusted_frames
< frames
)
1243 stream
->vol_adjusted_frames
= frames
;
1245 buf
= remap_channels(stream
, buf
, frames
);
1247 written
= snd_pcm_writei(stream
->pcm_handle
, buf
, frames
);
1251 if(written
== -EAGAIN
)
1255 WARN("writei failed, recovering: %ld (%s)\n", written
,
1256 snd_strerror(written
));
1258 ret
= snd_pcm_recover(stream
->pcm_handle
, written
, 0);
1260 WARN("Could not recover: %d (%s)\n", ret
, snd_strerror(ret
));
1264 written
= snd_pcm_writei(stream
->pcm_handle
, buf
, frames
);
1268 stream
->vol_adjusted_frames
-= written
;
1272 static snd_pcm_sframes_t
alsa_write_buffer_wrap(struct alsa_stream
*stream
, BYTE
*buf
,
1273 snd_pcm_uframes_t buflen
, snd_pcm_uframes_t offs
,
1274 snd_pcm_uframes_t to_write
)
1276 snd_pcm_sframes_t ret
= 0;
1279 snd_pcm_uframes_t chunk
;
1280 snd_pcm_sframes_t tmp
;
1282 if(offs
+ to_write
> buflen
)
1283 chunk
= buflen
- offs
;
1287 tmp
= alsa_write_best_effort(stream
, buf
+ offs
* stream
->fmt
->nBlockAlign
, chunk
);
1302 static UINT
buf_ptr_diff(UINT left
, UINT right
, UINT bufsize
)
1305 return right
- left
;
1306 return bufsize
- (left
- right
);
1309 static UINT
data_not_in_alsa(struct alsa_stream
*stream
)
1313 diff
= buf_ptr_diff(stream
->lcl_offs_frames
, stream
->wri_offs_frames
, stream
->bufsize_frames
);
1317 return stream
->held_frames
- stream
->data_in_alsa_frames
;
1320 /* Here's the buffer setup:
1322 * vvvvvvvv sent to HW already
1323 * vvvvvvvv in ALSA buffer but rewindable
1324 * [dddddddddddddddd] ALSA buffer
1325 * [dddddddddddddddd--------] mmdevapi buffer
1326 * ^^^^^^^^ data_in_alsa_frames
1327 * ^^^^^^^^^^^^^^^^ held_frames
1331 * GetCurrentPadding is held_frames
1333 * During period callback, we decrement held_frames, fill ALSA buffer, and move
1336 * During Stop, we rewind the ALSA buffer
1338 static void alsa_write_data(struct alsa_stream
*stream
)
1340 snd_pcm_sframes_t written
;
1341 snd_pcm_uframes_t avail
, max_copy_frames
, data_frames_played
;
1344 /* this call seems to be required to get an accurate snd_pcm_state() */
1345 avail
= snd_pcm_avail_update(stream
->pcm_handle
);
1347 if(snd_pcm_state(stream
->pcm_handle
) == SND_PCM_STATE_XRUN
){
1348 TRACE("XRun state, recovering\n");
1350 avail
= stream
->alsa_bufsize_frames
;
1352 if((err
= snd_pcm_recover(stream
->pcm_handle
, -EPIPE
, 1)) < 0)
1353 WARN("snd_pcm_recover failed: %d (%s)\n", err
, snd_strerror(err
));
1355 if((err
= snd_pcm_reset(stream
->pcm_handle
)) < 0)
1356 WARN("snd_pcm_reset failed: %d (%s)\n", err
, snd_strerror(err
));
1358 if((err
= snd_pcm_prepare(stream
->pcm_handle
)) < 0)
1359 WARN("snd_pcm_prepare failed: %d (%s)\n", err
, snd_strerror(err
));
1362 TRACE("avail: %ld\n", avail
);
1364 /* Add a lead-in when starting with too few frames to ensure
1365 * continuous rendering. Additional benefit: Force ALSA to start. */
1366 if(stream
->data_in_alsa_frames
== 0 && stream
->held_frames
< stream
->alsa_period_frames
)
1368 alsa_write_best_effort(stream
, stream
->silence_buf
,
1369 stream
->alsa_period_frames
- stream
->held_frames
);
1370 stream
->vol_adjusted_frames
= 0;
1374 max_copy_frames
= data_not_in_alsa(stream
);
1376 max_copy_frames
= 0;
1378 data_frames_played
= min(stream
->data_in_alsa_frames
, avail
);
1379 stream
->data_in_alsa_frames
-= data_frames_played
;
1381 if(stream
->held_frames
> data_frames_played
){
1383 stream
->held_frames
-= data_frames_played
;
1385 stream
->held_frames
= 0;
1387 while(avail
&& max_copy_frames
){
1388 snd_pcm_uframes_t to_write
;
1390 to_write
= min(avail
, max_copy_frames
);
1392 written
= alsa_write_buffer_wrap(stream
, stream
->local_buffer
,
1393 stream
->bufsize_frames
, stream
->lcl_offs_frames
, to_write
);
1398 stream
->lcl_offs_frames
+= written
;
1399 stream
->lcl_offs_frames
%= stream
->bufsize_frames
;
1400 stream
->data_in_alsa_frames
+= written
;
1401 max_copy_frames
-= written
;
1405 NtSetEvent(stream
->event
, NULL
);
1408 static void alsa_read_data(struct alsa_stream
*stream
)
1410 snd_pcm_sframes_t nread
;
1411 UINT32 pos
= stream
->wri_offs_frames
, limit
= stream
->held_frames
;
1414 if(!stream
->started
)
1417 /* FIXME: Detect overrun and signal DATA_DISCONTINUITY
1418 * How to count overrun frames and report them as position increase? */
1419 limit
= stream
->bufsize_frames
- max(limit
, pos
);
1421 nread
= snd_pcm_readi(stream
->pcm_handle
,
1422 stream
->local_buffer
+ pos
* stream
->fmt
->nBlockAlign
, limit
);
1423 TRACE("read %ld from %u limit %u\n", nread
, pos
, limit
);
1427 if(nread
== -EAGAIN
) /* no data yet */
1430 WARN("read failed, recovering: %ld (%s)\n", nread
, snd_strerror(nread
));
1432 ret
= snd_pcm_recover(stream
->pcm_handle
, nread
, 0);
1434 WARN("Recover failed: %d (%s)\n", ret
, snd_strerror(ret
));
1438 nread
= snd_pcm_readi(stream
->pcm_handle
,
1439 stream
->local_buffer
+ pos
* stream
->fmt
->nBlockAlign
, limit
);
1441 WARN("read failed: %ld (%s)\n", nread
, snd_strerror(nread
));
1446 for(i
= 0; i
< stream
->fmt
->nChannels
; i
++)
1447 if(stream
->vols
[i
] != 0.0f
)
1449 if(i
== stream
->fmt
->nChannels
){ /* mute */
1451 if((err
= snd_pcm_format_set_silence(stream
->alsa_format
,
1452 stream
->local_buffer
+ pos
* stream
->fmt
->nBlockAlign
,
1454 WARN("Setting buffer to silence failed: %d (%s)\n", err
,
1458 stream
->wri_offs_frames
+= nread
;
1459 stream
->wri_offs_frames
%= stream
->bufsize_frames
;
1460 stream
->held_frames
+= nread
;
1464 NtSetEvent(stream
->event
, NULL
);
1467 static snd_pcm_uframes_t
interp_elapsed_frames(struct alsa_stream
*stream
)
1469 LARGE_INTEGER time_freq
, current_time
, time_diff
;
1471 NtQueryPerformanceCounter(¤t_time
, &time_freq
);
1472 time_diff
.QuadPart
= current_time
.QuadPart
- stream
->last_period_time
.QuadPart
;
1473 return muldiv(time_diff
.QuadPart
, stream
->fmt
->nSamplesPerSec
, time_freq
.QuadPart
);
1476 static int alsa_rewind_best_effort(struct alsa_stream
*stream
)
1478 snd_pcm_uframes_t len
, leave
;
1480 /* we can't use snd_pcm_rewindable, some PCM devices crash. so follow
1481 * PulseAudio's example and rewind as much data as we believe is in the
1482 * buffer, minus 1.33ms for safety. */
1484 /* amount of data to leave in ALSA buffer */
1485 leave
= interp_elapsed_frames(stream
) + stream
->safe_rewind_frames
;
1487 if(stream
->held_frames
< leave
)
1488 stream
->held_frames
= 0;
1490 stream
->held_frames
-= leave
;
1492 if(stream
->data_in_alsa_frames
< leave
)
1495 len
= stream
->data_in_alsa_frames
- leave
;
1497 TRACE("rewinding %lu frames, now held %u\n", len
, stream
->held_frames
);
1500 /* snd_pcm_rewind return value is often broken, assume it succeeded */
1501 snd_pcm_rewind(stream
->pcm_handle
, len
);
1503 stream
->data_in_alsa_frames
= 0;
1508 static NTSTATUS
alsa_start(void *args
)
1510 struct start_params
*params
= args
;
1511 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1515 if((stream
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !stream
->event
)
1516 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_EVENTHANDLE_NOT_SET
);
1519 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_NOT_STOPPED
);
1521 if(stream
->flow
== eCapture
){
1522 /* dump any data that might be leftover in the ALSA capture buffer */
1523 snd_pcm_readi(stream
->pcm_handle
, stream
->local_buffer
,
1524 stream
->bufsize_frames
);
1526 snd_pcm_sframes_t avail
, written
;
1527 snd_pcm_uframes_t offs
;
1529 avail
= snd_pcm_avail_update(stream
->pcm_handle
);
1530 avail
= min(avail
, stream
->held_frames
);
1532 if(stream
->wri_offs_frames
< stream
->held_frames
)
1533 offs
= stream
->bufsize_frames
- stream
->held_frames
+ stream
->wri_offs_frames
;
1535 offs
= stream
->wri_offs_frames
- stream
->held_frames
;
1537 /* fill it with data */
1538 written
= alsa_write_buffer_wrap(stream
, stream
->local_buffer
,
1539 stream
->bufsize_frames
, offs
, avail
);
1542 stream
->lcl_offs_frames
= (offs
+ written
) % stream
->bufsize_frames
;
1543 stream
->data_in_alsa_frames
= written
;
1545 stream
->lcl_offs_frames
= offs
;
1546 stream
->data_in_alsa_frames
= 0;
1549 stream
->started
= TRUE
;
1551 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1554 static NTSTATUS
alsa_stop(void *args
)
1556 struct stop_params
*params
= args
;
1557 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1561 if(!stream
->started
)
1562 return alsa_unlock_result(stream
, ¶ms
->result
, S_FALSE
);
1564 if(stream
->flow
== eRender
)
1565 alsa_rewind_best_effort(stream
);
1567 stream
->started
= FALSE
;
1569 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1572 static NTSTATUS
alsa_reset(void *args
)
1574 struct reset_params
*params
= args
;
1575 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1580 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_NOT_STOPPED
);
1582 if(stream
->getbuf_last
)
1583 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_BUFFER_OPERATION_PENDING
);
1585 if(snd_pcm_drop(stream
->pcm_handle
) < 0)
1586 WARN("snd_pcm_drop failed\n");
1588 if(snd_pcm_reset(stream
->pcm_handle
) < 0)
1589 WARN("snd_pcm_reset failed\n");
1591 if(snd_pcm_prepare(stream
->pcm_handle
) < 0)
1592 WARN("snd_pcm_prepare failed\n");
1594 if(stream
->flow
== eRender
){
1595 stream
->written_frames
= 0;
1596 stream
->last_pos_frames
= 0;
1598 stream
->written_frames
+= stream
->held_frames
;
1600 stream
->held_frames
= 0;
1601 stream
->lcl_offs_frames
= 0;
1602 stream
->wri_offs_frames
= 0;
1604 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1607 static NTSTATUS
alsa_timer_loop(void *args
)
1609 struct timer_loop_params
*params
= args
;
1610 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1611 LARGE_INTEGER delay
, next
;
1616 delay
.QuadPart
= -stream
->mmdev_period_rt
;
1617 NtQueryPerformanceCounter(&stream
->last_period_time
, NULL
);
1618 next
.QuadPart
= stream
->last_period_time
.QuadPart
+ stream
->mmdev_period_rt
;
1620 while(!stream
->please_quit
){
1621 if(stream
->flow
== eRender
)
1622 alsa_write_data(stream
);
1623 else if(stream
->flow
== eCapture
)
1624 alsa_read_data(stream
);
1625 alsa_unlock(stream
);
1627 NtDelayExecution(FALSE
, &delay
);
1630 NtQueryPerformanceCounter(&stream
->last_period_time
, NULL
);
1631 adjust
= next
.QuadPart
- stream
->last_period_time
.QuadPart
;
1632 if(adjust
> stream
->mmdev_period_rt
/ 2)
1633 adjust
= stream
->mmdev_period_rt
/ 2;
1634 else if(adjust
< -stream
->mmdev_period_rt
/ 2)
1635 adjust
= -stream
->mmdev_period_rt
/ 2;
1636 delay
.QuadPart
= -(stream
->mmdev_period_rt
+ adjust
);
1637 next
.QuadPart
+= stream
->mmdev_period_rt
;
1640 alsa_unlock(stream
);
1642 return STATUS_SUCCESS
;
1645 static NTSTATUS
alsa_get_render_buffer(void *args
)
1647 struct get_render_buffer_params
*params
= args
;
1648 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1649 UINT32 write_pos
, frames
= params
->frames
;
1654 if(stream
->getbuf_last
)
1655 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1658 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1660 /* held_frames == GetCurrentPadding_nolock(); */
1661 if(stream
->held_frames
+ frames
> stream
->bufsize_frames
)
1662 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_BUFFER_TOO_LARGE
);
1664 write_pos
= stream
->wri_offs_frames
;
1665 if(write_pos
+ frames
> stream
->bufsize_frames
){
1666 if(stream
->tmp_buffer_frames
< frames
){
1667 if(stream
->tmp_buffer
){
1669 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
1670 stream
->tmp_buffer
= NULL
;
1672 size
= frames
* stream
->fmt
->nBlockAlign
;
1673 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, zero_bits(), &size
,
1674 MEM_COMMIT
, PAGE_READWRITE
)){
1675 stream
->tmp_buffer_frames
= 0;
1676 return alsa_unlock_result(stream
, ¶ms
->result
, E_OUTOFMEMORY
);
1678 stream
->tmp_buffer_frames
= frames
;
1680 *params
->data
= stream
->tmp_buffer
;
1681 stream
->getbuf_last
= -frames
;
1683 *params
->data
= stream
->local_buffer
+ write_pos
* stream
->fmt
->nBlockAlign
;
1684 stream
->getbuf_last
= frames
;
1687 silence_buffer(stream
, *params
->data
, frames
);
1689 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1692 static void alsa_wrap_buffer(struct alsa_stream
*stream
, BYTE
*buffer
, UINT32 written_frames
)
1694 snd_pcm_uframes_t write_offs_frames
= stream
->wri_offs_frames
;
1695 UINT32 write_offs_bytes
= write_offs_frames
* stream
->fmt
->nBlockAlign
;
1696 snd_pcm_uframes_t chunk_frames
= stream
->bufsize_frames
- write_offs_frames
;
1697 UINT32 chunk_bytes
= chunk_frames
* stream
->fmt
->nBlockAlign
;
1698 UINT32 written_bytes
= written_frames
* stream
->fmt
->nBlockAlign
;
1700 if(written_bytes
<= chunk_bytes
){
1701 memcpy(stream
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
1703 memcpy(stream
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
1704 memcpy(stream
->local_buffer
, buffer
+ chunk_bytes
,
1705 written_bytes
- chunk_bytes
);
1709 static NTSTATUS
alsa_release_render_buffer(void *args
)
1711 struct release_render_buffer_params
*params
= args
;
1712 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1713 UINT32 written_frames
= params
->written_frames
;
1718 if(!written_frames
){
1719 stream
->getbuf_last
= 0;
1720 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1723 if(!stream
->getbuf_last
)
1724 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1726 if(written_frames
> (stream
->getbuf_last
>= 0 ? stream
->getbuf_last
: -stream
->getbuf_last
))
1727 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_INVALID_SIZE
);
1729 if(stream
->getbuf_last
>= 0)
1730 buffer
= stream
->local_buffer
+ stream
->wri_offs_frames
* stream
->fmt
->nBlockAlign
;
1732 buffer
= stream
->tmp_buffer
;
1734 if(params
->flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
1735 silence_buffer(stream
, buffer
, written_frames
);
1737 if(stream
->getbuf_last
< 0)
1738 alsa_wrap_buffer(stream
, buffer
, written_frames
);
1740 stream
->wri_offs_frames
+= written_frames
;
1741 stream
->wri_offs_frames
%= stream
->bufsize_frames
;
1742 stream
->held_frames
+= written_frames
;
1743 stream
->written_frames
+= written_frames
;
1744 stream
->getbuf_last
= 0;
1746 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1749 static NTSTATUS
alsa_get_capture_buffer(void *args
)
1751 struct get_capture_buffer_params
*params
= args
;
1752 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1753 UINT32
*frames
= params
->frames
;
1758 if(stream
->getbuf_last
)
1759 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1761 if(stream
->held_frames
< stream
->mmdev_period_frames
){
1763 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_S_BUFFER_EMPTY
);
1765 *frames
= stream
->mmdev_period_frames
;
1767 if(stream
->lcl_offs_frames
+ *frames
> stream
->bufsize_frames
){
1768 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
1769 if(stream
->tmp_buffer_frames
< *frames
){
1770 if(stream
->tmp_buffer
){
1772 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
1773 stream
->tmp_buffer
= NULL
;
1775 size
= *frames
* stream
->fmt
->nBlockAlign
;
1776 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, zero_bits(), &size
,
1777 MEM_COMMIT
, PAGE_READWRITE
)){
1778 stream
->tmp_buffer_frames
= 0;
1779 return alsa_unlock_result(stream
, ¶ms
->result
, E_OUTOFMEMORY
);
1781 stream
->tmp_buffer_frames
= *frames
;
1784 *params
->data
= stream
->tmp_buffer
;
1785 chunk_bytes
= (stream
->bufsize_frames
- stream
->lcl_offs_frames
) *
1786 stream
->fmt
->nBlockAlign
;
1787 offs_bytes
= stream
->lcl_offs_frames
* stream
->fmt
->nBlockAlign
;
1788 frames_bytes
= *frames
* stream
->fmt
->nBlockAlign
;
1789 memcpy(stream
->tmp_buffer
, stream
->local_buffer
+ offs_bytes
, chunk_bytes
);
1790 memcpy(stream
->tmp_buffer
+ chunk_bytes
, stream
->local_buffer
,
1791 frames_bytes
- chunk_bytes
);
1793 *params
->data
= stream
->local_buffer
+
1794 stream
->lcl_offs_frames
* stream
->fmt
->nBlockAlign
;
1796 stream
->getbuf_last
= *frames
;
1800 *params
->devpos
= stream
->written_frames
;
1801 if(params
->qpcpos
){ /* fixme: qpc of recording time */
1802 LARGE_INTEGER stamp
, freq
;
1803 NtQueryPerformanceCounter(&stamp
, &freq
);
1804 *params
->qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
1807 return alsa_unlock_result(stream
, ¶ms
->result
, *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
);
1810 static NTSTATUS
alsa_release_capture_buffer(void *args
)
1812 struct release_capture_buffer_params
*params
= args
;
1813 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
1814 UINT32 done
= params
->done
;
1819 stream
->getbuf_last
= 0;
1820 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1823 if(!stream
->getbuf_last
)
1824 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1826 if(stream
->getbuf_last
!= done
)
1827 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_INVALID_SIZE
);
1829 stream
->written_frames
+= done
;
1830 stream
->held_frames
-= done
;
1831 stream
->lcl_offs_frames
+= done
;
1832 stream
->lcl_offs_frames
%= stream
->bufsize_frames
;
1833 stream
->getbuf_last
= 0;
1835 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
1838 static NTSTATUS
alsa_is_format_supported(void *args
)
1840 struct is_format_supported_params
*params
= args
;
1841 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)params
->fmt_in
;
1842 snd_pcm_t
*pcm_handle
;
1843 snd_pcm_hw_params_t
*hw_params
;
1844 snd_pcm_format_mask_t
*formats
= NULL
;
1845 snd_pcm_format_t format
;
1846 WAVEFORMATEXTENSIBLE
*closest
= NULL
;
1847 unsigned int max
= 0, min
= 0;
1849 int alsa_channels
, alsa_channel_map
[32];
1851 params
->result
= S_OK
;
1853 if(!params
->fmt_in
|| (params
->share
== AUDCLNT_SHAREMODE_SHARED
&& !params
->fmt_out
))
1854 params
->result
= E_POINTER
;
1855 else if(params
->share
!= AUDCLNT_SHAREMODE_SHARED
&& params
->share
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1856 params
->result
= E_INVALIDARG
;
1857 else if(params
->fmt_in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1858 if(params
->fmt_in
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1859 params
->result
= E_INVALIDARG
;
1860 else if(params
->fmt_in
->nAvgBytesPerSec
== 0 || params
->fmt_in
->nBlockAlign
== 0 ||
1861 (fmtex
->Samples
.wValidBitsPerSample
> params
->fmt_in
->wBitsPerSample
))
1862 params
->result
= E_INVALIDARG
;
1864 if(FAILED(params
->result
))
1865 return STATUS_SUCCESS
;
1867 if(params
->fmt_in
->nChannels
== 0){
1868 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1869 return STATUS_SUCCESS
;
1872 params
->result
= alsa_open_device(params
->device
, params
->flow
, &pcm_handle
, &hw_params
);
1873 if(FAILED(params
->result
))
1874 return STATUS_SUCCESS
;
1876 if((err
= snd_pcm_hw_params_any(pcm_handle
, hw_params
)) < 0){
1877 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1881 formats
= calloc(1, snd_pcm_format_mask_sizeof());
1883 params
->result
= E_OUTOFMEMORY
;
1887 snd_pcm_hw_params_get_format_mask(hw_params
, formats
);
1888 format
= alsa_format(params
->fmt_in
);
1889 if (format
== SND_PCM_FORMAT_UNKNOWN
||
1890 !snd_pcm_format_mask_test(formats
, format
)){
1891 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1895 closest
= clone_format(params
->fmt_in
);
1897 params
->result
= E_OUTOFMEMORY
;
1901 if((err
= snd_pcm_hw_params_get_rate_min(hw_params
, &min
, NULL
)) < 0){
1902 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1903 WARN("Unable to get min rate: %d (%s)\n", err
, snd_strerror(err
));
1907 if((err
= snd_pcm_hw_params_get_rate_max(hw_params
, &max
, NULL
)) < 0){
1908 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1909 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
1913 if(params
->fmt_in
->nSamplesPerSec
< min
|| params
->fmt_in
->nSamplesPerSec
> max
){
1914 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1918 if((err
= snd_pcm_hw_params_get_channels_min(hw_params
, &min
)) < 0){
1919 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1920 WARN("Unable to get min channels: %d (%s)\n", err
, snd_strerror(err
));
1924 if((err
= snd_pcm_hw_params_get_channels_max(hw_params
, &max
)) < 0){
1925 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1926 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
1929 if(params
->fmt_in
->nChannels
> max
){
1930 params
->result
= S_FALSE
;
1931 closest
->Format
.nChannels
= max
;
1932 }else if(params
->fmt_in
->nChannels
< min
){
1933 params
->result
= S_FALSE
;
1934 closest
->Format
.nChannels
= min
;
1937 map_channels(params
->flow
, params
->fmt_in
, &alsa_channels
, alsa_channel_map
);
1939 if(alsa_channels
> max
){
1940 params
->result
= S_FALSE
;
1941 closest
->Format
.nChannels
= max
;
1944 if(closest
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1945 closest
->dwChannelMask
= get_channel_mask(closest
->Format
.nChannels
);
1947 if(params
->fmt_in
->nBlockAlign
!= params
->fmt_in
->nChannels
* params
->fmt_in
->wBitsPerSample
/ 8 ||
1948 params
->fmt_in
->nAvgBytesPerSec
!= params
->fmt_in
->nBlockAlign
* params
->fmt_in
->nSamplesPerSec
||
1949 (params
->fmt_in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1950 fmtex
->Samples
.wValidBitsPerSample
< params
->fmt_in
->wBitsPerSample
))
1951 params
->result
= S_FALSE
;
1953 if(params
->share
== AUDCLNT_SHAREMODE_EXCLUSIVE
&& params
->fmt_in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
1954 if(fmtex
->dwChannelMask
== 0 || fmtex
->dwChannelMask
& SPEAKER_RESERVED
)
1955 params
->result
= S_FALSE
;
1959 if(params
->result
== S_FALSE
&& !params
->fmt_out
)
1960 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
1962 if(params
->result
== S_FALSE
&& params
->fmt_out
) {
1963 closest
->Format
.nBlockAlign
= closest
->Format
.nChannels
* closest
->Format
.wBitsPerSample
/ 8;
1964 closest
->Format
.nAvgBytesPerSec
= closest
->Format
.nBlockAlign
* closest
->Format
.nSamplesPerSec
;
1965 if(closest
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
1966 closest
->Samples
.wValidBitsPerSample
= closest
->Format
.wBitsPerSample
;
1967 memcpy(params
->fmt_out
, closest
, closest
->Format
.cbSize
);
1972 snd_pcm_close(pcm_handle
);
1974 return STATUS_SUCCESS
;
1977 static NTSTATUS
alsa_get_mix_format(void *args
)
1979 struct get_mix_format_params
*params
= args
;
1980 WAVEFORMATEXTENSIBLE
*fmt
= params
->fmt
;
1981 snd_pcm_t
*pcm_handle
;
1982 snd_pcm_hw_params_t
*hw_params
;
1983 snd_pcm_format_mask_t
*formats
;
1984 unsigned int max_rate
, max_channels
;
1987 params
->result
= alsa_open_device(params
->device
, params
->flow
, &pcm_handle
, &hw_params
);
1988 if(FAILED(params
->result
))
1989 return STATUS_SUCCESS
;
1991 formats
= calloc(1, snd_pcm_format_mask_sizeof());
1994 snd_pcm_close(pcm_handle
);
1995 params
->result
= E_OUTOFMEMORY
;
1996 return STATUS_SUCCESS
;
1999 if((err
= snd_pcm_hw_params_any(pcm_handle
, hw_params
)) < 0){
2000 WARN("Unable to get hw_params: %d (%s)\n", err
, snd_strerror(err
));
2001 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2005 snd_pcm_hw_params_get_format_mask(hw_params
, formats
);
2007 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
2008 if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_FLOAT_LE
)){
2009 fmt
->Format
.wBitsPerSample
= 32;
2010 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
2011 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S16_LE
)){
2012 fmt
->Format
.wBitsPerSample
= 16;
2013 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
2014 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_U8
)){
2015 fmt
->Format
.wBitsPerSample
= 8;
2016 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
2017 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S32_LE
)){
2018 fmt
->Format
.wBitsPerSample
= 32;
2019 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
2020 }else if(snd_pcm_format_mask_test(formats
, SND_PCM_FORMAT_S24_3LE
)){
2021 fmt
->Format
.wBitsPerSample
= 24;
2022 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
2024 ERR("Didn't recognize any available ALSA formats\n");
2025 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2029 if((err
= snd_pcm_hw_params_get_channels_max(hw_params
, &max_channels
)) < 0){
2030 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
2031 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2035 if(max_channels
> 6)
2036 fmt
->Format
.nChannels
= 2;
2038 fmt
->Format
.nChannels
= max_channels
;
2040 if(fmt
->Format
.nChannels
> 1 && (fmt
->Format
.nChannels
& 0x1)){
2041 /* For most hardware on Windows, users must choose a configuration with an even
2042 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
2043 * channels, but those channels are still reported to applications from
2044 * GetMixFormat! Some applications behave badly if given an odd number of
2045 * channels (e.g. 2.1). */
2047 if(fmt
->Format
.nChannels
< max_channels
)
2048 fmt
->Format
.nChannels
+= 1;
2050 /* We could "fake" more channels and downmix the emulated channels,
2051 * but at that point you really ought to tweak your ALSA setup or
2052 * just use PulseAudio. */
2053 WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt
->Format
.nChannels
);
2056 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
2058 if((err
= snd_pcm_hw_params_get_rate_max(hw_params
, &max_rate
, NULL
)) < 0){
2059 WARN("Unable to get max rate: %d (%s)\n", err
, snd_strerror(err
));
2060 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2064 if(max_rate
>= 48000)
2065 fmt
->Format
.nSamplesPerSec
= 48000;
2066 else if(max_rate
>= 44100)
2067 fmt
->Format
.nSamplesPerSec
= 44100;
2068 else if(max_rate
>= 22050)
2069 fmt
->Format
.nSamplesPerSec
= 22050;
2070 else if(max_rate
>= 11025)
2071 fmt
->Format
.nSamplesPerSec
= 11025;
2072 else if(max_rate
>= 8000)
2073 fmt
->Format
.nSamplesPerSec
= 8000;
2075 ERR("Unknown max rate: %u\n", max_rate
);
2076 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
2080 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
* fmt
->Format
.nChannels
) / 8;
2081 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
* fmt
->Format
.nBlockAlign
;
2083 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
2084 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
2089 snd_pcm_close(pcm_handle
);
2091 return STATUS_SUCCESS
;
2094 static NTSTATUS
alsa_get_buffer_size(void *args
)
2096 struct get_buffer_size_params
*params
= args
;
2097 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2101 *params
->frames
= stream
->bufsize_frames
;
2103 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2106 static NTSTATUS
alsa_get_latency(void *args
)
2108 struct get_latency_params
*params
= args
;
2109 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2113 /* Hide some frames in the ALSA buffer. Allows us to return GetCurrentPadding=0
2114 * yet have enough data left to play (as if it were in native's mixer). Add:
2115 * + mmdevapi_period such that at the end of it, ALSA still has data;
2116 * + EXTRA_SAFE (~4ms) to allow for late callback invocation / fluctuation;
2117 * + alsa_period such that ALSA always has at least one period to play. */
2118 if(stream
->flow
== eRender
)
2119 *params
->latency
= muldiv(stream
->hidden_frames
, 10000000, stream
->fmt
->nSamplesPerSec
);
2121 *params
->latency
= muldiv(stream
->alsa_period_frames
, 10000000, stream
->fmt
->nSamplesPerSec
)
2122 + stream
->mmdev_period_rt
;
2124 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2127 static NTSTATUS
alsa_get_current_padding(void *args
)
2129 struct get_current_padding_params
*params
= args
;
2130 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2134 /* padding is solely updated at callback time in shared mode */
2135 *params
->padding
= stream
->held_frames
;
2137 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2140 static NTSTATUS
alsa_get_next_packet_size(void *args
)
2142 struct get_next_packet_size_params
*params
= args
;
2143 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2147 *params
->frames
= stream
->held_frames
< stream
->mmdev_period_frames
? 0 : stream
->mmdev_period_frames
;
2149 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2152 static NTSTATUS
alsa_get_frequency(void *args
)
2154 struct get_frequency_params
*params
= args
;
2155 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2156 UINT64
*freq
= params
->freq
;
2160 if(stream
->share
== AUDCLNT_SHAREMODE_SHARED
)
2161 *freq
= (UINT64
)stream
->fmt
->nSamplesPerSec
* stream
->fmt
->nBlockAlign
;
2163 *freq
= stream
->fmt
->nSamplesPerSec
;
2165 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2168 static NTSTATUS
alsa_get_position(void *args
)
2170 struct get_position_params
*params
= args
;
2171 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2173 snd_pcm_state_t alsa_state
;
2177 /* avail_update required to get accurate snd_pcm_state() */
2178 snd_pcm_avail_update(stream
->pcm_handle
);
2179 alsa_state
= snd_pcm_state(stream
->pcm_handle
);
2181 if(stream
->flow
== eRender
){
2182 position
= stream
->written_frames
- stream
->held_frames
;
2184 if(stream
->started
&& alsa_state
== SND_PCM_STATE_RUNNING
&& stream
->held_frames
)
2185 /* we should be using snd_pcm_delay here, but it is broken
2186 * especially during ALSA device underrun. instead, let's just
2187 * interpolate between periods with the system timer. */
2188 position
+= interp_elapsed_frames(stream
);
2190 position
= min(position
, stream
->written_frames
- stream
->held_frames
+ stream
->mmdev_period_frames
);
2192 position
= min(position
, stream
->written_frames
);
2194 position
= stream
->written_frames
+ stream
->held_frames
;
2196 /* ensure monotic growth */
2197 if(position
< stream
->last_pos_frames
)
2198 position
= stream
->last_pos_frames
;
2200 stream
->last_pos_frames
= position
;
2202 TRACE("frames written: %u, held: %u, state: 0x%x, position: %u\n",
2203 (UINT32
)(stream
->written_frames
%1000000000), stream
->held_frames
,
2204 alsa_state
, (UINT32
)(position
%1000000000));
2206 if(stream
->share
== AUDCLNT_SHAREMODE_SHARED
)
2207 *params
->pos
= position
* stream
->fmt
->nBlockAlign
;
2209 *params
->pos
= position
;
2211 if(params
->qpctime
){
2212 LARGE_INTEGER stamp
, freq
;
2213 NtQueryPerformanceCounter(&stamp
, &freq
);
2214 *params
->qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
2217 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2220 static NTSTATUS
alsa_set_volumes(void *args
)
2222 struct set_volumes_params
*params
= args
;
2223 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2226 for(i
= 0; i
< stream
->fmt
->nChannels
; i
++)
2227 stream
->vols
[i
] = params
->volumes
[i
] * params
->session_volumes
[i
] * params
->master_volume
;
2229 return STATUS_SUCCESS
;
2232 static NTSTATUS
alsa_set_event_handle(void *args
)
2234 struct set_event_handle_params
*params
= args
;
2235 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2239 if(!(stream
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
))
2240 return alsa_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
);
2243 FIXME("called twice\n");
2244 return alsa_unlock_result(stream
, ¶ms
->result
, HRESULT_FROM_WIN32(ERROR_INVALID_NAME
));
2247 stream
->event
= params
->event
;
2249 return alsa_unlock_result(stream
, ¶ms
->result
, S_OK
);
2252 static NTSTATUS
alsa_is_started(void *args
)
2254 struct is_started_params
*params
= args
;
2255 struct alsa_stream
*stream
= handle_get_stream(params
->stream
);
2259 return alsa_unlock_result(stream
, ¶ms
->result
, stream
->started
? S_OK
: S_FALSE
);
2262 static unsigned int alsa_probe_num_speakers(char *name
)
2265 snd_pcm_hw_params_t
*params
;
2267 unsigned int max_channels
= 0;
2269 if ((err
= snd_pcm_open(&handle
, name
, SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
)) < 0) {
2270 WARN("The device \"%s\" failed to open: %d (%s).\n",
2271 name
, err
, snd_strerror(err
));
2275 params
= malloc(snd_pcm_hw_params_sizeof());
2277 WARN("Out of memory.\n");
2278 snd_pcm_close(handle
);
2282 if ((err
= snd_pcm_hw_params_any(handle
, params
)) < 0) {
2283 WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
2284 name
, err
, snd_strerror(err
));
2288 if ((err
= snd_pcm_hw_params_get_channels_max(params
,
2289 &max_channels
)) < 0){
2290 WARN("Unable to get max channels: %d (%s)\n", err
, snd_strerror(err
));
2296 snd_pcm_close(handle
);
2298 return max_channels
;
2301 enum AudioDeviceConnectionType
{
2302 AudioDeviceConnectionType_Unknown
= 0,
2303 AudioDeviceConnectionType_PCI
,
2304 AudioDeviceConnectionType_USB
2307 static NTSTATUS
alsa_get_prop_value(void *args
)
2309 struct get_prop_value_params
*params
= args
;
2310 const char *name
= params
->device
;
2311 EDataFlow flow
= params
->flow
;
2312 const GUID
*guid
= params
->guid
;
2313 const PROPERTYKEY
*prop
= params
->prop
;
2314 PROPVARIANT
*out
= params
->value
;
2315 static const PROPERTYKEY devicepath_key
= { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
2316 {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
2319 if(IsEqualPropertyKey(*prop
, devicepath_key
))
2321 enum AudioDeviceConnectionType connection
= AudioDeviceConnectionType_Unknown
;
2322 USHORT vendor_id
= 0, product_id
= 0;
2323 char uevent
[MAX_PATH
];
2324 FILE *fuevent
= NULL
;
2330 if(sscanf(name
, "plughw:%u,%u", &card
, &device
)){
2331 sprintf(uevent
, "/sys/class/sound/card%u/device/uevent", card
);
2332 fuevent
= fopen(uevent
, "r");
2338 while (fgets(line
, sizeof(line
), fuevent
)) {
2342 if((val
= strchr(line
, '='))) {
2346 val_len
= strlen(val
);
2347 if(val_len
> 0 && val
[val_len
- 1] == '\n') { val
[val_len
- 1] = 0; }
2349 if(!strcmp(line
, "PCI_ID")){
2350 connection
= AudioDeviceConnectionType_PCI
;
2351 if(sscanf(val
, "%hX:%hX", &vendor_id
, &product_id
)<2){
2352 WARN("Unexpected input when reading PCI_ID in uevent file.\n");
2353 connection
= AudioDeviceConnectionType_Unknown
;
2356 }else if(!strcmp(line
, "DEVTYPE") && !strcmp(val
,"usb_interface"))
2357 connection
= AudioDeviceConnectionType_USB
;
2358 else if(!strcmp(line
, "PRODUCT"))
2359 if(sscanf(val
, "%hx/%hx/", &vendor_id
, &product_id
)<2){
2360 WARN("Unexpected input when reading PRODUCT in uevent file.\n");
2361 connection
= AudioDeviceConnectionType_Unknown
;
2370 /* As hardly any audio devices have serial numbers, Windows instead
2371 appears to use a persistent random number. We emulate this here
2372 by instead using the last 8 hex digits of the GUID. */
2373 serial_number
= (guid
->Data4
[4] << 24) | (guid
->Data4
[5] << 16) | (guid
->Data4
[6] << 8) | guid
->Data4
[7];
2375 if(connection
== AudioDeviceConnectionType_USB
)
2376 sprintf(buf
, "{1}.USB\\VID_%04X&PID_%04X\\%u&%08X",
2377 vendor_id
, product_id
, device
, serial_number
);
2378 else if (connection
== AudioDeviceConnectionType_PCI
)
2379 sprintf(buf
, "{1}.HDAUDIO\\FUNC_01&VEN_%04X&DEV_%04X\\%u&%08X",
2380 vendor_id
, product_id
, device
, serial_number
);
2382 sprintf(buf
, "{1}.ROOT\\MEDIA\\%04u", serial_number
& 0x1FF);
2384 len
= strlen(buf
) + 1;
2385 if(*params
->buffer_size
< len
* sizeof(WCHAR
)){
2386 params
->result
= E_NOT_SUFFICIENT_BUFFER
;
2387 *params
->buffer_size
= len
* sizeof(WCHAR
);
2388 return STATUS_SUCCESS
;
2390 out
->vt
= VT_LPWSTR
;
2391 out
->pwszVal
= params
->buffer
;
2392 ntdll_umbstowcs(buf
, len
, out
->pwszVal
, len
);
2393 params
->result
= S_OK
;
2394 return STATUS_SUCCESS
;
2395 } else if (flow
!= eCapture
&& IsEqualPropertyKey(*prop
, PKEY_AudioEndpoint_PhysicalSpeakers
)) {
2396 unsigned int num_speakers
, card
, device
;
2399 if (sscanf(name
, "plughw:%u,%u", &card
, &device
))
2400 sprintf(hwname
, "hw:%u,%u", card
, device
); /* must be hw rather than plughw to work */
2402 strcpy(hwname
, name
);
2404 num_speakers
= alsa_probe_num_speakers(hwname
);
2405 if (num_speakers
== 0){
2406 params
->result
= E_FAIL
;
2407 return STATUS_SUCCESS
;
2411 if (num_speakers
> 6)
2412 out
->ulVal
= KSAUDIO_SPEAKER_STEREO
;
2413 else if (num_speakers
== 6)
2414 out
->ulVal
= KSAUDIO_SPEAKER_5POINT1
;
2415 else if (num_speakers
>= 4)
2416 out
->ulVal
= KSAUDIO_SPEAKER_QUAD
;
2417 else if (num_speakers
>= 2)
2418 out
->ulVal
= KSAUDIO_SPEAKER_STEREO
;
2419 else if (num_speakers
== 1)
2420 out
->ulVal
= KSAUDIO_SPEAKER_MONO
;
2422 params
->result
= S_OK
;
2423 return STATUS_SUCCESS
;
2426 TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop
->fmtid
), (unsigned)prop
->pid
);
2428 params
->result
= E_NOTIMPL
;
2429 return STATUS_SUCCESS
;
2432 unixlib_entry_t __wine_unix_call_funcs
[] =
2437 alsa_get_endpoint_ids
,
2439 alsa_release_stream
,
2444 alsa_get_render_buffer
,
2445 alsa_release_render_buffer
,
2446 alsa_get_capture_buffer
,
2447 alsa_release_capture_buffer
,
2448 alsa_is_format_supported
,
2449 alsa_get_mix_format
,
2451 alsa_get_buffer_size
,
2453 alsa_get_current_padding
,
2454 alsa_get_next_packet_size
,
2458 alsa_set_event_handle
,
2461 alsa_get_prop_value
,
2464 alsa_midi_out_message
,
2465 alsa_midi_in_message
,
2466 alsa_midi_notify_wait
,
2474 static NTSTATUS
alsa_wow64_get_endpoint_ids(void *args
)
2483 unsigned int default_idx
;
2485 struct get_endpoint_ids_params params
=
2487 .flow
= params32
->flow
,
2488 .endpoints
= ULongToPtr(params32
->endpoints
),
2489 .size
= params32
->size
2491 alsa_get_endpoint_ids(¶ms
);
2492 params32
->size
= params
.size
;
2493 params32
->result
= params
.result
;
2494 params32
->num
= params
.num
;
2495 params32
->default_idx
= params
.default_idx
;
2496 return STATUS_SUCCESS
;
2499 static NTSTATUS
alsa_wow64_create_stream(void *args
)
2506 AUDCLNT_SHAREMODE share
;
2508 REFERENCE_TIME duration
;
2509 REFERENCE_TIME period
;
2512 PTR32 channel_count
;
2515 struct create_stream_params params
=
2517 .name
= ULongToPtr(params32
->name
),
2518 .device
= ULongToPtr(params32
->device
),
2519 .flow
= params32
->flow
,
2520 .share
= params32
->share
,
2521 .flags
= params32
->flags
,
2522 .duration
= params32
->duration
,
2523 .period
= params32
->period
,
2524 .fmt
= ULongToPtr(params32
->fmt
),
2525 .channel_count
= ULongToPtr(params32
->channel_count
),
2526 .stream
= ULongToPtr(params32
->stream
)
2528 alsa_create_stream(¶ms
);
2529 params32
->result
= params
.result
;
2530 return STATUS_SUCCESS
;
2533 static NTSTATUS
alsa_wow64_release_stream(void *args
)
2537 stream_handle stream
;
2541 struct release_stream_params params
=
2543 .stream
= params32
->stream
,
2544 .timer_thread
= ULongToHandle(params32
->timer_thread
)
2546 alsa_release_stream(¶ms
);
2547 params32
->result
= params
.result
;
2548 return STATUS_SUCCESS
;
2551 static NTSTATUS
alsa_wow64_get_render_buffer(void *args
)
2555 stream_handle stream
;
2561 struct get_render_buffer_params params
=
2563 .stream
= params32
->stream
,
2564 .frames
= params32
->frames
,
2567 alsa_get_render_buffer(¶ms
);
2568 params32
->result
= params
.result
;
2569 *(unsigned int *)ULongToPtr(params32
->data
) = PtrToUlong(data
);
2570 return STATUS_SUCCESS
;
2573 static NTSTATUS
alsa_wow64_get_capture_buffer(void *args
)
2577 stream_handle stream
;
2586 struct get_capture_buffer_params params
=
2588 .stream
= params32
->stream
,
2590 .frames
= ULongToPtr(params32
->frames
),
2591 .flags
= ULongToPtr(params32
->flags
),
2592 .devpos
= ULongToPtr(params32
->devpos
),
2593 .qpcpos
= ULongToPtr(params32
->qpcpos
)
2595 alsa_get_capture_buffer(¶ms
);
2596 params32
->result
= params
.result
;
2597 *(unsigned int *)ULongToPtr(params32
->data
) = PtrToUlong(data
);
2598 return STATUS_SUCCESS
;
2601 static NTSTATUS
alsa_wow64_is_format_supported(void *args
)
2607 AUDCLNT_SHAREMODE share
;
2612 struct is_format_supported_params params
=
2614 .device
= ULongToPtr(params32
->device
),
2615 .flow
= params32
->flow
,
2616 .share
= params32
->share
,
2617 .fmt_in
= ULongToPtr(params32
->fmt_in
),
2618 .fmt_out
= ULongToPtr(params32
->fmt_out
)
2620 alsa_is_format_supported(¶ms
);
2621 params32
->result
= params
.result
;
2622 return STATUS_SUCCESS
;
2625 static NTSTATUS
alsa_wow64_get_mix_format(void *args
)
2634 struct get_mix_format_params params
=
2636 .device
= ULongToPtr(params32
->device
),
2637 .flow
= params32
->flow
,
2638 .fmt
= ULongToPtr(params32
->fmt
)
2640 alsa_get_mix_format(¶ms
);
2641 params32
->result
= params
.result
;
2642 return STATUS_SUCCESS
;
2645 static NTSTATUS
alsa_wow64_get_buffer_size(void *args
)
2649 stream_handle stream
;
2653 struct get_buffer_size_params params
=
2655 .stream
= params32
->stream
,
2656 .frames
= ULongToPtr(params32
->frames
)
2658 alsa_get_buffer_size(¶ms
);
2659 params32
->result
= params
.result
;
2660 return STATUS_SUCCESS
;
2663 static NTSTATUS
alsa_wow64_get_latency(void *args
)
2667 stream_handle stream
;
2671 struct get_latency_params params
=
2673 .stream
= params32
->stream
,
2674 .latency
= ULongToPtr(params32
->latency
)
2676 alsa_get_latency(¶ms
);
2677 params32
->result
= params
.result
;
2678 return STATUS_SUCCESS
;
2681 static NTSTATUS
alsa_wow64_get_current_padding(void *args
)
2685 stream_handle stream
;
2689 struct get_current_padding_params params
=
2691 .stream
= params32
->stream
,
2692 .padding
= ULongToPtr(params32
->padding
)
2694 alsa_get_current_padding(¶ms
);
2695 params32
->result
= params
.result
;
2696 return STATUS_SUCCESS
;
2699 static NTSTATUS
alsa_wow64_get_next_packet_size(void *args
)
2703 stream_handle stream
;
2707 struct get_next_packet_size_params params
=
2709 .stream
= params32
->stream
,
2710 .frames
= ULongToPtr(params32
->frames
)
2712 alsa_get_next_packet_size(¶ms
);
2713 params32
->result
= params
.result
;
2714 return STATUS_SUCCESS
;
2717 static NTSTATUS
alsa_wow64_get_frequency(void *args
)
2721 stream_handle stream
;
2725 struct get_frequency_params params
=
2727 .stream
= params32
->stream
,
2728 .freq
= ULongToPtr(params32
->freq
)
2730 alsa_get_frequency(¶ms
);
2731 params32
->result
= params
.result
;
2732 return STATUS_SUCCESS
;
2735 static NTSTATUS
alsa_wow64_get_position(void *args
)
2739 stream_handle stream
;
2745 struct get_position_params params
=
2747 .stream
= params32
->stream
,
2748 .device
= params32
->device
,
2749 .pos
= ULongToPtr(params32
->pos
),
2750 .qpctime
= ULongToPtr(params32
->qpctime
)
2752 alsa_get_position(¶ms
);
2753 params32
->result
= params
.result
;
2754 return STATUS_SUCCESS
;
2757 static NTSTATUS
alsa_wow64_set_volumes(void *args
)
2761 stream_handle stream
;
2762 float master_volume
;
2764 PTR32 session_volumes
;
2767 struct set_volumes_params params
=
2769 .stream
= params32
->stream
,
2770 .master_volume
= params32
->master_volume
,
2771 .volumes
= ULongToPtr(params32
->volumes
),
2772 .session_volumes
= ULongToPtr(params32
->session_volumes
),
2773 .channel
= params32
->channel
2775 return alsa_set_volumes(¶ms
);
2778 static NTSTATUS
alsa_wow64_set_event_handle(void *args
)
2782 stream_handle stream
;
2786 struct set_event_handle_params params
=
2788 .stream
= params32
->stream
,
2789 .event
= ULongToHandle(params32
->event
)
2792 alsa_set_event_handle(¶ms
);
2793 params32
->result
= params
.result
;
2794 return STATUS_SUCCESS
;
2797 static NTSTATUS
alsa_wow64_get_prop_value(void *args
)
2799 struct propvariant32
2802 WORD pad1
, pad2
, pad3
;
2807 ULARGE_INTEGER uhVal
;
2818 PTR32 buffer
; /* caller allocated buffer to hold value's strings */
2822 struct get_prop_value_params params
=
2824 .device
= ULongToPtr(params32
->device
),
2825 .flow
= params32
->flow
,
2826 .guid
= ULongToPtr(params32
->guid
),
2827 .prop
= ULongToPtr(params32
->prop
),
2829 .buffer
= ULongToPtr(params32
->buffer
),
2830 .buffer_size
= ULongToPtr(params32
->buffer_size
)
2832 alsa_get_prop_value(¶ms
);
2833 params32
->result
= params
.result
;
2834 if (SUCCEEDED(params
.result
))
2836 value32
= UlongToPtr(params32
->value
);
2837 value32
->vt
= value
.vt
;
2841 value32
->ulVal
= value
.ulVal
;
2844 value32
->ptr
= params32
->buffer
;
2847 FIXME("Unhandled vt %04x\n", value
.vt
);
2850 return STATUS_SUCCESS
;
2853 unixlib_entry_t __wine_unix_call_wow64_funcs
[] =
2858 alsa_wow64_get_endpoint_ids
,
2859 alsa_wow64_create_stream
,
2860 alsa_wow64_release_stream
,
2865 alsa_wow64_get_render_buffer
,
2866 alsa_release_render_buffer
,
2867 alsa_wow64_get_capture_buffer
,
2868 alsa_release_capture_buffer
,
2869 alsa_wow64_is_format_supported
,
2870 alsa_wow64_get_mix_format
,
2872 alsa_wow64_get_buffer_size
,
2873 alsa_wow64_get_latency
,
2874 alsa_wow64_get_current_padding
,
2875 alsa_wow64_get_next_packet_size
,
2876 alsa_wow64_get_frequency
,
2877 alsa_wow64_get_position
,
2878 alsa_wow64_set_volumes
,
2879 alsa_wow64_set_event_handle
,
2882 alsa_wow64_get_prop_value
,
2885 alsa_wow64_midi_out_message
,
2886 alsa_wow64_midi_in_message
,
2887 alsa_wow64_midi_notify_wait
,