[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, ¶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