winepulse-PulseAudio_Support: Reenable patchset.

Various patches have been modified to use static initialization of device data,
to avoid loader deadlocks when initializing PulseAudio too early. These are very
huge changes, so the new patchset should receive some additional testing before
the next release.
This commit is contained in:
Sebastian Lackner 2015-11-04 05:42:22 +01:00
parent 637b89cefa
commit 91a3fcc513
48 changed files with 1005 additions and 5987 deletions

View File

@ -281,7 +281,7 @@ for more details.*
* Support for MPEG2 DXVA2 GPU video decoding through vaapi
* Support for NVIDIA video encoder library (nvencodeapi)
* Support for NtQuerySection ([Wine Bug #37338](https://bugs.winehq.org/show_bug.cgi?id=37338))
* Support for PulseAudio backend for audio ([Wine Bug #10495](https://bugs.winehq.org/show_bug.cgi?id=10495))
* ~~Support for PulseAudio backend for audio~~ ([Wine Bug #10495](https://bugs.winehq.org/show_bug.cgi?id=10495))
* Support for SHCreateSessionKey ([Wine Bug #35630](https://bugs.winehq.org/show_bug.cgi?id=35630))
* Support for WTSEnumerateProcessesW ([Wine Bug #29903](https://bugs.winehq.org/show_bug.cgi?id=29903))
* Support for extra large and jumbo icon lists in shell32 ([Wine Bug #24721](https://bugs.winehq.org/show_bug.cgi?id=24721))

View File

@ -308,6 +308,7 @@ patch_enable_all ()
enable_winedbg_Windows_Version="$1"
enable_winedevice_Fix_Relocation="$1"
enable_winemenubuilder_Desktop_Icon_Path="$1"
enable_winepulse_PulseAudio_Support="$1"
enable_winex11_CandidateWindowPos="$1"
enable_winex11_Clipboard_HTML="$1"
enable_winex11_Window_Groups="$1"
@ -1023,6 +1024,9 @@ patch_enable ()
winemenubuilder-Desktop_Icon_Path)
enable_winemenubuilder_Desktop_Icon_Path="$2"
;;
winepulse-PulseAudio_Support)
enable_winepulse_PulseAudio_Support="$2"
;;
winex11-CandidateWindowPos)
enable_winex11_CandidateWindowPos="$2"
;;
@ -1631,6 +1635,9 @@ if test "$enable_category_stable" -eq 1; then
if test "$enable_winemenubuilder_Desktop_Icon_Path" -gt 1; then
abort "Patchset winemenubuilder-Desktop_Icon_Path disabled, but category-stable depends on that."
fi
if test "$enable_winepulse_PulseAudio_Support" -gt 1; then
abort "Patchset winepulse-PulseAudio_Support disabled, but category-stable depends on that."
fi
if test "$enable_winex11_Window_Style" -gt 1; then
abort "Patchset winex11-Window_Style disabled, but category-stable depends on that."
fi
@ -1717,6 +1724,7 @@ if test "$enable_category_stable" -eq 1; then
enable_wined3d_resource_check_usage=1
enable_wined3d_wined3d_swapchain_present=1
enable_winemenubuilder_Desktop_Icon_Path=1
enable_winepulse_PulseAudio_Support=1
enable_winex11_Window_Style=1
enable_winex11_XEMBED=1
enable_winex11_wglShareLists=1
@ -6192,6 +6200,47 @@ if test "$enable_winemenubuilder_Desktop_Icon_Path" -eq 1; then
) >> "$patchlist"
fi
# Patchset winepulse-PulseAudio_Support
# |
# | This patchset fixes the following Wine bugs:
# | * [#37042] Implement exclusive mode in PulseAudio backend
# |
# | Modified files:
# | * dlls/winepulse.drv/Makefile.in, dlls/winepulse.drv/mmdevdrv.c, dlls/winepulse.drv/winepulse.drv.spec
# |
if test "$enable_winepulse_PulseAudio_Support" -eq 1; then
patch_apply winepulse-PulseAudio_Support/0001-winepulse-handle-stream-create-failing-correctly.patch
patch_apply winepulse-PulseAudio_Support/0002-winepulse-Always-mute-buffer.patch
patch_apply winepulse-PulseAudio_Support/0003-winepulse-In-Shared-mode-track-device-position-in-by.patch
patch_apply winepulse-PulseAudio_Support/0004-winepulse-add-stub-for-GetPropValue.patch
patch_apply winepulse-PulseAudio_Support/0005-winepulse-return-PKEY_AudioEndpoint_PhysicalSpeakers.patch
patch_apply winepulse-PulseAudio_Support/0006-winepulse-Prefer-PulseAudio-driver.patch
patch_apply winepulse-PulseAudio_Support/0007-winepulse.drv-Use-delay-import-for-winealsa.drv.patch
patch_apply winepulse-PulseAudio_Support/0008-winepulse.drv-Use-a-separate-mainloop-and-ctx-for-pu.patch
patch_apply winepulse-PulseAudio_Support/0009-winepulse-expose-audio-devices-directly-to-programs.patch
patch_apply winepulse-PulseAudio_Support/0010-winepulse-implement-exclusive-mode.patch
patch_apply winepulse-PulseAudio_Support/0011-winepulse-fix-segfault-in-pulse_rd_loop.patch
patch_apply winepulse-PulseAudio_Support/0012-winepulse-implement-GetPropValue.patch
patch_apply winepulse-PulseAudio_Support/0013-winepulse-fetch-actual-program-name-if-possible.patch
patch_apply winepulse-PulseAudio_Support/0014-winepulse-return-PKEY_AudioEndpoint_PhysicalSpeakers.patch
(
echo '+ { "Mark Harmstone", "winepulse: handle stream create failing correctly.", 1 },';
echo '+ { "Andrew Eikum", "winepulse: Always mute buffer.", 1 },';
echo '+ { "Andrew Eikum", "winepulse: In Shared mode, track device position in bytes.", 1 },';
echo '+ { "Mark Harmstone", "winepulse: add stub for GetPropValue.", 1 },';
echo '+ { "Mark Harmstone", "winepulse: return PKEY_AudioEndpoint_PhysicalSpeakers device prop.", 1 },';
echo '+ { "Andrew Eikum", "winepulse: Prefer PulseAudio driver.", 1 },';
echo '+ { "Sebastian Lackner", "winepulse.drv: Use delay import for winealsa.drv.", 1 },';
echo '+ { "Sebastian Lackner", "winepulse.drv: Use a separate mainloop and ctx for pulse_test_connect.", 1 },';
echo '+ { "Mark Harmstone", "winepulse: expose audio devices directly to programs.", 1 },';
echo '+ { "Mark Harmstone", "winepulse: implement exclusive mode.", 1 },';
echo '+ { "Mark Harmstone", "winepulse: fix segfault in pulse_rd_loop.", 1 },';
echo '+ { "Mark Harmstone", "winepulse: implement GetPropValue.", 1 },';
echo '+ { "Mark Harmstone", "winepulse: fetch actual program name if possible.", 1 },';
echo '+ { "Mark Harmstone", "winepulse: return PKEY_AudioEndpoint_PhysicalSpeakers device prop.", 1 },';
) >> "$patchlist"
fi
# Patchset winex11-CandidateWindowPos
# |
# | This patchset fixes the following Wine bugs:

View File

@ -1,418 +0,0 @@
From 191d506cfbc49ad6a43632609e1f396ae10a6ca7 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 14 Jul 2014 09:50:02 +0200
Subject: winepulse: Add initial stub for pulseaudio support
---
configure.ac | 31 +++-
dlls/mmdevapi/main.c | 2 +-
dlls/winepulse.drv/Makefile.in | 7 +
dlls/winepulse.drv/mmdevdrv.c | 290 ++++++++++++++++++++++++++++++++++
dlls/winepulse.drv/winepulse.drv.spec | 5 +
5 files changed, 332 insertions(+), 3 deletions(-)
create mode 100644 dlls/winepulse.drv/Makefile.in
create mode 100644 dlls/winepulse.drv/mmdevdrv.c
create mode 100644 dlls/winepulse.drv/winepulse.drv.spec
diff --git a/configure.ac b/configure.ac
index d23227a..76aed78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -73,4 +73,5 @@ AC_ARG_WITH(png, AS_HELP_STRING([--without-png],[do not use PNG]))
AC_ARG_WITH(pthread, AS_HELP_STRING([--without-pthread],[do not use the pthread library]),
[if test "x$withval" = "xno"; then ac_cv_header_pthread_h=no; fi])
+AC_ARG_WITH(pulse, AC_HELP_STRING([--without-pulse],[do not use PulseAudio sound support]))
AC_ARG_WITH(sane, AS_HELP_STRING([--without-sane],[do not use SANE (scanner support)]))
AC_ARG_WITH(tiff, AS_HELP_STRING([--without-tiff],[do not use TIFF]))
@@ -1548,6 +1549,30 @@ then
[GetText ${notice_platform}development files not found (or too old), po files can't be rebuilt.])
fi
+dnl **** Check for PulseAudio ****
+AC_SUBST(PULSE_LIBS,"")
+AC_SUBST(PULSE_CFLAGS,"")
+if test "x$with_pulse" != "xno";
+then
+ ac_save_CPPFLAGS="$CPPFLAGS"
+ if test "$PKG_CONFIG" != "false";
+ then
+ ac_pulse_libs="`$PKG_CONFIG --libs libpulse 2>/dev/null`"
+ ac_pulse_cflags="`$PKG_CONFIG --cflags-only-I libpulse 2>/dev/null`"
+
+ CPPFLAGS="$CPPFLAGS $ac_pulse_cflags"
+ AC_CHECK_HEADERS(pulse/pulseaudio.h,
+ [AC_CHECK_LIB(pulse, pa_stream_is_corked,
+ [AC_DEFINE(HAVE_PULSEAUDIO, 1, [Define if you have pulseaudio])
+ PULSE_LIBS="$ac_pulse_libs"
+ PULSE_CFLAGS="$ac_pulse_cflags"],,$ac_pulse_libs)
+ ])
+ fi
+ CPPFLAGS="$ac_save_CPPFLAGS"
+fi
+WINE_WARNING_WITH(pulse, [test "$ac_cv_lib_pulse_pa_stream_is_corked" != "yes"],
+ [libpulse ${notice_platform}development files not found or too old, Pulse won't be supported.])
+
dnl **** Check for gstreamer ****
if test "x$with_gstreamer" != "xno"
then
@@ -1766,13 +1791,14 @@ fi
dnl **** Disable unsupported winmm drivers ****
test -n "$ALSA_LIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no}
test -n "$COREAUDIO_LIBS" || enable_winecoreaudio_drv=${enable_winecoreaudio_drv:-no}
+test -n "$PULSE_LIBS" || enable_winepulse_drv=${enable_winepulse_drv:-no}
test "x$ac_cv_member_oss_sysinfo_numaudioengines" = xyes || enable_wineoss_drv=${enable_wineoss_drv:-no}
test "$ac_cv_header_linux_joystick_h" = "yes" -o "$ac_cv_header_IOKit_hid_IOHIDLib_h" = "yes" || enable_winejoystick_drv=${enable_winejoystick_drv:-no}
dnl **** Check for any sound system ****
-if test "x$ALSA_LIBS$COREAUDIO_LIBS" = "x" -a \
+if test "x$ALSA_LIBS$COREAUDIO_LIBS$PULSE_LIBS" = "x" -a \
"x$ac_cv_member_oss_sysinfo_numaudioengines" != xyes -a \
- "x$with_alsa$with_coreaudio$with_oss" != xnonono
+ "x$with_alsa$with_coreaudio$with_oss$with_pulse" != xnononono
then
WINE_WARNING([No sound system was found. Windows applications will be silent.])
fi
@@ -3339,6 +3365,7 @@ WINE_CONFIG_DLL(winemp3.acm)
WINE_CONFIG_DLL(wineoss.drv)
WINE_CONFIG_DLL(wineps.drv,,[clean,po])
WINE_CONFIG_DLL(wineps16.drv16,enable_win16)
+WINE_CONFIG_DLL(winepulse.drv)
WINE_CONFIG_DLL(wineqtdecoder)
WINE_CONFIG_DLL(winex11.drv)
WINE_CONFIG_DLL(wing.dll16,enable_win16)
diff --git a/dlls/mmdevapi/main.c b/dlls/mmdevapi/main.c
index 52cf6f1..aa4baa5 100644
--- a/dlls/mmdevapi/main.c
+++ b/dlls/mmdevapi/main.c
@@ -113,7 +113,7 @@ static BOOL init_driver(void)
{
static const WCHAR drv_value[] = {'A','u','d','i','o',0};
- static WCHAR default_list[] = {'a','l','s','a',',','o','s','s',',',
+ static WCHAR default_list[] = {'p','u','l','s','e',',','a','l','s','a',',','o','s','s',',',
'c','o','r','e','a','u','d','i','o',0};
DriverFuncs driver;
diff --git a/dlls/winepulse.drv/Makefile.in b/dlls/winepulse.drv/Makefile.in
new file mode 100644
index 0000000..158bbc0
--- /dev/null
+++ b/dlls/winepulse.drv/Makefile.in
@@ -0,0 +1,7 @@
+MODULE = winepulse.drv
+IMPORTS = dxguid uuid winmm user32 advapi32 ole32
+EXTRALIBS = $(PULSE_LIBS) $(PTHREAD_LIBS)
+EXTRAINCL = $(PULSE_CFLAGS)
+
+C_SRCS = \
+ mmdevdrv.c
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
new file mode 100644
index 0000000..d187bdc
--- /dev/null
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2011-2012 Maarten Lankhorst
+ * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers
+ * Copyright 2011 Andrew Eikum for CodeWeavers
+ *
+ * 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
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Pulseaudio driver support.. hell froze over
+ */
+
+#define NONAMELESSUNION
+#define COBJMACROS
+#include "config.h"
+#include <poll.h>
+#include <pthread.h>
+
+#include <stdarg.h>
+#include <unistd.h>
+#include <math.h>
+#include <stdio.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "wine/list.h"
+
+#include "ole2.h"
+#include "dshow.h"
+#include "dsound.h"
+#include "propsys.h"
+
+#include "initguid.h"
+#include "ks.h"
+#include "ksmedia.h"
+#include "mmdeviceapi.h"
+#include "audioclient.h"
+#include "endpointvolume.h"
+#include "audiopolicy.h"
+
+#include "wine/list.h"
+
+#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
+
+WINE_DEFAULT_DEBUG_CHANNEL(pulse);
+
+static const REFERENCE_TIME MinimumPeriod = 30000;
+static const REFERENCE_TIME DefaultPeriod = 100000;
+
+static pa_context *pulse_ctx;
+static pa_mainloop *pulse_ml;
+
+static HANDLE pulse_thread;
+static pthread_mutex_t pulse_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
+
+static DWORD pulse_stream_volume;
+
+const WCHAR pulse_keyW[] = {'S','o','f','t','w','a','r','e','\\',
+ 'W','i','n','e','\\','P','u','l','s','e',0};
+const WCHAR pulse_streamW[] = { 'S','t','r','e','a','m','V','o','l',0 };
+
+BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
+{
+ if (reason == DLL_PROCESS_ATTACH) {
+ HKEY key;
+ if (RegOpenKeyW(HKEY_CURRENT_USER, pulse_keyW, &key) == ERROR_SUCCESS) {
+ DWORD size = sizeof(pulse_stream_volume);
+ RegQueryValueExW(key, pulse_streamW, 0, NULL,
+ (BYTE*)&pulse_stream_volume, &size);
+ RegCloseKey(key);
+ }
+ DisableThreadLibraryCalls(dll);
+ } else if (reason == DLL_PROCESS_DETACH) {
+ if (pulse_ctx) {
+ pa_context_disconnect(pulse_ctx);
+ pa_context_unref(pulse_ctx);
+ }
+ if (pulse_ml)
+ pa_mainloop_quit(pulse_ml, 0);
+ CloseHandle(pulse_thread);
+ }
+ return TRUE;
+}
+
+
+static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
+
+/* Following pulseaudio design here, mainloop has the lock taken whenever
+ * it is handling something for pulse, and the lock is required whenever
+ * doing any pa_* call that can affect the state in any way
+ *
+ * pa_cond_wait is used when waiting on results, because the mainloop needs
+ * the same lock taken to affect the state
+ *
+ * This is basically the same as the pa_threaded_mainloop implementation,
+ * but that cannot be used because it uses pthread_create directly
+ *
+ * pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
+ * pa_threaded_mainloop_signal -> pthread_cond_signal
+ * pa_threaded_mainloop_wait -> pthread_cond_wait
+ */
+
+static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) {
+ int r;
+ pthread_mutex_unlock(&pulse_lock);
+ r = poll(ufds, nfds, timeout);
+ pthread_mutex_lock(&pulse_lock);
+ return r;
+}
+
+static DWORD CALLBACK pulse_mainloop_thread(void *tmp) {
+ int ret;
+ pulse_ml = pa_mainloop_new();
+ pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
+ pthread_mutex_lock(&pulse_lock);
+ pthread_cond_signal(&pulse_cond);
+ pa_mainloop_run(pulse_ml, &ret);
+ pthread_mutex_unlock(&pulse_lock);
+ pa_mainloop_free(pulse_ml);
+ CloseHandle(pulse_thread);
+ return ret;
+}
+
+static void pulse_contextcallback(pa_context *c, void *userdata);
+
+static HRESULT pulse_connect(void)
+{
+ int len;
+ WCHAR path[PATH_MAX], *name;
+ char *str;
+
+ if (!pulse_thread)
+ {
+ if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL)))
+ {
+ ERR("Failed to create mainloop thread.\n");
+ return E_FAIL;
+ }
+ SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ }
+
+ if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx)))
+ return S_OK;
+ if (pulse_ctx)
+ pa_context_unref(pulse_ctx);
+
+ GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*path));
+ name = strrchrW(path, '\\');
+ if (!name)
+ name = path;
+ else
+ name++;
+ len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
+ str = pa_xmalloc(len);
+ WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
+ TRACE("Name: %s\n", str);
+ pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
+ pa_xfree(str);
+ if (!pulse_ctx) {
+ ERR("Failed to create context\n");
+ return E_FAIL;
+ }
+
+ pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
+
+ TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
+ if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
+ goto fail;
+
+ /* Wait for connection */
+ while (pthread_cond_wait(&pulse_cond, &pulse_lock)) {
+ pa_context_state_t state = pa_context_get_state(pulse_ctx);
+
+ if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
+ goto fail;
+
+ if (state == PA_CONTEXT_READY)
+ break;
+ }
+
+ TRACE("Connected to server %s with protocol version: %i.\n",
+ pa_context_get_server(pulse_ctx),
+ pa_context_get_server_protocol_version(pulse_ctx));
+ return S_OK;
+
+fail:
+ pa_context_unref(pulse_ctx);
+ pulse_ctx = NULL;
+ return E_FAIL;
+}
+
+static void pulse_contextcallback(pa_context *c, void *userdata) {
+ switch (pa_context_get_state(c)) {
+ default:
+ FIXME("Unhandled state: %i\n", pa_context_get_state(c));
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ case PA_CONTEXT_TERMINATED:
+ TRACE("State change to %i\n", pa_context_get_state(c));
+ return;
+
+ case PA_CONTEXT_READY:
+ TRACE("Ready\n");
+ break;
+
+ case PA_CONTEXT_FAILED:
+ ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c)));
+ break;
+ }
+ pthread_cond_signal(&pulse_cond);
+}
+
+HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys,
+ UINT *num, UINT *def_index)
+{
+ HRESULT hr = S_OK;
+ TRACE("%d %p %p %p\n", flow, ids, num, def_index);
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_connect();
+ pthread_mutex_unlock(&pulse_lock);
+ if (FAILED(hr))
+ return hr;
+ *num = 1;
+ *def_index = 0;
+
+ *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
+ if (!*ids)
+ return E_OUTOFMEMORY;
+
+ (*ids)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
+ if (!(*ids)[0]) {
+ HeapFree(GetProcessHeap(), 0, *ids);
+ return E_OUTOFMEMORY;
+ }
+
+ lstrcpyW((*ids)[0], defaultW);
+
+ *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(void *));
+ (*keys)[0] = NULL;
+
+ return S_OK;
+}
+
+int WINAPI AUDDRV_GetPriority(void)
+{
+ HRESULT hr;
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_connect();
+ pthread_mutex_unlock(&pulse_lock);
+ return SUCCEEDED(hr) ? 3 : 0;
+}
+
+HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev,
+ EDataFlow dataflow, IAudioClient **out)
+{
+ TRACE("%p %p %d %p\n", key, dev, dataflow, out);
+ if (dataflow != eRender && dataflow != eCapture)
+ return E_UNEXPECTED;
+
+ *out = NULL;
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
+ IAudioSessionManager2 **out)
+{
+ *out = NULL;
+ return E_NOTIMPL;
+}
diff --git a/dlls/winepulse.drv/winepulse.drv.spec b/dlls/winepulse.drv/winepulse.drv.spec
new file mode 100644
index 0000000..a089166
--- /dev/null
+++ b/dlls/winepulse.drv/winepulse.drv.spec
@@ -0,0 +1,5 @@
+# MMDevAPI driver functions
+@ stdcall -private GetPriority() AUDDRV_GetPriority
+@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
+@ stdcall -private GetAudioEndpoint(ptr ptr long ptr) AUDDRV_GetAudioEndpoint
+@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
--
2.3.2

