[PATCH 6/8] winealsa: Move is_format_supported to the unixlib.
Andrew Eikum
aeikum at codeweavers.com
Thu Feb 17 09:26:03 CST 2022
Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>
On Wed, Feb 16, 2022 at 09:34:52AM +0000, Huw Davies wrote:
> Signed-off-by: Huw Davies <huw at codeweavers.com>
> ---
> dlls/winealsa.drv/alsa.c | 291 +++++++++++++++++++++++++++++++++++
> dlls/winealsa.drv/mmdevdrv.c | 150 ++----------------
> dlls/winealsa.drv/unixlib.h | 11 ++
> 3 files changed, 317 insertions(+), 135 deletions(-)
>
> diff --git a/dlls/winealsa.drv/alsa.c b/dlls/winealsa.drv/alsa.c
> index 320d676effb..0680383f59b 100644
> --- a/dlls/winealsa.drv/alsa.c
> +++ b/dlls/winealsa.drv/alsa.c
> @@ -453,6 +453,27 @@ static NTSTATUS get_endpoint_ids(void *args)
> return STATUS_SUCCESS;
> }
>
> +static WAVEFORMATEXTENSIBLE *clone_format(const WAVEFORMATEX *fmt)
> +{
> + WAVEFORMATEXTENSIBLE *ret;
> + size_t size;
> +
> + if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
> + size = sizeof(WAVEFORMATEXTENSIBLE);
> + else
> + size = sizeof(WAVEFORMATEX);
> +
> + ret = malloc(size);
> + if(!ret)
> + return NULL;
> +
> + memcpy(ret, fmt, size);
> +
> + ret->Format.cbSize = size - sizeof(WAVEFORMATEX);
> +
> + return ret;
> +}
> +
> static HRESULT alsa_open_device(const char *alsa_name, EDataFlow flow, snd_pcm_t **pcm_handle,
> snd_pcm_hw_params_t **hw_params)
> {
> @@ -486,6 +507,78 @@ static HRESULT alsa_open_device(const char *alsa_name, EDataFlow flow, snd_pcm_t
> return S_OK;
> }
>
> +static snd_pcm_format_t alsa_format(const WAVEFORMATEX *fmt)
> +{
> + snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
> + const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
> +
> + if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
> + (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> + IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
> + if(fmt->wBitsPerSample == 8)
> + format = SND_PCM_FORMAT_U8;
> + else if(fmt->wBitsPerSample == 16)
> + format = SND_PCM_FORMAT_S16_LE;
> + else if(fmt->wBitsPerSample == 24)
> + format = SND_PCM_FORMAT_S24_3LE;
> + else if(fmt->wBitsPerSample == 32)
> + format = SND_PCM_FORMAT_S32_LE;
> + else
> + WARN("Unsupported bit depth: %u\n", fmt->wBitsPerSample);
> + if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> + fmt->wBitsPerSample != fmtex->Samples.wValidBitsPerSample){
> + if(fmtex->Samples.wValidBitsPerSample == 20 && fmt->wBitsPerSample == 24)
> + format = SND_PCM_FORMAT_S20_3LE;
> + else
> + WARN("Unsupported ValidBits: %u\n", fmtex->Samples.wValidBitsPerSample);
> + }
> + }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
> + (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> + IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
> + if(fmt->wBitsPerSample == 32)
> + format = SND_PCM_FORMAT_FLOAT_LE;
> + else if(fmt->wBitsPerSample == 64)
> + format = SND_PCM_FORMAT_FLOAT64_LE;
> + else
> + WARN("Unsupported float size: %u\n", fmt->wBitsPerSample);
> + }else
> + WARN("Unknown wave format: %04x\n", fmt->wFormatTag);
> + return format;
> +}
> +
> +static int alsa_channel_index(DWORD flag)
> +{
> + switch(flag){
> + case SPEAKER_FRONT_LEFT:
> + return 0;
> + case SPEAKER_FRONT_RIGHT:
> + return 1;
> + case SPEAKER_BACK_LEFT:
> + return 2;
> + case SPEAKER_BACK_RIGHT:
> + return 3;
> + case SPEAKER_FRONT_CENTER:
> + return 4;
> + case SPEAKER_LOW_FREQUENCY:
> + return 5;
> + case SPEAKER_SIDE_LEFT:
> + return 6;
> + case SPEAKER_SIDE_RIGHT:
> + return 7;
> + }
> + return -1;
> +}
> +
> +static BOOL need_remapping(const WAVEFORMATEX *fmt, int *map)
> +{
> + unsigned int i;
> + for(i = 0; i < fmt->nChannels; ++i){
> + if(map[i] != i)
> + return TRUE;
> + }
> + return FALSE;
> +}
> +
> static DWORD get_channel_mask(unsigned int channels)
> {
> switch(channels){
> @@ -512,6 +605,203 @@ static DWORD get_channel_mask(unsigned int channels)
> return 0;
> }
>
> +static HRESULT map_channels(EDataFlow flow, const WAVEFORMATEX *fmt, int *alsa_channels, int *map)
> +{
> + BOOL need_remap;
> +
> + if(flow != eCapture && (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE || fmt->nChannels > 2) ){
> + WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
> + DWORD mask, flag = SPEAKER_FRONT_LEFT;
> + UINT i = 0;
> +
> + if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> + fmtex->dwChannelMask != 0)
> + mask = fmtex->dwChannelMask;
> + else
> + mask = get_channel_mask(fmt->nChannels);
> +
> + *alsa_channels = 0;
> +
> + while(i < fmt->nChannels && !(flag & SPEAKER_RESERVED)){
> + if(mask & flag){
> + map[i] = alsa_channel_index(flag);
> + TRACE("Mapping mmdevapi channel %u (0x%x) to ALSA channel %d\n",
> + i, flag, map[i]);
> + if(map[i] >= *alsa_channels)
> + *alsa_channels = map[i] + 1;
> + ++i;
> + }
> + flag <<= 1;
> + }
> +
> + while(i < fmt->nChannels){
> + map[i] = *alsa_channels;
> + TRACE("Mapping mmdevapi channel %u to ALSA channel %d\n",
> + i, map[i]);
> + ++*alsa_channels;
> + ++i;
> + }
> +
> + for(i = 0; i < fmt->nChannels; ++i){
> + if(map[i] == -1){
> + map[i] = *alsa_channels;
> + ++*alsa_channels;
> + TRACE("Remapping mmdevapi channel %u to ALSA channel %d\n",
> + i, map[i]);
> + }
> + }
> +
> + need_remap = need_remapping(fmt, map);
> + }else{
> + *alsa_channels = fmt->nChannels;
> +
> + need_remap = FALSE;
> + }
> +
> + TRACE("need_remapping: %u, alsa_channels: %d\n", need_remap, *alsa_channels);
> +
> + return need_remap ? S_OK : S_FALSE;
> +}
> +
> +static NTSTATUS is_format_supported(void *args)
> +{
> + struct is_format_supported_params *params = args;
> + const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)params->fmt_in;
> + snd_pcm_t *pcm_handle;
> + snd_pcm_hw_params_t *hw_params;
> + snd_pcm_format_mask_t *formats = NULL;
> + snd_pcm_format_t format;
> + WAVEFORMATEXTENSIBLE *closest = NULL;
> + unsigned int max = 0, min = 0;
> + int err;
> + int alsa_channels, alsa_channel_map[32];
> +
> + params->result = S_OK;
> +
> + if(!params->fmt_in || (params->share == AUDCLNT_SHAREMODE_SHARED && !params->fmt_out))
> + params->result = E_POINTER;
> + else if(params->share != AUDCLNT_SHAREMODE_SHARED && params->share != AUDCLNT_SHAREMODE_EXCLUSIVE)
> + params->result = E_INVALIDARG;
> + else if(params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
> + if(params->fmt_in->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
> + params->result = E_INVALIDARG;
> + else if(params->fmt_in->nAvgBytesPerSec == 0 || params->fmt_in->nBlockAlign == 0 ||
> + (fmtex->Samples.wValidBitsPerSample > params->fmt_in->wBitsPerSample))
> + params->result = E_INVALIDARG;
> + }
> + if(FAILED(params->result))
> + return STATUS_SUCCESS;
> +
> + if(params->fmt_in->nChannels == 0){
> + params->result = AUDCLNT_E_UNSUPPORTED_FORMAT;
> + return STATUS_SUCCESS;
> + }
> +
> + params->result = alsa_open_device(params->alsa_name, params->flow, &pcm_handle, &hw_params);
> + if(FAILED(params->result))
> + return STATUS_SUCCESS;
> +
> + if((err = snd_pcm_hw_params_any(pcm_handle, hw_params)) < 0){
> + params->result = AUDCLNT_E_DEVICE_INVALIDATED;
> + goto exit;
> + }
> +
> + formats = calloc(1, snd_pcm_format_mask_sizeof());
> + if(!formats){
> + params->result = E_OUTOFMEMORY;
> + goto exit;
> + }
> +
> + snd_pcm_hw_params_get_format_mask(hw_params, formats);
> + format = alsa_format(params->fmt_in);
> + if (format == SND_PCM_FORMAT_UNKNOWN ||
> + !snd_pcm_format_mask_test(formats, format)){
> + params->result = AUDCLNT_E_UNSUPPORTED_FORMAT;
> + goto exit;
> + }
> +
> + closest = clone_format(params->fmt_in);
> + if(!closest){
> + params->result = E_OUTOFMEMORY;
> + goto exit;
> + }
> +
> + if((err = snd_pcm_hw_params_get_rate_min(hw_params, &min, NULL)) < 0){
> + params->result = AUDCLNT_E_DEVICE_INVALIDATED;
> + WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
> + goto exit;
> + }
> +
> + if((err = snd_pcm_hw_params_get_rate_max(hw_params, &max, NULL)) < 0){
> + params->result = AUDCLNT_E_DEVICE_INVALIDATED;
> + WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
> + goto exit;
> + }
> +
> + if(params->fmt_in->nSamplesPerSec < min || params->fmt_in->nSamplesPerSec > max){
> + params->result = AUDCLNT_E_UNSUPPORTED_FORMAT;
> + goto exit;
> + }
> +
> + if((err = snd_pcm_hw_params_get_channels_min(hw_params, &min)) < 0){
> + params->result = AUDCLNT_E_DEVICE_INVALIDATED;
> + WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
> + goto exit;
> + }
> +
> + if((err = snd_pcm_hw_params_get_channels_max(hw_params, &max)) < 0){
> + params->result = AUDCLNT_E_DEVICE_INVALIDATED;
> + WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
> + goto exit;
> + }
> + if(params->fmt_in->nChannels > max){
> + params->result = S_FALSE;
> + closest->Format.nChannels = max;
> + }else if(params->fmt_in->nChannels < min){
> + params->result = S_FALSE;
> + closest->Format.nChannels = min;
> + }
> +
> + map_channels(params->flow, params->fmt_in, &alsa_channels, alsa_channel_map);
> +
> + if(alsa_channels > max){
> + params->result = S_FALSE;
> + closest->Format.nChannels = max;
> + }
> +
> + if(closest->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
> + closest->dwChannelMask = get_channel_mask(closest->Format.nChannels);
> +
> + if(params->fmt_in->nBlockAlign != params->fmt_in->nChannels * params->fmt_in->wBitsPerSample / 8 ||
> + params->fmt_in->nAvgBytesPerSec != params->fmt_in->nBlockAlign * params->fmt_in->nSamplesPerSec ||
> + (params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> + fmtex->Samples.wValidBitsPerSample < params->fmt_in->wBitsPerSample))
> + params->result = S_FALSE;
> +
> + if(params->share == AUDCLNT_SHAREMODE_EXCLUSIVE && params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
> + if(fmtex->dwChannelMask == 0 || fmtex->dwChannelMask & SPEAKER_RESERVED)
> + params->result = S_FALSE;
> + }
> +
> +exit:
> + if(params->result == S_FALSE && !params->fmt_out)
> + params->result = AUDCLNT_E_UNSUPPORTED_FORMAT;
> +
> + if(params->result == S_FALSE && params->fmt_out) {
> + closest->Format.nBlockAlign = closest->Format.nChannels * closest->Format.wBitsPerSample / 8;
> + closest->Format.nAvgBytesPerSec = closest->Format.nBlockAlign * closest->Format.nSamplesPerSec;
> + if(closest->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
> + closest->Samples.wValidBitsPerSample = closest->Format.wBitsPerSample;
> + memcpy(params->fmt_out, closest, closest->Format.cbSize);
> + }
> + free(closest);
> + free(formats);
> + free(hw_params);
> + snd_pcm_close(pcm_handle);
> +
> + return STATUS_SUCCESS;
> +}
> +
> static NTSTATUS get_mix_format(void *args)
> {
> struct get_mix_format_params *params = args;
> @@ -632,5 +922,6 @@ exit:
> unixlib_entry_t __wine_unix_call_funcs[] =
> {
> get_endpoint_ids,
> + is_format_supported,
> get_mix_format,
> };
> diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
> index 24c6755d697..8a80acd871a 100644
> --- a/dlls/winealsa.drv/mmdevdrv.c
> +++ b/dlls/winealsa.drv/mmdevdrv.c
> @@ -1296,150 +1296,30 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
> WAVEFORMATEX **out)
> {
> ACImpl *This = impl_from_IAudioClient3(iface);
> - snd_pcm_format_mask_t *formats = NULL;
> - snd_pcm_format_t format;
> - HRESULT hr = S_OK;
> - WAVEFORMATEX *closest = NULL;
> - unsigned int max = 0, min = 0;
> - int err;
> - int alsa_channels, alsa_channel_map[32];
> + struct is_format_supported_params params;
>
> TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
> + if(fmt) dump_fmt(fmt);
>
> - if(!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
> - return E_POINTER;
> -
> - if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
> - return E_INVALIDARG;
> -
> - if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> - fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
> - return E_INVALIDARG;
> -
> - dump_fmt(fmt);
> + params.alsa_name = This->alsa_name;
> + params.flow = This->dataflow;
> + params.share = mode;
> + params.fmt_in = fmt;
> + params.fmt_out = NULL;
>
> if(out){
> *out = NULL;
> - if(mode != AUDCLNT_SHAREMODE_SHARED)
> - out = NULL;
> + if(mode == AUDCLNT_SHAREMODE_SHARED)
> + params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out));
> }
> + ALSA_CALL(is_format_supported, ¶ms);
>
> - if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> - (fmt->nAvgBytesPerSec == 0 ||
> - fmt->nBlockAlign == 0 ||
> - ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->wBitsPerSample))
> - return E_INVALIDARG;
> -
> - if(fmt->nChannels == 0)
> - return AUDCLNT_E_UNSUPPORTED_FORMAT;
> -
> - EnterCriticalSection(&This->lock);
> -
> - if((err = snd_pcm_hw_params_any(This->stream->pcm_handle, This->stream->hw_params)) < 0){
> - hr = AUDCLNT_E_DEVICE_INVALIDATED;
> - goto exit;
> - }
> -
> - formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
> - snd_pcm_format_mask_sizeof());
> - if(!formats){
> - hr = E_OUTOFMEMORY;
> - goto exit;
> - }
> -
> - snd_pcm_hw_params_get_format_mask(This->stream->hw_params, formats);
> - format = alsa_format(fmt);
> - if (format == SND_PCM_FORMAT_UNKNOWN ||
> - !snd_pcm_format_mask_test(formats, format)){
> - hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
> - goto exit;
> - }
> -
> - closest = clone_format(fmt);
> - if(!closest){
> - hr = E_OUTOFMEMORY;
> - goto exit;
> - }
> -
> - if((err = snd_pcm_hw_params_get_rate_min(This->stream->hw_params, &min, NULL)) < 0){
> - hr = AUDCLNT_E_DEVICE_INVALIDATED;
> - WARN("Unable to get min rate: %d (%s)\n", err, snd_strerror(err));
> - goto exit;
> - }
> -
> - if((err = snd_pcm_hw_params_get_rate_max(This->stream->hw_params, &max, NULL)) < 0){
> - hr = AUDCLNT_E_DEVICE_INVALIDATED;
> - WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
> - goto exit;
> - }
> -
> - if(fmt->nSamplesPerSec < min || fmt->nSamplesPerSec > max){
> - hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
> - goto exit;
> - }
> -
> - if((err = snd_pcm_hw_params_get_channels_min(This->stream->hw_params, &min)) < 0){
> - hr = AUDCLNT_E_DEVICE_INVALIDATED;
> - WARN("Unable to get min channels: %d (%s)\n", err, snd_strerror(err));
> - goto exit;
> - }
> -
> - if((err = snd_pcm_hw_params_get_channels_max(This->stream->hw_params, &max)) < 0){
> - hr = AUDCLNT_E_DEVICE_INVALIDATED;
> - WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
> - goto exit;
> - }
> - if(fmt->nChannels > max){
> - hr = S_FALSE;
> - closest->nChannels = max;
> - }else if(fmt->nChannels < min){
> - hr = S_FALSE;
> - closest->nChannels = min;
> - }
> -
> - map_channels(This, fmt, &alsa_channels, alsa_channel_map);
> -
> - if(alsa_channels > max){
> - hr = S_FALSE;
> - closest->nChannels = max;
> - }
> -
> - if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
> - ((WAVEFORMATEXTENSIBLE*)closest)->dwChannelMask = get_channel_mask(closest->nChannels);
> -
> - if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
> - fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
> - (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> - ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample < fmt->wBitsPerSample))
> - hr = S_FALSE;
> -
> - if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
> - fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
> - if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 ||
> - ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED)
> - hr = S_FALSE;
> - }
> -
> -exit:
> - LeaveCriticalSection(&This->lock);
> - HeapFree(GetProcessHeap(), 0, formats);
> -
> - if(hr == S_FALSE && !out)
> - hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
> -
> - if(hr == S_FALSE && out) {
> - closest->nBlockAlign =
> - closest->nChannels * closest->wBitsPerSample / 8;
> - closest->nAvgBytesPerSec =
> - closest->nBlockAlign * closest->nSamplesPerSec;
> - if(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
> - ((WAVEFORMATEXTENSIBLE*)closest)->Samples.wValidBitsPerSample = closest->wBitsPerSample;
> - *out = closest;
> - } else
> - CoTaskMemFree(closest);
> + if(params.result == S_FALSE)
> + *out = ¶ms.fmt_out->Format;
> + else
> + CoTaskMemFree(params.fmt_out);
>
> - TRACE("returning: %08x\n", hr);
> - return hr;
> + return params.result;
> }
>
> static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
> diff --git a/dlls/winealsa.drv/unixlib.h b/dlls/winealsa.drv/unixlib.h
> index ef5b470e1ed..cd8d303b5d3 100644
> --- a/dlls/winealsa.drv/unixlib.h
> +++ b/dlls/winealsa.drv/unixlib.h
> @@ -67,6 +67,16 @@ struct get_endpoint_ids_params
> unsigned int default_idx;
> };
>
> +struct is_format_supported_params
> +{
> + const char *alsa_name;
> + EDataFlow flow;
> + AUDCLNT_SHAREMODE share;
> + const WAVEFORMATEX *fmt_in;
> + WAVEFORMATEXTENSIBLE *fmt_out;
> + HRESULT result;
> +};
> +
> struct get_mix_format_params
> {
> const char *alsa_name;
> @@ -78,6 +88,7 @@ struct get_mix_format_params
> enum alsa_funcs
> {
> alsa_get_endpoint_ids,
> + alsa_is_format_supported,
> alsa_get_mix_format,
> };
>
> --
> 2.25.1
>
>
More information about the wine-devel
mailing list