From: Davide Beatrici git@davidebeatrici.dev
--- dlls/mmdevapi/Makefile.in | 2 +- dlls/mmdevapi/audiosessionmanager.c | 11 + dlls/mmdevapi/client.c | 788 ++++++++++++++++++++++++ dlls/mmdevapi/main.c | 2 + dlls/mmdevapi/mmdevapi_private.h | 2 + dlls/mmdevapi/session.c | 28 + dlls/winealsa.drv/Makefile.in | 2 +- dlls/winealsa.drv/mmdevdrv.c | 4 +- dlls/winecoreaudio.drv/Makefile.in | 2 +- dlls/winecoreaudio.drv/mmdevdrv.c | 4 +- dlls/wineoss.drv/Makefile.in | 2 +- dlls/wineoss.drv/mmdevdrv.c | 4 +- dlls/winepulse.drv/mmdevdrv.c | 887 +--------------------------- dlls/winepulse.drv/pulse.c | 165 +++++- 14 files changed, 1005 insertions(+), 898 deletions(-)
diff --git a/dlls/mmdevapi/Makefile.in b/dlls/mmdevapi/Makefile.in index ee2848ff2de..027854ea97b 100644 --- a/dlls/mmdevapi/Makefile.in +++ b/dlls/mmdevapi/Makefile.in @@ -1,5 +1,5 @@ MODULE = mmdevapi.dll -IMPORTS = uuid ole32 oleaut32 user32 advapi32 +IMPORTS = uuid ole32 oleaut32 user32 advapi32 version
C_SRCS = \ audiosessionmanager.c \ diff --git a/dlls/mmdevapi/audiosessionmanager.c b/dlls/mmdevapi/audiosessionmanager.c index 71799590e5e..6d177aa465c 100644 --- a/dlls/mmdevapi/audiosessionmanager.c +++ b/dlls/mmdevapi/audiosessionmanager.c @@ -47,6 +47,17 @@ void sessions_unlock(void) LeaveCriticalSection(&g_sessions_lock); }
+HRESULT get_audio_session(const GUID *sessionguid, IMMDevice *device, UINT channels, + AudioSession **out) +{ + (void)sessionguid; + (void)device; + (void)channels; + (void)out; + + return E_NOTIMPL; +} + static inline struct session_mgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface) { return CONTAINING_RECORD(iface, struct session_mgr, IAudioSessionManager2_iface); diff --git a/dlls/mmdevapi/client.c b/dlls/mmdevapi/client.c index ea279da34a1..6d4fe510fd0 100644 --- a/dlls/mmdevapi/client.c +++ b/dlls/mmdevapi/client.c @@ -21,6 +21,8 @@
#define COBJMACROS
+#include <wchar.h> + #include <audiopolicy.h> #include <mmdeviceapi.h> #include <winternl.h> @@ -33,9 +35,29 @@
WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
+typedef struct tagLANGANDCODEPAGE +{ + WORD wLanguage; + WORD wCodePage; +} LANGANDCODEPAGE; + extern void sessions_lock(void) DECLSPEC_HIDDEN; extern void sessions_unlock(void) DECLSPEC_HIDDEN;
+extern HRESULT get_audio_session(const GUID *sessionguid, IMMDevice *device, UINT channels, + AudioSession **out) DECLSPEC_HIDDEN; +extern struct audio_session_wrapper *session_wrapper_create(struct audio_client *client) DECLSPEC_HIDDEN; + +static HANDLE g_main_loop_thread; + +void stop_main_loop(void) +{ + if (g_main_loop_thread) { + WaitForSingleObject(g_main_loop_thread, INFINITE); + CloseHandle(g_main_loop_thread); + } +} + void set_stream_volumes(struct audio_client *This) { struct set_volumes_params params; @@ -53,6 +75,11 @@ static inline struct audio_client *impl_from_IAudioCaptureClient(IAudioCaptureCl return CONTAINING_RECORD(iface, struct audio_client, IAudioCaptureClient_iface); }
+static inline struct audio_client *impl_from_IAudioClient3(IAudioClient3 *iface) +{ + return CONTAINING_RECORD(iface, struct audio_client, IAudioClient3_iface); +} + static inline struct audio_client *impl_from_IAudioClock(IAudioClock *iface) { return CONTAINING_RECORD(iface, struct audio_client, IAudioClock_iface); @@ -73,6 +100,178 @@ static inline struct audio_client *impl_from_IAudioStreamVolume(IAudioStreamVolu return CONTAINING_RECORD(iface, struct audio_client, IAudioStreamVolume_iface); }
+static void dump_fmt(const WAVEFORMATEX *fmt) +{ + TRACE("wFormatTag: 0x%x (", fmt->wFormatTag); + switch (fmt->wFormatTag) { + case WAVE_FORMAT_PCM: + TRACE("WAVE_FORMAT_PCM"); + break; + case WAVE_FORMAT_IEEE_FLOAT: + TRACE("WAVE_FORMAT_IEEE_FLOAT"); + break; + case WAVE_FORMAT_EXTENSIBLE: + TRACE("WAVE_FORMAT_EXTENSIBLE"); + break; + default: + TRACE("Unknown"); + break; + } + TRACE(")\n"); + + TRACE("nChannels: %u\n", fmt->nChannels); + TRACE("nSamplesPerSec: %lu\n", fmt->nSamplesPerSec); + TRACE("nAvgBytesPerSec: %lu\n", fmt->nAvgBytesPerSec); + TRACE("nBlockAlign: %u\n", fmt->nBlockAlign); + TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample); + TRACE("cbSize: %u\n", fmt->cbSize); + + if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + WAVEFORMATEXTENSIBLE *fmtex = (void *)fmt; + TRACE("dwChannelMask: %08lx\n", fmtex->dwChannelMask); + TRACE("Samples: %04x\n", fmtex->Samples.wReserved); + TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat)); + } +} + +static DWORD CALLBACK main_loop_func(void *event) +{ + struct main_loop_params params; + + SetThreadDescription(GetCurrentThread(), L"audio_client_mainloop"); + + params.event = event; + + WINE_UNIX_CALL(main_loop, ¶ms); + + return 0; +} + +static DWORD CALLBACK timer_loop_func(void *user) +{ + struct timer_loop_params params; + struct audio_client *This = user; + + SetThreadDescription(GetCurrentThread(), L"audio_client_timer"); + + params.stream = This->stream; + + WINE_UNIX_CALL(timer_loop, ¶ms); + + return 0; +} + +static HRESULT stream_release(stream_handle stream, HANDLE timer_thread) +{ + struct release_stream_params params; + + params.stream = stream; + params.timer_thread = timer_thread; + + WINE_UNIX_CALL(release_stream, ¶ms); + + return params.result; +} + +static BOOL query_productname(void *data, LANGANDCODEPAGE *lang, LPVOID *buffer, UINT *len) +{ + WCHAR pn[37]; + swprintf(pn, ARRAY_SIZE(pn), L"\StringFileInfo\%04x%04x\ProductName", lang->wLanguage, lang->wCodePage); + return VerQueryValueW(data, pn, buffer, len) && *len; +} + +static WCHAR *get_application_name(void) +{ + WCHAR path[MAX_PATH], *name; + UINT translate_size, productname_size; + LANGANDCODEPAGE *translate; + LPVOID productname; + BOOL found = FALSE; + void *data = NULL; + unsigned int i; + LCID locale; + DWORD size; + + GetModuleFileNameW(NULL, path, ARRAY_SIZE(path)); + + size = GetFileVersionInfoSizeW(path, NULL); + if (!size) + goto skip; + + data = malloc(size); + if (!data) + goto skip; + + if (!GetFileVersionInfoW(path, 0, size, data)) + goto skip; + + if (!VerQueryValueW(data, L"\VarFileInfo\Translation", (LPVOID *)&translate, &translate_size)) + goto skip; + + /* No translations found. */ + if (translate_size < sizeof(LANGANDCODEPAGE)) + goto skip; + + /* The following code will try to find the best translation. We first search for an + * exact match of the language, then a match of the language PRIMARYLANGID, then we + * search for a LANG_NEUTRAL match, and if that still doesn't work we pick the + * first entry which contains a proper productname. */ + locale = GetThreadLocale(); + + for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); ++i) { + if (translate[i].wLanguage == locale && + query_productname(data, &translate[i], &productname, &productname_size)) { + found = TRUE; + break; + } + } + + if (!found) { + for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); ++i) { + if (PRIMARYLANGID(translate[i].wLanguage) == PRIMARYLANGID(locale) && + query_productname(data, &translate[i], &productname, &productname_size)) { + found = TRUE; + break; + } + } + } + + if (!found) { + for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); ++i) { + if (PRIMARYLANGID(translate[i].wLanguage) == LANG_NEUTRAL && + query_productname(data, &translate[i], &productname, &productname_size)) { + found = TRUE; + break; + } + } + } + + if (!found) { + for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); ++i) { + if (query_productname(data, &translate[i], &productname, &productname_size)) { + found = TRUE; + break; + } + } + } +skip: + if (found) { + name = wcsdup(productname); + free(data); + return name; + } + + free(data); + + name = wcsrchr(path, '\'); + if (!name) + name = path; + else + ++name; + + return wcsdup(name); +} + static HRESULT WINAPI capture_QueryInterface(IAudioCaptureClient *iface, REFIID riid, void **ppv) { struct audio_client *This = impl_from_IAudioCaptureClient(iface); @@ -341,6 +540,595 @@ const IAudioClock2Vtbl AudioClock2_Vtbl = clock2_GetDevicePosition };
+static HRESULT WINAPI client_QueryInterface(IAudioClient3 *iface, REFIID riid, void **ppv) +{ + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if (!ppv) + return E_POINTER; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IAudioClient) || + IsEqualIID(riid, &IID_IAudioClient2) || + IsEqualIID(riid, &IID_IAudioClient3)) + *ppv = iface; + else if(IsEqualIID(riid, &IID_IMarshal)) { + struct audio_client *This = impl_from_IAudioClient3(iface); + return IUnknown_QueryInterface(This->marshal, riid, ppv); + } else { + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*ppv); + + return S_OK; +} + +static ULONG WINAPI client_AddRef(IAudioClient3 *iface) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + ULONG ref = InterlockedIncrement(&This->ref); + TRACE("(%p) Refcount now %lu\n", This, ref); + return ref; +} + +static ULONG WINAPI client_Release(IAudioClient3 *iface) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + ULONG ref = InterlockedDecrement(&This->ref); + TRACE("(%p) Refcount now %lu\n", This, ref); + + if (!ref) { + IAudioClient3_Stop(iface); + IMMDevice_Release(This->parent); + IUnknown_Release(This->marshal); + + if (This->session) { + sessions_lock(); + list_remove(&This->entry); + sessions_unlock(); + } + + HeapFree(GetProcessHeap(), 0, This->vols); + + if (This->stream) + stream_release(This->stream, This->timer_thread); + + HeapFree(GetProcessHeap(), 0, This); + } + + return ref; +} + +static HRESULT WINAPI client_Initialize(IAudioClient3 *iface, AUDCLNT_SHAREMODE mode, DWORD flags, + REFERENCE_TIME duration, REFERENCE_TIME period, + const WAVEFORMATEX *fmt, const GUID *sessionguid) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + struct create_stream_params params; + unsigned int i, channel_count; + stream_handle stream; + WCHAR *name; + HRESULT hr; + + TRACE("(%p)->(%x, %lx, %s, %s, %p, %s)\n", This, mode, flags, wine_dbgstr_longlong(duration), + wine_dbgstr_longlong(period), fmt, + debugstr_guid(sessionguid)); + + if (!fmt) + return E_POINTER; + + dump_fmt(fmt); + + if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE) + return E_INVALIDARG; + + if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS | + AUDCLNT_STREAMFLAGS_LOOPBACK | + AUDCLNT_STREAMFLAGS_EVENTCALLBACK | + AUDCLNT_STREAMFLAGS_NOPERSIST | + AUDCLNT_STREAMFLAGS_RATEADJUST | + AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED | + AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE | + AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED | + AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY | + AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)) { + FIXME("Unknown flags: %08lx\n", flags); + return E_INVALIDARG; + } + + sessions_lock(); + + if (This->stream) { + sessions_unlock(); + return AUDCLNT_E_ALREADY_INITIALIZED; + } + + if (!g_main_loop_thread) { + HANDLE event = CreateEventW(NULL, TRUE, FALSE, NULL); + if (!(g_main_loop_thread = CreateThread(NULL, 0, main_loop_func, event, 0, NULL))) + { + ERR("Failed to create mainloop thread.\n"); + sessions_unlock(); + CloseHandle(event); + return E_FAIL; + } + + SetThreadPriority(g_main_loop_thread, THREAD_PRIORITY_TIME_CRITICAL); + WaitForSingleObject(event, INFINITE); + CloseHandle(event); + } + + params.name = name = get_application_name(); + params.device = This->device_name; + params.flow = This->dataflow; + params.share = mode; + params.flags = flags; + params.duration = duration; + params.period = period; + params.fmt = fmt; + params.channel_count = &channel_count; + params.stream = &stream; + + WINE_UNIX_CALL(create_stream, ¶ms); + + free(name); + + if (FAILED(hr = params.result)) { + sessions_unlock(); + return hr; + } + + if (!(This->vols = malloc(channel_count * sizeof(*This->vols)))) { + stream_release(stream, NULL); + sessions_unlock(); + return E_OUTOFMEMORY; + } + + for (i = 0; i < channel_count; ++i) + This->vols[i] = 1.f; + + hr = get_audio_session(sessionguid, This->parent, channel_count, &This->session); + if (FAILED(hr)) { + free(This->vols); + This->vols = NULL; + + sessions_unlock(); + + stream_release(stream, NULL); + + return E_OUTOFMEMORY; + } + + This->stream = stream; + This->channel_count = channel_count; + + list_add_tail(&This->session->clients, &This->entry); + + set_stream_volumes(This); + + sessions_unlock(); + + return S_OK; +} + +static HRESULT WINAPI client_GetBufferSize(IAudioClient3 *iface, UINT32 *out) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + struct get_buffer_size_params params; + + TRACE("(%p)->(%p)\n", This, out); + + if (!out) + return E_POINTER; + + if (!This->stream) + return AUDCLNT_E_NOT_INITIALIZED; + + params.stream = This->stream; + params.frames = out; + + WINE_UNIX_CALL(get_buffer_size, ¶ms); + + return params.result; +} + +static HRESULT WINAPI client_GetStreamLatency(IAudioClient3 *iface, REFERENCE_TIME *latency) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + struct get_latency_params params; + + TRACE("(%p)->(%p)\n", This, latency); + + if (!latency) + return E_POINTER; + + if (!This->stream) + return AUDCLNT_E_NOT_INITIALIZED; + + params.stream = This->stream; + params.latency = latency; + + WINE_UNIX_CALL(get_latency, ¶ms); + + return params.result; +} + +static HRESULT WINAPI client_GetCurrentPadding(IAudioClient3 *iface, UINT32 *out) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + struct get_current_padding_params params; + + TRACE("(%p)->(%p)\n", This, out); + + if (!out) + return E_POINTER; + + if (!This->stream) + return AUDCLNT_E_NOT_INITIALIZED; + + params.stream = This->stream; + params.padding = out; + + WINE_UNIX_CALL(get_current_padding, ¶ms); + + return params.result; +} + +static HRESULT WINAPI client_IsFormatSupported(IAudioClient3 *iface, AUDCLNT_SHAREMODE mode, + const WAVEFORMATEX *fmt, WAVEFORMATEX **out) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + struct is_format_supported_params params; + + TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out); + + if (!fmt) + return E_POINTER; + + dump_fmt(fmt); + + switch (mode) { + case AUDCLNT_SHAREMODE_SHARED: + if (!out) + return E_POINTER; + + params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out)); + break; + case AUDCLNT_SHAREMODE_EXCLUSIVE: + params.fmt_out = NULL; + break; + default: + return E_INVALIDARG; + } + + params.device = This->device_name; + params.flow = This->dataflow; + params.share = mode; + params.fmt_in = fmt; + + WINE_UNIX_CALL(is_format_supported, ¶ms); + + if (params.result == S_FALSE) { + *out = ¶ms.fmt_out->Format; + TRACE("Suggesting the following format:\n"); + dump_fmt(*out); + } else { + if (out) + *out = NULL; + + CoTaskMemFree(params.fmt_out); + } + + return params.result; +} + +static HRESULT WINAPI client_GetMixFormat(IAudioClient3 *iface, WAVEFORMATEX **pwfx) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + struct get_mix_format_params params; + + TRACE("(%p)->(%p)\n", This, pwfx); + + if (!pwfx) + return E_POINTER; + + *pwfx = NULL; + + params.device = This->device_name; + params.flow = This->dataflow; + params.fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE)); + if (!params.fmt) + return E_OUTOFMEMORY; + + WINE_UNIX_CALL(get_mix_format, ¶ms); + + if (SUCCEEDED(params.result)) { + *pwfx = ¶ms.fmt->Format; + dump_fmt(*pwfx); + } else + CoTaskMemFree(params.fmt); + + return params.result; +} + +static HRESULT WINAPI client_GetDevicePeriod(IAudioClient3 *iface, REFERENCE_TIME *defperiod, + REFERENCE_TIME *minperiod) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + struct get_device_period_params params; + + TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod); + + if (!defperiod && !minperiod) + return E_POINTER; + + params.device = This->device_name; + params.flow = This->dataflow; + params.def_period = defperiod; + params.min_period = minperiod; + + WINE_UNIX_CALL(get_device_period, ¶ms); + + return S_OK; +} + +static HRESULT WINAPI client_Start(IAudioClient3 *iface) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + struct start_params params; + + TRACE("(%p)\n", This); + + sessions_lock(); + + if (!This->stream) { + sessions_unlock(); + return AUDCLNT_E_NOT_INITIALIZED; + } + + params.stream = This->stream; + + WINE_UNIX_CALL(start, ¶ms); + + if (SUCCEEDED(params.result) && !This->timer_thread) { + This->timer_thread = CreateThread(NULL, 0, timer_loop_func, This, 0, NULL); + SetThreadPriority(This->timer_thread, THREAD_PRIORITY_TIME_CRITICAL); + } + + sessions_unlock(); + + return params.result; +} + +static HRESULT WINAPI client_Stop(IAudioClient3 *iface) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + struct stop_params params; + + TRACE("(%p)\n", This); + + if (!This->stream) + return AUDCLNT_E_NOT_INITIALIZED; + + params.stream = This->stream; + + WINE_UNIX_CALL(stop, ¶ms); + + return params.result; +} + +static HRESULT WINAPI client_Reset(IAudioClient3 *iface) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + struct reset_params params; + + TRACE("(%p)\n", This); + + if (!This->stream) + return AUDCLNT_E_NOT_INITIALIZED; + + params.stream = This->stream; + + WINE_UNIX_CALL(reset, ¶ms); + + return params.result; +} + +static HRESULT WINAPI client_SetEventHandle(IAudioClient3 *iface, HANDLE event) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + struct set_event_handle_params params; + + TRACE("(%p)->(%p)\n", This, event); + + if (!event) + return E_INVALIDARG; + + if (!This->stream) + return AUDCLNT_E_NOT_INITIALIZED; + + params.stream = This->stream; + params.event = event; + + WINE_UNIX_CALL(set_event_handle, ¶ms); + + return params.result; +} + +static HRESULT WINAPI client_GetService(IAudioClient3 *iface, REFIID riid, void **ppv) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv); + + if (!ppv) + return E_POINTER; + + *ppv = NULL; + + sessions_lock(); + + if (!This->stream) { + sessions_unlock(); + return AUDCLNT_E_NOT_INITIALIZED; + } + + if (IsEqualIID(riid, &IID_IAudioRenderClient)) { + if (This->dataflow != eRender) { + sessions_unlock(); + return AUDCLNT_E_WRONG_ENDPOINT_TYPE; + } + *ppv = &This->IAudioRenderClient_iface; + } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) { + if (This->dataflow != eCapture) { + sessions_unlock(); + return AUDCLNT_E_WRONG_ENDPOINT_TYPE; + } + *ppv = &This->IAudioCaptureClient_iface; + } else if (IsEqualIID(riid, &IID_IAudioClock)) { + *ppv = &This->IAudioClock_iface; + } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) { + *ppv = &This->IAudioStreamVolume_iface; + } else if (IsEqualIID(riid, &IID_IAudioSessionControl) || + IsEqualIID(riid, &IID_IChannelAudioVolume) || + IsEqualIID(riid, &IID_ISimpleAudioVolume)) { + if (!This->session_wrapper) { + This->session_wrapper = session_wrapper_create(This); + if (!This->session_wrapper) { + sessions_unlock(); + return E_OUTOFMEMORY; + } + } + if (IsEqualIID(riid, &IID_IAudioSessionControl)) + *ppv = &This->session_wrapper->IAudioSessionControl2_iface; + else if (IsEqualIID(riid, &IID_IChannelAudioVolume)) + *ppv = &This->session_wrapper->IChannelAudioVolume_iface; + else if (IsEqualIID(riid, &IID_ISimpleAudioVolume)) + *ppv = &This->session_wrapper->ISimpleAudioVolume_iface; + } + + if (*ppv) { + IUnknown_AddRef((IUnknown*)*ppv); + sessions_unlock(); + return S_OK; + } + + sessions_unlock(); + + return E_NOINTERFACE; +} + +static HRESULT WINAPI client_IsOffloadCapable(IAudioClient3 *iface, AUDIO_STREAM_CATEGORY category, + BOOL *offload_capable) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable); + + if (!offload_capable) + return E_INVALIDARG; + + *offload_capable = FALSE; + + return S_OK; +} + +static HRESULT WINAPI client_SetClientProperties(IAudioClient3 *iface, + const AudioClientProperties *prop) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop; + + TRACE("(%p)->(%p)\n", This, prop); + + if (!legacy_prop) + return E_POINTER; + + if (legacy_prop->cbSize == sizeof(AudioClientProperties)) { + TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n", legacy_prop->bIsOffload, + legacy_prop->eCategory, + prop->Options); + } else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)) { + TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n", legacy_prop->bIsOffload, + legacy_prop->eCategory); + } else { + WARN("Unsupported Size = %d\n", legacy_prop->cbSize); + return E_INVALIDARG; + } + + if (legacy_prop->bIsOffload) + return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE; + + return S_OK; +} + +static HRESULT WINAPI client_GetBufferSizeLimits(IAudioClient3 *iface, const WAVEFORMATEX *format, + BOOL event_driven, REFERENCE_TIME *min_duration, + REFERENCE_TIME *max_duration) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + FIXME("(%p)->(%p, %u, %p, %p) - stub\n", This, format, event_driven, min_duration, max_duration); + return E_NOTIMPL; +} + +static HRESULT WINAPI client_GetSharedModeEnginePeriod(IAudioClient3 *iface, + const WAVEFORMATEX *format, + UINT32 *default_period_frames, + UINT32 *unit_period_frames, + UINT32 *min_period_frames, + UINT32 *max_period_frames) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + FIXME("(%p)->(%p, %p, %p, %p, %p) - stub\n", This, format, default_period_frames, + unit_period_frames, min_period_frames, + max_period_frames); + return E_NOTIMPL; +} + +static HRESULT WINAPI client_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface, + WAVEFORMATEX **cur_format, + UINT32 *cur_period_frames) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + FIXME("(%p)->(%p, %p) - stub\n", This, cur_format, cur_period_frames); + return E_NOTIMPL; +} + +static HRESULT WINAPI client_InitializeSharedAudioStream(IAudioClient3 *iface, DWORD flags, + UINT32 period_frames, + const WAVEFORMATEX *format, + const GUID *session_guid) +{ + struct audio_client *This = impl_from_IAudioClient3(iface); + FIXME("(%p)->(0x%lx, %u, %p, %s) - stub\n", This, flags, period_frames, format, debugstr_guid(session_guid)); + return E_NOTIMPL; +} + +const IAudioClient3Vtbl AudioClient3_Vtbl = +{ + client_QueryInterface, + client_AddRef, + client_Release, + client_Initialize, + client_GetBufferSize, + client_GetStreamLatency, + client_GetCurrentPadding, + client_IsFormatSupported, + client_GetMixFormat, + client_GetDevicePeriod, + client_Start, + client_Stop, + client_Reset, + client_SetEventHandle, + client_GetService, + client_IsOffloadCapable, + client_SetClientProperties, + client_GetBufferSizeLimits, + client_GetSharedModeEnginePeriod, + client_GetCurrentSharedModeEnginePeriod, + client_InitializeSharedAudioStream, +}; + static HRESULT WINAPI render_QueryInterface(IAudioRenderClient *iface, REFIID riid, void **ppv) { struct audio_client *This = impl_from_IAudioRenderClient(iface); diff --git a/dlls/mmdevapi/main.c b/dlls/mmdevapi/main.c index d3d4ec6a905..7647cd92bbf 100644 --- a/dlls/mmdevapi/main.c +++ b/dlls/mmdevapi/main.c @@ -196,6 +196,8 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) DisableThreadLibraryCalls(hinstDLL); break; case DLL_PROCESS_DETACH: + stop_main_loop(); + if (drvs.module_unixlib) { const NTSTATUS status = __wine_unix_call(drvs.module_unixlib, process_detach, NULL); if (status) diff --git a/dlls/mmdevapi/mmdevapi_private.h b/dlls/mmdevapi/mmdevapi_private.h index 684d303eefb..4ae7de65fb8 100644 --- a/dlls/mmdevapi/mmdevapi_private.h +++ b/dlls/mmdevapi/mmdevapi_private.h @@ -80,4 +80,6 @@ extern HRESULT SpatialAudioClient_Create(IMMDevice *device, ISpatialAudioClient extern HRESULT load_devices_from_reg(void) DECLSPEC_HIDDEN; extern HRESULT load_driver_devices(EDataFlow flow) DECLSPEC_HIDDEN;
+extern void stop_main_loop(void) DECLSPEC_HIDDEN; + extern const WCHAR drv_keyW[] DECLSPEC_HIDDEN; diff --git a/dlls/mmdevapi/session.c b/dlls/mmdevapi/session.c index 81cea5b3482..fa012392108 100644 --- a/dlls/mmdevapi/session.c +++ b/dlls/mmdevapi/session.c @@ -1,4 +1,9 @@ /* + * Copyright 2011-2012 Maarten Lankhorst + * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers + * Copyright 2011 Andrew Eikum for CodeWeavers + * Copyright 2022 Huw Davies + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -545,3 +550,26 @@ const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl = simplevolume_SetMute, simplevolume_GetMute }; + +struct audio_session_wrapper *session_wrapper_create(struct audio_client *client) +{ + struct audio_session_wrapper *ret; + + ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct audio_session_wrapper)); + if (!ret) + return NULL; + + ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl; + ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl; + ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl; + + ret->ref = !client; + ret->client = client; + + if (client) { + ret->session = client->session; + IAudioClient3_AddRef(&client->IAudioClient3_iface); + } + + return ret; +} diff --git a/dlls/winealsa.drv/Makefile.in b/dlls/winealsa.drv/Makefile.in index 9494858c4dd..d1387520742 100644 --- a/dlls/winealsa.drv/Makefile.in +++ b/dlls/winealsa.drv/Makefile.in @@ -1,6 +1,6 @@ MODULE = winealsa.drv UNIXLIB = winealsa.so -IMPORTS = uuid ole32 advapi32 +IMPORTS = uuid ole32 advapi32 version PARENTSRC = ../mmdevapi DELAYIMPORTS = winmm UNIX_LIBS = $(ALSA_LIBS) $(PTHREAD_LIBS) diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c index e556bf5d0c1..45b8303d289 100644 --- a/dlls/winealsa.drv/mmdevdrv.c +++ b/dlls/winealsa.drv/mmdevdrv.c @@ -542,8 +542,8 @@ static AudioSession *create_session(const GUID *guid, IMMDevice *device,
/* if channels == 0, then this will return or create a session with * matching dataflow and GUID. otherwise, channels must also match */ -static HRESULT get_audio_session(const GUID *sessionguid, - IMMDevice *device, UINT channels, AudioSession **out) +HRESULT get_audio_session(const GUID *sessionguid, IMMDevice *device, + UINT channels, AudioSession **out) { AudioSession *session;
diff --git a/dlls/winecoreaudio.drv/Makefile.in b/dlls/winecoreaudio.drv/Makefile.in index 01c5a3d0073..ecf85cffda4 100644 --- a/dlls/winecoreaudio.drv/Makefile.in +++ b/dlls/winecoreaudio.drv/Makefile.in @@ -1,6 +1,6 @@ MODULE = winecoreaudio.drv UNIXLIB = winecoreaudio.so -IMPORTS = uuid ole32 user32 advapi32 +IMPORTS = uuid ole32 user32 advapi32 version PARENTSRC = ../mmdevapi DELAYIMPORTS = winmm UNIX_LIBS = $(COREAUDIO_LIBS) diff --git a/dlls/winecoreaudio.drv/mmdevdrv.c b/dlls/winecoreaudio.drv/mmdevdrv.c index 756aab93f71..58d001080e3 100644 --- a/dlls/winecoreaudio.drv/mmdevdrv.c +++ b/dlls/winecoreaudio.drv/mmdevdrv.c @@ -514,8 +514,8 @@ static AudioSession *create_session(const GUID *guid, IMMDevice *device,
/* if channels == 0, then this will return or create a session with * matching dataflow and GUID. otherwise, channels must also match */ -static HRESULT get_audio_session(const GUID *sessionguid, - IMMDevice *device, UINT channels, AudioSession **out) +HRESULT get_audio_session(const GUID *sessionguid, IMMDevice *device, + UINT channels, AudioSession **out) { AudioSession *session;
diff --git a/dlls/wineoss.drv/Makefile.in b/dlls/wineoss.drv/Makefile.in index 121595a7c6c..f571397257b 100644 --- a/dlls/wineoss.drv/Makefile.in +++ b/dlls/wineoss.drv/Makefile.in @@ -1,6 +1,6 @@ MODULE = wineoss.drv UNIXLIB = wineoss.so -IMPORTS = uuid ole32 user32 advapi32 +IMPORTS = uuid ole32 user32 advapi32 version PARENTSRC = ../mmdevapi DELAYIMPORTS = winmm UNIX_LIBS = $(OSS4_LIBS) $(PTHREAD_LIBS) diff --git a/dlls/wineoss.drv/mmdevdrv.c b/dlls/wineoss.drv/mmdevdrv.c index dac5790e384..97320875ee1 100644 --- a/dlls/wineoss.drv/mmdevdrv.c +++ b/dlls/wineoss.drv/mmdevdrv.c @@ -514,8 +514,8 @@ static AudioSession *create_session(const GUID *guid, IMMDevice *device,
/* if channels == 0, then this will return or create a session with * matching dataflow and GUID. otherwise, channels must also match */ -static HRESULT get_audio_session(const GUID *sessionguid, - IMMDevice *device, UINT channels, AudioSession **out) +HRESULT get_audio_session(const GUID *sessionguid, IMMDevice *device, + UINT channels, AudioSession **out) { AudioSession *session;
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index 9ec0e3b7f46..e3cb084aea4 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -54,9 +54,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(pulse);
#define MAX_PULSE_NAME_LEN 256
-#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER) - -static HANDLE pulse_thread; static struct list g_sessions = LIST_INIT(g_sessions); static struct list g_devices_cache = LIST_INIT(g_devices_cache);
@@ -115,16 +112,11 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
LIST_FOR_EACH_ENTRY_SAFE(device, device_next, &g_devices_cache, struct device_cache, entry) free(device); - - if (pulse_thread) { - WaitForSingleObject(pulse_thread, INFINITE); - CloseHandle(pulse_thread); - } } return TRUE; }
-static const IAudioClient3Vtbl AudioClient3_Vtbl; +extern const IAudioClient3Vtbl AudioClient3_Vtbl; extern const IAudioRenderClientVtbl AudioRenderClient_Vtbl; extern const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl; extern const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl; @@ -134,13 +126,6 @@ extern const IAudioClockVtbl AudioClock_Vtbl; extern const IAudioClock2Vtbl AudioClock2_Vtbl; extern const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
-static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client); - -static inline ACImpl *impl_from_IAudioClient3(IAudioClient3 *iface) -{ - return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_iface); -} - static void pulse_call(enum unix_funcs code, void *params) { NTSTATUS status; @@ -148,152 +133,6 @@ static void pulse_call(enum unix_funcs code, void *params) assert(!status); }
-static void pulse_release_stream(stream_handle stream, HANDLE timer) -{ - struct release_stream_params params; - params.stream = stream; - params.timer_thread = timer; - pulse_call(release_stream, ¶ms); -} - -static DWORD CALLBACK pulse_mainloop_thread(void *event) -{ - struct main_loop_params params; - params.event = event; - SetThreadDescription(GetCurrentThread(), L"winepulse_mainloop"); - pulse_call(main_loop, ¶ms); - return 0; -} - -typedef struct tagLANGANDCODEPAGE -{ - WORD wLanguage; - WORD wCodePage; -} LANGANDCODEPAGE; - -static BOOL query_productname(void *data, LANGANDCODEPAGE *lang, LPVOID *buffer, UINT *len) -{ - WCHAR pn[37]; - swprintf(pn, ARRAY_SIZE(pn), L"\StringFileInfo\%04x%04x\ProductName", lang->wLanguage, lang->wCodePage); - return VerQueryValueW(data, pn, buffer, len) && *len; -} - -static WCHAR *get_application_name(BOOL query_app_name) -{ - WCHAR path[MAX_PATH], *name; - - GetModuleFileNameW(NULL, path, ARRAY_SIZE(path)); - - if (query_app_name) - { - UINT translate_size, productname_size; - LANGANDCODEPAGE *translate; - LPVOID productname; - BOOL found = FALSE; - void *data = NULL; - unsigned int i; - LCID locale; - DWORD size; - - size = GetFileVersionInfoSizeW(path, NULL); - if (!size) - goto skip; - - data = malloc(size); - if (!data) - goto skip; - - if (!GetFileVersionInfoW(path, 0, size, data)) - goto skip; - - if (!VerQueryValueW(data, L"\VarFileInfo\Translation", (LPVOID *)&translate, &translate_size)) - goto skip; - - /* no translations found */ - if (translate_size < sizeof(LANGANDCODEPAGE)) - goto skip; - - /* The following code will try to find the best translation. We first search for an - * exact match of the language, then a match of the language PRIMARYLANGID, then we - * search for a LANG_NEUTRAL match, and if that still doesn't work we pick the - * first entry which contains a proper productname. */ - locale = GetThreadLocale(); - - for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) { - if (translate[i].wLanguage == locale && - query_productname(data, &translate[i], &productname, &productname_size)) { - found = TRUE; - break; - } - } - - if (!found) { - for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) { - if (PRIMARYLANGID(translate[i].wLanguage) == PRIMARYLANGID(locale) && - query_productname(data, &translate[i], &productname, &productname_size)) { - found = TRUE; - break; - } - } - } - - if (!found) { - for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) { - if (PRIMARYLANGID(translate[i].wLanguage) == LANG_NEUTRAL && - query_productname(data, &translate[i], &productname, &productname_size)) { - found = TRUE; - break; - } - } - } - - if (!found) { - for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) { - if (query_productname(data, &translate[i], &productname, &productname_size)) { - found = TRUE; - break; - } - } - } - - skip: - if (found) - { - name = wcsdup(productname); - free(data); - return name; - } - free(data); - } - - name = wcsrchr(path, '\'); - if (!name) - name = path; - else - name++; - return wcsdup(name); -} - -static DWORD WINAPI pulse_timer_cb(void *user) -{ - struct timer_loop_params params; - ACImpl *This = user; - params.stream = This->stream; - SetThreadDescription(GetCurrentThread(), L"winepulse_timer_loop"); - pulse_call(timer_loop, ¶ms); - return 0; -} - -static void set_stream_volumes(ACImpl *This) -{ - struct set_volumes_params params; - params.stream = This->stream; - params.master_volume = This->session->mute ? 0.0f : This->session->master_vol; - params.volumes = This->vols; - params.session_volumes = This->session->channel_vols; - pulse_call(set_volumes, ¶ms); -} - static void get_device_guid(HKEY drv_key, EDataFlow flow, const char *pulse_name, GUID *guid) { WCHAR key_name[MAX_PULSE_NAME_LEN + 2]; @@ -528,119 +367,6 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient return S_OK; }
-static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface, - REFIID riid, void **ppv) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - - TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); - - if (!ppv) - return E_POINTER; - - *ppv = NULL; - if (IsEqualIID(riid, &IID_IUnknown) || - IsEqualIID(riid, &IID_IAudioClient) || - IsEqualIID(riid, &IID_IAudioClient2) || - IsEqualIID(riid, &IID_IAudioClient3)) - *ppv = iface; - if (*ppv) { - IUnknown_AddRef((IUnknown*)*ppv); - return S_OK; - } - - if (IsEqualIID(riid, &IID_IMarshal)) - return IUnknown_QueryInterface(This->marshal, riid, ppv); - - WARN("Unknown interface %s\n", debugstr_guid(riid)); - return E_NOINTERFACE; -} - -static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - ULONG ref; - ref = InterlockedIncrement(&This->ref); - TRACE("(%p) Refcount now %lu\n", This, ref); - return ref; -} - -static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - ULONG ref; - ref = InterlockedDecrement(&This->ref); - TRACE("(%p) Refcount now %lu\n", This, ref); - if (!ref) { - if (This->stream) { - pulse_release_stream(This->stream, This->timer_thread); - This->stream = 0; - sessions_lock(); - list_remove(&This->entry); - sessions_unlock(); - } - IUnknown_Release(This->marshal); - IMMDevice_Release(This->parent); - HeapFree(GetProcessHeap(), 0, This); - } - return ref; -} - -static void dump_fmt(const WAVEFORMATEX *fmt) -{ - TRACE("wFormatTag: 0x%x (", fmt->wFormatTag); - switch(fmt->wFormatTag) { - case WAVE_FORMAT_PCM: - TRACE("WAVE_FORMAT_PCM"); - break; - case WAVE_FORMAT_IEEE_FLOAT: - TRACE("WAVE_FORMAT_IEEE_FLOAT"); - break; - case WAVE_FORMAT_EXTENSIBLE: - TRACE("WAVE_FORMAT_EXTENSIBLE"); - break; - default: - TRACE("Unknown"); - break; - } - TRACE(")\n"); - - TRACE("nChannels: %u\n", fmt->nChannels); - TRACE("nSamplesPerSec: %lu\n", fmt->nSamplesPerSec); - TRACE("nAvgBytesPerSec: %lu\n", fmt->nAvgBytesPerSec); - TRACE("nBlockAlign: %u\n", fmt->nBlockAlign); - TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample); - TRACE("cbSize: %u\n", fmt->cbSize); - - if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt; - TRACE("dwChannelMask: %08lx\n", fmtex->dwChannelMask); - TRACE("Samples: %04x\n", fmtex->Samples.wReserved); - TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat)); - } -} - -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) { @@ -688,8 +414,8 @@ static AudioSession *create_session(const GUID *guid, IMMDevice *device,
/* if channels == 0, then this will return or create a session with * matching dataflow and GUID. otherwise, channels must also match */ -static HRESULT get_audio_session(const GUID *sessionguid, - IMMDevice *device, UINT channels, AudioSession **out) +HRESULT get_audio_session(const GUID *sessionguid, IMMDevice *device, + UINT channels, AudioSession **out) { AudioSession *session;
@@ -720,613 +446,6 @@ static HRESULT get_audio_session(const GUID *sessionguid, return S_OK; }
-static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface, - AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration, - REFERENCE_TIME period, const WAVEFORMATEX *fmt, - const GUID *sessionguid) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - struct create_stream_params params; - unsigned int i, channel_count; - stream_handle stream; - WCHAR *name; - HRESULT hr; - - TRACE("(%p)->(%x, %lx, %s, %s, %p, %s)\n", This, mode, flags, - wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid)); - - if (!fmt) - return E_POINTER; - dump_fmt(fmt); - - if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE) - return E_INVALIDARG; - if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) - return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED; - - if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS | - AUDCLNT_STREAMFLAGS_LOOPBACK | - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | - AUDCLNT_STREAMFLAGS_NOPERSIST | - AUDCLNT_STREAMFLAGS_RATEADJUST | - AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED | - AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE | - AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED | - AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY | - AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)) { - FIXME("Unknown flags: %08lx\n", flags); - return E_INVALIDARG; - } - - sessions_lock(); - - if (This->stream) { - sessions_unlock(); - return AUDCLNT_E_ALREADY_INITIALIZED; - } - - if (!pulse_thread) - { - HANDLE event = CreateEventW(NULL, TRUE, FALSE, NULL); - if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, event, 0, NULL))) - { - ERR("Failed to create mainloop thread.\n"); - sessions_unlock(); - CloseHandle(event); - return E_FAIL; - } - SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL); - WaitForSingleObject(event, INFINITE); - CloseHandle(event); - } - - params.name = name = get_application_name(TRUE); - params.device = This->device_name; - params.flow = This->dataflow; - params.share = mode; - params.flags = flags; - params.duration = duration; - params.period = period; - params.fmt = fmt; - params.stream = &stream; - params.channel_count = &channel_count; - pulse_call(create_stream, ¶ms); - free(name); - if (FAILED(hr = params.result)) - { - sessions_unlock(); - return hr; - } - - if (!(This->vols = malloc(channel_count * sizeof(*This->vols)))) - { - pulse_release_stream(stream, NULL); - sessions_unlock(); - return E_OUTOFMEMORY; - } - for (i = 0; i < channel_count; i++) - This->vols[i] = 1.f; - - hr = get_audio_session(sessionguid, This->parent, channel_count, &This->session); - if (FAILED(hr)) - { - free(This->vols); - This->vols = NULL; - sessions_unlock(); - pulse_release_stream(stream, NULL); - return E_OUTOFMEMORY; - } - - This->stream = stream; - This->channel_count = channel_count; - list_add_tail(&This->session->clients, &This->entry); - set_stream_volumes(This); - - sessions_unlock(); - return S_OK; -} - -static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface, - UINT32 *out) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - struct get_buffer_size_params params; - - TRACE("(%p)->(%p)\n", This, out); - - if (!out) - return E_POINTER; - if (!This->stream) - return AUDCLNT_E_NOT_INITIALIZED; - - params.stream = This->stream; - params.frames = out; - pulse_call(get_buffer_size, ¶ms); - return params.result; -} - -static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface, - REFERENCE_TIME *latency) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - struct get_latency_params params; - - TRACE("(%p)->(%p)\n", This, latency); - - if (!latency) - return E_POINTER; - if (!This->stream) - return AUDCLNT_E_NOT_INITIALIZED; - - params.stream = This->stream; - params.latency = latency; - pulse_call(get_latency, ¶ms); - return params.result; -} - -static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface, - UINT32 *out) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - struct get_current_padding_params params; - - TRACE("(%p)->(%p)\n", This, out); - - if (!out) - return E_POINTER; - if (!This->stream) - return AUDCLNT_E_NOT_INITIALIZED; - - params.stream = This->stream; - params.padding = out; - pulse_call(get_current_padding, ¶ms); - return params.result; -} - -static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface, - AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt, - WAVEFORMATEX **out) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - HRESULT hr = S_OK; - WAVEFORMATEX *closest = NULL; - BOOL exclusive; - - 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 (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; - } - - 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; - } - - 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; - } - - if (exclusive && hr != S_OK) { - hr = AUDCLNT_E_UNSUPPORTED_FORMAT; - CoTaskMemFree(closest); - } else if (hr != S_FALSE) - CoTaskMemFree(closest); - else - *out = closest; - - /* 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; -} - -static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface, - WAVEFORMATEX **pwfx) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - struct get_mix_format_params params; - - TRACE("(%p)->(%p)\n", This, pwfx); - - if (!pwfx) - return E_POINTER; - *pwfx = NULL; - - params.device = This->device_name; - params.flow = This->dataflow; - params.fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE)); - if (!params.fmt) - return E_OUTOFMEMORY; - - pulse_call(get_mix_format, ¶ms); - - if (SUCCEEDED(params.result)) { - *pwfx = ¶ms.fmt->Format; - dump_fmt(*pwfx); - } else { - CoTaskMemFree(params.fmt); - } - - return params.result; -} - -static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface, - REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod) -{ - struct get_device_period_params params; - ACImpl *This = impl_from_IAudioClient3(iface); - - TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod); - - if (!defperiod && !minperiod) - return E_POINTER; - - params.flow = This->dataflow; - params.device = This->device_name; - params.def_period = defperiod; - params.min_period = minperiod; - - pulse_call(get_device_period, ¶ms); - - return params.result; -} - -static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - struct start_params params; - HRESULT hr; - - TRACE("(%p)\n", This); - - if (!This->stream) - return AUDCLNT_E_NOT_INITIALIZED; - - params.stream = This->stream; - pulse_call(start, ¶ms); - if (FAILED(hr = params.result)) - return hr; - - if (!This->timer_thread) { - This->timer_thread = CreateThread(NULL, 0, pulse_timer_cb, This, 0, NULL); - SetThreadPriority(This->timer_thread, THREAD_PRIORITY_TIME_CRITICAL); - } - - return S_OK; -} - -static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - struct stop_params params; - - TRACE("(%p)\n", This); - - if (!This->stream) - return AUDCLNT_E_NOT_INITIALIZED; - - params.stream = This->stream; - pulse_call(stop, ¶ms); - return params.result; -} - -static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - struct reset_params params; - - TRACE("(%p)\n", This); - - if (!This->stream) - return AUDCLNT_E_NOT_INITIALIZED; - - params.stream = This->stream; - pulse_call(reset, ¶ms); - return params.result; -} - -static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface, - HANDLE event) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - struct set_event_handle_params params; - - TRACE("(%p)->(%p)\n", This, event); - - if (!event) - return E_INVALIDARG; - if (!This->stream) - return AUDCLNT_E_NOT_INITIALIZED; - - params.stream = This->stream; - params.event = event; - pulse_call(set_event_handle, ¶ms); - return params.result; -} - -static HRESULT WINAPI AudioClient_GetService(IAudioClient3 *iface, REFIID riid, - void **ppv) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - - TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv); - - if (!ppv) - return E_POINTER; - *ppv = NULL; - - if (!This->stream) - return AUDCLNT_E_NOT_INITIALIZED; - - if (IsEqualIID(riid, &IID_IAudioRenderClient)) { - if (This->dataflow != eRender) - return AUDCLNT_E_WRONG_ENDPOINT_TYPE; - *ppv = &This->IAudioRenderClient_iface; - } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) { - if (This->dataflow != eCapture) - return AUDCLNT_E_WRONG_ENDPOINT_TYPE; - *ppv = &This->IAudioCaptureClient_iface; - } else if (IsEqualIID(riid, &IID_IAudioClock)) { - *ppv = &This->IAudioClock_iface; - } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) { - *ppv = &This->IAudioStreamVolume_iface; - } else if (IsEqualIID(riid, &IID_IAudioSessionControl) || - IsEqualIID(riid, &IID_IChannelAudioVolume) || - IsEqualIID(riid, &IID_ISimpleAudioVolume)) { - if (!This->session_wrapper) { - This->session_wrapper = AudioSessionWrapper_Create(This); - if (!This->session_wrapper) - return E_OUTOFMEMORY; - } - if (IsEqualIID(riid, &IID_IAudioSessionControl)) - *ppv = &This->session_wrapper->IAudioSessionControl2_iface; - else if (IsEqualIID(riid, &IID_IChannelAudioVolume)) - *ppv = &This->session_wrapper->IChannelAudioVolume_iface; - else if (IsEqualIID(riid, &IID_ISimpleAudioVolume)) - *ppv = &This->session_wrapper->ISimpleAudioVolume_iface; - } - - if (*ppv) { - IUnknown_AddRef((IUnknown*)*ppv); - return S_OK; - } - - FIXME("stub %s\n", debugstr_guid(riid)); - return E_NOINTERFACE; -} - -static HRESULT WINAPI AudioClient_IsOffloadCapable(IAudioClient3 *iface, - AUDIO_STREAM_CATEGORY category, BOOL *offload_capable) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - - TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable); - - if(!offload_capable) - return E_INVALIDARG; - - *offload_capable = FALSE; - - return S_OK; -} - -static HRESULT WINAPI AudioClient_SetClientProperties(IAudioClient3 *iface, - const AudioClientProperties *prop) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop; - - TRACE("(%p)->(%p)\n", This, prop); - - if(!legacy_prop) - return E_POINTER; - - if(legacy_prop->cbSize == sizeof(AudioClientProperties)){ - TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n", - legacy_prop->bIsOffload, - legacy_prop->eCategory, - prop->Options); - }else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)){ - TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n", - legacy_prop->bIsOffload, - legacy_prop->eCategory); - }else{ - WARN("Unsupported Size = %d\n", legacy_prop->cbSize); - return E_INVALIDARG; - } - - - if(legacy_prop->bIsOffload) - return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE; - - return S_OK; -} - -static HRESULT WINAPI AudioClient_GetBufferSizeLimits(IAudioClient3 *iface, - const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration, - REFERENCE_TIME *max_duration) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - - FIXME("(%p)->(%p, %u, %p, %p)\n", This, format, event_driven, min_duration, max_duration); - - return E_NOTIMPL; -} - -static HRESULT WINAPI AudioClient_GetSharedModeEnginePeriod(IAudioClient3 *iface, - const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames, - UINT32 *min_period_frames, UINT32 *max_period_frames) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - - FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, format, default_period_frames, unit_period_frames, - min_period_frames, max_period_frames); - - return E_NOTIMPL; -} - -static HRESULT WINAPI AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface, - WAVEFORMATEX **cur_format, UINT32 *cur_period_frames) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - - FIXME("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames); - - return E_NOTIMPL; -} - -static HRESULT WINAPI AudioClient_InitializeSharedAudioStream(IAudioClient3 *iface, - DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format, - const GUID *session_guid) -{ - ACImpl *This = impl_from_IAudioClient3(iface); - - FIXME("(%p)->(0x%lx, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid)); - - return E_NOTIMPL; -} - -static const IAudioClient3Vtbl AudioClient3_Vtbl = -{ - AudioClient_QueryInterface, - AudioClient_AddRef, - AudioClient_Release, - AudioClient_Initialize, - AudioClient_GetBufferSize, - AudioClient_GetStreamLatency, - AudioClient_GetCurrentPadding, - AudioClient_IsFormatSupported, - AudioClient_GetMixFormat, - AudioClient_GetDevicePeriod, - AudioClient_Start, - AudioClient_Stop, - AudioClient_Reset, - AudioClient_SetEventHandle, - AudioClient_GetService, - AudioClient_IsOffloadCapable, - AudioClient_SetClientProperties, - AudioClient_GetBufferSizeLimits, - AudioClient_GetSharedModeEnginePeriod, - AudioClient_GetCurrentSharedModeEnginePeriod, - AudioClient_InitializeSharedAudioStream, -}; - static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client) { AudioSessionWrapper *ret; diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c index a3354c5efe2..25c70008b22 100644 --- a/dlls/winepulse.drv/pulse.c +++ b/dlls/winepulse.drv/pulse.c @@ -1116,12 +1116,17 @@ static NTSTATUS pulse_create_stream(void *args) struct pulse_stream *stream; unsigned int i, bufsize_bytes; HRESULT hr; - char *name = wstr_to_str(params->name); + char *name; + + if (params->share == AUDCLNT_SHAREMODE_EXCLUSIVE) { + params->result = AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED; + return STATUS_SUCCESS; + }
pulse_lock();
+ name = wstr_to_str(params->name); params->result = pulse_connect(name); - free(name);
if (FAILED(params->result)) @@ -2077,6 +2082,134 @@ 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; + + if (params->fmt_in->nChannels == 0) + return AUDCLNT_E_UNSUPPORTED_FORMAT; + + memcpy(&in, params->fmt_in, params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? + sizeof(in) : sizeof(in.Format)); + + if (exclusive) + out = ∈ + else { + out = params->fmt_out; + memcpy(out, fmt, fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? + sizeof(*out) : sizeof((*out).Format)); + } + + params->result = S_OK; + + 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 +2528,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 +2694,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 +2997,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,