[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, ¶ms);
- 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 = ¶ms.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