[PATCH 1/5] winepulse: Add initial stub for pulseaudio support

Andrew Eikum aeikum at codeweavers.com
Thu Oct 29 12:04:11 CDT 2015


From: Maarten Lankhorst <m.b.lankhorst at gmail.com>

Includes API compatibility patch by Juergen Tretthahn <orson at orson.at>.

Synchronous static data initialization by Andrew Eikum <aeikum at codeweavers.com>

Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>
---
 configure.ac                          |  31 +++-
 dlls/mmdevapi/main.c                  |   2 +-
 dlls/winepulse.drv/Makefile.in        |   7 +
 dlls/winepulse.drv/mmdevdrv.c         | 284 ++++++++++++++++++++++++++++++++++
 dlls/winepulse.drv/winepulse.drv.spec |   5 +
 include/config.h.in                   |   6 +
 6 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 3d48b67..23a9613 100644
--- a/configure.ac
+++ b/configure.ac
@@ -72,6 +72,7 @@ AC_ARG_WITH(pcap,      AS_HELP_STRING([--without-pcap],[do not use the Packet Ca
 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]))
 AC_ARG_WITH(v4l,       AS_HELP_STRING([--without-v4l],[do not use v4l1 (v4l support)]))
@@ -1547,6 +1548,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
@@ -1779,13 +1804,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
@@ -3401,6 +3427,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..d660063
--- /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..04d9f19
--- /dev/null
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -0,0 +1,284 @@
+/*
+ * 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
+ */
+
+#define NONAMELESSUNION
+#define COBJMACROS
+#define _GNU_SOURCE
+
+#include "config.h"
+#include <poll.h>
+#include <pthread.h>
+
+#include <stdarg.h>
+#include <unistd.h>
+#include <math.h>
+#include <stdio.h>
+#include <errno.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);
+
+/* From <dlls/mmdevapi/mmdevapi.h> */
+enum DriverPriority {
+    Priority_Unavailable = 0,
+    Priority_Low,
+    Priority_Neutral,
+    Priority_Preferred
+};
+
+static const REFERENCE_TIME MinimumPeriod = 30000;
+static const REFERENCE_TIME DefaultPeriod = 100000;
+
+static pa_context *pulse_ctx;
+static pa_mainloop *pulse_ml;
+
+static pthread_mutex_t pulse_lock;
+
+static GUID pulse_render_guid =
+{ 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
+static GUID pulse_capture_guid =
+{ 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
+
+BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
+{
+    if (reason == DLL_PROCESS_ATTACH) {
+        pthread_mutexattr_t attr;
+
+        DisableThreadLibraryCalls(dll);
+
+        pthread_mutexattr_init(&attr);
+        pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
+
+        if (pthread_mutex_init(&pulse_lock, &attr) != 0)
+            pthread_mutex_init(&pulse_lock, NULL);
+    } 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);
+    }
+    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 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;
+    }
+}
+
+
+/* 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
+ * static data. */
+static HRESULT pulse_test_connect(void)
+{
+    int len, ret;
+    WCHAR path[PATH_MAX], *name;
+    char *str;
+
+    pulse_ml = pa_mainloop_new();
+
+    pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
+
+    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");
+        pa_mainloop_free(pulse_ml);
+        pulse_ml = NULL;
+        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 (pa_mainloop_iterate(pulse_ml, 1, &ret) >= 0) {
+        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("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_unref(pulse_ctx);
+    pulse_ctx = NULL;
+    pa_mainloop_free(pulse_ml);
+    pulse_ml = NULL;
+
+    return S_OK;
+
+fail:
+    pa_context_unref(pulse_ctx);
+    pulse_ctx = NULL;
+    pa_mainloop_free(pulse_ml);
+    pulse_ml = NULL;
+
+    return E_FAIL;
+}
+
+HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys,
+        UINT *num, UINT *def_index)
+{
+    WCHAR *id;
+
+    TRACE("%d %p %p %p\n", flow, ids, num, def_index);
+
+    *num = 1;
+    *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);
+        *ids = NULL;
+        *keys = NULL;
+        return E_OUTOFMEMORY;
+    }
+    memcpy(id, defaultW, sizeof(defaultW));
+
+    if (flow == eRender)
+        (*keys)[0] = pulse_render_guid;
+    else
+        (*keys)[0] = pulse_capture_guid;
+
+    return S_OK;
+}
+
+int WINAPI AUDDRV_GetPriority(void)
+{
+    HRESULT hr;
+    pthread_mutex_lock(&pulse_lock);
+    hr = pulse_test_connect();
+    pthread_mutex_unlock(&pulse_lock);
+    return SUCCEEDED(hr) ? Priority_Low : Priority_Unavailable;
+}
+
+HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
+{
+    TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
+    *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..612bf46
--- /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 ptr) AUDDRV_GetAudioEndpoint
+@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
diff --git a/include/config.h.in b/include/config.h.in
index eb61a94..817b5f2 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -696,6 +696,12 @@
 /* Define to 1 if you have the <pthread_np.h> header file. */
 #undef HAVE_PTHREAD_NP_H
 
+/* Define if you have pulseaudio */
+#undef HAVE_PULSEAUDIO
+
+/* Define to 1 if you have the <pulse/pulseaudio.h> header file. */
+#undef HAVE_PULSE_PULSEAUDIO_H
+
 /* Define to 1 if you have the <pwd.h> header file. */
 #undef HAVE_PWD_H
 
-- 
2.6.2





More information about the wine-patches mailing list