[PATCH 6/8] winealsa: Move is_format_supported to the unixlib.

Huw Davies huw at codeweavers.com
Wed Feb 16 03:34:52 CST 2022


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, &params);
 
-    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 = &params.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