View File

@ -1,17 +1,18 @@
From 2d638b335a2d9cdddfa9cfd7d7b6e889d124503c Mon Sep 17 00:00:00 2001
From f83ea4f5911613771d8a6629ddaf5ac39064cd12 Mon Sep 17 00:00:00 2001
From: Mark Harmstone <mark@harmstone.com>
Date: Mon, 14 Jul 2014 09:50:06 +0200
Subject: [PATCH 42/42] winepulse: handle stream create failing correctly
Date: Tue, 3 Nov 2015 16:21:41 -0600
Subject: winepulse: handle stream create failing correctly
Signed-off-by: Andrew Eikum <aeikum@codeweavers.com>
---
dlls/winepulse.drv/mmdevdrv.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index f052a08..e755e8a 100644
index 6b69488..3895f4c 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -701,6 +701,12 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
@@ -753,6 +753,12 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
ret = InterlockedIncrement(&number);
sprintf(buffer, "audio stream #%i", ret);
This->stream = pa_stream_new(pulse_ctx, buffer, &This->ss, &This->map);
@ -25,5 +26,5 @@ index f052a08..e755e8a 100644
pa_stream_set_buffer_attr_callback(This->stream, pulse_attr_update, This);
pa_stream_set_moved_callback(This->stream, pulse_attr_update, This);
--
2.0.0
2.6.2

