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 586317 Details for
Bug 821207
Re-enable winepulse (1.5.4 patch[s] attached)
[?]
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 1.5.4 wine-pulse patch.
wine-pulse-1.5.4.patch (text/plain), 106.27 KB, created by
Gilboa Davara
on 2012-05-23 11:36:48 UTC
(
hide
)
Description:
wine 1.5.4 wine-pulse patch.
Filename:
MIME Type:
Creator:
Gilboa Davara
Created:
2012-05-23 11:36:48 UTC
Size:
106.27 KB
patch
obsolete
>diff -uNr wine-1.5.4.old/configure wine-1.5.4/configure >--- wine-1.5.4.old/configure 2012-05-11 21:08:09.000000000 +0300 >+++ wine-1.5.4/configure 2012-05-23 12:37:16.166737017 +0300 >@@ -629,6 +629,8 @@ > ALSALIBS > GSTREAMER_INCL > GSTREAMER_LIBS >+PULSEINCL >+PULSELIBS > LIBGETTEXTPO > ZLIB > FREETYPEINCL >@@ -805,6 +807,7 @@ > with_oss > with_png > with_pthread >+with_pulse > with_sane > with_tiff > with_v4l >@@ -1495,6 +1498,7 @@ > --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) >@@ -2679,6 +2683,12 @@ > 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; >@@ -10639,6 +10649,87 @@ > 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" >@@ -11898,12 +11989,13 @@ > > 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 >@@ -15430,6 +15522,7 @@ > 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 winequartz.drv enable_winequartz_drv > wine_fn_config_dll winex11.drv enable_winex11_drv >diff -uNr wine-1.5.4.old/configure.ac wine-1.5.4/configure.ac >--- wine-1.5.4.old/configure.ac 2012-05-11 21:08:09.000000000 +0300 >+++ wine-1.5.4/configure.ac 2012-05-23 12:38:20.969468598 +0300 >@@ -74,6 +74,7 @@ > [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]) >@@ -1482,6 +1483,30 @@ > [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 >@@ -1688,13 +1713,14 @@ > 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 >@@ -2918,6 +2944,7 @@ > 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(winequartz.drv) > WINE_CONFIG_DLL(winex11.drv) >diff -uNr wine-1.5.4.old/dlls/mmdevapi/main.c wine-1.5.4/dlls/mmdevapi/main.c >--- wine-1.5.4.old/dlls/mmdevapi/main.c 2012-05-11 21:08:09.000000000 +0300 >+++ wine-1.5.4/dlls/mmdevapi/main.c 2012-05-23 12:13:41.597012820 +0300 >@@ -111,7 +111,7 @@ > { > 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 -uNr wine-1.5.4.old/dlls/winepulse.drv/Makefile.in wine-1.5.4/dlls/winepulse.drv/Makefile.in >--- wine-1.5.4.old/dlls/winepulse.drv/Makefile.in 1970-01-01 02:00:00.000000000 +0200 >+++ wine-1.5.4/dlls/winepulse.drv/Makefile.in 2012-05-23 12:03:21.068645000 +0300 >@@ -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 -uNr wine-1.5.4.old/dlls/winepulse.drv/mmdevdrv.c wine-1.5.4/dlls/winepulse.drv/mmdevdrv.c >--- wine-1.5.4.old/dlls/winepulse.drv/mmdevdrv.c 1970-01-01 02:00:00.000000000 +0200 >+++ wine-1.5.4/dlls/winepulse.drv/mmdevdrv.c 2012-05-23 12:03:21.069645000 +0300 >@@ -0,0 +1,3101 @@ >+/* >+ * 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); >+WINE_DECLARE_DEBUG_CHANNEL(winediag); >+ >+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 struct list g_sessions = LIST_INIT(g_sessions); >+ >+/* 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','\\', >+ '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) >+{ >+ 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); >+ if (pulse_thread) >+ CloseHandle(pulse_thread); >+ if (warn_once) >+ CloseHandle(warn_once); >+ } >+ return TRUE; >+} >+ >+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; >+ 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; >+ >+ AudioSession *session; >+ AudioSessionWrapper *session_wrapper; >+ 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 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); >+} >+ >+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); >+} >+ >+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); >+} >+ >+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 >+ * >+ * 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 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) >+{ >+ 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)); >+ pulse_probe_settings(1, &pulse_fmt[0]); >+ pulse_probe_settings(0, &pulse_fmt[1]); >+ 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); >+} >+ >+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_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"); >+ 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); >+ 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) >+ 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); >+ assert(This->peek_ofs < src_len); >+ src_len -= This->peek_ofs; >+ assert(src_len <= bytes); >+ >+ 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); >+ TRACE("Stream state changed to %i\n", state); >+ 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, const WCHAR ***ids, GUID **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(**ids)); >+ if (!*ids) >+ return E_OUTOFMEMORY; >+ (*ids)[0] = defaultW; >+ >+ *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys)); >+ if (!*keys) { >+ HeapFree(GetProcessHeap(), 0, *ids); >+ *ids = NULL; >+ return E_OUTOFMEMORY; >+ } >+ if (flow == eRender) >+ (*keys)[0] = pulse_render_guid; >+ else >+ (*keys)[0] = pulse_capture_guid; >+ >+ return S_OK; >+} >+ >+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); >+ return SUCCEEDED(hr) ? 3 : 0; >+} >+ >+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 >+ */ >+ 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 (IsEqualGUID(guid, &pulse_render_guid)) >+ dataflow = eRender; >+ else if (IsEqualGUID(guid, &pulse_capture_guid)) >+ dataflow = eCapture; >+ else >+ return E_UNEXPECTED; >+ >+ *out = NULL; >+ 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->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; >+ 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 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); >+ 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; >+ else >+ return AUDCLNT_E_UNSUPPORTED_FORMAT; >+ 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; >+ break; >+ default: >+ return AUDCLNT_E_UNSUPPORTED_FORMAT; >+ } >+ } >+ This->map.channels = fmt->nChannels; >+ 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]; >+ } >+ >+ /* 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 (i < fmt->nChannels || (mask & SPEAKER_RESERVED)) { >+ This->map.channels = 0; >+ ERR("Invalid channel mask: %i/%i and %x(%x)\n", i, fmt->nChannels, mask, wfe->dwChannelMask); >+ break; >+ } >+ break; >+ } >+ 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) { >+ 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); >+ } >+ } >+ 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)) { >+ 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; >+ 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; >+ 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); >+ 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 (IsEqualIID(riid, &IID_IAudioRenderClient)) { >+ if (This->dataflow != eRender) >+ return AUDCLNT_E_WRONG_ENDPOINT_TYPE; >+ *ppv = &This->IAudioRenderClient_iface; >+ } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) { >+ if (This->dataflow != eCapture) >+ return AUDCLNT_E_WRONG_ENDPOINT_TYPE; >+ *ppv = &This->IAudioCaptureClient_iface; >+ } else if (IsEqualIID(riid, &IID_IAudioClock)) { >+ *ppv = &This->IAudioClock_iface; >+ } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) { >+ *ppv = &This->IAudioStreamVolume_iface; >+ } else if (IsEqualIID(riid, &IID_IAudioSessionControl) || >+ IsEqualIID(riid, &IID_IChannelAudioVolume) || >+ IsEqualIID(riid, &IID_ISimpleAudioVolume)) { >+ if (!This->session_wrapper) { >+ This->session_wrapper = AudioSessionWrapper_Create(This); >+ if (!This->session_wrapper) >+ return E_OUTOFMEMORY; >+ } >+ if (IsEqualIID(riid, &IID_IAudioSessionControl)) >+ *ppv = &This->session_wrapper->IAudioSessionControl2_iface; >+ else if (IsEqualIID(riid, &IID_IChannelAudioVolume)) >+ *ppv = &This->session_wrapper->IChannelAudioVolume_iface; >+ else if (IsEqualIID(riid, &IID_ISimpleAudioVolume)) >+ *ppv = &This->session_wrapper->ISimpleAudioVolume_iface; >+ } >+ >+ if (*ppv) { >+ IUnknown_AddRef((IUnknown*)*ppv); >+ return S_OK; >+ } >+ >+ FIXME("stub %s\n", debugstr_guid(riid)); >+ return E_NOINTERFACE; >+} >+ >+static 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 >+}; >+ >+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 >+}; >+ >+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 >+}; >+ >+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 >+}; >+ >+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; >+ if (!This) >+ return E_OUTOFMEMORY; >+ This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl; >+ This->device = device; >+ This->ref = 1; >+ *out = &This->IAudioSessionManager2_iface; >+ return S_OK; >+} >diff -uNr wine-1.5.4.old/dlls/winepulse.drv/winepulse.drv.spec wine-1.5.4/dlls/winepulse.drv/winepulse.drv.spec >--- wine-1.5.4.old/dlls/winepulse.drv/winepulse.drv.spec 1970-01-01 02:00:00.000000000 +0200 >+++ wine-1.5.4/dlls/winepulse.drv/winepulse.drv.spec 2012-05-23 12:03:21.069645000 +0300 >@@ -0,0 +1,5 @@ >+# MMDevAPI driver functions >+@ stdcall -private GetPriority() AUDDRV_GetPriority >+@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs >+@ stdcall -private GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint >+@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
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 821207
: 586317 |
586318