-- v3: winepulse: Implement is_format_supported in unixlib.
From: Davide Beatrici git@davidebeatrici.dev
--- dlls/winepulse.drv/mmdevdrv.c | 174 ++++------------------------------ dlls/winepulse.drv/pulse.c | 171 ++++++++++++++++++++++++++++++++- 2 files changed, 186 insertions(+), 159 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index 9ec0e3b7f46..110f23e1319 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -620,27 +620,6 @@ static void dump_fmt(const WAVEFORMATEX *fmt) } }
-static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt) -{ - WAVEFORMATEX *ret; - size_t size; - - if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) - size = sizeof(WAVEFORMATEXTENSIBLE); - else - size = sizeof(WAVEFORMATEX); - - ret = CoTaskMemAlloc(size); - if (!ret) - return NULL; - - memcpy(ret, fmt, size); - - ret->cbSize = size - sizeof(WAVEFORMATEX); - - return ret; -} - static void session_init_vols(AudioSession *session, UINT channels) { if (session->channel_count < channels) { @@ -888,152 +867,33 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface, WAVEFORMATEX **out) { ACImpl *This = impl_from_IAudioClient3(iface); - HRESULT hr = S_OK; - WAVEFORMATEX *closest = NULL; - BOOL exclusive; + struct is_format_supported_params params;
TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
- if (!fmt) - return E_POINTER; - - if (out) - *out = NULL; - - if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) { - exclusive = 1; - out = NULL; - } else if (mode == AUDCLNT_SHAREMODE_SHARED) { - exclusive = 0; - if (!out) - return E_POINTER; - } else - return E_INVALIDARG; - - if (fmt->nChannels == 0) - return AUDCLNT_E_UNSUPPORTED_FORMAT; - - closest = clone_format(fmt); - if (!closest) - return E_OUTOFMEMORY; - - dump_fmt(fmt); - - switch (fmt->wFormatTag) { - case WAVE_FORMAT_EXTENSIBLE: { - WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest; - - if ((fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) && - fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE)) || - fmt->nBlockAlign != fmt->wBitsPerSample / 8 * fmt->nChannels || - ext->Samples.wValidBitsPerSample > fmt->wBitsPerSample || - fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) { - hr = E_INVALIDARG; - break; - } - - if (exclusive) { - UINT32 mask = 0, i, channels = 0; - - if (!(ext->dwChannelMask & (SPEAKER_ALL | SPEAKER_RESERVED))) { - for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) { - if (i & ext->dwChannelMask) { - mask |= i; - channels++; - } - } - - if (channels != fmt->nChannels || (ext->dwChannelMask & ~mask)) { - hr = AUDCLNT_E_UNSUPPORTED_FORMAT; - break; - } - } else { - hr = AUDCLNT_E_UNSUPPORTED_FORMAT; - break; - } - } - - if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { - if (fmt->wBitsPerSample != 32) { - hr = E_INVALIDARG; - break; - } + if (fmt) + dump_fmt(fmt);
- if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample) { - hr = S_FALSE; - ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample; - } - } else if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) { - if (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8) { - hr = E_INVALIDARG; - break; - } + params.device = This->device_name; + params.flow = This->dataflow; + params.share = mode; + params.fmt_in = fmt; + params.fmt_out = NULL;
- if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample && - !(fmt->wBitsPerSample == 32 && - ext->Samples.wValidBitsPerSample == 24)) { - hr = S_FALSE; - ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample; - break; - } - } else { - hr = AUDCLNT_E_UNSUPPORTED_FORMAT; - break; - } - - break; + if (out) { + *out = NULL; + if (mode == AUDCLNT_SHAREMODE_SHARED) + params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out)); }
- case WAVE_FORMAT_ALAW: - case WAVE_FORMAT_MULAW: - if (fmt->wBitsPerSample != 8) { - hr = E_INVALIDARG; - break; - } - /* Fall-through */ - case WAVE_FORMAT_IEEE_FLOAT: - if (fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->wBitsPerSample != 32) { - hr = E_INVALIDARG; - break; - } - /* Fall-through */ - case WAVE_FORMAT_PCM: - if (fmt->wFormatTag == WAVE_FORMAT_PCM && - (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8)) { - hr = E_INVALIDARG; - break; - } - - if (fmt->nChannels > 2) { - hr = AUDCLNT_E_UNSUPPORTED_FORMAT; - break; - } - /* - * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be - * ignored, invalid values are happily accepted. - */ - break; - default: - hr = AUDCLNT_E_UNSUPPORTED_FORMAT; - break; - } + pulse_call(is_format_supported, ¶ms);
- if (exclusive && hr != S_OK) { - hr = AUDCLNT_E_UNSUPPORTED_FORMAT; - CoTaskMemFree(closest); - } else if (hr != S_FALSE) - CoTaskMemFree(closest); + if (params.result == S_FALSE) + *out = ¶ms.fmt_out->Format; else - *out = closest; + CoTaskMemFree(params.fmt_out);
- /* Winepulse does not currently support exclusive mode, if you know of an - * application that uses it, I will correct this.. - */ - if (hr == S_OK && exclusive) - return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED; - - TRACE("returning: %08lx %p\n", hr, out ? *out : NULL); - return hr; + return params.result; }
static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface, diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index a3354c5efe2..5ba66a8aa1d 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -2077,6 +2077,149 @@ static NTSTATUS pulse_release_capture_buffer(void *args) return STATUS_SUCCESS; }
+static NTSTATUS pulse_is_format_supported(void *args) +{ + struct is_format_supported_params *params = args; + WAVEFORMATEXTENSIBLE in; + WAVEFORMATEXTENSIBLE *out; + const WAVEFORMATEX *fmt = &in.Format; + const BOOLEAN exclusive = params->share == AUDCLNT_SHAREMODE_EXCLUSIVE; + + 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 { + memcpy(&in, params->fmt_in, params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? + sizeof(in) : sizeof(in.Format)); + + if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + if (fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) + params->result = E_INVALIDARG; + else if (fmt->nAvgBytesPerSec == 0 || fmt->nBlockAlign == 0 || + (in.Samples.wValidBitsPerSample > fmt->wBitsPerSample)) + params->result = E_INVALIDARG; + else if (fmt->nChannels == 0) + params->result = AUDCLNT_E_UNSUPPORTED_FORMAT; + } + } + + if (FAILED(params->result)) + return STATUS_SUCCESS; + + if (exclusive) + out = ∈ + else { + out = params->fmt_out; + memcpy(out, fmt, fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? + sizeof(*out) : sizeof((*out).Format)); + } + + switch (fmt->wFormatTag) { + case WAVE_FORMAT_EXTENSIBLE: { + if ((fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) && + fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE)) || + fmt->nBlockAlign != fmt->wBitsPerSample / 8 * fmt->nChannels || + in.Samples.wValidBitsPerSample > fmt->wBitsPerSample || + fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) { + params->result = E_INVALIDARG; + break; + } + + if (exclusive) { + UINT32 mask = 0, i, channels = 0; + + if (!(in.dwChannelMask & (SPEAKER_ALL | SPEAKER_RESERVED))) { + for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) { + if (i & in.dwChannelMask) { + mask |= i; + ++channels; + } + } + + if (channels != fmt->nChannels || (in.dwChannelMask & ~mask)) { + params->result = AUDCLNT_E_UNSUPPORTED_FORMAT; + break; + } + } else { + params->result = AUDCLNT_E_UNSUPPORTED_FORMAT; + break; + } + } + + if (IsEqualGUID(&in.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { + if (fmt->wBitsPerSample != 32) { + params->result = E_INVALIDARG; + break; + } + + if (in.Samples.wValidBitsPerSample != fmt->wBitsPerSample) { + params->result = S_FALSE; + out->Samples.wValidBitsPerSample = fmt->wBitsPerSample; + } + } else if (IsEqualGUID(&in.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) { + if (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8) { + params->result = E_INVALIDARG; + break; + } + + if (in.Samples.wValidBitsPerSample != fmt->wBitsPerSample && + !(fmt->wBitsPerSample == 32 && + in.Samples.wValidBitsPerSample == 24)) { + params->result = S_FALSE; + out->Samples.wValidBitsPerSample = fmt->wBitsPerSample; + break; + } + } else { + params->result = AUDCLNT_E_UNSUPPORTED_FORMAT; + break; + } + + break; + } + case WAVE_FORMAT_ALAW: + case WAVE_FORMAT_MULAW: + if (fmt->wBitsPerSample != 8) { + params->result = E_INVALIDARG; + break; + } + /* Fall-through */ + case WAVE_FORMAT_IEEE_FLOAT: + if (fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->wBitsPerSample != 32) { + params->result = E_INVALIDARG; + break; + } + /* Fall-through */ + case WAVE_FORMAT_PCM: { + if (fmt->wFormatTag == WAVE_FORMAT_PCM && + (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8)) { + params->result = E_INVALIDARG; + break; + } + + if (fmt->nChannels > 2) { + params->result = AUDCLNT_E_UNSUPPORTED_FORMAT; + break; + } + + /* fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be + * ignored, invalid values are happily accepted. */ + break; + } + default: + params->result = AUDCLNT_E_UNSUPPORTED_FORMAT; + break; + } + + /* This driver does not support exclusive mode. */ + if (exclusive && params->result == S_OK) + params->result = params->flow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED; + + return STATUS_SUCCESS; +} + static NTSTATUS pulse_get_mix_format(void *args) { struct get_mix_format_params *params = args; @@ -2395,7 +2538,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = pulse_release_render_buffer, pulse_get_capture_buffer, pulse_release_capture_buffer, - pulse_not_implemented, + pulse_is_format_supported, pulse_get_mix_format, pulse_get_device_period, pulse_get_buffer_size, @@ -2561,6 +2704,30 @@ static NTSTATUS pulse_wow64_get_capture_buffer(void *args) return STATUS_SUCCESS; };
+static NTSTATUS pulse_wow64_is_format_supported(void *args) +{ + struct + { + PTR32 device; + EDataFlow flow; + AUDCLNT_SHAREMODE share; + PTR32 fmt_in; + PTR32 fmt_out; + HRESULT result; + } *params32 = args; + struct is_format_supported_params params = + { + .device = ULongToPtr(params32->device), + .flow = params32->flow, + .share = params32->share, + .fmt_in = ULongToPtr(params32->fmt_in), + .fmt_out = ULongToPtr(params32->fmt_out) + }; + pulse_is_format_supported(¶ms); + params32->result = params.result; + return STATUS_SUCCESS; +} + static NTSTATUS pulse_wow64_get_mix_format(void *args) { struct @@ -2840,7 +3007,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = pulse_release_render_buffer, pulse_wow64_get_capture_buffer, pulse_release_capture_buffer, - pulse_not_implemented, + pulse_wow64_is_format_supported, pulse_wow64_get_mix_format, pulse_wow64_get_device_period, pulse_wow64_get_buffer_size,
On Tue May 23 08:14:03 2023 +0000, Davide Beatrici wrote:
changed this line in [version 3 of the diff](/wine/wine/-/merge_requests/2869/diffs?diff_id=48187&start_sha=c514b40634b895bbfb114dbec37f97ed12be4159#152f5af0ef9650fb7529b3638869ab8c7c4ff082_2190_2157)
Is the alignment for the conditions in the `if` statements okay?
This merge request was approved by Huw Davies.