View File

@ -1,172 +0,0 @@
From 560601710fa0fae144f31be450e5be556ee5c338 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 14 Jul 2014 09:50:02 +0200
Subject: [PATCH 10/42] winepulse: Add format and period probing
---
dlls/winepulse.drv/mmdevdrv.c | 128 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 128 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index d187bdc..40db26d 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -70,6 +70,10 @@ static HANDLE pulse_thread;
static pthread_mutex_t pulse_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
+/* Mixer format + period times */
+static WAVEFORMATEXTENSIBLE pulse_fmt[2];
+static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
+
static DWORD pulse_stream_volume;
const WCHAR pulse_keyW[] = {'S','o','f','t','w','a','r','e','\\',
@@ -139,6 +143,121 @@ static DWORD CALLBACK pulse_mainloop_thread(void *tmp) {
}
static void pulse_contextcallback(pa_context *c, void *userdata);
+static void pulse_stream_state(pa_stream *s, void *user);
+
+static const enum pa_channel_position pulse_pos_from_wfx[] = {
+ PA_CHANNEL_POSITION_FRONT_LEFT,
+ PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_FRONT_CENTER,
+ PA_CHANNEL_POSITION_LFE,
+ PA_CHANNEL_POSITION_REAR_LEFT,
+ PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
+ PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
+ PA_CHANNEL_POSITION_REAR_CENTER,
+ PA_CHANNEL_POSITION_SIDE_LEFT,
+ PA_CHANNEL_POSITION_SIDE_RIGHT,
+ PA_CHANNEL_POSITION_TOP_CENTER,
+ PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
+ PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
+ PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_TOP_REAR_LEFT,
+ PA_CHANNEL_POSITION_TOP_REAR_CENTER,
+ PA_CHANNEL_POSITION_TOP_REAR_RIGHT
+};
+
+static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
+ WAVEFORMATEX *wfx = &fmt->Format;
+ pa_stream *stream;
+ pa_channel_map map;
+ pa_sample_spec ss;
+ pa_buffer_attr attr;
+ int ret, i;
+ unsigned int length = 0;
+
+ pa_channel_map_init_auto(&map, 2, PA_CHANNEL_MAP_ALSA);
+ ss.rate = 48000;
+ ss.format = PA_SAMPLE_FLOAT32LE;
+ ss.channels = map.channels;
+
+ attr.maxlength = -1;
+ attr.tlength = -1;
+ attr.minreq = attr.fragsize = pa_frame_size(&ss);
+ attr.prebuf = 0;
+
+ stream = pa_stream_new(pulse_ctx, "format test stream", &ss, &map);
+ if (stream)
+ pa_stream_set_state_callback(stream, pulse_stream_state, NULL);
+ if (!stream)
+ ret = -1;
+ else if (render)
+ ret = pa_stream_connect_playback(stream, NULL, &attr,
+ PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
+ else
+ ret = pa_stream_connect_record(stream, NULL, &attr, PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS);
+ if (ret >= 0) {
+ while (pa_stream_get_state(stream) == PA_STREAM_CREATING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ if (pa_stream_get_state(stream) == PA_STREAM_READY) {
+ ss = *pa_stream_get_sample_spec(stream);
+ map = *pa_stream_get_channel_map(stream);
+ if (render)
+ length = pa_stream_get_buffer_attr(stream)->minreq;
+ else
+ length = pa_stream_get_buffer_attr(stream)->fragsize;
+ pa_stream_disconnect(stream);
+ while (pa_stream_get_state(stream) == PA_STREAM_READY)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ }
+ }
+ if (stream)
+ pa_stream_unref(stream);
+ if (length)
+ pulse_def_period[!render] = pulse_min_period[!render] = pa_bytes_to_usec(10 * length, &ss);
+ else
+ pulse_min_period[!render] = MinimumPeriod;
+ if (pulse_def_period[!render] <= DefaultPeriod)
+ pulse_def_period[!render] = DefaultPeriod;
+
+ wfx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfx->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+ wfx->nChannels = ss.channels;
+ wfx->wBitsPerSample = 8 * pa_sample_size_of_format(ss.format);
+ wfx->nSamplesPerSec = ss.rate;
+ wfx->nBlockAlign = pa_frame_size(&ss);
+ wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
+ if (ss.format != PA_SAMPLE_S24_32LE)
+ fmt->Samples.wValidBitsPerSample = wfx->wBitsPerSample;
+ else
+ fmt->Samples.wValidBitsPerSample = 24;
+ if (ss.format == PA_SAMPLE_FLOAT32LE)
+ fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ else
+ fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+
+ fmt->dwChannelMask = 0;
+ for (i = 0; i < map.channels; ++i)
+ switch (map.map[i]) {
+ default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(map.map[i])); break;
+ case PA_CHANNEL_POSITION_FRONT_LEFT: fmt->dwChannelMask |= SPEAKER_FRONT_LEFT; break;
+ case PA_CHANNEL_POSITION_MONO:
+ case PA_CHANNEL_POSITION_FRONT_CENTER: fmt->dwChannelMask |= SPEAKER_FRONT_CENTER; break;
+ case PA_CHANNEL_POSITION_FRONT_RIGHT: fmt->dwChannelMask |= SPEAKER_FRONT_RIGHT; break;
+ case PA_CHANNEL_POSITION_REAR_LEFT: fmt->dwChannelMask |= SPEAKER_BACK_LEFT; break;
+ case PA_CHANNEL_POSITION_REAR_CENTER: fmt->dwChannelMask |= SPEAKER_BACK_CENTER; break;
+ case PA_CHANNEL_POSITION_REAR_RIGHT: fmt->dwChannelMask |= SPEAKER_BACK_RIGHT; break;
+ case PA_CHANNEL_POSITION_LFE: fmt->dwChannelMask |= SPEAKER_LOW_FREQUENCY; break;
+ case PA_CHANNEL_POSITION_SIDE_LEFT: fmt->dwChannelMask |= SPEAKER_SIDE_LEFT; break;
+ case PA_CHANNEL_POSITION_SIDE_RIGHT: fmt->dwChannelMask |= SPEAKER_SIDE_RIGHT; break;
+ case PA_CHANNEL_POSITION_TOP_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_CENTER; break;
+ case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_LEFT; break;
+ case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_CENTER; break;
+ case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_RIGHT; break;
+ case PA_CHANNEL_POSITION_TOP_REAR_LEFT: fmt->dwChannelMask |= SPEAKER_TOP_BACK_LEFT; break;
+ case PA_CHANNEL_POSITION_TOP_REAR_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_BACK_CENTER; break;
+ case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: fmt->dwChannelMask |= SPEAKER_TOP_BACK_RIGHT; break;
+ }
+}
static HRESULT pulse_connect(void)
{
@@ -199,6 +318,8 @@ static HRESULT pulse_connect(void)
TRACE("Connected to server %s with protocol version: %i.\n",
pa_context_get_server(pulse_ctx),
pa_context_get_server_protocol_version(pulse_ctx));
+ pulse_probe_settings(1, &pulse_fmt[0]);
+ pulse_probe_settings(0, &pulse_fmt[1]);
return S_OK;
fail:
@@ -230,6 +351,13 @@ static void pulse_contextcallback(pa_context *c, void *userdata) {
pthread_cond_signal(&pulse_cond);
}
+static void pulse_stream_state(pa_stream *s, void *user)
+{
+ pa_stream_state_t state = pa_stream_get_state(s);
+ TRACE("Stream state changed to %i\n", state);
+ pthread_cond_signal(&pulse_cond);
+}
+
HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys,
UINT *num, UINT *def_index)
{
--
2.0.0

View File

@ -0,0 +1,65 @@
From 59f7ed9326957728f0c3040b6c1f3a5cef5cde14 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Tue, 3 Nov 2015 16:21:48 -0600
Subject: winepulse: Always mute buffer
Signed-off-by: Andrew Eikum <aeikum@codeweavers.com>
---
dlls/winepulse.drv/mmdevdrv.c | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 3895f4c..0e2bc08 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -572,6 +572,11 @@ static HRESULT pulse_stream_valid(ACImpl *This) {
return S_OK;
}
+static void silence_buffer(pa_sample_format_t format, BYTE *buffer, UINT32 bytes)
+{
+ memset(buffer, format == PA_SAMPLE_U8 ? 0x80 : 0, bytes);
+}
+
static void dump_attr(const pa_buffer_attr *attr) {
TRACE("maxlength: %u\n", attr->maxlength);
TRACE("minreq: %u\n", attr->minreq);
@@ -1315,7 +1320,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
else {
ACPacket *cur_packet = (ACPacket*)((char*)This->tmp_buffer + This->bufsize_bytes);
BYTE *data = This->tmp_buffer;
- memset(This->tmp_buffer, This->ss.format == PA_SAMPLE_U8 ? 0x80 : 0, This->bufsize_bytes);
+ silence_buffer(This->ss.format, This->tmp_buffer, This->bufsize_bytes);
list_init(&This->packet_free_head);
list_init(&This->packet_filled_head);
for (i = 0; i < capture_packets; ++i, ++cur_packet) {
@@ -1985,18 +1990,17 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
return AUDCLNT_E_INVALID_SIZE;
}
- if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
- if (This->ss.format == PA_SAMPLE_U8)
- memset(This->tmp_buffer, 128, written_bytes);
- else
- memset(This->tmp_buffer, 0, written_bytes);
- }
-
This->locked = 0;
- if (This->locked_ptr)
+ if (This->locked_ptr) {
+ if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
+ silence_buffer(This->ss.format, This->locked_ptr, written_bytes);
pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
- else
+ } else {
+ if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
+ silence_buffer(This->ss.format, This->tmp_buffer, written_bytes);
pa_stream_write(This->stream, This->tmp_buffer, written_bytes, pulse_free_noop, 0, PA_SEEK_RELATIVE);
+ }
+
This->pad += written_bytes;
This->locked_ptr = NULL;
TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
--
2.6.2

View File

@ -1,17 +1,18 @@
From ca6122875e93dbcde90fcdb361c84a67ad1a5d44 Mon Sep 17 00:00:00 2001
From 7479a9a46a748bd2a6b386bb20d3fe1c7c467d11 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 5 Jan 2015 11:31:56 +0100
Date: Tue, 3 Nov 2015 16:21:53 -0600
Subject: winepulse: In Shared mode, track device position in bytes
Signed-off-by: Andrew Eikum <aeikum@codeweavers.com>
---
dlls/winepulse.drv/mmdevdrv.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 1ef2ea2..3463cd8 100644
index 0e2bc08..e5ba869 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -2154,8 +2154,12 @@ static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
@@ -2207,8 +2207,12 @@ static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
pthread_mutex_lock(&pulse_lock);
hr = pulse_stream_valid(This);
@ -26,7 +27,7 @@ index 1ef2ea2..3463cd8 100644
pthread_mutex_unlock(&pulse_lock);
return hr;
}
@@ -2180,6 +2184,9 @@ static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
@@ -2233,6 +2237,9 @@ static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
*pos = This->clock_written;
@ -36,7 +37,7 @@ index 1ef2ea2..3463cd8 100644
/* Make time never go backwards */
if (*pos < This->clock_lastpos)
*pos = This->clock_lastpos;
@@ -2248,7 +2255,7 @@ static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
@@ -2301,7 +2308,7 @@ static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
{
ACImpl *This = impl_from_IAudioClock2(iface);
HRESULT hr = AudioClock_GetPosition(&This->IAudioClock_iface, pos, qpctime);
@ -46,5 +47,5 @@ index 1ef2ea2..3463cd8 100644
return hr;
}
--
2.2.1
2.6.2

View File

@ -1,352 +0,0 @@
From ad8e16a8030be3579609e4dc9ce9727b3dbce2da Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 14 Jul 2014 09:50:03 +0200
Subject: [PATCH 12/42] winepulse: Add IAudioRenderClient and
IAudioCaptureClient
---
dlls/winepulse.drv/mmdevdrv.c | 301 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 301 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 37d85ff..01cfd25 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -146,12 +146,27 @@ struct ACImpl {
static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
static const IAudioClientVtbl AudioClient_Vtbl;
+static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
+static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
+static const IAudioClockVtbl AudioClock_Vtbl;
+static const IAudioClock2Vtbl AudioClock2_Vtbl;
+static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
{
return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
}
+static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
+{
+ return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
+}
+
+static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
+{
+ return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
+}
+
/* Following pulseaudio design here, mainloop has the lock taken whenever
* it is handling something for pulse, and the lock is required whenever
* doing any pa_* call that can affect the state in any way
@@ -701,6 +716,11 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev,
return E_OUTOFMEMORY;
This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
+ This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
+ This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
+ This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
+ This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
+ This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
This->dataflow = dataflow;
This->parent = dev;
This->clock_pulse = PA_USEC_INVALID;
@@ -1421,6 +1441,16 @@ static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
if (FAILED(hr))
return hr;
+ 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;
+ }
+
if (*ppv) {
IUnknown_AddRef((IUnknown*)*ppv);
return S_OK;
@@ -1449,6 +1479,277 @@ static const IAudioClientVtbl AudioClient_Vtbl =
AudioClient_GetService
};
+static HRESULT WINAPI AudioRenderClient_QueryInterface(
+ IAudioRenderClient *iface, REFIID riid, void **ppv)
+{
+ 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_IAudioRenderClient))
+ *ppv = iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
+{
+ ACImpl *This = impl_from_IAudioRenderClient(iface);
+ return AudioClient_AddRef(&This->IAudioClient_iface);
+}
+
+static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
+{
+ ACImpl *This = impl_from_IAudioRenderClient(iface);
+ return AudioClient_Release(&This->IAudioClient_iface);
+}
+
+static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
+ UINT32 frames, BYTE **data)
+{
+ ACImpl *This = impl_from_IAudioRenderClient(iface);
+ UINT32 avail, pad, req, bytes = frames * pa_frame_size(&This->ss);
+ HRESULT hr = S_OK;
+ int ret = -1;
+
+ TRACE("(%p)->(%u, %p)\n", This, frames, data);
+
+ if (!data)
+ return E_POINTER;
+ *data = NULL;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr) || This->locked) {
+ pthread_mutex_unlock(&pulse_lock);
+ return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
+ }
+ if (!frames) {
+ pthread_mutex_unlock(&pulse_lock);
+ return S_OK;
+ }
+
+ ACImpl_GetRenderPad(This, &pad);
+ avail = This->bufsize_frames - pad;
+ if (avail < frames || bytes > This->bufsize_bytes) {
+ pthread_mutex_unlock(&pulse_lock);
+ WARN("Wanted to write %u, but only %u available\n", frames, avail);
+ return AUDCLNT_E_BUFFER_TOO_LARGE;
+ }
+
+ This->locked = frames;
+ req = bytes;
+ ret = pa_stream_begin_write(This->stream, &This->locked_ptr, &req);
+ if (ret < 0 || req < bytes) {
+ FIXME("%p Not using pulse locked data: %i %u/%u %u/%u\n", This, ret, req/pa_frame_size(&This->ss), frames, pad, This->bufsize_frames);
+ if (ret >= 0)
+ pa_stream_cancel_write(This->stream);
+ *data = This->tmp_buffer;
+ This->locked_ptr = NULL;
+ } else
+ *data = This->locked_ptr;
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+}
+
+static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
+ IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
+{
+ ACImpl *This = impl_from_IAudioRenderClient(iface);
+ UINT32 written_bytes = written_frames * pa_frame_size(&This->ss);
+
+ TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
+
+ pthread_mutex_lock(&pulse_lock);
+ if (!This->locked || !written_frames) {
+ if (This->locked_ptr)
+ pa_stream_cancel_write(This->stream);
+ This->locked = 0;
+ This->locked_ptr = NULL;
+ pthread_mutex_unlock(&pulse_lock);
+ return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
+ }
+
+ if (This->locked < written_frames) {
+ pthread_mutex_unlock(&pulse_lock);
+ return AUDCLNT_E_INVALID_SIZE;
+ }
+
+ if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
+ if (This->ss.format == PA_SAMPLE_U8)
+ memset(This->tmp_buffer, 128, written_bytes);
+ else
+ memset(This->tmp_buffer, 0, written_bytes);
+ }
+
+ This->locked = 0;
+ if (This->locked_ptr)
+ pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
+ else
+ pa_stream_write(This->stream, This->tmp_buffer, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
+ This->pad += written_bytes;
+ This->locked_ptr = NULL;
+ TRACE("Released %u, pad %u\n", written_frames, This->pad / pa_frame_size(&This->ss));
+ assert(This->pad <= This->bufsize_bytes);
+ pthread_mutex_unlock(&pulse_lock);
+ return S_OK;
+}
+
+static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
+ AudioRenderClient_QueryInterface,
+ AudioRenderClient_AddRef,
+ AudioRenderClient_Release,
+ AudioRenderClient_GetBuffer,
+ AudioRenderClient_ReleaseBuffer
+};
+
+static HRESULT WINAPI AudioCaptureClient_QueryInterface(
+ IAudioCaptureClient *iface, REFIID riid, void **ppv)
+{
+ 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_IAudioCaptureClient))
+ *ppv = iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
+{
+ ACImpl *This = impl_from_IAudioCaptureClient(iface);
+ return IAudioClient_AddRef(&This->IAudioClient_iface);
+}
+
+static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
+{
+ ACImpl *This = impl_from_IAudioCaptureClient(iface);
+ return IAudioClient_Release(&This->IAudioClient_iface);
+}
+
+static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
+ BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
+ UINT64 *qpcpos)
+{
+ ACImpl *This = impl_from_IAudioCaptureClient(iface);
+ HRESULT hr;
+ ACPacket *packet;
+
+ TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
+ devpos, qpcpos);
+
+ if (!data || !frames || !flags)
+ return E_POINTER;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr) || This->locked) {
+ pthread_mutex_unlock(&pulse_lock);
+ return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
+ }
+
+ ACImpl_GetCapturePad(This, NULL);
+ if ((packet = This->locked_ptr)) {
+ *frames = This->capture_period / pa_frame_size(&This->ss);
+ *flags = 0;
+ if (packet->discont)
+ *flags |= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY;
+ if (devpos) {
+ if (packet->discont)
+ *devpos = (This->clock_written + This->capture_period) / pa_frame_size(&This->ss);
+ else
+ *devpos = This->clock_written / pa_frame_size(&This->ss);
+ }
+ if (qpcpos)
+ *qpcpos = packet->qpcpos;
+ *data = packet->data;
+ }
+ else
+ *frames = 0;
+ This->locked = *frames;
+ pthread_mutex_unlock(&pulse_lock);
+ return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
+}
+
+static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
+ IAudioCaptureClient *iface, UINT32 done)
+{
+ ACImpl *This = impl_from_IAudioCaptureClient(iface);
+
+ TRACE("(%p)->(%u)\n", This, done);
+
+ pthread_mutex_lock(&pulse_lock);
+ if (!This->locked && done) {
+ pthread_mutex_unlock(&pulse_lock);
+ return AUDCLNT_E_OUT_OF_ORDER;
+ }
+ if (done && This->locked != done) {
+ pthread_mutex_unlock(&pulse_lock);
+ return AUDCLNT_E_INVALID_SIZE;
+ }
+ if (done) {
+ ACPacket *packet = This->locked_ptr;
+ This->locked_ptr = NULL;
+ This->pad -= This->capture_period;
+ if (packet->discont)
+ This->clock_written += 2 * This->capture_period;
+ else
+ This->clock_written += This->capture_period;
+ list_add_tail(&This->packet_free_head, &packet->entry);
+ }
+ This->locked = 0;
+ pthread_mutex_unlock(&pulse_lock);
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
+ IAudioCaptureClient *iface, UINT32 *frames)
+{
+ ACImpl *This = impl_from_IAudioCaptureClient(iface);
+ ACPacket *p;
+
+ TRACE("(%p)->(%p)\n", This, frames);
+ if (!frames)
+ return E_POINTER;
+
+ pthread_mutex_lock(&pulse_lock);
+ ACImpl_GetCapturePad(This, NULL);
+ p = This->locked_ptr;
+ if (p)
+ *frames = This->capture_period / pa_frame_size(&This->ss);
+ else
+ *frames = 0;
+ pthread_mutex_unlock(&pulse_lock);
+ return S_OK;
+}
+
+static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
+{
+ AudioCaptureClient_QueryInterface,
+ AudioCaptureClient_AddRef,
+ AudioCaptureClient_Release,
+ AudioCaptureClient_GetBuffer,
+ AudioCaptureClient_ReleaseBuffer,
+ AudioCaptureClient_GetNextPacketSize
+};
+
HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
IAudioSessionManager2 **out)
{
--
2.0.0

View File

@ -0,0 +1,50 @@
From f7f8676db491fee877020ad654fe46f0099e7274 Mon Sep 17 00:00:00 2001
From: Mark Harmstone <mark@harmstone.com>
Date: Tue, 3 Nov 2015 16:22:09 -0600
Subject: winepulse: add stub for GetPropValue
Edited by Maarten Lankhorst: No support for multiple devices in winepulse yet.
Signed-off-by: Andrew Eikum <aeikum@codeweavers.com>
---
dlls/winepulse.drv/mmdevdrv.c | 7 +++++++
dlls/winepulse.drv/winepulse.drv.spec | 1 +
2 files changed, 8 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index e5ba869..64e6c2a 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -50,6 +50,7 @@
#include "initguid.h"
#include "ks.h"
#include "ksmedia.h"
+#include "propkey.h"
#include "mmdeviceapi.h"
#include "audioclient.h"
#include "endpointvolume.h"
@@ -3198,3 +3199,9 @@ HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
*out = &This->IAudioSessionManager2_iface;
return S_OK;
}
+
+HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
+{
+ TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
+ return E_NOTIMPL;
+}
diff --git a/dlls/winepulse.drv/winepulse.drv.spec b/dlls/winepulse.drv/winepulse.drv.spec
index 288de87..7aeb175 100644
--- a/dlls/winepulse.drv/winepulse.drv.spec
+++ b/dlls/winepulse.drv/winepulse.drv.spec
@@ -3,6 +3,7 @@
@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
@ stdcall -private GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint
@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
+@ stdcall -private GetPropValue(ptr ptr ptr) AUDDRV_GetPropValue
# WinMM driver functions
@ stdcall -private DriverProc(long long long long long) winealsa.drv.DriverProc
--
2.6.2

View File

@ -1,209 +0,0 @@
From d9c51b0534a261c95cea362195dcba1688be815b Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 14 Jul 2014 09:50:03 +0200
Subject: [PATCH 13/42] winepulse: Add IAudioClock and IAudioClock2
---
dlls/winepulse.drv/mmdevdrv.c | 172 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 172 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 01cfd25..3ed2288 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -167,6 +167,16 @@ static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
}
+static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
+{
+ return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
+}
+
+static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
+{
+ return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
+}
+
/* Following pulseaudio design here, mainloop has the lock taken whenever
* it is handling something for pulse, and the lock is required whenever
* doing any pa_* call that can affect the state in any way
@@ -1449,6 +1459,8 @@ static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
if (This->dataflow != eCapture)
return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
*ppv = &This->IAudioCaptureClient_iface;
+ } else if (IsEqualIID(riid, &IID_IAudioClock)) {
+ *ppv = &This->IAudioClock_iface;
}
if (*ppv) {
@@ -1750,6 +1762,166 @@ static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
AudioCaptureClient_GetNextPacketSize
};
+static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
+ REFIID riid, void **ppv)
+{
+ ACImpl *This = impl_from_IAudioClock(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_IAudioClock))
+ *ppv = iface;
+ else if (IsEqualIID(riid, &IID_IAudioClock2))
+ *ppv = &This->IAudioClock2_iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
+{
+ ACImpl *This = impl_from_IAudioClock(iface);
+ return IAudioClient_AddRef(&This->IAudioClient_iface);
+}
+
+static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
+{
+ ACImpl *This = impl_from_IAudioClock(iface);
+ return IAudioClient_Release(&This->IAudioClient_iface);
+}
+
+static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
+{
+ ACImpl *This = impl_from_IAudioClock(iface);
+ HRESULT hr;
+
+ TRACE("(%p)->(%p)\n", This, freq);
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (SUCCEEDED(hr))
+ *freq = This->ss.rate * pa_frame_size(&This->ss);
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+}
+
+static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
+ UINT64 *qpctime)
+{
+ ACImpl *This = impl_from_IAudioClock(iface);
+ pa_usec_t time;
+ HRESULT hr;
+
+ TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
+
+ if (!pos)
+ return E_POINTER;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr)) {
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+ }
+
+ *pos = This->clock_written;
+ if (This->clock_pulse != PA_USEC_INVALID && pa_stream_get_time(This->stream, &time) >= 0) {
+ UINT32 delta = pa_usec_to_bytes(time - This->clock_pulse, &This->ss);
+ if (delta < This->pad)
+ *pos += delta;
+ else
+ *pos += This->pad;
+ }
+
+ /* Make time never go backwards */
+ if (*pos < This->clock_lastpos)
+ *pos = This->clock_lastpos;
+ else
+ This->clock_lastpos = *pos;
+ pthread_mutex_unlock(&pulse_lock);
+
+ TRACE("%p Position: %u\n", This, (unsigned)*pos);
+
+ if (qpctime) {
+ LARGE_INTEGER stamp, freq;
+ QueryPerformanceCounter(&stamp);
+ QueryPerformanceFrequency(&freq);
+ *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
+ }
+
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
+ DWORD *chars)
+{
+ ACImpl *This = impl_from_IAudioClock(iface);
+
+ TRACE("(%p)->(%p)\n", This, chars);
+
+ if (!chars)
+ return E_POINTER;
+
+ *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
+
+ return S_OK;
+}
+
+static const IAudioClockVtbl AudioClock_Vtbl =
+{
+ AudioClock_QueryInterface,
+ AudioClock_AddRef,
+ AudioClock_Release,
+ AudioClock_GetFrequency,
+ AudioClock_GetPosition,
+ AudioClock_GetCharacteristics
+};
+
+static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
+ REFIID riid, void **ppv)
+{
+ ACImpl *This = impl_from_IAudioClock2(iface);
+ return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
+}
+
+static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
+{
+ ACImpl *This = impl_from_IAudioClock2(iface);
+ return IAudioClient_AddRef(&This->IAudioClient_iface);
+}
+
+static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
+{
+ ACImpl *This = impl_from_IAudioClock2(iface);
+ return IAudioClient_Release(&This->IAudioClient_iface);
+}
+
+static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
+ UINT64 *pos, UINT64 *qpctime)
+{
+ ACImpl *This = impl_from_IAudioClock2(iface);
+ HRESULT hr = AudioClock_GetPosition(&This->IAudioClock_iface, pos, qpctime);
+ if (SUCCEEDED(hr))
+ *pos /= pa_frame_size(&This->ss);
+ return hr;
+}
+
+static const IAudioClock2Vtbl AudioClock2_Vtbl =
+{
+ AudioClock2_QueryInterface,
+ AudioClock2_AddRef,
+ AudioClock2_Release,
+ AudioClock2_GetDevicePosition
+};
+
HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
IAudioSessionManager2 **out)
{
--
2.0.0

View File

@ -1,17 +1,31 @@
From 044624c3318488cb5c540797bd03994e18a221fd Mon Sep 17 00:00:00 2001
From 690e73e4d7201a602f06b6d7d6896ed766f1c9f2 Mon Sep 17 00:00:00 2001
From: Mark Harmstone <mark@harmstone.com>
Date: Wed, 10 Dec 2014 18:08:41 +0000
Date: Tue, 3 Nov 2015 16:22:28 -0600
Subject: winepulse: return PKEY_AudioEndpoint_PhysicalSpeakers device prop
Edited by Maarten Lankhorst: No support for multiple devices in winepulse yet.
Synchronous static data initialization by Andrew Eikum <aeikum@codeweavers.com>
Signed-off-by: Andrew Eikum <aeikum@codeweavers.com>
---
dlls/winepulse.drv/mmdevdrv.c | 91 +++++++++++++++++++++++++++++++------------
1 file changed, 67 insertions(+), 24 deletions(-)
dlls/winepulse.drv/mmdevdrv.c | 84 +++++++++++++++++++++++++++++++------------
1 file changed, 61 insertions(+), 23 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 445c242..c66ba54 100644
index 64e6c2a..9aa1537 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -322,13 +322,44 @@ static const enum pa_channel_position pulse_pos_from_wfx[] = {
@@ -79,6 +79,8 @@ static pthread_mutex_t pulse_lock;
static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
static struct list g_sessions = LIST_INIT(g_sessions);
+static UINT g_phys_speakers_mask = 0;
+
/* Mixer format + period times */
static WAVEFORMATEXTENSIBLE pulse_fmt[2];
static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
@@ -330,13 +332,44 @@ static const enum pa_channel_position pulse_pos_from_wfx[] = {
PA_CHANNEL_POSITION_TOP_REAR_RIGHT
};
@ -57,7 +71,7 @@ index 445c242..c66ba54 100644
unsigned int length = 0;
pa_channel_map_init_auto(&map, 2, PA_CHANNEL_MAP_ALSA);
@@ -391,28 +422,7 @@ static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
@@ -401,28 +434,7 @@ static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
else
fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
@ -86,70 +100,60 @@ index 445c242..c66ba54 100644
+ fmt->dwChannelMask = pulse_channel_map_to_channel_mask(&map);
}
typedef struct tagLANGANDCODEPAGE
@@ -3632,6 +3642,10 @@ static void pulse_prop_values_sink_info_cb(pa_context *c, const pa_sink_info *i,
st->pv->vt = VT_UI4;
st->pv->u.ulVal = Speakers;
st->hr = S_OK;
+ } else if (IsEqualPropertyKey(*st->prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
+ st->pv->vt = VT_UI4;
+ st->pv->u.ulVal = pulse_channel_map_to_channel_mask(&i->channel_map);
+ st->hr = S_OK;
} else
st->hr = E_NOTIMPL;
}
@@ -3658,6 +3672,16 @@ static void pulse_prop_values_source_info_cb(pa_context *c, const pa_source_info
pthread_cond_signal(&pulse_cond);
static HRESULT pulse_connect(void)
@@ -492,6 +504,14 @@ fail:
return E_FAIL;
}
+/* For default Pulseaudio render device, OR together all of the
+ * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
+static void pulse_phys_speakers_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata)
+{
+ PROPVARIANT *pv = userdata;
+
+ if (i)
+ pv->u.ulVal |= pulse_channel_map_to_channel_mask(&i->channel_map);
+
+ pthread_cond_signal(&pulse_cond);
+ g_phys_speakers_mask |= pulse_channel_map_to_channel_mask(&i->channel_map);
+}
+
HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
{
struct pulse_prop_values_info_cb_data userdata;
@@ -3667,6 +3691,24 @@ HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARI
/* some poorly-behaved applications call audio functions during DllMain, so we
* have to do as much as possible without creating a new thread. this function
* sets up a synchronous connection to verify the server is running and query
@@ -501,6 +521,7 @@ static HRESULT pulse_test_connect(void)
int len, ret;
WCHAR path[PATH_MAX], *name;
char *str;
+ pa_operation *o;
TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
pulse_ml = pa_mainloop_new();
+ if (IsEqualGUID(guid, &pulse_render_guid) && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
+ /* For default Pulseaudio render device, OR together all of the
+ * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
+
+ out->vt = VT_UI4;
+ out->u.ulVal = 0;
+
+ pthread_mutex_lock(&pulse_lock);
+ o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, out);
+ if (o) {
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ pa_operation_unref(o);
+ }
+ pthread_mutex_unlock(&pulse_lock);
+ return o ? S_OK : E_FAIL;
@@ -549,6 +570,15 @@ static HRESULT pulse_test_connect(void)
pulse_probe_settings(1, &pulse_fmt[0]);
pulse_probe_settings(0, &pulse_fmt[1]);
+ g_phys_speakers_mask = 0;
+ o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, NULL);
+ if (o) {
+ while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
+ pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ {}
+ pa_operation_unref(o);
+ }
+
if (IsEqualGUID(guid, &pulse_render_guid) || IsEqualGUID(guid, &pulse_capture_guid))
return E_NOTIMPL;
@@ -3676,7 +3718,8 @@ HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARI
}
if (!IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_FormFactor) &&
- !IsEqualPropertyKey(*prop, devicepath_key)) {
+ !IsEqualPropertyKey(*prop, devicepath_key) &&
+ (flow == eCapture || !IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers))) {
return E_NOTIMPL;
}
pa_context_unref(pulse_ctx);
pulse_ctx = NULL;
pa_mainloop_free(pulse_ml);
@@ -3203,5 +3233,13 @@ HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
{
TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
+
+ if (IsEqualGUID(guid, &pulse_render_guid) && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
+ out->vt = VT_UI4;
+ out->u.ulVal = g_phys_speakers_mask;
+
+ return out->u.ulVal ? S_OK : E_FAIL;
+ }
+
return E_NOTIMPL;
}
--
2.2.1
2.6.2

View File

@ -1,285 +0,0 @@
From 9cefd9930adad40581db955cf9291708e282d8b5 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 14 Jul 2014 09:50:03 +0200
Subject: [PATCH 14/42] winepulse: Add audiostreamvolume
---
Pulse allows streams to set volume, but for various reasons it's
better off being disabled by default.
It can be enabled with HKCU\Software\Wine\Pulse\StreamVol=0x1
---
dlls/winepulse.drv/mmdevdrv.c | 236 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 236 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 3ed2288..b7414c2 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -177,6 +177,11 @@ static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
}
+static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
+{
+ return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
+}
+
/* Following pulseaudio design here, mainloop has the lock taken whenever
* it is handling something for pulse, and the lock is required whenever
* doing any pa_* call that can affect the state in any way
@@ -444,6 +449,12 @@ static void pulse_op_cb(pa_stream *s, int success, void *user) {
pthread_cond_signal(&pulse_cond);
}
+static void pulse_ctx_op_cb(pa_context *c, int success, void *user) {
+ TRACE("Success: %i\n", success);
+ *(int*)user = success;
+ pthread_cond_signal(&pulse_cond);
+}
+
static void pulse_attr_update(pa_stream *s, void *user) {
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s);
TRACE("New attributes or device moved:\n");
@@ -1461,6 +1472,8 @@ static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
*ppv = &This->IAudioCaptureClient_iface;
} else if (IsEqualIID(riid, &IID_IAudioClock)) {
*ppv = &This->IAudioClock_iface;
+ } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) {
+ *ppv = &This->IAudioStreamVolume_iface;
}
if (*ppv) {
@@ -1922,6 +1935,229 @@ static const IAudioClock2Vtbl AudioClock2_Vtbl =
AudioClock2_GetDevicePosition
};
+static HRESULT WINAPI AudioStreamVolume_QueryInterface(
+ IAudioStreamVolume *iface, REFIID riid, void **ppv)
+{
+ 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_IAudioStreamVolume))
+ *ppv = iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+ return IAudioClient_AddRef(&This->IAudioClient_iface);
+}
+
+static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+ return IAudioClient_Release(&This->IAudioClient_iface);
+}
+
+static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
+ IAudioStreamVolume *iface, UINT32 *out)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+
+ TRACE("(%p)->(%p)\n", This, out);
+
+ if (!out)
+ return E_POINTER;
+
+ *out = This->ss.channels;
+
+ return S_OK;
+}
+
+struct pulse_info_cb_data {
+ UINT32 n;
+ float *levels;
+};
+
+static void pulse_sink_input_info_cb(pa_context *c, const pa_sink_input_info *info, int eol, void *data)
+{
+ struct pulse_info_cb_data *d = data;
+ int i;
+ if (eol)
+ return;
+ for (i = 0; i < d->n; ++i)
+ d->levels[i] = (float)info->volume.values[i] / (float)PA_VOLUME_NORM;
+ pthread_cond_signal(&pulse_cond);
+}
+
+static void pulse_source_info_cb(pa_context *c, const pa_source_info *info, int eol, void *data)
+{
+ struct pulse_info_cb_data *d = data;
+ int i;
+ if (eol)
+ return;
+ for (i = 0; i < d->n; ++i)
+ d->levels[i] = (float)info->volume.values[i] / (float)PA_VOLUME_NORM;
+ pthread_cond_signal(&pulse_cond);
+}
+
+static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
+ IAudioStreamVolume *iface, UINT32 count, const float *levels)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+ pa_operation *o;
+ HRESULT hr;
+ int success = 0, i;
+ pa_cvolume cv;
+
+ TRACE("(%p)->(%d, %p)\n", This, count, levels);
+
+ if (!levels)
+ return E_POINTER;
+
+ if (count != This->ss.channels)
+ return E_INVALIDARG;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr))
+ goto out;
+
+ if (pulse_stream_volume) {
+ cv.channels = count;
+ for (i = 0; i < cv.channels; ++i)
+ cv.values[i] = levels[i] * (float)PA_VOLUME_NORM;
+ if (This->dataflow == eRender)
+ o = pa_context_set_sink_input_volume(pulse_ctx, pa_stream_get_index(This->stream), &cv, pulse_ctx_op_cb, &success);
+ else
+ o = pa_context_set_source_volume_by_index(pulse_ctx, pa_stream_get_device_index(This->stream), &cv, pulse_ctx_op_cb, &success);
+ if (o) {
+ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ pa_operation_unref(o);
+ }
+ if (!success)
+ hr = AUDCLNT_E_BUFFER_ERROR;
+ } else {
+ int i;
+ for (i = 0; i < count; ++i)
+ This->vol[i] = levels[i];
+ }
+
+out:
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+}
+
+static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
+ IAudioStreamVolume *iface, UINT32 count, float *levels)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+ pa_operation *o;
+ HRESULT hr;
+ struct pulse_info_cb_data info;
+
+ TRACE("(%p)->(%d, %p)\n", This, count, levels);
+
+ if (!levels)
+ return E_POINTER;
+
+ if (count != This->ss.channels)
+ return E_INVALIDARG;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr))
+ goto out;
+
+ if (pulse_stream_volume) {
+ info.n = count;
+ info.levels = levels;
+ if (This->dataflow == eRender)
+ o = pa_context_get_sink_input_info(pulse_ctx, pa_stream_get_index(This->stream), pulse_sink_input_info_cb, &info);
+ else
+ o = pa_context_get_source_info_by_index(pulse_ctx, pa_stream_get_device_index(This->stream), pulse_source_info_cb, &info);
+ if (o) {
+ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ pa_operation_unref(o);
+ } else
+ hr = AUDCLNT_E_BUFFER_ERROR;
+ } else {
+ int i;
+ for (i = 0; i < count; ++i)
+ levels[i] = This->vol[i];
+ }
+
+out:
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+}
+
+static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
+ IAudioStreamVolume *iface, UINT32 index, float level)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+ HRESULT hr;
+ float volumes[PA_CHANNELS_MAX];
+
+ TRACE("(%p)->(%d, %f)\n", This, index, level);
+
+ if (level < 0.f || level > 1.f)
+ return E_INVALIDARG;
+
+ if (index >= This->ss.channels)
+ return E_INVALIDARG;
+
+ hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
+ volumes[index] = level;
+ if (SUCCEEDED(hr))
+ hr = AudioStreamVolume_SetAllVolumes(iface, This->ss.channels, volumes);
+ return hr;
+}
+
+static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
+ IAudioStreamVolume *iface, UINT32 index, float *level)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+ float volumes[PA_CHANNELS_MAX];
+ HRESULT hr;
+
+ TRACE("(%p)->(%d, %p)\n", This, index, level);
+
+ if (!level)
+ return E_POINTER;
+
+ if (index >= This->ss.channels)
+ return E_INVALIDARG;
+
+ hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
+ if (SUCCEEDED(hr))
+ *level = volumes[index];
+ return hr;
+}
+
+static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
+{
+ AudioStreamVolume_QueryInterface,
+ AudioStreamVolume_AddRef,
+ AudioStreamVolume_Release,
+ AudioStreamVolume_GetChannelCount,
+ AudioStreamVolume_SetChannelVolume,
+ AudioStreamVolume_GetChannelVolume,
+ AudioStreamVolume_SetAllVolumes,
+ AudioStreamVolume_GetAllVolumes
+};
+
HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
IAudioSessionManager2 **out)
{
--
2.0.0

View File

@ -0,0 +1,26 @@
From 9d4e023a3c2f57e3e85ac406135a8a41064b3311 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Tue, 3 Nov 2015 16:22:53 -0600
Subject: winepulse: Prefer PulseAudio driver
Signed-off-by: Andrew Eikum <aeikum@codeweavers.com>
---
dlls/winepulse.drv/mmdevdrv.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 9aa1537..b2f83c8 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -869,7 +869,7 @@ int WINAPI AUDDRV_GetPriority(void)
pthread_mutex_lock(&pulse_lock);
hr = pulse_test_connect();
pthread_mutex_unlock(&pulse_lock);
- return SUCCEEDED(hr) ? Priority_Low : Priority_Unavailable;
+ return SUCCEEDED(hr) ? Priority_Preferred : Priority_Unavailable;
}
HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
--
2.6.2

View File

@ -0,0 +1,23 @@
From f0fe2f14297ba745299f3dd11871dad288666c63 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Wed, 4 Nov 2015 02:20:43 +0100
Subject: winepulse.drv: Use delay import for winealsa.drv.
---
dlls/winepulse.drv/Makefile.in | 1 +
1 file changed, 1 insertion(+)
diff --git a/dlls/winepulse.drv/Makefile.in b/dlls/winepulse.drv/Makefile.in
index d660063..5671c0d 100644
--- a/dlls/winepulse.drv/Makefile.in
+++ b/dlls/winepulse.drv/Makefile.in
@@ -1,5 +1,6 @@
MODULE = winepulse.drv
IMPORTS = dxguid uuid winmm user32 advapi32 ole32
+DELAYIMPORTS = winealsa.drv
EXTRALIBS = $(PULSE_LIBS) $(PTHREAD_LIBS)
EXTRAINCL = $(PULSE_CFLAGS)
--
2.6.2

View File

@ -1,35 +0,0 @@
From 48782b64e7a62612d8166e74271dbeb1cd83bb3c Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 14 Jul 2014 09:50:03 +0200
Subject: [PATCH 16/42] fix fdels trailing whitespaces
Happy? :P
---
dlls/winepulse.drv/mmdevdrv.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 64ee62e..5a71a3d 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -1898,7 +1898,7 @@ static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
TRACE("(%p)->(%p)\n", This, frames);
if (!frames)
return E_POINTER;
-
+
pthread_mutex_lock(&pulse_lock);
ACImpl_GetCapturePad(This, NULL);
p = This->locked_ptr;
@@ -1998,7 +1998,7 @@ static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
else
*pos += This->pad;
}
-
+
/* Make time never go backwards */
if (*pos < This->clock_lastpos)
*pos = This->clock_lastpos;
--
2.0.0

View File

@ -0,0 +1,146 @@
From cacb40ffeaf3d0c09d5d78479a1b93b3f7434240 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Wed, 4 Nov 2015 02:57:56 +0100
Subject: winepulse.drv: Use a separate mainloop and ctx for
pulse_test_connect.
---
dlls/winepulse.drv/mmdevdrv.c | 57 ++++++++++++++++++++-----------------------
1 file changed, 26 insertions(+), 31 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index b2f83c8..36822a4 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -363,7 +363,7 @@ static DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map) {
return mask;
}
-static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
+static void pulse_probe_settings(pa_mainloop *ml, pa_context *ctx, int render, WAVEFORMATEXTENSIBLE *fmt) {
WAVEFORMATEX *wfx = &fmt->Format;
pa_stream *stream;
pa_channel_map map;
@@ -382,7 +382,7 @@ static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
attr.minreq = attr.fragsize = pa_frame_size(&ss);
attr.prebuf = 0;
- stream = pa_stream_new(pulse_ctx, "format test stream", &ss, &map);
+ stream = pa_stream_new(ctx, "format test stream", &ss, &map);
if (stream)
pa_stream_set_state_callback(stream, pulse_stream_state, NULL);
if (!stream)
@@ -393,7 +393,7 @@ static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
else
ret = pa_stream_connect_record(stream, NULL, &attr, PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS);
if (ret >= 0) {
- while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
+ while (pa_mainloop_iterate(ml, 1, &ret) >= 0 &&
pa_stream_get_state(stream) == PA_STREAM_CREATING)
{}
if (pa_stream_get_state(stream) == PA_STREAM_READY) {
@@ -404,7 +404,7 @@ static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
else
length = pa_stream_get_buffer_attr(stream)->fragsize;
pa_stream_disconnect(stream);
- while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
+ while (pa_mainloop_iterate(ml, 1, &ret) >= 0 &&
pa_stream_get_state(stream) == PA_STREAM_READY)
{}
}
@@ -522,10 +522,12 @@ static HRESULT pulse_test_connect(void)
WCHAR path[PATH_MAX], *name;
char *str;
pa_operation *o;
+ pa_mainloop *ml;
+ pa_context *ctx;
- pulse_ml = pa_mainloop_new();
+ ml = pa_mainloop_new();
- pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
+ pa_mainloop_set_poll_func(ml, pulse_poll_func, NULL);
GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*path));
name = strrchrW(path, '\\');
@@ -537,24 +539,23 @@ static HRESULT pulse_test_connect(void)
str = pa_xmalloc(len);
WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
TRACE("Name: %s\n", str);
- pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
+ ctx = pa_context_new(pa_mainloop_get_api(ml), str);
pa_xfree(str);
- if (!pulse_ctx) {
+ if (!ctx) {
ERR("Failed to create context\n");
- pa_mainloop_free(pulse_ml);
- pulse_ml = NULL;
+ pa_mainloop_free(ml);
return E_FAIL;
}
- pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
+ pa_context_set_state_callback(ctx, pulse_contextcallback, NULL);
- TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
- if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
+ TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(ctx), PA_API_VERSION);
+ if (pa_context_connect(ctx, NULL, 0, NULL) < 0)
goto fail;
/* Wait for connection */
- while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0) {
- pa_context_state_t state = pa_context_get_state(pulse_ctx);
+ while (pa_mainloop_iterate(ml, 1, &ret) >= 0) {
+ pa_context_state_t state = pa_context_get_state(ctx);
if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
goto fail;
@@ -564,34 +565,28 @@ static HRESULT pulse_test_connect(void)
}
TRACE("Test-connected to server %s with protocol version: %i.\n",
- pa_context_get_server(pulse_ctx),
- pa_context_get_server_protocol_version(pulse_ctx));
+ pa_context_get_server(ctx),
+ pa_context_get_server_protocol_version(ctx));
- pulse_probe_settings(1, &pulse_fmt[0]);
- pulse_probe_settings(0, &pulse_fmt[1]);
+ pulse_probe_settings(ml, ctx, 1, &pulse_fmt[0]);
+ pulse_probe_settings(ml, ctx, 0, &pulse_fmt[1]);
g_phys_speakers_mask = 0;
- o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, NULL);
+ o = pa_context_get_sink_info_list(ctx, &pulse_phys_speakers_cb, NULL);
if (o) {
- while (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0 &&
+ while (pa_mainloop_iterate(ml, 1, &ret) >= 0 &&
pa_operation_get_state(o) == PA_OPERATION_RUNNING)
{}
pa_operation_unref(o);
}
- pa_context_unref(pulse_ctx);
- pulse_ctx = NULL;
- pa_mainloop_free(pulse_ml);
- pulse_ml = NULL;
-
+ pa_context_unref(ctx);
+ pa_mainloop_free(ml);
return S_OK;
fail:
- pa_context_unref(pulse_ctx);
- pulse_ctx = NULL;
- pa_mainloop_free(pulse_ml);
- pulse_ml = NULL;
-
+ pa_context_unref(ctx);
+ pa_mainloop_free(ml);
return E_FAIL;
}
--
2.6.2

