[PATCH 5/8] winealsa: Move get_mix_format to the unixlib.

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


Signed-off-by: Huw Davies <huw at codeweavers.com>
---
 dlls/winealsa.drv/alsa.c     | 178 +++++++++++++++++++++++++++++++++++
 dlls/winealsa.drv/mmdevdrv.c | 126 +++----------------------
 dlls/winealsa.drv/unixlib.h  |   9 ++
 3 files changed, 199 insertions(+), 114 deletions(-)

diff --git a/dlls/winealsa.drv/alsa.c b/dlls/winealsa.drv/alsa.c
index ae05c7a8b9b..320d676effb 100644
--- a/dlls/winealsa.drv/alsa.c
+++ b/dlls/winealsa.drv/alsa.c
@@ -39,6 +39,7 @@
 #include "wine/list.h"
 #include "wine/unixlib.h"
 
+#include "initguid.h"
 #include "unixlib.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(alsa);
@@ -452,7 +453,184 @@ static NTSTATUS get_endpoint_ids(void *args)
     return STATUS_SUCCESS;
 }
 
+static HRESULT alsa_open_device(const char *alsa_name, EDataFlow flow, snd_pcm_t **pcm_handle,
+                                snd_pcm_hw_params_t **hw_params)
+{
+    snd_pcm_stream_t pcm_stream;
+    int err;
+
+    if(flow == eRender)
+        pcm_stream = SND_PCM_STREAM_PLAYBACK;
+    else if(flow == eCapture)
+        pcm_stream = SND_PCM_STREAM_CAPTURE;
+    else
+        return E_UNEXPECTED;
+
+    err = snd_pcm_open(pcm_handle, alsa_name, pcm_stream, SND_PCM_NONBLOCK);
+    if(err < 0){
+        WARN("Unable to open PCM \"%s\": %d (%s)\n", alsa_name, err, snd_strerror(err));
+        switch(err){
+        case -EBUSY:
+            return AUDCLNT_E_DEVICE_IN_USE;
+        default:
+            return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
+        }
+    }
+
+    *hw_params = malloc(snd_pcm_hw_params_sizeof());
+    if(!*hw_params){
+        snd_pcm_close(*pcm_handle);
+        return E_OUTOFMEMORY;
+    }
+
+    return S_OK;
+}
+
+static DWORD get_channel_mask(unsigned int channels)
+{
+    switch(channels){
+    case 0:
+        return 0;
+    case 1:
+        return KSAUDIO_SPEAKER_MONO;
+    case 2:
+        return KSAUDIO_SPEAKER_STEREO;
+    case 3:
+        return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
+    case 4:
+        return KSAUDIO_SPEAKER_QUAD;    /* not _SURROUND */
+    case 5:
+        return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
+    case 6:
+        return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
+    case 7:
+        return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
+    case 8:
+        return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
+    }
+    FIXME("Unknown speaker configuration: %u\n", channels);
+    return 0;
+}
+
+static NTSTATUS get_mix_format(void *args)
+{
+    struct get_mix_format_params *params = args;
+    WAVEFORMATEXTENSIBLE *fmt = params->fmt;
+    snd_pcm_t *pcm_handle;
+    snd_pcm_hw_params_t *hw_params;
+    snd_pcm_format_mask_t *formats;
+    unsigned int max_rate, max_channels;
+    int err;
+
+    params->result = alsa_open_device(params->alsa_name, params->flow, &pcm_handle, &hw_params);
+    if(FAILED(params->result))
+        return STATUS_SUCCESS;
+
+    formats = calloc(1, snd_pcm_format_mask_sizeof());
+    if(!formats){
+        free(hw_params);
+        snd_pcm_close(pcm_handle);
+        params->result = E_OUTOFMEMORY;
+        return STATUS_SUCCESS;
+    }
+
+    if((err = snd_pcm_hw_params_any(pcm_handle, hw_params)) < 0){
+        WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
+        params->result = AUDCLNT_E_DEVICE_INVALIDATED;
+        goto exit;
+    }
+
+    snd_pcm_hw_params_get_format_mask(hw_params, formats);
+
+    fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+    if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
+        fmt->Format.wBitsPerSample = 32;
+        fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+    }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
+        fmt->Format.wBitsPerSample = 16;
+        fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+    }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
+        fmt->Format.wBitsPerSample = 8;
+        fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+    }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
+        fmt->Format.wBitsPerSample = 32;
+        fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+    }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
+        fmt->Format.wBitsPerSample = 24;
+        fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+    }else{
+        ERR("Didn't recognize any available ALSA formats\n");
+        params->result = AUDCLNT_E_DEVICE_INVALIDATED;
+        goto exit;
+    }
+
+    if((err = snd_pcm_hw_params_get_channels_max(hw_params, &max_channels)) < 0){
+        WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
+        params->result = AUDCLNT_E_DEVICE_INVALIDATED;
+        goto exit;
+    }
+
+    if(max_channels > 6)
+        fmt->Format.nChannels = 2;
+    else
+        fmt->Format.nChannels = max_channels;
+
+    if(fmt->Format.nChannels > 1 && (fmt->Format.nChannels & 0x1)){
+        /* For most hardware on Windows, users must choose a configuration with an even
+         * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
+         * channels, but those channels are still reported to applications from
+         * GetMixFormat! Some applications behave badly if given an odd number of
+         * channels (e.g. 2.1). */
+
+        if(fmt->Format.nChannels < max_channels)
+            fmt->Format.nChannels += 1;
+        else
+            /* We could "fake" more channels and downmix the emulated channels,
+             * but at that point you really ought to tweak your ALSA setup or
+             * just use PulseAudio. */
+            WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt->Format.nChannels);
+    }
+
+    fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
+
+    if((err = snd_pcm_hw_params_get_rate_max(hw_params, &max_rate, NULL)) < 0){
+        WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
+        params->result = AUDCLNT_E_DEVICE_INVALIDATED;
+        goto exit;
+    }
+
+    if(max_rate >= 48000)
+        fmt->Format.nSamplesPerSec = 48000;
+    else if(max_rate >= 44100)
+        fmt->Format.nSamplesPerSec = 44100;
+    else if(max_rate >= 22050)
+        fmt->Format.nSamplesPerSec = 22050;
+    else if(max_rate >= 11025)
+        fmt->Format.nSamplesPerSec = 11025;
+    else if(max_rate >= 8000)
+        fmt->Format.nSamplesPerSec = 8000;
+    else{
+        ERR("Unknown max rate: %u\n", max_rate);
+        params->result = AUDCLNT_E_DEVICE_INVALIDATED;
+        goto exit;
+    }
+
+    fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample * fmt->Format.nChannels) / 8;
+    fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec * fmt->Format.nBlockAlign;
+
+    fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
+    fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+
+exit:
+    free(formats);
+    free(hw_params);
+    snd_pcm_close(pcm_handle);
+
+    return STATUS_SUCCESS;
+}
+
 unixlib_entry_t __wine_unix_call_funcs[] =
 {
     get_endpoint_ids,
+    get_mix_format,
 };
diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index ecd74d373aa..24c6755d697 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -1446,11 +1446,7 @@ static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
         WAVEFORMATEX **pwfx)
 {
     ACImpl *This = impl_from_IAudioClient3(iface);
-    WAVEFORMATEXTENSIBLE *fmt;
-    snd_pcm_format_mask_t *formats;
-    unsigned int max_rate, max_channels;
-    int err;
-    HRESULT hr = S_OK;
+    struct get_mix_format_params params;
 
     TRACE("(%p)->(%p)\n", This, pwfx);
 
@@ -1458,119 +1454,21 @@ static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
         return E_POINTER;
     *pwfx = NULL;
 
-    fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
-    if(!fmt)
+    params.alsa_name = This->alsa_name;
+    params.flow = This->dataflow;
+    params.fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
+    if(!params.fmt)
         return E_OUTOFMEMORY;
 
-    formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_format_mask_sizeof());
-    if(!formats){
-        CoTaskMemFree(fmt);
-        return E_OUTOFMEMORY;
-    }
-
-    EnterCriticalSection(&This->lock);
-
-    if((err = snd_pcm_hw_params_any(This->stream->pcm_handle, This->stream->hw_params)) < 0){
-        WARN("Unable to get hw_params: %d (%s)\n", err, snd_strerror(err));
-        hr = AUDCLNT_E_DEVICE_INVALIDATED;
-        goto exit;
-    }
-
-    snd_pcm_hw_params_get_format_mask(This->stream->hw_params, formats);
-
-    fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
-    if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_FLOAT_LE)){
-        fmt->Format.wBitsPerSample = 32;
-        fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
-    }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S16_LE)){
-        fmt->Format.wBitsPerSample = 16;
-        fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
-    }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_U8)){
-        fmt->Format.wBitsPerSample = 8;
-        fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
-    }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S32_LE)){
-        fmt->Format.wBitsPerSample = 32;
-        fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
-    }else if(snd_pcm_format_mask_test(formats, SND_PCM_FORMAT_S24_3LE)){
-        fmt->Format.wBitsPerSample = 24;
-        fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
-    }else{
-        ERR("Didn't recognize any available ALSA formats\n");
-        hr = AUDCLNT_E_DEVICE_INVALIDATED;
-        goto exit;
-    }
-
-    if((err = snd_pcm_hw_params_get_channels_max(This->stream->hw_params,
-                    &max_channels)) < 0){
-        WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
-        hr = AUDCLNT_E_DEVICE_INVALIDATED;
-        goto exit;
-    }
-
-    if(max_channels > 6)
-        fmt->Format.nChannels = 2;
-    else
-        fmt->Format.nChannels = max_channels;
-
-    if(fmt->Format.nChannels > 1 && (fmt->Format.nChannels & 0x1)){
-        /* For most hardware on Windows, users must choose a configuration with an even
-         * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
-         * channels, but those channels are still reported to applications from
-         * GetMixFormat! Some applications behave badly if given an odd number of
-         * channels (e.g. 2.1). */
-
-        if(fmt->Format.nChannels < max_channels)
-            fmt->Format.nChannels += 1;
-        else
-            /* We could "fake" more channels and downmix the emulated channels,
-             * but at that point you really ought to tweak your ALSA setup or
-             * just use PulseAudio. */
-            WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt->Format.nChannels);
-    }
-
-    fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
+    ALSA_CALL(get_mix_format, &params);
 
