Signed-off-by: Huw Davies huw@codeweavers.com --- dlls/wineoss.drv/mmdevdrv.c | 300 ++++-------------------------------- dlls/wineoss.drv/oss.c | 139 +++++++++++++++++ dlls/wineoss.drv/unixlib.h | 21 +++ 3 files changed, 191 insertions(+), 269 deletions(-)
diff --git a/dlls/wineoss.drv/mmdevdrv.c b/dlls/wineoss.drv/mmdevdrv.c index 56b188424fb..2ea3c80f3ca 100644 --- a/dlls/wineoss.drv/mmdevdrv.c +++ b/dlls/wineoss.drv/mmdevdrv.c @@ -260,6 +260,16 @@ int WINAPI AUDDRV_GetPriority(void) return params.priority; }
+static HRESULT stream_release(struct oss_stream *stream) +{ + struct release_stream_params params; + + params.stream = stream; + OSS_CALL(release_stream, ¶ms); + + return params.result; +} + static void oss_lock(struct oss_stream *stream) { pthread_mutex_lock(&stream->lock); @@ -342,13 +352,6 @@ static void get_device_guid(EDataFlow flow, const char *device, GUID *guid) RegCloseKey(key); }
-static int open_device(const char *device, EDataFlow flow) -{ - int flags = ((flow == eRender) ? O_WRONLY : O_RDONLY) | O_NONBLOCK; - - return open(device, flags, 0); -} - static void set_stream_volumes(ACImpl *This) { struct oss_stream *stream = This->stream; @@ -524,7 +527,6 @@ static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface) static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface) { ACImpl *This = impl_from_IAudioClient3(iface); - struct oss_stream *stream = This->stream; ULONG ref;
ref = InterlockedDecrement(&This->ref); @@ -550,21 +552,8 @@ static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface) LeaveCriticalSection(&g_sessions_lock); } HeapFree(GetProcessHeap(), 0, This->vols); - if(stream){ - SIZE_T size; - close(stream->fd); - if(stream->local_buffer){ - size = 0; - NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, &size, MEM_RELEASE); - } - if(stream->tmp_buffer){ - size = 0; - NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, &size, MEM_RELEASE); - } - CoTaskMemFree(stream->fmt); - pthread_mutex_destroy(&stream->lock); - HeapFree(GetProcessHeap(), 0, stream); - } + if(This->stream) + stream_release(This->stream); HeapFree(GetProcessHeap(), 0, This); } return ref; @@ -604,177 +593,6 @@ static void dump_fmt(const WAVEFORMATEX *fmt) } }
-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 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 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 HRESULT setup_oss_device(AUDCLNT_SHAREMODE mode, int fd, - const WAVEFORMATEX *fmt, WAVEFORMATEX **out) -{ - int tmp, oss_format; - double tenth; - HRESULT ret = S_OK; - WAVEFORMATEX *closest = NULL; - - 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 && - (fmt->nAvgBytesPerSec == 0 || - fmt->nBlockAlign == 0 || - ((WAVEFORMATEXTENSIBLE*)fmt)->Samples.wValidBitsPerSample > fmt->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)); - CoTaskMemFree(closest); - return E_FAIL; - } - tenth = fmt->nSamplesPerSec * 0.1; - if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){ - ret = S_FALSE; - closest->nSamplesPerSec = tmp; - } - - tmp = fmt->nChannels; - if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){ - WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno)); - CoTaskMemFree(closest); - return E_FAIL; - } - if(tmp != fmt->nChannels){ - ret = S_FALSE; - closest->nChannels = tmp; - } - - 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)) - ret = S_FALSE; - - if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE && - fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){ - if(((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask == 0 || - ((WAVEFORMATEXTENSIBLE*)fmt)->dwChannelMask & SPEAKER_RESERVED) - ret = S_FALSE; - } - - if(ret == S_FALSE && !out) - ret = AUDCLNT_E_UNSUPPORTED_FORMAT; - - if(ret == 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); - - TRACE("returning: %08x\n", ret); - return ret; -} - static void session_init_vols(AudioSession *session, UINT channels) { if(session->channel_count < channels){ @@ -860,11 +678,9 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface, const GUID *sessionguid) { ACImpl *This = impl_from_IAudioClient3(iface); + struct create_stream_params params; struct oss_stream *stream; - oss_audioinfo ai; - SIZE_T size; - int i; - HRESULT hr; + unsigned int i;
TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags, wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid)); @@ -920,92 +736,38 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface, return AUDCLNT_E_ALREADY_INITIALIZED; }
- stream = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This->stream)); - if(!stream){ - LeaveCriticalSection(&g_sessions_lock); - return E_OUTOFMEMORY; - } - stream->flow = This->dataflow; - pthread_mutex_init(&stream->lock, NULL); - - stream->fd = open_device(This->devnode, This->dataflow); - if(stream->fd < 0){ - WARN("Unable to open device %s: %d (%s)\n", This->devnode, errno, strerror(errno)); - hr = AUDCLNT_E_DEVICE_INVALIDATED; - goto exit; - } - - ai.dev = -1; - if(ioctl(stream->fd, SNDCTL_ENGINEINFO, &ai) < 0){ - WARN("Unable to get audio info for device %s: %d (%s)\n", This->devnode, errno, strerror(errno)); - hr = E_FAIL; - goto exit; - } - - TRACE("OSS audioinfo:\n"); - TRACE("devnode: %s\n", ai.devnode); - TRACE("name: %s\n", ai.name); - TRACE("busy: %x\n", ai.busy); - TRACE("caps: %x\n", ai.caps); - TRACE("iformats: %x\n", ai.iformats); - TRACE("oformats: %x\n", ai.oformats); - TRACE("enabled: %d\n", ai.enabled); - TRACE("min_rate: %d\n", ai.min_rate); - TRACE("max_rate: %d\n", ai.max_rate); - TRACE("min_channels: %d\n", ai.min_channels); - TRACE("max_channels: %d\n", ai.max_channels); - - hr = setup_oss_device(mode, stream->fd, fmt, NULL); - if(FAILED(hr)) - goto exit; - - stream->fmt = clone_format(fmt); - if(!stream->fmt){ - hr = E_OUTOFMEMORY; - goto exit; - } - - stream->period_us = period / 10; - stream->period_frames = MulDiv(fmt->nSamplesPerSec, period, 10000000); + params.device = This->devnode; + params.flow = This->dataflow; + params.share = mode; + params.flags = flags; + params.duration = duration; + params.period = period; + params.fmt = fmt; + params.stream = &stream;
- stream->bufsize_frames = MulDiv(duration, fmt->nSamplesPerSec, 10000000); - if(mode == AUDCLNT_SHAREMODE_EXCLUSIVE) - stream->bufsize_frames -= stream->bufsize_frames % stream->period_frames; - size = stream->bufsize_frames * fmt->nBlockAlign; - if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, 0, &size, - MEM_COMMIT, PAGE_READWRITE)){ - hr = E_OUTOFMEMORY; - goto exit; + OSS_CALL(create_stream, ¶ms); + if(FAILED(params.result)){ + LeaveCriticalSection(&g_sessions_lock); + return params.result; }
This->channel_count = fmt->nChannels; This->vols = HeapAlloc(GetProcessHeap(), 0, This->channel_count * sizeof(float)); if(!This->vols){ - hr = E_OUTOFMEMORY; + params.result = E_OUTOFMEMORY; goto exit; } for(i = 0; i < This->channel_count; ++i) This->vols[i] = 1.f;
- stream->share = mode; - stream->flags = flags; - stream->oss_bufsize_bytes = 0; - - hr = get_audio_session(sessionguid, This->parent, This->channel_count, + params.result = get_audio_session(sessionguid, This->parent, This->channel_count, &This->session);
exit: - if(FAILED(hr)){ + if(FAILED(params.result)){ + stream_release(stream); HeapFree(GetProcessHeap(), 0, This->vols); This->vols = NULL; - CoTaskMemFree(stream->fmt); - if(stream->fd >= 0) close(stream->fd); - if(stream->local_buffer){ - size = 0; - NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, &size, MEM_RELEASE); - } - pthread_mutex_destroy(&stream->lock); - HeapFree(GetProcessHeap(), 0, stream); } else { list_add_tail(&This->session->clients, &This->entry); This->stream = stream; @@ -1014,7 +776,7 @@ exit:
LeaveCriticalSection(&g_sessions_lock);
- return hr; + return params.result; }
static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface, diff --git a/dlls/wineoss.drv/oss.c b/dlls/wineoss.drv/oss.c index 389ec73ee10..628d0c5818a 100644 --- a/dlls/wineoss.drv/oss.c +++ b/dlls/wineoss.drv/oss.c @@ -31,6 +31,7 @@ #include <unistd.h> #include <errno.h> #include <sys/soundcard.h> +#include <pthread.h>
#include "ntstatus.h" #define WIN32_NO_STATUS @@ -45,6 +46,30 @@
WINE_DEFAULT_DEBUG_CHANNEL(oss);
+/* copied from kernelbase */ +static int muldiv( int a, int b, int c ) +{ + LONGLONG ret; + + if (!c) return -1; + + /* We want to deal with a positive divisor to simplify the logic. */ + if (c < 0) + { + a = -a; + c = -c; + } + + /* If the result is positive, we "add" to round. else, we subtract to round. */ + if ((a < 0 && b < 0) || (a >= 0 && b >= 0)) + ret = (((LONGLONG)a * b) + (c / 2)) / c; + else + ret = (((LONGLONG)a * b) - (c / 2)) / c; + + if (ret > 2147483647 || ret < -2147483647) return -1; + return ret; +} + static NTSTATUS test_connect(void *args) { struct test_connect_params *params = args; @@ -466,6 +491,118 @@ static HRESULT setup_oss_device(AUDCLNT_SHAREMODE share, int fd, return ret; }
+static NTSTATUS create_stream(void *args) +{ + struct create_stream_params *params = args; + WAVEFORMATEXTENSIBLE *fmtex; + struct oss_stream *stream; + oss_audioinfo ai; + SIZE_T size; + + stream = calloc(1, sizeof(*stream)); + if(!stream){ + params->result = E_OUTOFMEMORY; + return STATUS_SUCCESS; + } + + stream->flow = params->flow; + pthread_mutex_init(&stream->lock, NULL); + + stream->fd = open_device(params->device, params->flow); + if(stream->fd < 0){ + WARN("Unable to open device %s: %d (%s)\n", params->device, errno, strerror(errno)); + params->result = AUDCLNT_E_DEVICE_INVALIDATED; + goto exit; + } + + ai.dev = -1; + if(ioctl(stream->fd, SNDCTL_ENGINEINFO, &ai) < 0){ + WARN("Unable to get audio info for device %s: %d (%s)\n", params->device, errno, strerror(errno)); + params->result = E_FAIL; + goto exit; + } + + TRACE("OSS audioinfo:\n"); + TRACE("devnode: %s\n", ai.devnode); + TRACE("name: %s\n", ai.name); + TRACE("busy: %x\n", ai.busy); + TRACE("caps: %x\n", ai.caps); + TRACE("iformats: %x\n", ai.iformats); + TRACE("oformats: %x\n", ai.oformats); + TRACE("enabled: %d\n", ai.enabled); + TRACE("min_rate: %d\n", ai.min_rate); + TRACE("max_rate: %d\n", ai.max_rate); + TRACE("min_channels: %d\n", ai.min_channels); + TRACE("max_channels: %d\n", ai.max_channels); + + params->result = setup_oss_device(params->share, stream->fd, params->fmt, NULL); + if(FAILED(params->result)) + goto exit; + + fmtex = clone_format(params->fmt); + if(!fmtex){ + params->result = E_OUTOFMEMORY; + goto exit; + } + stream->fmt = &fmtex->Format; + + stream->period_us = params->period / 10; + stream->period_frames = muldiv(params->fmt->nSamplesPerSec, params->period, 10000000); + + stream->bufsize_frames = muldiv(params->duration, params->fmt->nSamplesPerSec, 10000000); + if(params->share == AUDCLNT_SHAREMODE_EXCLUSIVE) + stream->bufsize_frames -= stream->bufsize_frames % stream->period_frames; + size = stream->bufsize_frames * params->fmt->nBlockAlign; + if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, 0, &size, + MEM_COMMIT, PAGE_READWRITE)){ + params->result = E_OUTOFMEMORY; + goto exit; + } + + stream->share = params->share; + stream->flags = params->flags; + stream->oss_bufsize_bytes = 0; + +exit: + if(FAILED(params->result)){ + if(stream->fd >= 0) close(stream->fd); + if(stream->local_buffer){ + size = 0; + NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, &size, MEM_RELEASE); + } + pthread_mutex_destroy(&stream->lock); + free(stream->fmt); + free(stream); + }else{ + *params->stream = stream; + } + + return STATUS_SUCCESS; +} + +static NTSTATUS release_stream(void *args) +{ + struct release_stream_params *params = args; + struct oss_stream *stream = params->stream; + SIZE_T size; + + close(stream->fd); + if(stream->local_buffer){ + size = 0; + NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, &size, MEM_RELEASE); + } + if(stream->tmp_buffer){ + size = 0; + NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, &size, MEM_RELEASE); + } + free(stream->fmt); + pthread_mutex_destroy(&stream->lock); + free(stream); + + params->result = S_OK; + return STATUS_SUCCESS; +} + static NTSTATUS is_format_supported(void *args) { struct is_format_supported_params *params = args; @@ -599,6 +736,8 @@ unixlib_entry_t __wine_unix_call_funcs[] = { test_connect, get_endpoint_ids, + create_stream, + release_stream, is_format_supported, get_mix_format, }; diff --git a/dlls/wineoss.drv/unixlib.h b/dlls/wineoss.drv/unixlib.h index 8f82518b10e..c0a0b25c374 100644 --- a/dlls/wineoss.drv/unixlib.h +++ b/dlls/wineoss.drv/unixlib.h @@ -69,6 +69,25 @@ struct get_endpoint_ids_params unsigned int default_idx; };
+struct create_stream_params +{ + const char *device; + EDataFlow flow; + AUDCLNT_SHAREMODE share; + UINT flags; + REFERENCE_TIME duration; + REFERENCE_TIME period; + const WAVEFORMATEX *fmt; + HRESULT result; + struct oss_stream **stream; +}; + +struct release_stream_params +{ + struct oss_stream *stream; + HRESULT result; +}; + struct is_format_supported_params { const char *device; @@ -91,6 +110,8 @@ enum oss_funcs { oss_test_connect, oss_get_endpoint_ids, + oss_create_stream, + oss_release_stream, oss_is_format_supported, oss_get_mix_format, };