View File

@ -1,4 +1,4 @@
From c70a0c4b7242ae062876ee1a6b74f03f7e60c0d6 Mon Sep 17 00:00:00 2001
From 08980bf07929d455b5891941388f4291319924bb Mon Sep 17 00:00:00 2001
From: Mark Harmstone <mark@harmstone.com>
Date: Mon, 3 Nov 2014 02:06:40 +0000
Subject: winepulse: expose audio devices directly to programs
@ -14,15 +14,30 @@ Changes by Sebastian Lackner <sebastian@fds-team.de>:
* Merge functions set_device_guid and get_device_guid as they are always used together
* Fixed compiler warnings with -Werror
* Some style fixes and better error handling
* Move initialization code to pulse_test_connect()
---
dlls/winepulse.drv/mmdevdrv.c | 233 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 226 insertions(+), 7 deletions(-)
dlls/winepulse.drv/mmdevdrv.c | 260 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 230 insertions(+), 30 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 063b1db..8a75f0e 100644
index 36822a4..9342762 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -84,6 +84,11 @@ static struct list g_sessions = LIST_INIT(g_sessions);
@@ -79,12 +79,25 @@ static pthread_mutex_t pulse_lock;
static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
static struct list g_sessions = LIST_INIT(g_sessions);
+typedef struct _PhysDevice {
+ struct list entry;
+ GUID guid;
+ WCHAR name[0];
+} PhysDevice;
+
static UINT g_phys_speakers_mask = 0;
+static struct list g_phys_speakers = LIST_INIT(g_phys_speakers);
+static struct list g_phys_sources = LIST_INIT(g_phys_sources);
/* Mixer format + period times */
static WAVEFORMATEXTENSIBLE pulse_fmt[2];
static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
@ -34,7 +49,21 @@ index 063b1db..8a75f0e 100644
static GUID pulse_render_guid =
{ 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
static GUID pulse_capture_guid =
@@ -161,6 +166,7 @@ struct ACImpl {
@@ -103,6 +116,13 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
if (pthread_mutex_init(&pulse_lock, &attr) != 0)
pthread_mutex_init(&pulse_lock, NULL);
} else if (reason == DLL_PROCESS_DETACH) {
+ PhysDevice *dev, *dev_next;
+
+ LIST_FOR_EACH_ENTRY_SAFE(dev, dev_next, &g_phys_speakers, PhysDevice, entry)
+ HeapFree(GetProcessHeap(), 0, dev);
+ LIST_FOR_EACH_ENTRY_SAFE(dev, dev_next, &g_phys_sources, PhysDevice, entry)
+ HeapFree(GetProcessHeap(), 0, dev);
+
if (pulse_thread)
SetThreadPriority(pulse_thread, 0);
if (pulse_ctx) {
@@ -164,6 +184,7 @@ struct ACImpl {
IMMDevice *parent;
struct list entry;
float vol[PA_CHANNELS_MAX];
@ -42,47 +71,21 @@ index 063b1db..8a75f0e 100644
LONG ref;
EDataFlow dataflow;
@@ -679,6 +685,9 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
char buffer[64];
static LONG number;
pa_buffer_attr attr;
+ int moving = 0;
+ const char *dev = NULL;
+
if (This->stream) {
pa_stream_disconnect(This->stream);
while (pa_stream_get_state(This->stream) == PA_STREAM_READY)
@@ -703,12 +712,21 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
attr.maxlength = attr.tlength = This->bufsize_bytes;
attr.prebuf = pa_frame_size(&This->ss);
dump_attr(&attr);
+
+ /* If device name is given use exactly the specified device */
+ if (This->device[0]){
+ moving = PA_STREAM_DONT_MOVE;
+ dev = This->device;
+ }
+
if (This->dataflow == eRender)
- ret = pa_stream_connect_playback(This->stream, NULL, &attr,
- PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
+ ret = pa_stream_connect_playback(This->stream, dev, &attr,
+ PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|
+ PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS|moving, NULL, NULL);
else
- ret = pa_stream_connect_record(This->stream, NULL, &attr,
- PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS);
+ ret = pa_stream_connect_record(This->stream, dev, &attr,
+ PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|
+ PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS|moving);
if (ret < 0) {
WARN("Returns %i\n", ret);
return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
@@ -727,11 +745,131 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
return S_OK;
@@ -186,8 +207,6 @@ struct ACImpl {
struct list packet_filled_head;
};
-static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
-
static const IAudioClientVtbl AudioClient_Vtbl;
static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
@@ -504,12 +523,92 @@ fail:
return E_FAIL;
}
-HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys,
-/* For default Pulseaudio render device, OR together all of the
- * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
+static BOOL get_device_guid(EDataFlow flow, const char *device, GUID *guid)
+{
+ HKEY key, dev_key;
@ -132,114 +135,193 @@ index 063b1db..8a75f0e 100644
+ return TRUE;
+}
+
+struct pulse_all_info_cb_data {
+ EDataFlow flow;
+ WCHAR **ids;
+ GUID *keys;
+ UINT num;
+};
+
+static void pulse_all_sink_info_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) {
+ struct pulse_all_info_cb_data *st = userdata;
+ void *tmp;
+ DWORD len;
+
+ if (!i) goto out;
+
+ tmp = HeapReAlloc(GetProcessHeap(), 0, st->ids, sizeof(WCHAR*) * (st->num + 1));
+ if (!tmp) goto out;
+ st->ids = tmp;
+
+ tmp = HeapReAlloc(GetProcessHeap(), 0, st->keys, sizeof(GUID) * (st->num + 1));
+ if (!tmp) goto out;
+ st->keys = tmp;
+
+ len = MultiByteToWideChar(CP_UTF8, 0, i->description, -1, NULL, 0);
+ if (!len) goto out;
+
+ st->ids[st->num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ if (!st->ids[st->num]) goto out;
+
+ MultiByteToWideChar(CP_UTF8, 0, i->description, -1, st->ids[st->num], len);
+ if (!get_device_guid(st->flow, i->name, &(st->keys[st->num])))
+ CoCreateGuid(&(st->keys[st->num]));
+
+ st->num++;
+
+out:
+ pthread_cond_signal(&pulse_cond);
+static void pulse_add_device(struct list *list, GUID *guid, const char *name)
+{
+ int len = MultiByteToWideChar(CP_UNIXCP, 0, name, -1, NULL, 0);
+ if (len) {
+ PhysDevice *dev = HeapAlloc(GetProcessHeap(), 0, offsetof(PhysDevice, name[len]));
+ if (dev) {
+ MultiByteToWideChar(CP_UNIXCP, 0, name, -1, dev->name, len);
+ dev->guid = *guid;
+ list_add_tail(list, &dev->entry);
+ }
+ }
+}
+
+static void pulse_all_source_info_cb(pa_context *c, const pa_source_info *i, int eol, void *userdata) {
+ struct pulse_all_info_cb_data *st = userdata;
+ void *tmp;
+ DWORD len;
+
+ if (!i) goto out;
+
+ tmp = HeapReAlloc(GetProcessHeap(), 0, st->ids, sizeof(WCHAR*) * (st->num + 1));
+ if (!tmp) goto out;
+ st->ids = tmp;
+
+ tmp = HeapReAlloc(GetProcessHeap(), 0, st->keys, sizeof(GUID) * (st->num + 1));
+ if (!tmp) goto out;
+ st->keys = tmp;
+
+ len = MultiByteToWideChar(CP_UTF8, 0, i->description, -1, NULL, 0);
+ if (!len) goto out;
+
+ st->ids[st->num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ if (!st->ids[st->num]) goto out;
+
+ MultiByteToWideChar(CP_UTF8, 0, i->description, -1, st->ids[st->num], len);
+ if (!get_device_guid(st->flow, i->name, &(st->keys[st->num])))
+ CoCreateGuid(&(st->keys[st->num]));
+
+ st->num++;
+
+out:
+ pthread_cond_signal(&pulse_cond);
+}
+
+HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **keys,
UINT *num, UINT *def_index)
static void pulse_phys_speakers_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata)
{
HRESULT hr = S_OK;
WCHAR *id;
+ pa_operation* o;
+ struct pulse_all_info_cb_data st;
TRACE("%d %p %p %p\n", flow, ids, num, def_index);
@@ -765,6 +903,27 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **
else
(*keys)[0] = pulse_capture_guid;
+ st.flow = flow;
+ st.ids = *ids;
+ st.keys = *keys;
+ st.num = *num;
- if (i)
+ GUID guid;
+
+ pthread_mutex_lock(&pulse_lock);
+ if (flow == eRender)
+ o = pa_context_get_sink_info_list(pulse_ctx, &pulse_all_sink_info_cb, &st);
+ else
+ o = pa_context_get_source_info_list(pulse_ctx, &pulse_all_source_info_cb, &st);
+ if (i) {
+ /* For default Pulseaudio render device, OR together all of the
+ * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
g_phys_speakers_mask |= pulse_channel_map_to_channel_mask(&i->channel_map);
+
+ if (!get_device_guid(eRender, i->name, &guid))
+ CoCreateGuid(&guid);
+ pulse_add_device(&g_phys_speakers, &guid, i->description);
+ }
+}
+
+static void pulse_phys_sources_cb(pa_context *c, const pa_source_info *i, int eol, void *userdata)
+{
+ GUID guid;
+
+ if (i) {
+ if (!get_device_guid(eCapture, i->name, &guid))
+ CoCreateGuid(&guid);
+ pulse_add_device(&g_phys_sources, &guid, i->description);
+ }
}
/* some poorly-behaved applications call audio functions during DllMain, so we
@@ -525,6 +624,10 @@ static HRESULT pulse_test_connect(void)
pa_mainloop *ml;
pa_context *ctx;
+ /* Make sure we never run this function twice accidentially */
+ if (!list_empty(&g_phys_speakers))
+ return S_OK;
+
ml = pa_mainloop_new();
pa_mainloop_set_poll_func(ml, pulse_poll_func, NULL);
@@ -572,6 +675,9 @@ static HRESULT pulse_test_connect(void)
pulse_probe_settings(ml, ctx, 0, &pulse_fmt[1]);
g_phys_speakers_mask = 0;
+ pulse_add_device(&g_phys_speakers, &pulse_render_guid, "Pulseaudio");
+ pulse_add_device(&g_phys_sources, &pulse_capture_guid, "Pulseaudio");
+
o = pa_context_get_sink_info_list(ctx, &pulse_phys_speakers_cb, NULL);
if (o) {
while (pa_mainloop_iterate(ml, 1, &ret) >= 0 &&
@@ -580,6 +686,14 @@ static HRESULT pulse_test_connect(void)
pa_operation_unref(o);
}
+ o = pa_context_get_source_info_list(ctx, &pulse_phys_sources_cb, NULL);
+ if (o) {
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ while (pa_mainloop_iterate(ml, 1, &ret) >= 0 &&
+ pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ {}
+ pa_operation_unref(o);
+ }
+ pthread_mutex_unlock(&pulse_lock);
+
+ *ids = st.ids;
+ *keys = st.keys;
+ *num = st.num;
pa_context_unref(ctx);
pa_mainloop_free(ml);
return S_OK;
@@ -775,6 +889,9 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
char buffer[64];
static LONG number;
pa_buffer_attr attr;
+ int moving = 0;
+ const char *dev = NULL;
+
if (This->stream) {
pa_stream_disconnect(This->stream);
while (pa_stream_get_state(This->stream) == PA_STREAM_READY)
@@ -799,12 +916,21 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
attr.maxlength = attr.tlength = This->bufsize_bytes;
attr.prebuf = pa_frame_size(&This->ss);
dump_attr(&attr);
+
+ /* If device name is given use exactly the specified device */
+ if (This->device[0]){
+ moving = PA_STREAM_DONT_MOVE;
+ dev = This->device;
+ }
+
if (This->dataflow == eRender)
- ret = pa_stream_connect_playback(This->stream, NULL, &attr,
- PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
+ ret = pa_stream_connect_playback(This->stream, dev, &attr,
+ PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|
+ PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS|moving, NULL, NULL);
else
- ret = pa_stream_connect_record(This->stream, NULL, &attr,
- PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS);
+ ret = pa_stream_connect_record(This->stream, dev, &attr,
+ PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|
+ PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS|moving);
if (ret < 0) {
WARN("Returns %i\n", ret);
return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
@@ -823,39 +949,53 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
return S_OK;
}
@@ -777,20 +936,79 @@ int WINAPI AUDDRV_GetPriority(void)
-HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys,
+HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **keys,
UINT *num, UINT *def_index)
{
+ struct list *list = (flow == eRender) ? &g_phys_speakers : &g_phys_sources;
+ PhysDevice *dev;
+ DWORD count;
WCHAR *id;
TRACE("%d %p %p %p\n", flow, ids, num, def_index);
- *num = 1;
+ *num = count = list_count(list);
*def_index = 0;
- *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(**ids));
- *keys = NULL;
- if (!*ids)
- return E_OUTOFMEMORY;
-
- (*ids)[0] = id = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
- *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys));
- if (!*keys || !id) {
- HeapFree(GetProcessHeap(), 0, id);
- HeapFree(GetProcessHeap(), 0, *keys);
- HeapFree(GetProcessHeap(), 0, *ids);
+ if (!count) {
*ids = NULL;
*keys = NULL;
- return E_OUTOFMEMORY;
+ return E_FAIL;
}
- memcpy(id, defaultW, sizeof(defaultW));
- if (flow == eRender)
- (*keys)[0] = pulse_render_guid;
- else
- (*keys)[0] = pulse_capture_guid;
+ *ids = HeapAlloc(GetProcessHeap(), 0, count * sizeof(**ids));
+ *keys = HeapAlloc(GetProcessHeap(), 0, count * sizeof(**keys));
+ if (!*ids || !*keys) {
+ count = 0;
+ goto err;
+ }
+
+ count = 0;
+ LIST_FOR_EACH_ENTRY(dev, list, PhysDevice, entry) {
+ id = HeapAlloc(GetProcessHeap(), 0, (strlenW(dev->name) + 1) * sizeof(WCHAR));
+ if (!id)
+ goto err;
+ (*ids)[count] = id;
+ (*keys)[count] = dev->guid;
+ strcpyW(id, dev->name);
+ count++;
+ }
return S_OK;
+
+err:
+ while (count)
+ HeapFree(GetProcessHeap(), 0, (*ids)[--count]);
+ HeapFree(GetProcessHeap(), 0, *keys);
+ HeapFree(GetProcessHeap(), 0, *ids);
+ *ids = NULL;
+ *keys = NULL;
+ return E_OUTOFMEMORY;
}
int WINAPI AUDDRV_GetPriority(void)
@@ -867,20 +1007,79 @@ int WINAPI AUDDRV_GetPriority(void)
return SUCCEEDED(hr) ? Priority_Preferred : Priority_Unavailable;
}
@ -303,10 +385,10 @@ index 063b1db..8a75f0e 100644
HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
{
+ char pulse_name[256] = {0};
HRESULT hr;
ACImpl *This;
int i;
EDataFlow dataflow;
HRESULT hr;
TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
+
@ -320,8 +402,8 @@ index 063b1db..8a75f0e 100644
+ return AUDCLNT_E_DEVICE_INVALIDATED;
*out = NULL;
pthread_mutex_lock(&pulse_lock);
@@ -813,6 +1031,7 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
@@ -898,6 +1097,7 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
This->parent = dev;
for (i = 0; i < PA_CHANNELS_MAX; ++i)
This->vol[i] = 1.f;
@ -330,5 +412,5 @@ index 063b1db..8a75f0e 100644
hr = CoCreateFreeThreadedMarshaler((IUnknown*)This, &This->marshal);
if (hr) {
--
2.2.1
2.6.2

View File

@ -1,48 +0,0 @@
From 1f92761fc130d432299df976841b77b60b64efa5 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 14 Jul 2014 09:50:03 +0200
Subject: [PATCH 17/42] winepulse v12
Changes since v11:
- Fix incorrect assertions which may fail on moving a capture device
- Whitespace apply fixes
Changes since v10:
- Make some members static
- Fix small memory leak in GetService
---
dlls/winepulse.drv/mmdevdrv.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 5a71a3d..960af3c 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -596,10 +596,11 @@ static void pulse_rd_loop(ACImpl *This, size_t bytes)
dst = p->data;
while (rem) {
pa_stream_peek(This->stream, (const void**)&src, &src_len);
- assert(src_len && src_len <= bytes);
+ assert(src_len);
assert(This->peek_ofs < src_len);
src += This->peek_ofs;
src_len -= This->peek_ofs;
+ assert(src_len <= bytes);
copy = rem;
if (copy > src_len)
@@ -627,9 +628,10 @@ static void pulse_rd_drop(ACImpl *This, size_t bytes)
while (rem) {
const void *src;
pa_stream_peek(This->stream, &src, &src_len);
- assert(src_len && src_len <= bytes);
+ assert(src_len);
assert(This->peek_ofs < src_len);
src_len -= This->peek_ofs;
+ assert(src_len <= bytes);
copy = rem;
if (copy > src_len)
--
2.0.0

Some files were not shown because too many files have changed in this diff Show More