Signed-off-by: Huw Davies huw@codeweavers.com --- dlls/wineoss.drv/mmdevdrv.c | 51 ++++----- dlls/wineoss.drv/oss.c | 202 ++++++++++++++++++++++++++++++++++++ dlls/wineoss.drv/unixlib.h | 11 ++ 3 files changed, 233 insertions(+), 31 deletions(-)
diff --git a/dlls/wineoss.drv/mmdevdrv.c b/dlls/wineoss.drv/mmdevdrv.c index 4f8460a3b3b..242d2e0b359 100644 --- a/dlls/wineoss.drv/mmdevdrv.c +++ b/dlls/wineoss.drv/mmdevdrv.c @@ -1094,45 +1094,34 @@ static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface, }
static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface, - AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *pwfx, - WAVEFORMATEX **outpwfx) + AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt, + WAVEFORMATEX **out) { ACImpl *This = impl_from_IAudioClient3(iface); - int fd = -1; - HRESULT ret; + struct is_format_supported_params params;
- TRACE("(%p)->(%x, %p, %p)\n", This, mode, pwfx, outpwfx); + TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out); + if(fmt) dump_fmt(fmt);
- if(!pwfx || (mode == AUDCLNT_SHAREMODE_SHARED && !outpwfx)) - return E_POINTER; - - if(mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE) - return E_INVALIDARG; + params.device = This->devnode; + params.flow = This->dataflow; + params.share = mode; + params.fmt_in = fmt; + params.fmt_out = NULL;
- if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE && - pwfx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) - return E_INVALIDARG; - - dump_fmt(pwfx); - - if(outpwfx){ - *outpwfx = NULL; - if(mode != AUDCLNT_SHAREMODE_SHARED) - outpwfx = NULL; + if(out){ + *out = NULL; + if(mode == AUDCLNT_SHAREMODE_SHARED) + params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out)); } + OSS_CALL(is_format_supported, ¶ms);
- fd = open_device(This->devnode, This->dataflow); - if(fd < 0){ - WARN("Unable to open device %s: %d (%s)\n", This->devnode, errno, - strerror(errno)); - return AUDCLNT_E_DEVICE_INVALIDATED; - } - - ret = setup_oss_device(mode, fd, pwfx, outpwfx); - - close(fd); + if(params.result == S_FALSE) + *out = ¶ms.fmt_out->Format; + else + CoTaskMemFree(params.fmt_out);
- return ret; + return params.result; }
static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface, diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c index ab7751ac174..49173ec6b6b 100644 --- a/dlls/wineoss.drv/oss.c +++ b/dlls/wineoss.drv/oss.c @@ -35,6 +35,7 @@ #include "ntstatus.h" #define WIN32_NO_STATUS #include "winternl.h" +#include "initguid.h" #include "audioclient.h"
#include "wine/debug.h" @@ -294,8 +295,209 @@ static NTSTATUS get_endpoint_ids(void *args) return STATUS_SUCCESS; }
+static UINT 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 int get_oss_format(const WAVEFORMATEX *fmt) +{ + WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt; + + if(fmt->wFormatTag == WAVE_FORMAT_PCM || + (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE && + IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){ + switch(fmt->wBitsPerSample){ + case 8: + return AFMT_U8; + case 16: + return AFMT_S16_LE; + case 24: + return AFMT_S24_LE; + case 32: + return AFMT_S32_LE; + } + return -1; + } + +#ifdef AFMT_FLOAT + if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT || + (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE && + IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){ + if(fmt->wBitsPerSample != 32) + return -1; + + return AFMT_FLOAT; + } +#endif + + return -1; +} + +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 setup_oss_device(AUDCLNT_SHAREMODE share, int fd, + const WAVEFORMATEX *fmt, WAVEFORMATEXTENSIBLE *out) +{ + const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt; + int tmp, oss_format; + double tenth; + HRESULT ret = S_OK; + WAVEFORMATEXTENSIBLE *closest; + + tmp = oss_format = get_oss_format(fmt); + if(oss_format < 0) + return AUDCLNT_E_UNSUPPORTED_FORMAT; + if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){ + WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno)); + return E_FAIL; + } + if(tmp != oss_format){ + TRACE("Format unsupported by this OSS version: %x\n", oss_format); + return AUDCLNT_E_UNSUPPORTED_FORMAT; + } + + if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE && + (fmtex->Format.nAvgBytesPerSec == 0 || + fmtex->Format.nBlockAlign == 0 || + fmtex->Samples.wValidBitsPerSample > fmtex->Format.wBitsPerSample)) + return E_INVALIDARG; + + if(fmt->nChannels == 0) + return AUDCLNT_E_UNSUPPORTED_FORMAT; + + closest = clone_format(fmt); + if(!closest) + return E_OUTOFMEMORY; + + tmp = fmt->nSamplesPerSec; + if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){ + WARN("SPEED failed: %d (%s)\n", errno, strerror(errno)); + free(closest); + return E_FAIL; + } + tenth = fmt->nSamplesPerSec * 0.1; + if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){ + ret = S_FALSE; + closest->Format.nSamplesPerSec = tmp; + } + + tmp = fmt->nChannels; + if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){ + WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno)); + free(closest); + return E_FAIL; + } + if(tmp != fmt->nChannels){ + ret = S_FALSE; + closest->Format.nChannels = tmp; + } + + if(closest->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) + closest->dwChannelMask = get_channel_mask(closest->Format.nChannels); + + if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 || + fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec || + (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE && + fmtex->Samples.wValidBitsPerSample < fmtex->Format.wBitsPerSample)) + ret = S_FALSE; + + if(share == AUDCLNT_SHAREMODE_EXCLUSIVE && + fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){ + if(fmtex->dwChannelMask == 0 || fmtex->dwChannelMask & SPEAKER_RESERVED) + ret = S_FALSE; + } + + if(ret == S_FALSE && !out) + ret = AUDCLNT_E_UNSUPPORTED_FORMAT; + + if(ret == S_FALSE && 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(out, closest, closest->Format.cbSize + sizeof(WAVEFORMATEX)); + } + free(closest); + + TRACE("returning: %08x\n", ret); + return ret; +} + +static NTSTATUS is_format_supported(void *args) +{ + struct is_format_supported_params *params = args; + int fd; + + 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 && + params->fmt_in->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) + params->result = E_INVALIDARG; + if(FAILED(params->result)) + return STATUS_SUCCESS; + + fd = open_device(params->device, params->flow); + if(fd < 0){ + WARN("Unable to open device %s: %d (%s)\n", params->device, errno, strerror(errno)); + params->result = AUDCLNT_E_DEVICE_INVALIDATED; + return STATUS_SUCCESS; + } + params->result = setup_oss_device(params->share, fd, params->fmt_in, params->fmt_out); + close(fd); + + return STATUS_SUCCESS; +} + unixlib_entry_t __wine_unix_call_funcs[] = { test_connect, get_endpoint_ids, + is_format_supported, }; diff --git a/dlls/wineoss.drv/unixlib.h b/dlls/wineoss.drv/unixlib.h index f498609898a..b89e2142d93 100644 --- a/dlls/wineoss.drv/unixlib.h +++ b/dlls/wineoss.drv/unixlib.h @@ -67,10 +67,21 @@ struct get_endpoint_ids_params unsigned int default_idx; };
+struct is_format_supported_params +{ + const char *device; + EDataFlow flow; + AUDCLNT_SHAREMODE share; + const WAVEFORMATEX *fmt_in; + WAVEFORMATEXTENSIBLE *fmt_out; + HRESULT result; +}; + enum oss_funcs { oss_test_connect, oss_get_endpoint_ids, + oss_is_format_supported, };
extern unixlib_handle_t oss_handle;