Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 683132 Details for
Bug 892617
wine-1.5.21 is available
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
[patch]
wine-pulse for 1.5.21
wine-pulse-1.5.21.patch (text/plain), 163.82 KB, created by
Michael Cronenworth
on 2013-01-19 16:54:28 UTC
(
hide
)
Description:
wine-pulse for 1.5.21
Filename:
MIME Type:
Creator:
Michael Cronenworth
Created:
2013-01-19 16:54:28 UTC
Size:
163.82 KB
patch
obsolete
>From d59e37be456055d1debb185e656289ff0d0da0fd Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <m.b.lankhorst@gmail.com> >Date: Mon, 7 Jan 2013 14:11:35 +0100 >Subject: [PATCH 09/35] winepulse: Add initial stub for pulseaudio support > >--- >Just the basic of initialization and pulseaudio mainloop support is >added here. >--- > configure | 97 +++++++++++- > configure.ac | 31 +++- > dlls/mmdevapi/main.c | 2 +- > dlls/winepulse.drv/Makefile.in | 9 ++ > dlls/winepulse.drv/mmdevdrv.c | 290 ++++++++++++++++++++++++++++++++++ > dlls/winepulse.drv/winepulse.drv.spec | 5 + > 6 files changed, 429 insertions(+), 5 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 b/configure >index ca5da34..c9f15db 100755 >--- a/configure >+++ b/configure >@@ -649,6 +649,8 @@ OSS4INCL > ALSALIBS > GSTREAMER_INCL > GSTREAMER_LIBS >+PULSEINCL >+PULSELIBS > LIBGETTEXTPO > ZLIB > FREETYPEINCL >@@ -826,6 +828,7 @@ with_osmesa > with_oss > with_png > with_pthread >+with_pulse > with_sane > with_tiff > with_v4l >@@ -1516,6 +1519,7 @@ Optional Packages: > --without-oss do not use the OSS sound support > --without-png do not use PNG > --without-pthread do not use the pthread library >+ --without-pulse do not use PulseAudio sound support > --without-sane do not use SANE (scanner support) > --without-tiff do not use TIFF > --without-v4l do not use v4l1 (v4l support) >@@ -2707,6 +2711,12 @@ if test "${with_pthread+set}" = set; then : > fi > > >+# Check whether --with-pulse was given. >+if test "${with_pulse+set}" = set; then : >+ withval=$with_pulse; >+fi >+ >+ > # Check whether --with-sane was given. > if test "${with_sane+set}" = set; then : > withval=$with_sane; >@@ -10746,6 +10756,87 @@ esac > fi > fi > >+PULSELIBS="" >+ >+PULSEINCL="" >+ >+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" >+ for ac_header in pulse/pulseaudio.h >+do : >+ ac_fn_c_check_header_mongrel "$LINENO" "pulse/pulseaudio.h" "ac_cv_header_pulse_pulseaudio_h" "$ac_includes_default" >+if test "x$ac_cv_header_pulse_pulseaudio_h" = xyes; then : >+ cat >>confdefs.h <<_ACEOF >+#define HAVE_PULSE_PULSEAUDIO_H 1 >+_ACEOF >+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pa_stream_is_corked in -lpulse" >&5 >+$as_echo_n "checking for pa_stream_is_corked in -lpulse... " >&6; } >+if ${ac_cv_lib_pulse_pa_stream_is_corked+:} false; then : >+ $as_echo_n "(cached) " >&6 >+else >+ ac_check_lib_save_LIBS=$LIBS >+LIBS="-lpulse $ac_pulse_libs $LIBS" >+cat confdefs.h - <<_ACEOF >conftest.$ac_ext >+/* end confdefs.h. */ >+ >+/* Override any GCC internal prototype to avoid an error. >+ Use char because int might match the return type of a GCC >+ builtin and then its argument prototype would still apply. */ >+#ifdef __cplusplus >+extern "C" >+#endif >+char pa_stream_is_corked (); >+int >+main () >+{ >+return pa_stream_is_corked (); >+ ; >+ return 0; >+} >+_ACEOF >+if ac_fn_c_try_link "$LINENO"; then : >+ ac_cv_lib_pulse_pa_stream_is_corked=yes >+else >+ ac_cv_lib_pulse_pa_stream_is_corked=no >+fi >+rm -f core conftest.err conftest.$ac_objext \ >+ conftest$ac_exeext conftest.$ac_ext >+LIBS=$ac_check_lib_save_LIBS >+fi >+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pulse_pa_stream_is_corked" >&5 >+$as_echo "$ac_cv_lib_pulse_pa_stream_is_corked" >&6; } >+if test "x$ac_cv_lib_pulse_pa_stream_is_corked" = xyes; then : >+ >+$as_echo "#define HAVE_PULSEAUDIO 1" >>confdefs.h >+ >+ PULSELIBS="$ac_pulse_libs" >+ PULSEINCL="$ac_pulse_cflags" >+fi >+ >+ >+fi >+ >+done >+ >+ fi >+ CPPFLAGS="$ac_save_CPPFLAGS" >+fi >+if test "$ac_cv_lib_pulse_pa_stream_is_corked" != "yes"; then : >+ case "x$with_pulse" in >+ x) as_fn_append wine_warnings "|libpulse ${notice_platform}development files not found or too old, Pulse won't be supported." ;; >+ xno) ;; >+ *) as_fn_error $? "libpulse ${notice_platform}development files not found or too old, Pulse won't be supported. >+This is an error since --with-pulse was requested." "$LINENO" 5 ;; >+esac >+fi >+ > if test "x$with_gstreamer" != "xno" > then > ac_save_CPPFLAGS="$CPPFLAGS" >@@ -12005,12 +12096,13 @@ fi > > test -n "$ALSALIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no} > test -n "$COREAUDIO" || enable_winecoreaudio_drv=${enable_winecoreaudio_drv:-no} >+test -n "$PULSELIBS" || 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" || enable_winejoystick_drv=${enable_winejoystick_drv:-no} > >-if test "x$ALSALIBS$COREAUDIO" = "x" -a \ >+if test "x$ALSALIBS$COREAUDIO$PULSELIBS" = "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 > as_fn_append wine_warnings "|No sound system was found. Windows applications will be silent." > fi >@@ -15897,6 +15989,7 @@ wine_fn_config_dll winemp3.acm enable_winemp3_acm > wine_fn_config_dll wineoss.drv enable_wineoss_drv > wine_fn_config_dll wineps.drv enable_wineps_drv install-lib,po > wine_fn_config_dll wineps16.drv16 enable_win16 >+wine_fn_config_dll winepulse.drv enable_winepulse_drv > wine_fn_config_dll wineqtdecoder enable_wineqtdecoder > wine_fn_config_dll winex11.drv enable_winex11_drv > wine_fn_config_dll wing.dll16 enable_win16 >diff --git a/configure.ac b/configure.ac >index c06e108..3912c05 100644 >--- a/configure.ac >+++ b/configure.ac >@@ -74,6 +74,7 @@ AC_ARG_WITH(png, AS_HELP_STRING([--without-png],[do not use PNG]), > [if test "x$withval" = "xno"; then ac_cv_header_png_h=no; fi]) > 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]), > [if test "x$withval" = "xno"; then ac_cv_header_tiffio_h=no; fi]) >@@ -1490,6 +1491,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(PULSELIBS,"") >+AC_SUBST(PULSEINCL,"") >+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]) >+ PULSELIBS="$ac_pulse_libs" >+ PULSEINCL="$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 >@@ -1696,13 +1721,14 @@ WINE_CHECK_SONAME(odbc,SQLConnect,,[AC_DEFINE_UNQUOTED(SONAME_LIBODBC,["libodbc. > dnl **** Disable unsupported winmm drivers **** > test -n "$ALSALIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no} > test -n "$COREAUDIO" || enable_winecoreaudio_drv=${enable_winecoreaudio_drv:-no} >+test -n "$PULSELIBS" || 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" || enable_winejoystick_drv=${enable_winejoystick_drv:-no} > > dnl **** Check for any sound system **** >-if test "x$ALSALIBS$COREAUDIO" = "x" -a \ >+if test "x$ALSALIBS$COREAUDIO$PULSELIBS" = "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 >@@ -3072,6 +3098,7 @@ WINE_CONFIG_DLL(winemp3.acm) > WINE_CONFIG_DLL(wineoss.drv) > WINE_CONFIG_DLL(wineps.drv,,[install-lib,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 1518b02..c4d7075 100644 >--- a/dlls/mmdevapi/main.c >+++ b/dlls/mmdevapi/main.c >@@ -110,7 +110,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..0f595f1 >--- /dev/null >+++ b/dlls/winepulse.drv/Makefile.in >@@ -0,0 +1,9 @@ >+MODULE = winepulse.drv >+IMPORTS = dxguid uuid winmm user32 advapi32 ole32 >+EXTRALIBS = @PULSELIBS@ @LIBPTHREAD@ >+EXTRAINCL = @PULSEINCL@ >+ >+C_SRCS = \ >+ mmdevdrv.c >+ >+@MAKE_DLL_RULES@ >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."); >+ 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 >-- >1.8.0.3 > >From 80c139c1abd5138933136be289ab6d8facbd4646 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <m.b.lankhorst@gmail.com> >Date: Mon, 7 Jan 2013 14:11:35 +0100 >Subject: [PATCH 10/35] 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) > { >-- >1.8.0.3 > >From 514e2794c6093ba226733ba44c9a8ce7769848cf Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <m.b.lankhorst@gmail.com> >Date: Mon, 7 Jan 2013 14:11:35 +0100 >Subject: [PATCH 11/35] winepulse: Add audioclient > >--- >Without AudioRenderClient and AudioCaptureClient it won't work, >but it's easier to review >--- > dlls/winepulse.drv/mmdevdrv.c | 1041 ++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 1040 insertions(+), 1 deletion(-) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index 40db26d..37d85ff 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -103,9 +103,55 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) > return TRUE; > } > >+typedef struct ACImpl ACImpl; >+ >+typedef struct _ACPacket { >+ struct list entry; >+ UINT64 qpcpos; >+ BYTE *data; >+ UINT32 discont; >+} ACPacket; >+ >+struct ACImpl { >+ IAudioClient IAudioClient_iface; >+ IAudioRenderClient IAudioRenderClient_iface; >+ IAudioCaptureClient IAudioCaptureClient_iface; >+ IAudioClock IAudioClock_iface; >+ IAudioClock2 IAudioClock2_iface; >+ IAudioStreamVolume IAudioStreamVolume_iface; >+ IMMDevice *parent; >+ struct list entry; >+ float vol[PA_CHANNELS_MAX]; >+ >+ LONG ref; >+ EDataFlow dataflow; >+ DWORD flags; >+ AUDCLNT_SHAREMODE share; >+ HANDLE event; >+ >+ UINT32 bufsize_frames, bufsize_bytes, locked, capture_period, pad, started, peek_ofs; >+ void *locked_ptr, *tmp_buffer; >+ >+ pa_stream *stream; >+ pa_sample_spec ss; >+ pa_channel_map map; >+ >+ INT64 clock_lastpos, clock_written; >+ pa_usec_t clock_pulse; >+ >+ struct list packet_free_head; >+ 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 inline ACImpl *impl_from_IAudioClient(IAudioClient *iface) >+{ >+ return CONTAINING_RECORD(iface, ACImpl, IAudioClient_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 >@@ -351,6 +397,192 @@ static void pulse_contextcallback(pa_context *c, void *userdata) { > pthread_cond_signal(&pulse_cond); > } > >+static HRESULT pulse_stream_valid(ACImpl *This) { >+ if (!This->stream) >+ return AUDCLNT_E_NOT_INITIALIZED; >+ if (!This->stream || pa_stream_get_state(This->stream) != PA_STREAM_READY) >+ return AUDCLNT_E_DEVICE_INVALIDATED; >+ return S_OK; >+} >+ >+static void dump_attr(const pa_buffer_attr *attr) { >+ TRACE("maxlength: %u\n", attr->maxlength); >+ TRACE("minreq: %u\n", attr->minreq); >+ TRACE("fragsize: %u\n", attr->fragsize); >+ TRACE("tlength: %u\n", attr->tlength); >+ TRACE("prebuf: %u\n", attr->prebuf); >+} >+ >+static void pulse_op_cb(pa_stream *s, 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"); >+ dump_attr(attr); >+} >+ >+static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata) >+{ >+ ACImpl *This = userdata; >+ pa_usec_t time; >+ UINT32 oldpad = This->pad; >+ >+ if (bytes < This->bufsize_bytes) >+ This->pad = This->bufsize_bytes - bytes; >+ else >+ This->pad = 0; >+ >+ assert(oldpad >= This->pad); >+ >+ if (0 && This->pad && pa_stream_get_time(This->stream, &time) >= 0) >+ This->clock_pulse = time; >+ else >+ This->clock_pulse = PA_USEC_INVALID; >+ >+ This->clock_written += oldpad - This->pad; >+ TRACE("New pad: %u (-%u)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss)); >+ >+ if (This->event) >+ SetEvent(This->event); >+} >+ >+static void pulse_underflow_callback(pa_stream *s, void *userdata) >+{ >+ ACImpl *This = userdata; >+ This->clock_pulse = PA_USEC_INVALID; >+ WARN("Underflow\n"); >+} >+ >+/* Latency is periodically updated even when nothing is played, >+ * because of PA_STREAM_AUTO_TIMING_UPDATE so use it as timer >+ * >+ * Perfect for passing all tests :) >+ */ >+static void pulse_latency_callback(pa_stream *s, void *userdata) >+{ >+ ACImpl *This = userdata; >+ if (!This->pad && This->event) >+ SetEvent(This->event); >+} >+ >+static void pulse_started_callback(pa_stream *s, void *userdata) >+{ >+ ACImpl *This = userdata; >+ pa_usec_t time; >+ >+ TRACE("(Re)started playing\n"); >+ assert(This->clock_pulse == PA_USEC_INVALID); >+ if (0 && pa_stream_get_time(This->stream, &time) >= 0) >+ This->clock_pulse = time; >+ if (This->event) >+ SetEvent(This->event); >+} >+ >+static void pulse_rd_loop(ACImpl *This, size_t bytes) >+{ >+ while (bytes >= This->capture_period) { >+ ACPacket *p, *next; >+ LARGE_INTEGER stamp, freq; >+ BYTE *dst, *src; >+ UINT32 src_len, copy, rem = This->capture_period; >+ if (!(p = (ACPacket*)list_head(&This->packet_free_head))) { >+ p = (ACPacket*)list_head(&This->packet_filled_head); >+ if (!p->discont) { >+ next = (ACPacket*)p->entry.next; >+ next->discont = 1; >+ } else >+ p = (ACPacket*)list_tail(&This->packet_filled_head); >+ assert(This->pad == This->bufsize_bytes); >+ } else { >+ assert(This->pad < This->bufsize_bytes); >+ This->pad += This->capture_period; >+ assert(This->pad <= This->bufsize_bytes); >+ } >+ QueryPerformanceCounter(&stamp); >+ QueryPerformanceFrequency(&freq); >+ p->qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart; >+ p->discont = 0; >+ list_remove(&p->entry); >+ list_add_tail(&This->packet_filled_head, &p->entry); >+ >+ dst = p->data; >+ while (rem) { >+ pa_stream_peek(This->stream, (const void**)&src, &src_len); >+ assert(src_len && src_len <= bytes); >+ assert(This->peek_ofs < src_len); >+ src += This->peek_ofs; >+ src_len -= This->peek_ofs; >+ >+ copy = rem; >+ if (copy > src_len) >+ copy = src_len; >+ memcpy(dst, src, rem); >+ src += copy; >+ src_len -= copy; >+ dst += copy; >+ rem -= copy; >+ >+ if (!src_len) { >+ This->peek_ofs = 0; >+ pa_stream_drop(This->stream); >+ } else >+ This->peek_ofs += copy; >+ } >+ bytes -= This->capture_period; >+ } >+} >+ >+static void pulse_rd_drop(ACImpl *This, size_t bytes) >+{ >+ while (bytes >= This->capture_period) { >+ UINT32 src_len, copy, rem = This->capture_period; >+ while (rem) { >+ const void *src; >+ pa_stream_peek(This->stream, &src, &src_len); >+ assert(src_len && src_len <= bytes); >+ assert(This->peek_ofs < src_len); >+ src_len -= This->peek_ofs; >+ >+ copy = rem; >+ if (copy > src_len) >+ copy = src_len; >+ >+ src_len -= copy; >+ rem -= copy; >+ >+ if (!src_len) { >+ This->peek_ofs = 0; >+ pa_stream_drop(This->stream); >+ } else >+ This->peek_ofs += copy; >+ bytes -= copy; >+ } >+ } >+} >+ >+static void pulse_rd_callback(pa_stream *s, size_t bytes, void *userdata) >+{ >+ ACImpl *This = userdata; >+ >+ TRACE("Readable total: %u, fragsize: %u\n", bytes, pa_stream_get_buffer_attr(s)->fragsize); >+ assert(bytes >= This->peek_ofs); >+ bytes -= This->peek_ofs; >+ if (bytes < This->capture_period) >+ return; >+ >+ if (This->started) >+ pulse_rd_loop(This, bytes); >+ else >+ pulse_rd_drop(This, bytes); >+ >+ if (This->event) >+ SetEvent(This->event); >+} >+ > static void pulse_stream_state(pa_stream *s, void *user) > { > pa_stream_state_t state = pa_stream_get_state(s); >@@ -358,6 +590,53 @@ static void pulse_stream_state(pa_stream *s, void *user) > pthread_cond_signal(&pulse_cond); > } > >+static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) { >+ int ret; >+ char buffer[64]; >+ static LONG number; >+ pa_buffer_attr attr; >+ if (This->stream) { >+ pa_stream_disconnect(This->stream); >+ while (pa_stream_get_state(This->stream) == PA_STREAM_READY) >+ pthread_cond_wait(&pulse_cond, &pulse_lock); >+ pa_stream_unref(This->stream); >+ } >+ ret = InterlockedIncrement(&number); >+ sprintf(buffer, "audio stream #%i", ret); >+ This->stream = pa_stream_new(pulse_ctx, buffer, &This->ss, &This->map); >+ pa_stream_set_state_callback(This->stream, pulse_stream_state, This); >+ pa_stream_set_buffer_attr_callback(This->stream, pulse_attr_update, This); >+ pa_stream_set_moved_callback(This->stream, pulse_attr_update, This); >+ >+ /* Pulseaudio will fill in correct values */ >+ attr.minreq = attr.fragsize = period_bytes; >+ attr.maxlength = attr.tlength = This->bufsize_bytes; >+ attr.prebuf = pa_frame_size(&This->ss); >+ dump_attr(&attr); >+ 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); >+ 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); >+ if (ret < 0) { >+ WARN("Returns %i\n", ret); >+ return AUDCLNT_E_ENDPOINT_CREATE_FAILED; >+ } >+ while (pa_stream_get_state(This->stream) == PA_STREAM_CREATING) >+ pthread_cond_wait(&pulse_cond, &pulse_lock); >+ if (pa_stream_get_state(This->stream) != PA_STREAM_READY) >+ return AUDCLNT_E_ENDPOINT_CREATE_FAILED; >+ >+ if (This->dataflow == eRender) { >+ pa_stream_set_write_callback(This->stream, pulse_wr_callback, This); >+ pa_stream_set_underflow_callback(This->stream, pulse_underflow_callback, This); >+ pa_stream_set_started_callback(This->stream, pulse_started_callback, This); >+ } else >+ pa_stream_set_read_callback(This->stream, pulse_rd_callback, This); >+ return S_OK; >+} >+ > HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys, > UINT *num, UINT *def_index) > { >@@ -402,14 +681,774 @@ int WINAPI AUDDRV_GetPriority(void) > HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev, > EDataFlow dataflow, IAudioClient **out) > { >+ HRESULT hr; >+ ACImpl *This; >+ int i; >+ > TRACE("%p %p %d %p\n", key, dev, dataflow, out); > if (dataflow != eRender && dataflow != eCapture) > return E_UNEXPECTED; > > *out = NULL; >- return E_NOTIMPL; >+ pthread_mutex_lock(&pulse_lock); >+ hr = pulse_connect(); >+ pthread_mutex_unlock(&pulse_lock); >+ if (FAILED(hr)) >+ return hr; >+ >+ This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This)); >+ if (!This) >+ return E_OUTOFMEMORY; >+ >+ This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl; >+ This->dataflow = dataflow; >+ This->parent = dev; >+ This->clock_pulse = PA_USEC_INVALID; >+ for (i = 0; i < PA_CHANNELS_MAX; ++i) >+ This->vol[i] = 1.f; >+ IMMDevice_AddRef(This->parent); >+ >+ *out = &This->IAudioClient_iface; >+ IAudioClient_AddRef(&This->IAudioClient_iface); >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *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_IAudioClient)) >+ *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 AudioClient_AddRef(IAudioClient *iface) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ ULONG ref; >+ ref = InterlockedIncrement(&This->ref); >+ TRACE("(%p) Refcount now %u\n", This, ref); >+ return ref; >+} >+ >+static ULONG WINAPI AudioClient_Release(IAudioClient *iface) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ ULONG ref; >+ ref = InterlockedDecrement(&This->ref); >+ TRACE("(%p) Refcount now %u\n", This, ref); >+ if (!ref) { >+ if (This->stream) { >+ pthread_mutex_lock(&pulse_lock); >+ if (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream))) { >+ pa_stream_disconnect(This->stream); >+ while (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream))) >+ pthread_cond_wait(&pulse_cond, &pulse_lock); >+ } >+ pa_stream_unref(This->stream); >+ This->stream = NULL; >+ list_remove(&This->entry); >+ pthread_mutex_unlock(&pulse_lock); >+ } >+ IMMDevice_Release(This->parent); >+ HeapFree(GetProcessHeap(), 0, This->tmp_buffer); >+ HeapFree(GetProcessHeap(), 0, This); >+ } >+ return ref; >+} >+ >+static void dump_fmt(const WAVEFORMATEX *fmt) >+{ >+ TRACE("wFormatTag: 0x%x (", fmt->wFormatTag); >+ switch(fmt->wFormatTag) { >+ case WAVE_FORMAT_PCM: >+ TRACE("WAVE_FORMAT_PCM"); >+ break; >+ case WAVE_FORMAT_IEEE_FLOAT: >+ TRACE("WAVE_FORMAT_IEEE_FLOAT"); >+ break; >+ case WAVE_FORMAT_EXTENSIBLE: >+ TRACE("WAVE_FORMAT_EXTENSIBLE"); >+ break; >+ default: >+ TRACE("Unknown"); >+ break; >+ } >+ TRACE(")\n"); >+ >+ TRACE("nChannels: %u\n", fmt->nChannels); >+ TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec); >+ TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec); >+ TRACE("nBlockAlign: %u\n", fmt->nBlockAlign); >+ TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample); >+ TRACE("cbSize: %u\n", fmt->cbSize); >+ >+ if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { >+ WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt; >+ TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask); >+ TRACE("Samples: %04x\n", fmtex->Samples.wReserved); >+ TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat)); >+ } >+} >+ >+static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt) >+{ >+ WAVEFORMATEX *ret; >+ size_t size; >+ >+ if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) >+ size = sizeof(WAVEFORMATEXTENSIBLE); >+ else >+ size = sizeof(WAVEFORMATEX); >+ >+ ret = CoTaskMemAlloc(size); >+ if (!ret) >+ return NULL; >+ >+ memcpy(ret, fmt, size); >+ >+ ret->cbSize = size - sizeof(WAVEFORMATEX); >+ >+ return ret; >+} >+ >+static DWORD get_channel_mask(unsigned int channels) >+{ >+ switch(channels) { >+ case 0: >+ return 0; >+ case 1: >+ return SPEAKER_FRONT_CENTER; >+ case 2: >+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; >+ case 3: >+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | >+ SPEAKER_LOW_FREQUENCY; >+ case 4: >+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | >+ SPEAKER_BACK_RIGHT; >+ case 5: >+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | >+ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY; >+ case 6: >+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | >+ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER; >+ case 7: >+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | >+ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER | >+ SPEAKER_BACK_CENTER; >+ case 8: >+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | >+ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER | >+ SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; >+ } >+ FIXME("Unknown speaker configuration: %u\n", channels); >+ return 0; >+} >+ >+static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt) >+{ >+ pa_channel_map_init(&This->map); >+ This->ss.rate = fmt->nSamplesPerSec; >+ This->ss.format = PA_SAMPLE_INVALID; >+ switch(fmt->wFormatTag) { >+ case WAVE_FORMAT_IEEE_FLOAT: >+ if (!fmt->nChannels || fmt->nChannels > 2 || fmt->wBitsPerSample != 32) >+ break; >+ This->ss.format = PA_SAMPLE_FLOAT32LE; >+ pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA); >+ break; >+ case WAVE_FORMAT_PCM: >+ if (!fmt->nChannels || fmt->nChannels > 2) >+ break; >+ if (fmt->wBitsPerSample == 8) >+ This->ss.format = PA_SAMPLE_U8; >+ else if (fmt->wBitsPerSample == 16) >+ This->ss.format = PA_SAMPLE_S16LE; >+ pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA); >+ break; >+ case WAVE_FORMAT_EXTENSIBLE: { >+ WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)fmt; >+ DWORD mask = wfe->dwChannelMask; >+ DWORD i = 0, j; >+ if (fmt->cbSize != (sizeof(*wfe) - sizeof(*fmt)) && fmt->cbSize != sizeof(*wfe)) >+ break; >+ if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) && >+ (!wfe->Samples.wValidBitsPerSample || wfe->Samples.wValidBitsPerSample == 32) && >+ fmt->wBitsPerSample == 32) >+ This->ss.format = PA_SAMPLE_FLOAT32LE; >+ else if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) { >+ DWORD valid = wfe->Samples.wValidBitsPerSample; >+ if (!valid) >+ valid = fmt->wBitsPerSample; >+ if (!valid || valid > fmt->wBitsPerSample) >+ break; >+ switch (fmt->wBitsPerSample) { >+ case 8: >+ if (valid == 8) >+ This->ss.format = PA_SAMPLE_U8; >+ break; >+ case 16: >+ if (valid == 16) >+ This->ss.format = PA_SAMPLE_S16LE; >+ break; >+ case 24: >+ if (valid == 24) >+ This->ss.format = PA_SAMPLE_S24LE; >+ break; >+ case 32: >+ if (valid == 24) >+ This->ss.format = PA_SAMPLE_S24_32LE; >+ else if (valid == 32) >+ This->ss.format = PA_SAMPLE_S32LE; >+ default: >+ break; >+ } >+ } >+ This->map.channels = fmt->nChannels; >+ if (!mask) >+ mask = get_channel_mask(fmt->nChannels); >+ for (j = 0; j < sizeof(pulse_pos_from_wfx)/sizeof(*pulse_pos_from_wfx) && i < fmt->nChannels; ++j) { >+ if (mask & (1 << j)) >+ This->map.map[i++] = pulse_pos_from_wfx[j]; >+ } >+ >+ /* Special case for mono since pulse appears to map it differently */ >+ if (mask == SPEAKER_FRONT_CENTER) >+ This->map.map[0] = PA_CHANNEL_POSITION_MONO; >+ >+ if ((mask & SPEAKER_ALL) && i < fmt->nChannels) { >+ This->map.map[i++] = PA_CHANNEL_POSITION_MONO; >+ FIXME("Is the 'all' channel mapped correctly?\n"); >+ } >+ >+ if (i < fmt->nChannels || (mask & SPEAKER_RESERVED)) { >+ This->map.channels = 0; >+ ERR("Invalid channel mask: %i/%i and %x\n", i, fmt->nChannels, mask); >+ break; >+ } >+ break; >+ } >+ default: FIXME("Unhandled tag %x\n", fmt->wFormatTag); >+ } >+ This->ss.channels = This->map.channels; >+ if (!pa_channel_map_valid(&This->map) || This->ss.format == PA_SAMPLE_INVALID) { >+ ERR("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This->map), This->ss.format); >+ dump_fmt(fmt); >+ return AUDCLNT_E_UNSUPPORTED_FORMAT; >+ } >+ return S_OK; > } > >+static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface, >+ AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration, >+ REFERENCE_TIME period, const WAVEFORMATEX *fmt, >+ const GUID *sessionguid) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ HRESULT hr = S_OK; >+ UINT period_bytes; >+ >+ TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags, >+ wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid)); >+ >+ if (!fmt) >+ return E_POINTER; >+ >+ if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE) >+ return AUDCLNT_E_NOT_INITIALIZED; >+ if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) >+ return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED; >+ >+ if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS | >+ AUDCLNT_STREAMFLAGS_LOOPBACK | >+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK | >+ AUDCLNT_STREAMFLAGS_NOPERSIST | >+ AUDCLNT_STREAMFLAGS_RATEADJUST | >+ AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED | >+ AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE | >+ AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)) { >+ TRACE("Unknown flags: %08x\n", flags); >+ return E_INVALIDARG; >+ } >+ >+ pthread_mutex_lock(&pulse_lock); >+ if (This->stream) { >+ pthread_mutex_unlock(&pulse_lock); >+ return AUDCLNT_E_ALREADY_INITIALIZED; >+ } >+ >+ hr = pulse_spec_from_waveformat(This, fmt); >+ if (FAILED(hr)) >+ goto exit; >+ >+ if (mode == AUDCLNT_SHAREMODE_SHARED) { >+ period = pulse_def_period[This->dataflow == eCapture]; >+ if (duration < 2 * period) >+ duration = 2 * period; >+ } >+ period_bytes = pa_frame_size(&This->ss) * MulDiv(period, This->ss.rate, 10000000); >+ >+ if (duration < 20000000) >+ This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec); >+ else >+ This->bufsize_frames = 2 * fmt->nSamplesPerSec; >+ This->bufsize_bytes = This->bufsize_frames * pa_frame_size(&This->ss); >+ >+ This->share = mode; >+ This->flags = flags; >+ hr = pulse_stream_connect(This, period_bytes); >+ if (SUCCEEDED(hr)) { >+ UINT32 unalign; >+ const pa_buffer_attr *attr = pa_stream_get_buffer_attr(This->stream); >+ /* Update frames according to new size */ >+ dump_attr(attr); >+ if (This->dataflow == eRender) >+ This->bufsize_bytes = attr->tlength; >+ else { >+ This->capture_period = period_bytes = attr->fragsize; >+ if ((unalign = This->bufsize_bytes % period_bytes)) >+ This->bufsize_bytes += period_bytes - unalign; >+ } >+ This->bufsize_frames = This->bufsize_bytes / pa_frame_size(&This->ss); >+ } >+ if (SUCCEEDED(hr)) { >+ UINT32 i, capture_packets = This->capture_period ? This->bufsize_bytes / This->capture_period : 0; >+ This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_bytes + capture_packets * sizeof(ACPacket)); >+ if (!This->tmp_buffer) >+ hr = E_OUTOFMEMORY; >+ 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); >+ list_init(&This->packet_free_head); >+ list_init(&This->packet_filled_head); >+ for (i = 0; i < capture_packets; ++i, ++cur_packet) { >+ list_add_tail(&This->packet_free_head, &cur_packet->entry); >+ cur_packet->data = data; >+ data += This->capture_period; >+ } >+ assert(!This->capture_period || This->bufsize_bytes == This->capture_period * capture_packets); >+ assert(!capture_packets || data - This->bufsize_bytes == This->tmp_buffer); >+ } >+ } >+ >+exit: >+ if (FAILED(hr)) { >+ HeapFree(GetProcessHeap(), 0, This->tmp_buffer); >+ This->tmp_buffer = NULL; >+ if (This->stream) { >+ pa_stream_disconnect(This->stream); >+ pa_stream_unref(This->stream); >+ This->stream = NULL; >+ } >+ } >+ pthread_mutex_unlock(&pulse_lock); >+ return hr; >+} >+ >+static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface, >+ UINT32 *out) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ HRESULT hr; >+ >+ TRACE("(%p)->(%p)\n", This, out); >+ >+ if (!out) >+ return E_POINTER; >+ >+ pthread_mutex_lock(&pulse_lock); >+ hr = pulse_stream_valid(This); >+ if (SUCCEEDED(hr)) >+ *out = This->bufsize_frames; >+ pthread_mutex_unlock(&pulse_lock); >+ >+ return hr; >+} >+ >+static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface, >+ REFERENCE_TIME *latency) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ const pa_buffer_attr *attr; >+ REFERENCE_TIME lat; >+ HRESULT hr; >+ >+ TRACE("(%p)->(%p)\n", This, latency); >+ >+ if (!latency) >+ return E_POINTER; >+ >+ pthread_mutex_lock(&pulse_lock); >+ hr = pulse_stream_valid(This); >+ if (FAILED(hr)) { >+ pthread_mutex_unlock(&pulse_lock); >+ return hr; >+ } >+ attr = pa_stream_get_buffer_attr(This->stream); >+ if (This->dataflow == eRender) >+ lat = attr->minreq / pa_frame_size(&This->ss); >+ else >+ lat = attr->fragsize / pa_frame_size(&This->ss); >+ *latency = 10000000; >+ *latency *= lat; >+ *latency /= This->ss.rate; >+ pthread_mutex_unlock(&pulse_lock); >+ TRACE("Latency: %u ms\n", (DWORD)(*latency / 10000)); >+ return S_OK; >+} >+ >+static void ACImpl_GetRenderPad(ACImpl *This, UINT32 *out) >+{ >+ *out = This->pad / pa_frame_size(&This->ss); >+} >+ >+static void ACImpl_GetCapturePad(ACImpl *This, UINT32 *out) >+{ >+ ACPacket *packet = This->locked_ptr; >+ if (!packet && !list_empty(&This->packet_filled_head)) { >+ packet = (ACPacket*)list_head(&This->packet_filled_head); >+ This->locked_ptr = packet; >+ list_remove(&packet->entry); >+ } >+ if (out) >+ *out = This->pad / pa_frame_size(&This->ss); >+} >+ >+static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface, >+ UINT32 *out) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ HRESULT hr; >+ >+ TRACE("(%p)->(%p)\n", This, out); >+ >+ if (!out) >+ return E_POINTER; >+ >+ pthread_mutex_lock(&pulse_lock); >+ hr = pulse_stream_valid(This); >+ if (FAILED(hr)) { >+ pthread_mutex_unlock(&pulse_lock); >+ return hr; >+ } >+ >+ if (This->dataflow == eRender) >+ ACImpl_GetRenderPad(This, out); >+ else >+ ACImpl_GetCapturePad(This, out); >+ pthread_mutex_unlock(&pulse_lock); >+ >+ TRACE("%p Pad: %u ms (%u)\n", This, MulDiv(*out, 1000, This->ss.rate), *out); >+ return S_OK; >+} >+ >+static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface, >+ AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt, >+ WAVEFORMATEX **out) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ HRESULT hr = S_OK; >+ WAVEFORMATEX *closest = NULL; >+ >+ TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out); >+ >+ if (!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out)) >+ return E_POINTER; >+ >+ if (out) >+ *out = NULL; >+ if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE) >+ return E_INVALIDARG; >+ if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) >+ return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED; >+ if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE && >+ fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) >+ return E_INVALIDARG; >+ >+ dump_fmt(fmt); >+ >+ closest = clone_format(fmt); >+ if (!closest) >+ hr = E_OUTOFMEMORY; >+ >+ if (hr == S_OK || !out) { >+ CoTaskMemFree(closest); >+ if (out) >+ *out = NULL; >+ } else if (closest) { >+ closest->nBlockAlign = >+ closest->nChannels * closest->wBitsPerSample / 8; >+ closest->nAvgBytesPerSec = >+ closest->nBlockAlign * closest->nSamplesPerSec; >+ *out = closest; >+ } >+ >+ TRACE("returning: %08x %p\n", hr, out ? *out : NULL); >+ return hr; >+} >+ >+static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface, >+ WAVEFORMATEX **pwfx) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ WAVEFORMATEXTENSIBLE *fmt = &pulse_fmt[This->dataflow == eCapture]; >+ >+ TRACE("(%p)->(%p)\n", This, pwfx); >+ >+ if (!pwfx) >+ return E_POINTER; >+ >+ *pwfx = clone_format(&fmt->Format); >+ if (!*pwfx) >+ return E_OUTOFMEMORY; >+ dump_fmt(*pwfx); >+ return S_OK; >+} >+ >+static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface, >+ REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ >+ TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod); >+ >+ if (!defperiod && !minperiod) >+ return E_POINTER; >+ >+ if (defperiod) >+ *defperiod = pulse_def_period[This->dataflow == eCapture]; >+ if (minperiod) >+ *minperiod = pulse_min_period[This->dataflow == eCapture]; >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI AudioClient_Start(IAudioClient *iface) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ HRESULT hr = S_OK; >+ int success; >+ pa_operation *o; >+ >+ TRACE("(%p)\n", This); >+ >+ pthread_mutex_lock(&pulse_lock); >+ hr = pulse_stream_valid(This); >+ if (FAILED(hr)) { >+ pthread_mutex_unlock(&pulse_lock); >+ return hr; >+ } >+ >+ if ((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event) { >+ pthread_mutex_unlock(&pulse_lock); >+ return AUDCLNT_E_EVENTHANDLE_NOT_SET; >+ } >+ >+ if (This->started) { >+ pthread_mutex_unlock(&pulse_lock); >+ return AUDCLNT_E_NOT_STOPPED; >+ } >+ This->clock_pulse = PA_USEC_INVALID; >+ >+ if (pa_stream_is_corked(This->stream)) { >+ o = pa_stream_cork(This->stream, 0, pulse_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); >+ } else >+ success = 0; >+ if (!success) >+ hr = E_FAIL; >+ } >+ if (SUCCEEDED(hr)) { >+ This->started = TRUE; >+ if (This->dataflow == eRender && This->event) >+ pa_stream_set_latency_update_callback(This->stream, pulse_latency_callback, This); >+ } >+ pthread_mutex_unlock(&pulse_lock); >+ return hr; >+} >+ >+static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ HRESULT hr = S_OK; >+ pa_operation *o; >+ int success; >+ >+ TRACE("(%p)\n", This); >+ >+ pthread_mutex_lock(&pulse_lock); >+ hr = pulse_stream_valid(This); >+ if (FAILED(hr)) { >+ pthread_mutex_unlock(&pulse_lock); >+ return hr; >+ } >+ >+ if (!This->started) { >+ pthread_mutex_unlock(&pulse_lock); >+ return S_FALSE; >+ } >+ >+ if (This->dataflow == eRender) { >+ o = pa_stream_cork(This->stream, 1, pulse_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); >+ } else >+ success = 0; >+ if (!success) >+ hr = E_FAIL; >+ } >+ if (SUCCEEDED(hr)) { >+ This->started = FALSE; >+ This->clock_pulse = PA_USEC_INVALID; >+ } >+ pthread_mutex_unlock(&pulse_lock); >+ return hr; >+} >+ >+static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ HRESULT hr = S_OK; >+ >+ TRACE("(%p)\n", This); >+ >+ pthread_mutex_lock(&pulse_lock); >+ hr = pulse_stream_valid(This); >+ if (FAILED(hr)) { >+ pthread_mutex_unlock(&pulse_lock); >+ return hr; >+ } >+ >+ if (This->started) { >+ pthread_mutex_unlock(&pulse_lock); >+ return AUDCLNT_E_NOT_STOPPED; >+ } >+ >+ if (This->locked) { >+ pthread_mutex_unlock(&pulse_lock); >+ return AUDCLNT_E_BUFFER_OPERATION_PENDING; >+ } >+ >+ if (This->dataflow == eRender) { >+ /* If there is still data in the render buffer it needs to be removed from the server */ >+ int success = 0; >+ if (This->pad) { >+ pa_operation *o = pa_stream_flush(This->stream, pulse_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 || !This->pad) >+ This->clock_lastpos = This->clock_written = This->pad = 0; >+ } else { >+ ACPacket *p; >+ This->clock_written += This->pad; >+ This->pad = 0; >+ >+ if ((p = This->locked_ptr)) { >+ This->locked_ptr = NULL; >+ list_add_tail(&This->packet_free_head, &p->entry); >+ } >+ list_move_tail(&This->packet_free_head, &This->packet_filled_head); >+ } >+ pthread_mutex_unlock(&pulse_lock); >+ >+ return hr; >+} >+ >+static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface, >+ HANDLE event) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ HRESULT hr; >+ >+ TRACE("(%p)->(%p)\n", This, event); >+ >+ if (!event) >+ return E_INVALIDARG; >+ >+ pthread_mutex_lock(&pulse_lock); >+ hr = pulse_stream_valid(This); >+ if (FAILED(hr)) { >+ pthread_mutex_unlock(&pulse_lock); >+ return hr; >+ } >+ >+ if (!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) >+ hr = AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED; >+ else if (This->event) >+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME); >+ else >+ This->event = event; >+ pthread_mutex_unlock(&pulse_lock); >+ return hr; >+} >+ >+static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid, >+ void **ppv) >+{ >+ ACImpl *This = impl_from_IAudioClient(iface); >+ HRESULT hr; >+ >+ TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv); >+ >+ if (!ppv) >+ return E_POINTER; >+ *ppv = NULL; >+ >+ pthread_mutex_lock(&pulse_lock); >+ hr = pulse_stream_valid(This); >+ pthread_mutex_unlock(&pulse_lock); >+ if (FAILED(hr)) >+ return hr; >+ >+ if (*ppv) { >+ IUnknown_AddRef((IUnknown*)*ppv); >+ return S_OK; >+ } >+ >+ FIXME("stub %s\n", debugstr_guid(riid)); >+ return E_NOINTERFACE; >+} >+ >+static const IAudioClientVtbl AudioClient_Vtbl = >+{ >+ AudioClient_QueryInterface, >+ AudioClient_AddRef, >+ AudioClient_Release, >+ AudioClient_Initialize, >+ AudioClient_GetBufferSize, >+ AudioClient_GetStreamLatency, >+ AudioClient_GetCurrentPadding, >+ AudioClient_IsFormatSupported, >+ AudioClient_GetMixFormat, >+ AudioClient_GetDevicePeriod, >+ AudioClient_Start, >+ AudioClient_Stop, >+ AudioClient_Reset, >+ AudioClient_SetEventHandle, >+ AudioClient_GetService >+}; >+ > HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device, > IAudioSessionManager2 **out) > { >-- >1.8.0.3 > >From e4089422b8c0629e14b86243a3eb9a93e9aaeda6 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <m.b.lankhorst@gmail.com> >Date: Mon, 7 Jan 2013 14:11:35 +0100 >Subject: [PATCH 12/35] 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) > { >-- >1.8.0.3 > >From 0473bfde7b44ecb4712fb74fa182575b71ff0e42 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <m.b.lankhorst@gmail.com> >Date: Mon, 7 Jan 2013 14:11:35 +0100 >Subject: [PATCH 13/35] 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) > { >-- >1.8.0.3 > >From 182169cf1031312a780e039234991153cf786650 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <m.b.lankhorst@gmail.com> >Date: Mon, 7 Jan 2013 14:11:35 +0100 >Subject: [PATCH 14/35] 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) > { >-- >1.8.0.3 > >From 4d44c28b5e73b2bfbae917e5ac6e9f163d413ec6 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <m.b.lankhorst@gmail.com> >Date: Mon, 7 Jan 2013 14:11:35 +0100 >Subject: [PATCH 15/35] winepulse: Add session support > >--- >Copied verbatim from winealsa >--- > dlls/winepulse.drv/mmdevdrv.c | 849 +++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 848 insertions(+), 1 deletion(-) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index b7414c2..64ee62e 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -69,6 +69,7 @@ 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 struct list g_sessions = LIST_INIT(g_sessions); > > /* Mixer format + period times */ > static WAVEFORMATEXTENSIBLE pulse_fmt[2]; >@@ -105,6 +106,31 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) > > typedef struct ACImpl ACImpl; > >+typedef struct _AudioSession { >+ GUID guid; >+ struct list clients; >+ >+ IMMDevice *device; >+ >+ float master_vol; >+ UINT32 channel_count; >+ float *channel_vols; >+ BOOL mute; >+ >+ struct list entry; >+} AudioSession; >+ >+typedef struct _AudioSessionWrapper { >+ IAudioSessionControl2 IAudioSessionControl2_iface; >+ IChannelAudioVolume IChannelAudioVolume_iface; >+ ISimpleAudioVolume ISimpleAudioVolume_iface; >+ >+ LONG ref; >+ >+ ACImpl *client; >+ AudioSession *session; >+} AudioSessionWrapper; >+ > typedef struct _ACPacket { > struct list entry; > UINT64 qpcpos; >@@ -139,6 +165,8 @@ struct ACImpl { > INT64 clock_lastpos, clock_written; > pa_usec_t clock_pulse; > >+ AudioSession *session; >+ AudioSessionWrapper *session_wrapper; > struct list packet_free_head; > struct list packet_filled_head; > }; >@@ -148,10 +176,15 @@ 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 IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl; >+static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl; >+static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl; > static const IAudioClockVtbl AudioClock_Vtbl; > static const IAudioClock2Vtbl AudioClock2_Vtbl; > static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl; > >+static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client); >+ > static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface) > { > return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface); >@@ -167,6 +200,21 @@ static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface) > return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface); > } > >+static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface) >+{ >+ return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface); >+} >+ >+static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface) >+{ >+ return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface); >+} >+ >+static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface) >+{ >+ return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface); >+} >+ > static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface) > { > return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface); >@@ -897,6 +945,85 @@ static DWORD get_channel_mask(unsigned int channels) > return 0; > } > >+static void session_init_vols(AudioSession *session, UINT channels) >+{ >+ if (session->channel_count < channels) { >+ UINT i; >+ >+ if (session->channel_vols) >+ session->channel_vols = HeapReAlloc(GetProcessHeap(), 0, >+ session->channel_vols, sizeof(float) * channels); >+ else >+ session->channel_vols = HeapAlloc(GetProcessHeap(), 0, >+ sizeof(float) * channels); >+ if (!session->channel_vols) >+ return; >+ >+ for(i = session->channel_count; i < channels; ++i) >+ session->channel_vols[i] = 1.f; >+ >+ session->channel_count = channels; >+ } >+} >+ >+static AudioSession *create_session(const GUID *guid, IMMDevice *device, >+ UINT num_channels) >+{ >+ AudioSession *ret; >+ >+ ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession)); >+ if (!ret) >+ return NULL; >+ >+ memcpy(&ret->guid, guid, sizeof(GUID)); >+ >+ ret->device = device; >+ >+ list_init(&ret->clients); >+ >+ list_add_head(&g_sessions, &ret->entry); >+ >+ session_init_vols(ret, num_channels); >+ >+ ret->master_vol = 1.f; >+ >+ return ret; >+} >+ >+/* if channels == 0, then this will return or create a session with >+ * matching dataflow and GUID. otherwise, channels must also match */ >+static HRESULT get_audio_session(const GUID *sessionguid, >+ IMMDevice *device, UINT channels, AudioSession **out) >+{ >+ AudioSession *session; >+ >+ if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) { >+ *out = create_session(&GUID_NULL, device, channels); >+ if (!*out) >+ return E_OUTOFMEMORY; >+ >+ return S_OK; >+ } >+ >+ *out = NULL; >+ LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) { >+ if (session->device == device && >+ IsEqualGUID(sessionguid, &session->guid)) { >+ session_init_vols(session, channels); >+ *out = session; >+ break; >+ } >+ } >+ >+ if (!*out) { >+ *out = create_session(sessionguid, device, channels); >+ if (!*out) >+ return E_OUTOFMEMORY; >+ } >+ >+ return S_OK; >+} >+ > static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt) > { > pa_channel_map_init(&This->map); >@@ -1083,6 +1210,10 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface, > assert(!capture_packets || data - This->bufsize_bytes == This->tmp_buffer); > } > } >+ if (SUCCEEDED(hr)) >+ hr = get_audio_session(sessionguid, This->parent, fmt->nChannels, &This->session); >+ if (SUCCEEDED(hr)) >+ list_add_tail(&This->session->clients, &This->entry); > > exit: > if (FAILED(hr)) { >@@ -1474,6 +1605,20 @@ static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid, > *ppv = &This->IAudioClock_iface; > } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) { > *ppv = &This->IAudioStreamVolume_iface; >+ } else if (IsEqualIID(riid, &IID_IAudioSessionControl) || >+ IsEqualIID(riid, &IID_IChannelAudioVolume) || >+ IsEqualIID(riid, &IID_ISimpleAudioVolume)) { >+ if (!This->session_wrapper) { >+ This->session_wrapper = AudioSessionWrapper_Create(This); >+ if (!This->session_wrapper) >+ return E_OUTOFMEMORY; >+ } >+ if (IsEqualIID(riid, &IID_IAudioSessionControl)) >+ *ppv = &This->session_wrapper->IAudioSessionControl2_iface; >+ else if (IsEqualIID(riid, &IID_IChannelAudioVolume)) >+ *ppv = &This->session_wrapper->IChannelAudioVolume_iface; >+ else if (IsEqualIID(riid, &IID_ISimpleAudioVolume)) >+ *ppv = &This->session_wrapper->ISimpleAudioVolume_iface; > } > > if (*ppv) { >@@ -2158,9 +2303,711 @@ static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl = > AudioStreamVolume_GetAllVolumes > }; > >+static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client) >+{ >+ AudioSessionWrapper *ret; >+ >+ ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, >+ sizeof(AudioSessionWrapper)); >+ if (!ret) >+ return NULL; >+ >+ ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl; >+ ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl; >+ ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl; >+ >+ ret->ref = !client; >+ >+ ret->client = client; >+ if (client) { >+ ret->session = client->session; >+ AudioClient_AddRef(&client->IAudioClient_iface); >+ } >+ >+ return ret; >+} >+ >+static HRESULT WINAPI AudioSessionControl_QueryInterface( >+ IAudioSessionControl2 *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_IAudioSessionControl) || >+ IsEqualIID(riid, &IID_IAudioSessionControl2)) >+ *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 AudioSessionControl_AddRef(IAudioSessionControl2 *iface) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ ULONG ref; >+ ref = InterlockedIncrement(&This->ref); >+ TRACE("(%p) Refcount now %u\n", This, ref); >+ return ref; >+} >+ >+static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ ULONG ref; >+ ref = InterlockedDecrement(&This->ref); >+ TRACE("(%p) Refcount now %u\n", This, ref); >+ if (!ref) { >+ if (This->client) { >+ This->client->session_wrapper = NULL; >+ AudioClient_Release(&This->client->IAudioClient_iface); >+ } >+ HeapFree(GetProcessHeap(), 0, This); >+ } >+ return ref; >+} >+ >+static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface, >+ AudioSessionState *state) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ ACImpl *client; >+ >+ TRACE("(%p)->(%p)\n", This, state); >+ >+ if (!state) >+ return NULL_PTR_ERR; >+ >+ pthread_mutex_lock(&pulse_lock); >+ if (list_empty(&This->session->clients)) { >+ *state = AudioSessionStateExpired; >+ goto out; >+ } >+ LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry) { >+ if (client->started) { >+ *state = AudioSessionStateActive; >+ goto out; >+ } >+ } >+ *state = AudioSessionStateInactive; >+ >+out: >+ pthread_mutex_unlock(&pulse_lock); >+ return S_OK; >+} >+ >+static HRESULT WINAPI AudioSessionControl_GetDisplayName( >+ IAudioSessionControl2 *iface, WCHAR **name) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ FIXME("(%p)->(%p) - stub\n", This, name); >+ >+ return E_NOTIMPL; >+} >+ >+static HRESULT WINAPI AudioSessionControl_SetDisplayName( >+ IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session)); >+ >+ return E_NOTIMPL; >+} >+ >+static HRESULT WINAPI AudioSessionControl_GetIconPath( >+ IAudioSessionControl2 *iface, WCHAR **path) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ FIXME("(%p)->(%p) - stub\n", This, path); >+ >+ return E_NOTIMPL; >+} >+ >+static HRESULT WINAPI AudioSessionControl_SetIconPath( >+ IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session)); >+ >+ return E_NOTIMPL; >+} >+ >+static HRESULT WINAPI AudioSessionControl_GetGroupingParam( >+ IAudioSessionControl2 *iface, GUID *group) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ FIXME("(%p)->(%p) - stub\n", This, group); >+ >+ return E_NOTIMPL; >+} >+ >+static HRESULT WINAPI AudioSessionControl_SetGroupingParam( >+ IAudioSessionControl2 *iface, const GUID *group, const GUID *session) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group), >+ debugstr_guid(session)); >+ >+ return E_NOTIMPL; >+} >+ >+static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification( >+ IAudioSessionControl2 *iface, IAudioSessionEvents *events) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ FIXME("(%p)->(%p) - stub\n", This, events); >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification( >+ IAudioSessionControl2 *iface, IAudioSessionEvents *events) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ FIXME("(%p)->(%p) - stub\n", This, events); >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier( >+ IAudioSessionControl2 *iface, WCHAR **id) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ FIXME("(%p)->(%p) - stub\n", This, id); >+ >+ return E_NOTIMPL; >+} >+ >+static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier( >+ IAudioSessionControl2 *iface, WCHAR **id) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ FIXME("(%p)->(%p) - stub\n", This, id); >+ >+ return E_NOTIMPL; >+} >+ >+static HRESULT WINAPI AudioSessionControl_GetProcessId( >+ IAudioSessionControl2 *iface, DWORD *pid) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ TRACE("(%p)->(%p)\n", This, pid); >+ >+ if (!pid) >+ return E_POINTER; >+ >+ *pid = GetCurrentProcessId(); >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession( >+ IAudioSessionControl2 *iface) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ TRACE("(%p)\n", This); >+ >+ return S_FALSE; >+} >+ >+static HRESULT WINAPI AudioSessionControl_SetDuckingPreference( >+ IAudioSessionControl2 *iface, BOOL optout) >+{ >+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); >+ >+ TRACE("(%p)->(%d)\n", This, optout); >+ >+ return S_OK; >+} >+ >+static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl = >+{ >+ AudioSessionControl_QueryInterface, >+ AudioSessionControl_AddRef, >+ AudioSessionControl_Release, >+ AudioSessionControl_GetState, >+ AudioSessionControl_GetDisplayName, >+ AudioSessionControl_SetDisplayName, >+ AudioSessionControl_GetIconPath, >+ AudioSessionControl_SetIconPath, >+ AudioSessionControl_GetGroupingParam, >+ AudioSessionControl_SetGroupingParam, >+ AudioSessionControl_RegisterAudioSessionNotification, >+ AudioSessionControl_UnregisterAudioSessionNotification, >+ AudioSessionControl_GetSessionIdentifier, >+ AudioSessionControl_GetSessionInstanceIdentifier, >+ AudioSessionControl_GetProcessId, >+ AudioSessionControl_IsSystemSoundsSession, >+ AudioSessionControl_SetDuckingPreference >+}; >+ >+typedef struct _SessionMgr { >+ IAudioSessionManager2 IAudioSessionManager2_iface; >+ >+ LONG ref; >+ >+ IMMDevice *device; >+} SessionMgr; >+ >+static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *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_IAudioSessionManager) || >+ IsEqualIID(riid, &IID_IAudioSessionManager2)) >+ *ppv = iface; >+ if (*ppv) { >+ IUnknown_AddRef((IUnknown*)*ppv); >+ return S_OK; >+ } >+ >+ WARN("Unknown interface %s\n", debugstr_guid(riid)); >+ return E_NOINTERFACE; >+} >+ >+static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface) >+{ >+ return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface); >+} >+ >+static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface) >+{ >+ SessionMgr *This = impl_from_IAudioSessionManager2(iface); >+ ULONG ref; >+ ref = InterlockedIncrement(&This->ref); >+ TRACE("(%p) Refcount now %u\n", This, ref); >+ return ref; >+} >+ >+static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface) >+{ >+ SessionMgr *This = impl_from_IAudioSessionManager2(iface); >+ ULONG ref; >+ ref = InterlockedDecrement(&This->ref); >+ TRACE("(%p) Refcount now %u\n", This, ref); >+ if (!ref) >+ HeapFree(GetProcessHeap(), 0, This); >+ return ref; >+} >+ >+static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl( >+ IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags, >+ IAudioSessionControl **out) >+{ >+ SessionMgr *This = impl_from_IAudioSessionManager2(iface); >+ AudioSession *session; >+ AudioSessionWrapper *wrapper; >+ HRESULT hr; >+ >+ TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid), >+ flags, out); >+ >+ hr = get_audio_session(session_guid, This->device, 0, &session); >+ if (FAILED(hr)) >+ return hr; >+ >+ wrapper = AudioSessionWrapper_Create(NULL); >+ if (!wrapper) >+ return E_OUTOFMEMORY; >+ >+ wrapper->session = session; >+ >+ *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface; >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume( >+ IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags, >+ ISimpleAudioVolume **out) >+{ >+ SessionMgr *This = impl_from_IAudioSessionManager2(iface); >+ AudioSession *session; >+ AudioSessionWrapper *wrapper; >+ HRESULT hr; >+ >+ TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid), >+ flags, out); >+ >+ hr = get_audio_session(session_guid, This->device, 0, &session); >+ if (FAILED(hr)) >+ return hr; >+ >+ wrapper = AudioSessionWrapper_Create(NULL); >+ if (!wrapper) >+ return E_OUTOFMEMORY; >+ >+ wrapper->session = session; >+ >+ *out = &wrapper->ISimpleAudioVolume_iface; >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator( >+ IAudioSessionManager2 *iface, IAudioSessionEnumerator **out) >+{ >+ SessionMgr *This = impl_from_IAudioSessionManager2(iface); >+ FIXME("(%p)->(%p) - stub\n", This, out); >+ return E_NOTIMPL; >+} >+ >+static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification( >+ IAudioSessionManager2 *iface, IAudioSessionNotification *notification) >+{ >+ SessionMgr *This = impl_from_IAudioSessionManager2(iface); >+ FIXME("(%p)->(%p) - stub\n", This, notification); >+ return E_NOTIMPL; >+} >+ >+static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification( >+ IAudioSessionManager2 *iface, IAudioSessionNotification *notification) >+{ >+ SessionMgr *This = impl_from_IAudioSessionManager2(iface); >+ FIXME("(%p)->(%p) - stub\n", This, notification); >+ return E_NOTIMPL; >+} >+ >+static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification( >+ IAudioSessionManager2 *iface, const WCHAR *session_id, >+ IAudioVolumeDuckNotification *notification) >+{ >+ SessionMgr *This = impl_from_IAudioSessionManager2(iface); >+ FIXME("(%p)->(%p) - stub\n", This, notification); >+ return E_NOTIMPL; >+} >+ >+static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification( >+ IAudioSessionManager2 *iface, >+ IAudioVolumeDuckNotification *notification) >+{ >+ SessionMgr *This = impl_from_IAudioSessionManager2(iface); >+ FIXME("(%p)->(%p) - stub\n", This, notification); >+ return E_NOTIMPL; >+} >+ >+static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl = >+{ >+ AudioSessionManager_QueryInterface, >+ AudioSessionManager_AddRef, >+ AudioSessionManager_Release, >+ AudioSessionManager_GetAudioSessionControl, >+ AudioSessionManager_GetSimpleAudioVolume, >+ AudioSessionManager_GetSessionEnumerator, >+ AudioSessionManager_RegisterSessionNotification, >+ AudioSessionManager_UnregisterSessionNotification, >+ AudioSessionManager_RegisterDuckNotification, >+ AudioSessionManager_UnregisterDuckNotification >+}; >+ >+static HRESULT WINAPI SimpleAudioVolume_QueryInterface( >+ ISimpleAudioVolume *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_ISimpleAudioVolume)) >+ *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 SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface) >+{ >+ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); >+ return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface); >+} >+ >+static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface) >+{ >+ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); >+ return AudioSessionControl_Release(&This->IAudioSessionControl2_iface); >+} >+ >+static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume( >+ ISimpleAudioVolume *iface, float level, const GUID *context) >+{ >+ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); >+ AudioSession *session = This->session; >+ >+ TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context)); >+ >+ if (level < 0.f || level > 1.f) >+ return E_INVALIDARG; >+ >+ if (context) >+ FIXME("Notifications not supported yet\n"); >+ >+ TRACE("Pulseaudio does not support session volume control\n"); >+ >+ pthread_mutex_lock(&pulse_lock); >+ session->master_vol = level; >+ pthread_mutex_unlock(&pulse_lock); >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume( >+ ISimpleAudioVolume *iface, float *level) >+{ >+ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); >+ AudioSession *session = This->session; >+ >+ TRACE("(%p)->(%p)\n", session, level); >+ >+ if (!level) >+ return NULL_PTR_ERR; >+ >+ *level = session->master_vol; >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface, >+ BOOL mute, const GUID *context) >+{ >+ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); >+ AudioSession *session = This->session; >+ >+ TRACE("(%p)->(%u, %p)\n", session, mute, context); >+ >+ if (context) >+ FIXME("Notifications not supported yet\n"); >+ >+ session->mute = mute; >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface, >+ BOOL *mute) >+{ >+ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); >+ AudioSession *session = This->session; >+ >+ TRACE("(%p)->(%p)\n", session, mute); >+ >+ if (!mute) >+ return NULL_PTR_ERR; >+ >+ *mute = session->mute; >+ >+ return S_OK; >+} >+ >+static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl = >+{ >+ SimpleAudioVolume_QueryInterface, >+ SimpleAudioVolume_AddRef, >+ SimpleAudioVolume_Release, >+ SimpleAudioVolume_SetMasterVolume, >+ SimpleAudioVolume_GetMasterVolume, >+ SimpleAudioVolume_SetMute, >+ SimpleAudioVolume_GetMute >+}; >+ >+static HRESULT WINAPI ChannelAudioVolume_QueryInterface( >+ IChannelAudioVolume *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_IChannelAudioVolume)) >+ *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 ChannelAudioVolume_AddRef(IChannelAudioVolume *iface) >+{ >+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); >+ return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface); >+} >+ >+static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface) >+{ >+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); >+ return AudioSessionControl_Release(&This->IAudioSessionControl2_iface); >+} >+ >+static HRESULT WINAPI ChannelAudioVolume_GetChannelCount( >+ IChannelAudioVolume *iface, UINT32 *out) >+{ >+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); >+ AudioSession *session = This->session; >+ >+ TRACE("(%p)->(%p)\n", session, out); >+ >+ if (!out) >+ return NULL_PTR_ERR; >+ >+ *out = session->channel_count; >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume( >+ IChannelAudioVolume *iface, UINT32 index, float level, >+ const GUID *context) >+{ >+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); >+ AudioSession *session = This->session; >+ >+ TRACE("(%p)->(%d, %f, %s)\n", session, index, level, >+ wine_dbgstr_guid(context)); >+ >+ if (level < 0.f || level > 1.f) >+ return E_INVALIDARG; >+ >+ if (index >= session->channel_count) >+ return E_INVALIDARG; >+ >+ if (context) >+ FIXME("Notifications not supported yet\n"); >+ >+ TRACE("Pulseaudio does not support session volume control\n"); >+ >+ pthread_mutex_lock(&pulse_lock); >+ session->channel_vols[index] = level; >+ pthread_mutex_unlock(&pulse_lock); >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume( >+ IChannelAudioVolume *iface, UINT32 index, float *level) >+{ >+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); >+ AudioSession *session = This->session; >+ >+ TRACE("(%p)->(%d, %p)\n", session, index, level); >+ >+ if (!level) >+ return NULL_PTR_ERR; >+ >+ if (index >= session->channel_count) >+ return E_INVALIDARG; >+ >+ *level = session->channel_vols[index]; >+ >+ return S_OK; >+} >+ >+static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes( >+ IChannelAudioVolume *iface, UINT32 count, const float *levels, >+ const GUID *context) >+{ >+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); >+ AudioSession *session = This->session; >+ int i; >+ >+ TRACE("(%p)->(%d, %p, %s)\n", session, count, levels, >+ wine_dbgstr_guid(context)); >+ >+ if (!levels) >+ return NULL_PTR_ERR; >+ >+ if (count != session->channel_count) >+ return E_INVALIDARG; >+ >+ if (context) >+ FIXME("Notifications not supported yet\n"); >+ >+ TRACE("Pulseaudio does not support session volume control\n"); >+ >+ pthread_mutex_lock(&pulse_lock); >+ for(i = 0; i < count; ++i) >+ session->channel_vols[i] = levels[i]; >+ pthread_mutex_unlock(&pulse_lock); >+ return S_OK; >+} >+ >+static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes( >+ IChannelAudioVolume *iface, UINT32 count, float *levels) >+{ >+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); >+ AudioSession *session = This->session; >+ int i; >+ >+ TRACE("(%p)->(%d, %p)\n", session, count, levels); >+ >+ if (!levels) >+ return NULL_PTR_ERR; >+ >+ if (count != session->channel_count) >+ return E_INVALIDARG; >+ >+ for(i = 0; i < count; ++i) >+ levels[i] = session->channel_vols[i]; >+ >+ return S_OK; >+} >+ >+static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl = >+{ >+ ChannelAudioVolume_QueryInterface, >+ ChannelAudioVolume_AddRef, >+ ChannelAudioVolume_Release, >+ ChannelAudioVolume_GetChannelCount, >+ ChannelAudioVolume_SetChannelVolume, >+ ChannelAudioVolume_GetChannelVolume, >+ ChannelAudioVolume_SetAllVolumes, >+ ChannelAudioVolume_GetAllVolumes >+}; >+ > HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device, > IAudioSessionManager2 **out) > { >+ SessionMgr *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr)); > *out = NULL; >- return E_NOTIMPL; >+ if (!This) >+ return E_OUTOFMEMORY; >+ This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl; >+ This->device = device; >+ This->ref = 1; >+ *out = &This->IAudioSessionManager2_iface; >+ return S_OK; > } >-- >1.8.0.3 > >From bf461bbc767b0ce9e5661dbc4d76d0213695eef6 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <m.b.lankhorst@gmail.com> >Date: Mon, 7 Jan 2013 14:11:35 +0100 >Subject: [PATCH 16/35] 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; >-- >1.8.0.3 > >From d3d35c1db6bbb2ed21892208c156429ee8a34c32 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <m.b.lankhorst@gmail.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 17/35] 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) >-- >1.8.0.3 > >From 2096d5f2dd698de8db7992e0377f2317a87e7dcd Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <m.b.lankhorst@gmail.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 18/35] winepulse v15: Add support for missing formats, and > silence an error for missing format tags > >--- > dlls/winepulse.drv/mmdevdrv.c | 17 ++++++++++++++++- > 1 file changed, 16 insertions(+), 1 deletion(-) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index 960af3c..f52f119 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -1109,7 +1109,22 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt) > } > break; > } >- default: FIXME("Unhandled tag %x\n", fmt->wFormatTag); >+ case WAVE_FORMAT_ALAW: >+ case WAVE_FORMAT_MULAW: >+ if (fmt->wBitsPerSample != 8) { >+ FIXME("Unsupported bpp %u for LAW\n", fmt->wBitsPerSample); >+ return AUDCLNT_E_UNSUPPORTED_FORMAT; >+ } >+ if (fmt->nChannels != 1 && fmt->nChannels != 2) { >+ FIXME("Unsupported channels %u for LAW\n", fmt->nChannels); >+ return AUDCLNT_E_UNSUPPORTED_FORMAT; >+ } >+ This->ss.format = fmt->wFormatTag == WAVE_FORMAT_MULAW ? PA_SAMPLE_ULAW : PA_SAMPLE_ALAW; >+ pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA); >+ break; >+ default: >+ WARN("Unhandled tag %x\n", fmt->wFormatTag); >+ return AUDCLNT_E_UNSUPPORTED_FORMAT; > } > This->ss.channels = This->map.channels; > if (!pa_channel_map_valid(&This->map) || This->ss.format == PA_SAMPLE_INVALID) { >-- >1.8.0.3 > >From e4188bf5ed264779db3f1866524e892d47f5d2d3 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <m.b.lankhorst@gmail.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 19/35] winepulse v16: Add official warning wine doesn't want > to support winepulse > >And give an alternative place to ask for support. >I wish it didn't have to come to this. >--- > dlls/winepulse.drv/mmdevdrv.c | 25 +++++++++++++++++++++++-- > 1 file changed, 23 insertions(+), 2 deletions(-) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index f52f119..76a2e0e 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -59,6 +59,7 @@ > #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER) > > WINE_DEFAULT_DEBUG_CHANNEL(pulse); >+WINE_DECLARE_DEBUG_CHANNEL(winediag); > > static const REFERENCE_TIME MinimumPeriod = 30000; > static const REFERENCE_TIME DefaultPeriod = 100000; >@@ -81,6 +82,8 @@ 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 }; > >+static HANDLE warn_once; >+ > BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) > { > if (reason == DLL_PROCESS_ATTACH) { >@@ -99,7 +102,10 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) > } > if (pulse_ml) > pa_mainloop_quit(pulse_ml, 0); >- CloseHandle(pulse_thread); >+ if (pulse_thread) >+ CloseHandle(pulse_thread); >+ if (warn_once) >+ CloseHandle(warn_once); > } > return TRUE; > } >@@ -758,6 +764,10 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys, > int WINAPI AUDDRV_GetPriority(void) > { > HRESULT hr; >+ if (getenv("WINENOPULSE")) { >+ FIXME_(winediag)("winepulse has been temporarily disabled through the environment\n"); >+ return 0; >+ } > pthread_mutex_lock(&pulse_lock); > hr = pulse_connect(); > pthread_mutex_unlock(&pulse_lock); >@@ -771,7 +781,18 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev, > ACImpl *This; > int i; > >- TRACE("%p %p %d %p\n", key, dev, dataflow, out); >+ /* Give one visible warning per session >+ * Sadly wine has chosen not to accept the winepulse patch, so support ourselves >+ */ >+ if (!warn_once && (warn_once = CreateEventA(0, 0, 0, "__winepulse_warn_event")) && GetLastError() != ERROR_ALREADY_EXISTS) { >+ FIXME_(winediag)("Winepulse is not officially supported by the wine project\n"); >+ FIXME_(winediag)("For sound related feedback and support, please visit http://ubuntuforums.org/showthread.php?t=1960599\n"); >+ } else { >+ WARN_(winediag)("Winepulse is not officially supported by the wine project\n"); >+ WARN_(winediag)("For sound related feedback and support, please visit http://ubuntuforums.org/showthread.php?t=1960599\n"); >+ } >+ >+ TRACE("%s %p %p\n", debugstr_guid(guid), dev, out); > if (dataflow != eRender && dataflow != eCapture) > return E_UNEXPECTED; > >-- >1.8.0.3 > >From 4771b95845d71ae6091b1bdeb449b957eff89146 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <m.b.lankhorst@gmail.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 20/35] winepulse v17: Fix winmm tests > >Handle dwChannelMask = SPEAKER_ALL better so WAVE_FORMAT_EXTENSIBLE tests pass too >--- > dlls/winepulse.drv/mmdevdrv.c | 72 +++++++++++++++++++++++++++++++++---------- > 1 file changed, 56 insertions(+), 16 deletions(-) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index 76a2e0e..6e75674 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -1066,6 +1066,8 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt) > This->ss.format = PA_SAMPLE_U8; > else if (fmt->wBitsPerSample == 16) > This->ss.format = PA_SAMPLE_S16LE; >+ else >+ return AUDCLNT_E_UNSUPPORTED_FORMAT; > pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA); > break; > case WAVE_FORMAT_EXTENSIBLE: { >@@ -1102,13 +1104,16 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt) > This->ss.format = PA_SAMPLE_S24_32LE; > else if (valid == 32) > This->ss.format = PA_SAMPLE_S32LE; >- default: > break; >+ default: >+ return AUDCLNT_E_UNSUPPORTED_FORMAT; > } > } > This->map.channels = fmt->nChannels; >- if (!mask) >+ if (!mask || mask == SPEAKER_ALL) > mask = get_channel_mask(fmt->nChannels); >+ else if (mask == ~0U && fmt->nChannels == 1) >+ mask = SPEAKER_FRONT_CENTER; > for (j = 0; j < sizeof(pulse_pos_from_wfx)/sizeof(*pulse_pos_from_wfx) && i < fmt->nChannels; ++j) { > if (mask & (1 << j)) > This->map.map[i++] = pulse_pos_from_wfx[j]; >@@ -1118,14 +1123,9 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt) > if (mask == SPEAKER_FRONT_CENTER) > This->map.map[0] = PA_CHANNEL_POSITION_MONO; > >- if ((mask & SPEAKER_ALL) && i < fmt->nChannels) { >- This->map.map[i++] = PA_CHANNEL_POSITION_MONO; >- FIXME("Is the 'all' channel mapped correctly?\n"); >- } >- > if (i < fmt->nChannels || (mask & SPEAKER_RESERVED)) { > This->map.channels = 0; >- ERR("Invalid channel mask: %i/%i and %x\n", i, fmt->nChannels, mask); >+ ERR("Invalid channel mask: %i/%i and %x(%x)\n", i, fmt->nChannels, mask, wfe->dwChannelMask); > break; > } > break; >@@ -1383,15 +1383,55 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface, > return E_INVALIDARG; > if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) > return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED; >- if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE && >- fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) >- return E_INVALIDARG; >- >- dump_fmt(fmt); >- >+ switch (fmt->wFormatTag) { >+ case WAVE_FORMAT_EXTENSIBLE: >+ if (fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) >+ return E_INVALIDARG; >+ dump_fmt(fmt); >+ break; >+ case WAVE_FORMAT_ALAW: >+ case WAVE_FORMAT_MULAW: >+ case WAVE_FORMAT_IEEE_FLOAT: >+ case WAVE_FORMAT_PCM: >+ dump_fmt(fmt); >+ break; >+ default: >+ dump_fmt(fmt); >+ return AUDCLNT_E_UNSUPPORTED_FORMAT; >+ } >+ if (fmt->nChannels == 0) >+ return AUDCLNT_E_UNSUPPORTED_FORMAT; > closest = clone_format(fmt); >- if (!closest) >- hr = E_OUTOFMEMORY; >+ if (!closest) { >+ if (out) >+ *out = NULL; >+ return E_OUTOFMEMORY; >+ } >+ >+ if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { >+ UINT32 mask = 0, i, channels = 0; >+ WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest; >+ >+ if ((fmt->nChannels > 1 && ext->dwChannelMask == SPEAKER_ALL) || >+ (fmt->nChannels == 1 && ext->dwChannelMask == ~0U)) { >+ mask = ext->dwChannelMask; >+ channels = fmt->nChannels; >+ } else if (ext->dwChannelMask) { >+ for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) { >+ if (i & ext->dwChannelMask) { >+ mask |= i; >+ channels++; >+ } >+ } >+ if (channels < fmt->nChannels) >+ mask = get_channel_mask(fmt->nChannels); >+ } else >+ mask = ext->dwChannelMask; >+ if (ext->dwChannelMask != mask) { >+ ext->dwChannelMask = mask; >+ hr = S_FALSE; >+ } >+ } > > if (hr == S_OK || !out) { > CoTaskMemFree(closest); >-- >1.8.0.3 > >From 6e4b92fc913785e58306549ec36c8dc1752916da Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <maarten.lankhorst@canonical.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 21/35] winepulse v18: Latency and compilation improvements > >Changes since v17: >- Remove clock_pulse interpolation > * Couldn't work, sadly >- Allow 2 * MinimumPeriod for shared buffers >- Fix all compiler warnings when compiling with 64-bits >- Dynamically select low latency mode if less than 2 default periods are request > * This requires the rtkit patch to be useful >--- > dlls/winepulse.drv/mmdevdrv.c | 55 +++++++++++++++++-------------------------- > 1 file changed, 22 insertions(+), 33 deletions(-) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index 6e75674..8e76826 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -169,7 +169,6 @@ struct ACImpl { > pa_channel_map map; > > INT64 clock_lastpos, clock_written; >- pa_usec_t clock_pulse; > > AudioSession *session; > AudioSessionWrapper *session_wrapper; >@@ -518,7 +517,6 @@ static void pulse_attr_update(pa_stream *s, void *user) { > static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata) > { > ACImpl *This = userdata; >- pa_usec_t time; > UINT32 oldpad = This->pad; > > if (bytes < This->bufsize_bytes) >@@ -528,13 +526,8 @@ static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata) > > assert(oldpad >= This->pad); > >- if (0 && This->pad && pa_stream_get_time(This->stream, &time) >= 0) >- This->clock_pulse = time; >- else >- This->clock_pulse = PA_USEC_INVALID; >- > This->clock_written += oldpad - This->pad; >- TRACE("New pad: %u (-%u)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss)); >+ TRACE("New pad: %zu (-%zu)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss)); > > if (This->event) > SetEvent(This->event); >@@ -542,8 +535,6 @@ static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata) > > static void pulse_underflow_callback(pa_stream *s, void *userdata) > { >- ACImpl *This = userdata; >- This->clock_pulse = PA_USEC_INVALID; > WARN("Underflow\n"); > } > >@@ -562,12 +553,8 @@ static void pulse_latency_callback(pa_stream *s, void *userdata) > static void pulse_started_callback(pa_stream *s, void *userdata) > { > ACImpl *This = userdata; >- pa_usec_t time; > > TRACE("(Re)started playing\n"); >- assert(This->clock_pulse == PA_USEC_INVALID); >- if (0 && pa_stream_get_time(This->stream, &time) >= 0) >- This->clock_pulse = time; > if (This->event) > SetEvent(This->event); > } >@@ -578,7 +565,7 @@ static void pulse_rd_loop(ACImpl *This, size_t bytes) > ACPacket *p, *next; > LARGE_INTEGER stamp, freq; > BYTE *dst, *src; >- UINT32 src_len, copy, rem = This->capture_period; >+ size_t src_len, copy, rem = This->capture_period; > if (!(p = (ACPacket*)list_head(&This->packet_free_head))) { > p = (ACPacket*)list_head(&This->packet_filled_head); > if (!p->discont) { >@@ -630,7 +617,7 @@ static void pulse_rd_loop(ACImpl *This, size_t bytes) > static void pulse_rd_drop(ACImpl *This, size_t bytes) > { > while (bytes >= This->capture_period) { >- UINT32 src_len, copy, rem = This->capture_period; >+ size_t src_len, copy, rem = This->capture_period; > while (rem) { > const void *src; > pa_stream_peek(This->stream, &src, &src_len); >@@ -660,7 +647,7 @@ static void pulse_rd_callback(pa_stream *s, size_t bytes, void *userdata) > { > ACImpl *This = userdata; > >- TRACE("Readable total: %u, fragsize: %u\n", bytes, pa_stream_get_buffer_attr(s)->fragsize); >+ TRACE("Readable total: %zu, fragsize: %u\n", bytes, pa_stream_get_buffer_attr(s)->fragsize); > assert(bytes >= This->peek_ofs); > bytes -= This->peek_ofs; > if (bytes < This->capture_period) >@@ -815,7 +802,6 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev, > This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl; > This->dataflow = dataflow; > This->parent = dev; >- This->clock_pulse = PA_USEC_INVALID; > for (i = 0; i < PA_CHANNELS_MAX; ++i) > This->vol[i] = 1.f; > IMMDevice_AddRef(This->parent); >@@ -1199,7 +1185,19 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface, > goto exit; > > if (mode == AUDCLNT_SHAREMODE_SHARED) { >- period = pulse_def_period[This->dataflow == eCapture]; >+ REFERENCE_TIME def = pulse_def_period[This->dataflow == eCapture]; >+ REFERENCE_TIME min = pulse_min_period[This->dataflow == eCapture]; >+ >+ /* Switch to low latency mode if below 2 default periods, >+ * which is 20 ms by default, this will increase the amount >+ * of interrupts but allows very low latency. In dsound I >+ * managed to get a total latency of ~8ms, which is well below >+ * default >+ */ >+ if (duration < 2 * def) >+ period = min; >+ else >+ period = def; > if (duration < 2 * period) > duration = 2 * period; > } >@@ -1510,7 +1508,6 @@ static HRESULT WINAPI AudioClient_Start(IAudioClient *iface) > pthread_mutex_unlock(&pulse_lock); > return AUDCLNT_E_NOT_STOPPED; > } >- This->clock_pulse = PA_USEC_INVALID; > > if (pa_stream_is_corked(This->stream)) { > o = pa_stream_cork(This->stream, 0, pulse_op_cb, &success); >@@ -1566,7 +1563,6 @@ static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface) > } > if (SUCCEEDED(hr)) { > This->started = FALSE; >- This->clock_pulse = PA_USEC_INVALID; > } > pthread_mutex_unlock(&pulse_lock); > return hr; >@@ -1764,7 +1760,8 @@ 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); >+ size_t avail, req, bytes = frames * pa_frame_size(&This->ss); >+ UINT32 pad; > HRESULT hr = S_OK; > int ret = -1; > >@@ -1789,7 +1786,7 @@ static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface, > 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); >+ WARN("Wanted to write %u, but only %zu available\n", frames, avail); > return AUDCLNT_E_BUFFER_TOO_LARGE; > } > >@@ -1797,7 +1794,7 @@ static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface, > 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); >+ FIXME("%p Not using pulse locked data: %i %zu/%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; >@@ -1845,7 +1842,7 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer( > 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)); >+ TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss)); > assert(This->pad <= This->bufsize_bytes); > pthread_mutex_unlock(&pulse_lock); > return S_OK; >@@ -2053,7 +2050,6 @@ 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); >@@ -2069,13 +2065,6 @@ static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos, > } > > *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) >-- >1.8.0.3 > >From 1d768d2b45a13af6a424d5f3399605ce04216c79 Mon Sep 17 00:00:00 2001 >From: Juergen Tretthahn <orson@orson.at> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 22/35] winepulse: API Compatibility with 1.5.2 onward, v2 > >V2: Add winepulse.drv.spec to commit too >V1: Original version > >Commit e87cb774d131963d2642d075977571585ec5da8d changed the driver api >leave this commit out to build for builds prior to this > >Not needed for: 1.5.1, 1.5 and 1.4 builds >--- > dlls/winepulse.drv/mmdevdrv.c | 34 ++++++++++++++++++++++------------ > dlls/winepulse.drv/winepulse.drv.spec | 2 +- > 2 files changed, 23 insertions(+), 13 deletions(-) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index 8e76826..b374b53 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -82,6 +82,11 @@ 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 }; > >+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 } }; >+ > static HANDLE warn_once; > > BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) >@@ -716,7 +721,7 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) { > return S_OK; > } > >-HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys, >+HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys, > UINT *num, UINT *def_index) > { > HRESULT hr = S_OK; >@@ -730,20 +735,21 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys, > *num = 1; > *def_index = 0; > >- *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *)); >+ *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(**ids)); > if (!*ids) > return E_OUTOFMEMORY; >+ (*ids)[0] = defaultW; > >- (*ids)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW)); >- if (!(*ids)[0]) { >+ *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys)); >+ if (!*keys) { > HeapFree(GetProcessHeap(), 0, *ids); >+ *ids = NULL; > return E_OUTOFMEMORY; > } >- >- lstrcpyW((*ids)[0], defaultW); >- >- *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(void *)); >- (*keys)[0] = NULL; >+ if (flow == eRender) >+ (*keys)[0] = pulse_render_guid; >+ else >+ (*keys)[0] = pulse_capture_guid; > > return S_OK; > } >@@ -761,12 +767,12 @@ int WINAPI AUDDRV_GetPriority(void) > return SUCCEEDED(hr) ? 3 : 0; > } > >-HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev, >- EDataFlow dataflow, IAudioClient **out) >+HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out) > { > HRESULT hr; > ACImpl *This; > int i; >+ EDataFlow dataflow; > > /* Give one visible warning per session > * Sadly wine has chosen not to accept the winepulse patch, so support ourselves >@@ -780,7 +786,11 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev, > } > > TRACE("%s %p %p\n", debugstr_guid(guid), dev, out); >- if (dataflow != eRender && dataflow != eCapture) >+ if (IsEqualGUID(guid, &pulse_render_guid)) >+ dataflow = eRender; >+ else if (IsEqualGUID(guid, &pulse_capture_guid)) >+ dataflow = eCapture; >+ else > return E_UNEXPECTED; > > *out = NULL; >diff --git a/dlls/winepulse.drv/winepulse.drv.spec b/dlls/winepulse.drv/winepulse.drv.spec >index a089166..612bf46 100644 >--- a/dlls/winepulse.drv/winepulse.drv.spec >+++ b/dlls/winepulse.drv/winepulse.drv.spec >@@ -1,5 +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 GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint > @ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager >-- >1.8.0.3 > >From b75266af35a304e7dcc962c61ef3abcaf7f50734 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <maarten.lankhorst@canonical.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 23/35] winepulse: Fix low latency support > >Some games request a 20 ms buffer and will only fill 20 ms. >Since 10 ms periods are too long in this case for winepulse, >fill change period size to 5 ms and force a trigger if >there's still data left to fill. >--- > dlls/winepulse.drv/mmdevdrv.c | 14 ++++++++++++-- > 1 file changed, 12 insertions(+), 2 deletions(-) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index b374b53..7c07f54 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -1205,11 +1205,15 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface, > * default > */ > if (duration < 2 * def) >- period = min; >+ period = min; > else >- period = def; >+ period = def; > if (duration < 2 * period) > duration = 2 * period; >+ >+ /* Uh oh, really low latency requested.. */ >+ if (duration <= 2 * period) >+ period /= 2; > } > period_bytes = pa_frame_size(&This->ss) * MulDiv(period, This->ss.rate, 10000000); > >@@ -1820,6 +1824,7 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer( > { > ACImpl *This = impl_from_IAudioRenderClient(iface); > UINT32 written_bytes = written_frames * pa_frame_size(&This->ss); >+ UINT32 period; > > TRACE("(%p)->(%u, %x)\n", This, written_frames, flags); > >@@ -1854,6 +1859,11 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer( > This->locked_ptr = NULL; > TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss)); > assert(This->pad <= This->bufsize_bytes); >+ >+ period = pa_stream_get_buffer_attr(This->stream)->minreq; >+ /* Require a minimum of 3 periods filled, if possible */ >+ if (This->event && This->pad + period <= This->bufsize_bytes && This->pad < period * 3) >+ SetEvent(This->event); > pthread_mutex_unlock(&pulse_lock); > return S_OK; > } >-- >1.8.0.3 > >From f8d35826dcb3cc65c477f2ee163e2129ec929aee Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <maarten.lankhorst@canonical.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 24/35] winepulse: drop realtime priority before thread > destruction > >prevents having to handle a kernel RT Watchdog Timeout. >--- > dlls/winepulse.drv/mmdevdrv.c | 2 ++ > 1 file changed, 2 insertions(+) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index 7c07f54..ba68102 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -101,6 +101,8 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) > } > DisableThreadLibraryCalls(dll); > } else if (reason == DLL_PROCESS_DETACH) { >+ if (pulse_thread) >+ SetThreadPriority(pulse_thread, 0); > if (pulse_ctx) { > pa_context_disconnect(pulse_ctx); > pa_context_unref(pulse_ctx); >-- >1.8.0.3 > >From 1954f340d76fa590b81885edf31360e6d08fdfe2 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <maarten.lankhorst@canonical.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 25/35] winepulse: remove bogus SetEvent from > pulse_started_callback > >--- > dlls/winepulse.drv/mmdevdrv.c | 4 ---- > 1 file changed, 4 deletions(-) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index ba68102..68de00c 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -559,11 +559,7 @@ static void pulse_latency_callback(pa_stream *s, void *userdata) > > static void pulse_started_callback(pa_stream *s, void *userdata) > { >- ACImpl *This = userdata; >- > TRACE("(Re)started playing\n"); >- if (This->event) >- SetEvent(This->event); > } > > static void pulse_rd_loop(ACImpl *This, size_t bytes) >-- >1.8.0.3 > >From 0c9bfe6c81e70ae4cf6f0f4908e9490bdede8668 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <maarten.lankhorst@canonical.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 26/35] winepulse: disable the setevent part of the latency > hack > >If you get playback glitches in skyrim or other games as a result of >this patch, PLEASE REPORT TO ME! >--- > dlls/winepulse.drv/mmdevdrv.c | 8 ++++---- > 1 file changed, 4 insertions(+), 4 deletions(-) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index 68de00c..643d55e 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -1822,7 +1822,7 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer( > { > ACImpl *This = impl_from_IAudioRenderClient(iface); > UINT32 written_bytes = written_frames * pa_frame_size(&This->ss); >- UINT32 period; >+// UINT32 period; > > TRACE("(%p)->(%u, %x)\n", This, written_frames, flags); > >@@ -1858,10 +1858,10 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer( > TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss)); > assert(This->pad <= This->bufsize_bytes); > >- period = pa_stream_get_buffer_attr(This->stream)->minreq; >+// period = pa_stream_get_buffer_attr(This->stream)->minreq; > /* Require a minimum of 3 periods filled, if possible */ >- if (This->event && This->pad + period <= This->bufsize_bytes && This->pad < period * 3) >- SetEvent(This->event); >+// if (This->event && This->pad + period <= This->bufsize_bytes && This->pad < period * 3) >+// SetEvent(This->event); > pthread_mutex_unlock(&pulse_lock); > return S_OK; > } >-- >1.8.0.3 > >From 39cd033d5b7649f65e59294e986a49d99d1dff27 Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <maarten.lankhorst@canonical.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 27/35] winepulse v20: fix the checks in IsFormatSupported > >Thanks to DGhost001 for reporting and isolating the issue. >--- > dlls/winepulse.drv/mmdevdrv.c | 4 ++++ > 1 file changed, 4 insertions(+) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index 643d55e..86dd10a 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -1443,6 +1443,10 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface, > } > } > >+ if (fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 || >+ fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) >+ hr = S_FALSE; >+ > if (hr == S_OK || !out) { > CoTaskMemFree(closest); > if (out) >-- >1.8.0.3 > >From 17479d867132201522b2de56df1095dd31c729bf Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <maarten.lankhorst@canonical.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 28/35] winepulse: fixup IsFormatSupported calls > >--- > dlls/mmdevapi/tests/render.c | 164 +++++++++++++++++++++++++++++++++++++++ > dlls/winepulse.drv/mmdevdrv.c | 176 ++++++++++++++++++++++++++++-------------- > 2 files changed, 283 insertions(+), 57 deletions(-) > >diff --git a/dlls/mmdevapi/tests/render.c b/dlls/mmdevapi/tests/render.c >index 7d06978..88f627f 100644 >--- a/dlls/mmdevapi/tests/render.c >+++ b/dlls/mmdevapi/tests/render.c >@@ -468,6 +468,169 @@ static void test_formats(AUDCLNT_SHAREMODE mode) > } > } > >+static void test_formats2(void) >+{ >+ IAudioClient *ac; >+ HRESULT hr; >+ WAVEFORMATEX *pwfx, *pwfx2; >+ WAVEFORMATEXTENSIBLE *pwfe, wfe, *pwfe2; >+ >+ hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, >+ NULL, (void**)&ac); >+ >+ ok(hr == S_OK, "Activation failed with %08x\n", hr); >+ if (hr != S_OK) >+ return; >+ >+ hr = IAudioClient_GetMixFormat(ac, &pwfx); >+ ok(hr == S_OK, "GetMixFormat failed: %08x\n", hr); >+ if (hr != S_OK) >+ return; >+ >+ ok(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE, "Invalid wFormatTag\n"); >+ if (pwfx->wFormatTag != WAVE_FORMAT_EXTENSIBLE) { >+ CoTaskMemFree(pwfx); >+ return; >+ } >+ >+ pwfe = (WAVEFORMATEXTENSIBLE*)pwfx; >+ ok(pwfe->Samples.wValidBitsPerSample, "wValidBitsPerSample should be non-zero\n"); >+ >+ if (pwfx->nChannels > 2) { >+ trace("Limiting channels to 2\n"); >+ pwfx->nChannels = 2; >+ pwfx->nBlockAlign = pwfx->wBitsPerSample / 8 * pwfx->nChannels; >+ pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; >+ pwfe->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; >+ } >+ >+ wfe = *pwfe; >+ pwfx->nAvgBytesPerSec = pwfx->nBlockAlign = 0; >+ >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL); >+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, >+ "Exclusive IsFormatSupported with nAvgBytesPerSec=0 and nBlockAlign=0 returned %08x\n", hr); >+ >+ pwfx2 = NULL; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); >+ ok(hr == E_INVALIDARG && !pwfx2, >+ "Shared IsFormatSupported with nAvgBytesPerSec=0 and nBlockAlign=0 returned %08x %p\n", hr, pwfx2); >+ CoTaskMemFree(pwfx2); >+ >+ pwfx->wFormatTag = WAVE_FORMAT_PCM; >+ pwfx2 = NULL; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); >+ ok(hr == S_OK && !pwfx2, >+ "Shared IsFormatSupported with nAvgBytesPerSec=0 and nBlockAlign=0 returned %08x %p\n", hr, pwfx2); >+ CoTaskMemFree(pwfx2); >+ >+ *pwfe = wfe; >+ pwfe->dwChannelMask = 0; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL); >+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, >+ "Exclusive IsFormatSupported with dwChannelMask=0 returned %08x\n", hr); >+ >+ pwfx2 = NULL; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); >+ ok(hr == S_OK, >+ "Shared IsFormatSupported with dwChannelMask=0 returned %08x\n", hr); >+ CoTaskMemFree(pwfx2); >+ >+ >+ pwfe->dwChannelMask = 0x3ffff; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL); >+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, >+ "Exclusive IsFormatSupported with dwChannelMask=0x3ffff returned %08x\n", hr); >+ >+ pwfx2 = NULL; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); >+ ok(hr == S_OK && !pwfx2, >+ "Shared IsFormatSupported with dwChannelMask=0x3ffff returned %08x %p\n", hr, pwfx2); >+ CoTaskMemFree(pwfx2); >+ >+ >+ pwfe->dwChannelMask = 0x40000000; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL); >+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, >+ "Exclusive IsFormatSupported with dwChannelMask=0x40000000 returned %08x\n", hr); >+ >+ pwfx2 = NULL; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); >+ ok(hr == S_OK && !pwfx2, >+ "Shared IsFormatSupported with dwChannelMask=0x40000000 returned %08x %p\n", hr, pwfx2); >+ CoTaskMemFree(pwfx2); >+ >+ pwfe->dwChannelMask = SPEAKER_ALL | SPEAKER_RESERVED; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL); >+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, >+ "Exclusive IsFormatSupported with dwChannelMask=SPEAKER_ALL | SPEAKER_RESERVED returned %08x\n", hr); >+ >+ pwfx2 = NULL; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); >+ ok(hr == S_OK && !pwfx2, >+ "Shared IsFormatSupported with dwChannelMask=SPEAKER_ALL | SPEAKER_RESERVED returned %08x %p\n", hr, pwfx2); >+ CoTaskMemFree(pwfx2); >+ >+ *pwfe = wfe; >+ pwfe->Samples.wValidBitsPerSample = 0; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL); >+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, >+ "Exclusive IsFormatSupported with wValidBitsPerSample=0 returned %08x\n", hr); >+ >+ pwfx2 = NULL; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); >+ ok(hr == S_FALSE && pwfx2, >+ "Shared IsFormatSupported with wValidBitsPerSample=0 returned %08x %p\n", hr, pwfx2); >+ if (pwfx2) { >+ pwfe2 = (WAVEFORMATEXTENSIBLE*)pwfx2; >+ ok(pwfe2->Samples.wValidBitsPerSample == pwfx->wBitsPerSample, >+ "Shared IsFormatSupported had wValidBitsPerSample set to %u, not %u\n", >+ pwfe2->Samples.wValidBitsPerSample, pwfx->wBitsPerSample); >+ CoTaskMemFree(pwfx2); >+ } >+ >+ pwfx2 = NULL; >+ pwfe->Samples.wValidBitsPerSample = pwfx->wBitsPerSample + 1; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); >+ ok(hr == E_INVALIDARG && !pwfx2, >+ "Shared IsFormatSupported with wValidBitsPerSample += 1 returned %08x %p\n", hr, pwfx2); >+ >+ *pwfe = wfe; >+ memset(&pwfe->SubFormat, 0xff, 16); >+ pwfx2 = NULL; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); >+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT && !pwfx2, >+ "Shared IsFormatSupported with SubFormat=-1 returned %08x %p\n", hr, pwfx2); >+ CoTaskMemFree(pwfx2); >+ >+ *pwfe = wfe; >+ pwfx2 = NULL; >+ pwfe->Samples.wValidBitsPerSample = pwfx->wBitsPerSample = 256; >+ pwfx->nBlockAlign = pwfx->wBitsPerSample / 8 * pwfx->nChannels; >+ pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); >+ ok(hr == E_INVALIDARG && !pwfx2, >+ "Shared IsFormatSupported with wBitsPerSample=256 returned %08x %p\n", hr, pwfx2); >+ CoTaskMemFree(pwfx2); >+ >+ *pwfe = wfe; >+ pwfx2 = NULL; >+ pwfe->Samples.wValidBitsPerSample = pwfx->wBitsPerSample - 1; >+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); >+ ok(hr == S_FALSE && pwfx2, >+ "Shared IsFormatSupported with wValidBitsPerSample-=1 returned %08x %p\n", hr, pwfx2); >+ if (pwfx2) { >+ pwfe2 = (WAVEFORMATEXTENSIBLE*)pwfx2; >+ ok(pwfe2->Samples.wValidBitsPerSample == pwfx->wBitsPerSample, >+ "Shared IsFormatSupported had wValidBitsPerSample set to %u, not %u\n", >+ pwfe2->Samples.wValidBitsPerSample, pwfx->wBitsPerSample); >+ CoTaskMemFree(pwfx2); >+ } >+ >+ CoTaskMemFree(pwfx); >+ IAudioClient_Release(ac); >+} >+ > static void test_references(void) > { > IAudioClient *ac; >@@ -2170,6 +2333,7 @@ START_TEST(render) > test_audioclient(); > test_formats(AUDCLNT_SHAREMODE_EXCLUSIVE); > test_formats(AUDCLNT_SHAREMODE_SHARED); >+ test_formats2(); > test_references(); > trace("Output to a MS-DOS console is particularly slow and disturbs timing.\n"); > trace("Please redirect output to a file.\n"); >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index 86dd10a..554a9fc 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -1104,10 +1104,8 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt) > } > } > This->map.channels = fmt->nChannels; >- if (!mask || mask == SPEAKER_ALL) >+ if (!mask || (mask & (SPEAKER_ALL|SPEAKER_RESERVED))) > mask = get_channel_mask(fmt->nChannels); >- else if (mask == ~0U && fmt->nChannels == 1) >- mask = SPEAKER_FRONT_CENTER; > for (j = 0; j < sizeof(pulse_pos_from_wfx)/sizeof(*pulse_pos_from_wfx) && i < fmt->nChannels; ++j) { > if (mask & (1 << j)) > This->map.map[i++] = pulse_pos_from_wfx[j]; >@@ -1381,83 +1379,147 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface, > ACImpl *This = impl_from_IAudioClient(iface); > HRESULT hr = S_OK; > WAVEFORMATEX *closest = NULL; >+ BOOL exclusive; > > TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out); > >- if (!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out)) >+ if (!fmt) > return E_POINTER; > > if (out) > *out = NULL; >- if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE) >+ >+ if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) { >+ exclusive = 1; >+ out = NULL; >+ } else if (mode == AUDCLNT_SHAREMODE_SHARED) { >+ exclusive = 0; >+ if (!out) >+ return E_POINTER; >+ } else > return E_INVALIDARG; >- if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) >- return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED; >- switch (fmt->wFormatTag) { >- case WAVE_FORMAT_EXTENSIBLE: >- if (fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)) >- return E_INVALIDARG; >- dump_fmt(fmt); >- break; >- case WAVE_FORMAT_ALAW: >- case WAVE_FORMAT_MULAW: >- case WAVE_FORMAT_IEEE_FLOAT: >- case WAVE_FORMAT_PCM: >- dump_fmt(fmt); >- break; >- default: >- dump_fmt(fmt); >- return AUDCLNT_E_UNSUPPORTED_FORMAT; >- } >+ > if (fmt->nChannels == 0) > return AUDCLNT_E_UNSUPPORTED_FORMAT; >+ > closest = clone_format(fmt); >- if (!closest) { >- if (out) >- *out = NULL; >+ if (!closest) > return E_OUTOFMEMORY; >- } > >- if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { >- UINT32 mask = 0, i, channels = 0; >+ dump_fmt(fmt); >+ >+ switch (fmt->wFormatTag) { >+ case WAVE_FORMAT_EXTENSIBLE: { > WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest; > >- if ((fmt->nChannels > 1 && ext->dwChannelMask == SPEAKER_ALL) || >- (fmt->nChannels == 1 && ext->dwChannelMask == ~0U)) { >- mask = ext->dwChannelMask; >- channels = fmt->nChannels; >- } else if (ext->dwChannelMask) { >- for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) { >- if (i & ext->dwChannelMask) { >- mask |= i; >- channels++; >+ if ((fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) && >+ fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE)) || >+ fmt->nBlockAlign != fmt->wBitsPerSample / 8 * fmt->nChannels || >+ ext->Samples.wValidBitsPerSample > fmt->wBitsPerSample || >+ fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) { >+ hr = E_INVALIDARG; >+ break; >+ } >+ >+ if (exclusive) { >+ UINT32 mask = 0, i, channels = 0; >+ >+ if (!(ext->dwChannelMask & (SPEAKER_ALL | SPEAKER_RESERVED))) { >+ for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) { >+ if (i & ext->dwChannelMask) { >+ mask |= i; >+ channels++; >+ } >+ } >+ >+ if (channels != fmt->nChannels || (ext->dwChannelMask & ~mask)) { >+ hr = AUDCLNT_E_UNSUPPORTED_FORMAT; >+ break; > } >+ } else { >+ hr = AUDCLNT_E_UNSUPPORTED_FORMAT; >+ break; > } >- if (channels < fmt->nChannels) >- mask = get_channel_mask(fmt->nChannels); >- } else >- mask = ext->dwChannelMask; >- if (ext->dwChannelMask != mask) { >- ext->dwChannelMask = mask; >- hr = S_FALSE; > } >+ >+ if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { >+ if (fmt->wBitsPerSample != 32) { >+ hr = E_INVALIDARG; >+ break; >+ } >+ >+ if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample) { >+ hr = S_FALSE; >+ ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample; >+ } >+ } else if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) { >+ if (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8) { >+ hr = E_INVALIDARG; >+ break; >+ } >+ >+ if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample && >+ !(fmt->wBitsPerSample == 32 && >+ ext->Samples.wValidBitsPerSample == 24)) { >+ hr = S_FALSE; >+ ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample; >+ break; >+ } >+ } else { >+ hr = AUDCLNT_E_UNSUPPORTED_FORMAT; >+ break; >+ } >+ >+ break; > } > >- if (fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 || >- fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) >- hr = S_FALSE; >+ case WAVE_FORMAT_ALAW: >+ case WAVE_FORMAT_MULAW: >+ if (fmt->wBitsPerSample != 8) { >+ hr = E_INVALIDARG; >+ break; >+ } >+ /* Fall-through */ >+ case WAVE_FORMAT_IEEE_FLOAT: >+ if (fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->wBitsPerSample != 32) { >+ hr = E_INVALIDARG; >+ break; >+ } >+ /* Fall-through */ >+ case WAVE_FORMAT_PCM: >+ if (fmt->wFormatTag == WAVE_FORMAT_PCM && >+ (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8)) { >+ hr = E_INVALIDARG; >+ break; >+ } >+ >+ if (fmt->nChannels > 2) { >+ hr = AUDCLNT_E_UNSUPPORTED_FORMAT; >+ break; >+ } >+ /* >+ * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be >+ * ignored, invalid values are happily accepted. >+ */ >+ break; >+ default: >+ hr = AUDCLNT_E_UNSUPPORTED_FORMAT; >+ break; >+ } > >- if (hr == S_OK || !out) { >+ if (exclusive && hr != S_OK) { >+ hr = AUDCLNT_E_UNSUPPORTED_FORMAT; > CoTaskMemFree(closest); >- if (out) >- *out = NULL; >- } else if (closest) { >- closest->nBlockAlign = >- closest->nChannels * closest->wBitsPerSample / 8; >- closest->nAvgBytesPerSec = >- closest->nBlockAlign * closest->nSamplesPerSec; >+ } else if (hr != S_FALSE) >+ CoTaskMemFree(closest); >+ else > *out = closest; >- } >+ >+ /* Winepulse does not currently support exclusive mode, if you know of an >+ * application that uses it, I will correct this.. >+ */ >+ if (hr == S_OK && exclusive) >+ return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED; > > TRACE("returning: %08x %p\n", hr, out ? *out : NULL); > return hr; >-- >1.8.0.3 > >From 574bab54d3df1d3151c02c91c2f77ad8e9a8641c Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <maarten.lankhorst@canonical.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 29/35] winepulse v21: return early if padding didn't update > >--- > dlls/winepulse.drv/mmdevdrv.c | 5 ++++- > 1 file changed, 4 insertions(+), 1 deletion(-) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index 554a9fc..a4575d5 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -531,7 +531,10 @@ static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata) > else > This->pad = 0; > >- assert(oldpad >= This->pad); >+ if (oldpad == This->pad) >+ return; >+ >+ assert(oldpad > This->pad); > > This->clock_written += oldpad - This->pad; > TRACE("New pad: %zu (-%zu)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss)); >-- >1.8.0.3 > >From ce58ea59eae832640bfeefefef55a1c926059cda Mon Sep 17 00:00:00 2001 >From: Maarten Lankhorst <maarten.lankhorst@canonical.com> >Date: Mon, 7 Jan 2013 14:11:36 +0100 >Subject: [PATCH 30/35] winepulse: fix unneeded free in write.. > >--- > dlls/winepulse.drv/mmdevdrv.c | 6 +++++- > 1 file changed, 5 insertions(+), 1 deletion(-) > >diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c >index a4575d5..3ca68fd 100644 >--- a/dlls/winepulse.drv/mmdevdrv.c >+++ b/dlls/winepulse.drv/mmdevdrv.c >@@ -1886,6 +1886,10 @@ static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface, > return hr; > } > >+static void pulse_free_noop(void *buf) >+{ >+} >+ > static HRESULT WINAPI AudioRenderClient_ReleaseBuffer( > IAudioRenderClient *iface, UINT32 written_frames, DWORD flags) > { >@@ -1921,7 +1925,7 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer( > 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); >+ 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)); >-- >1.8.0.3 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 892617
: 683132