Signed-off-by: Andrew Eikum aeikum@codeweavers.com
On Wed, Feb 16, 2022 at 09:34:52AM +0000, Huw Davies wrote:
Signed-off-by: Huw Davies huw@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;elseWARN("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;elseWARN("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;elseWARN("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;elsemask = 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