-    if((err = snd_pcm_hw_params_get_rate_max(This->stream->hw_params, &max_rate,
-                    NULL)) < 0){
-        WARN("Unable to get max rate: %d (%s)\n", err, snd_strerror(err));
-        hr = AUDCLNT_E_DEVICE_INVALIDATED;
-        goto exit;
-    }
-
-    if(max_rate >= 48000)
-        fmt->Format.nSamplesPerSec = 48000;
-    else if(max_rate >= 44100)
-        fmt->Format.nSamplesPerSec = 44100;
-    else if(max_rate >= 22050)
-        fmt->Format.nSamplesPerSec = 22050;
-    else if(max_rate >= 11025)
-        fmt->Format.nSamplesPerSec = 11025;
-    else if(max_rate >= 8000)
-        fmt->Format.nSamplesPerSec = 8000;
-    else{
-        ERR("Unknown max rate: %u\n", max_rate);
-        hr = AUDCLNT_E_DEVICE_INVALIDATED;
-        goto exit;
-    }
-
-    fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
-            fmt->Format.nChannels) / 8;
-    fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
-        fmt->Format.nBlockAlign;
-
-    fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
-    fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
-
-    dump_fmt((WAVEFORMATEX*)fmt);
-    *pwfx = (WAVEFORMATEX*)fmt;
-
-exit:
-    LeaveCriticalSection(&This->lock);
-    if(FAILED(hr))
-        CoTaskMemFree(fmt);
-    HeapFree(GetProcessHeap(), 0, formats);
+    if(SUCCEEDED(params.result)){
+        *pwfx = &params.fmt->Format;
+        dump_fmt(*pwfx);
+    } else
+        CoTaskMemFree(params.fmt);
 
-    return hr;
+    return params.result;
 }
 
 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
diff --git a/dlls/winealsa.drv/unixlib.h b/dlls/winealsa.drv/unixlib.h
index 8a20710b7e5..ef5b470e1ed 100644
--- a/dlls/winealsa.drv/unixlib.h
+++ b/dlls/winealsa.drv/unixlib.h
@@ -67,9 +67,18 @@ struct get_endpoint_ids_params
     unsigned int default_idx;
 };
 
+struct get_mix_format_params
+{
+    const char *alsa_name;
+    EDataFlow flow;
+    WAVEFORMATEXTENSIBLE *fmt;
+    HRESULT result;
+};
+
 enum alsa_funcs
 {
     alsa_get_endpoint_ids,
+    alsa_get_mix_format,
 };
 
 extern unixlib_handle_t alsa_handle;
-- 
2.25.1




More information about the wine-devel mailing list