Wine-devel
Threads by month
- ----- 2026 -----
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
February 2015
- 58 participants
- 166 discussions
17 Feb '15
Hi,
While running your changed tests on Windows, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at
https://testbot.winehq.org/JobDetails.pl?Key=11582
Your paranoid android.
=== wvistau64 (32 bit ddraw1) ===
ddraw1.c:2453: Test failed: Expected message 0x46, but didn't receive it.
ddraw1.c:2455: Test failed: Expected screen size 1024x768, got 0x0.
ddraw1.c:2461: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2492: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2500: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2527: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2547: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2571: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2598: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2615: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2652: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2663: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2690: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2710: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2727: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2754: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2771: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
ddraw1.c:2809: Test failed: Expected {0, 0, 1024, 768}, got {-8, -8, 1032, 746}.
=== w8 (32 bit ddraw1) ===
ddraw1.c:6286: Test failed: Got unexpected color 0x000000ff.
ddraw1.c:6290: Test failed: Got unexpected color 0x00000080.
1
0
Hi,
While running your changed tests on Windows, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at
https://testbot.winehq.org/JobDetails.pl?Key=11540
Your paranoid android.
=== w2000pro (32 bit job) ===
job.c:300: Test failed: Expected exit_code to be 1816 (ERROR_NOT_ENOUGH_QUOTA) is 0
=== wxppro (32 bit job) ===
job.c:300: Test failed: Expected exit_code to be 1816 (ERROR_NOT_ENOUGH_QUOTA) is 0
3
3
16 Feb '15
On 15 February 2015 at 20:39, Stefan Dösinger <stefan(a)codeweavers.com> wrote:
> diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c
> + color = get_surface_color(rt, 85, 85);
> + ok(compare_color(color, 0x00ff0000, 1), "Got unexpected color 0x%08x.\n", color);
> + color = get_surface_color(rt, 95, 95);
> + ok(compare_color(color, 0x00ff0000, 1), "Got unexpected color 0x%08x.\n", color);
> + color = get_surface_color(rt, 105, 105);
> + ok(compare_color(color, 0x0000ff00, 1), "Got unexpected color 0x%08x.\n", color);
> + color = get_surface_color(rt, 115, 115);
> + ok(compare_color(color, 0x00ff0000, 1), "Got unexpected color 0x%08x.\n", color);
This fails for me on Windows:
ddraw1.c:6350: Test failed: Got unexpected color 0x0000ff00.
ddraw1: 4215 tests executed (0 marked as todo, 1 failure), 0 skipped.
The test seemed to pass when it was still in visual.c, for what it's worth.
2
1
This patch causes build failures on our Ubuntu Precise builders. I don't have a machine to test it directly, but somehow unistd.h is still included indirectly:
In file included from http.c:37:0:
../../include/ws2tcpip.h:57:13: error: conflicting types for 'socklen_t'
/usr/include/unistd.h:275:21: note: previous declaration of 'socklen_t' was here
Full build log:
https://launchpadlibrarian.net/197555168/buildlog_ubuntu-precise-i386.wine-…
On 13.02.2015 12:12, Jacek Caban wrote:
> ---
> dlls/wininet/Makefile.in | 4 +-
> dlls/wininet/cookie.c | 10 +--
> dlls/wininet/dialogs.c | 7 +-
> dlls/wininet/ftp.c | 61 ++++----------
> dlls/wininet/http.c | 22 ++---
> dlls/wininet/internet.c | 18 +---
> dlls/wininet/internet.h | 23 ------
> dlls/wininet/netconnection.c | 193
> ++++---------------------------------------
> dlls/wininet/urlcache.c | 11 +--
> dlls/wininet/utility.c | 60 ++------------
> 10 files changed, 49 insertions(+), 360 deletions(-)
>
>
>
>
>
3
6
16 Feb '15
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi,
Looks good to me. I have asked Henri to have a look. Jacek says they're
OK too. I also verified the patches build OK. Thanks again for your work
and patience! I'd say give Henri a workday or two to check them against
the SDK headers and send the remaining 3 once the first 3 patches are in.
There's one obstacle I ran into:
Am 2015-02-13 um 14:06 schrieb Martell Malone:
> * Copyright 2010 Rico Schüller
> * Copyright 2013 Austin English
> + * Copyright 2015 Martell Malone
Fun stuff. There's something about the ü in Rico Schüller's name that
breaks git am:
fatal: cannot convert from y to UTF-8
But Jacek says downloading from
http://source.winehq.org/patches/data/109106 and applying works, so it's
probably nothing you have to worry about.
Cheers,
Stefan
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2
iQIcBAEBAgAGBQJU3f+1AAoJEN0/YqbEcdMwQWAP/ApeB1n37Dc2afkabeA8zt2r
j9Eo3HgRN/BfuTP2hPfOSMyC82Ic+1bQMgTbal0OtqNoKSR8BCUYM/mcV5V0z8qm
tHkMoe68pQimulw89EJ2Z2u4FtGbZ16lvK8HAABMV+2NiRQdZFXrby/+pNlzRTAS
1gus/456vTyy/weyOd4HaOobLrFs3mmqOXDAS+z/CPzGXpN5YLAiyOVv39U7Lkt4
e5Gs8meen8PXuBiltM90QCRPQjwLWZBIW/fORH0Ljk3KhxOFQktQGVNferARSUWC
9xwIdz4KBBfqLqeYSiAZZCdt5DFMmyjnTe9KZ/LpXZOnDOvu7djYFNzypos3VK4E
n0Y14WoTP+po9VtyFVLzWXFStIV3syJiv6Q6I/98M25xRAi+CPIV2RvStCi9RJAN
YFSzD9zdr+Se01QBpvRl5XjLi01bOes5PBBDqzJ7LLinF1G2+nMHZcAGMxAgICvQ
A/47RevBfmrdyP4MlIhKeWo47M+Onti6nrZ4EktSIqCp6LvZgIcuDj3mGhGx3Wuf
HLe+YmTNl5BkGo9vUEoy/BLsyMPDXtN5QyfJpSds5pCDOgzFXipZLpdYcUdTbiO2
0dSqke/PHJTuFeltl1HaxfDpHewt3ySf+sISPhdYPD+aY2T8XNHuq+81voVD5sjg
HPlmZRbNuuzLf/GuNsJj
=11zU
-----END PGP SIGNATURE-----
3
5
Hi,
While running your changed tests on Windows, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at
https://testbot.winehq.org/JobDetails.pl?Key=11571
Your paranoid android.
=== build (build) ===
Patch failed to apply
1
0
Hi,
While running your changed tests on Windows, I think I found new failures.
Being a bot and all I'm not very good at pattern recognition, so I might be
wrong, but could you please double-check?
Full results can be found at
https://testbot.winehq.org/JobDetails.pl?Key=11570
Your paranoid android.
=== build (build) ===
Patch failed to apply
1
0
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Am 2015-02-16 um 11:21 schrieb Jonathan Vollebregt:
> +static int reg_add( const WCHAR *key_name, const WCHAR *value_name, const BOOL value_empty,
> + const WCHAR *type, const WCHAR separator, const WCHAR *data,
> + const BOOL force)
Forgive me if I have asked this already: Do we have any precedent for
this function declaration style in Wine?
Patches 1-6 look good to me. I will tell Alexandre on IRC.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2
iQIcBAEBAgAGBQJU4dRLAAoJEN0/YqbEcdMwlrIP/0HfUXV9QWb2ZiR0VOq8fIjn
qZ75XFoN2yMxdHloVVHFqQe0sRRG9udVuGUq33b5eA9JCGUdCEV4FV1R2KrKUNaP
2nmwFCh67qqQBH1AnRZLVcQgzQGbvkVgzT8w46vtIlvL1EqAaT51lCB+Q1kWEj34
Y9W1SR0T4Yesxtg/rKDsUJZC3WoMq5q/tsPhs//K/1KTAlllVFLnTtu955kPr1IB
EwfB6nfsdtLi7D/BTjbTmuYBblCF9EnvRXjbul2AwN/he6UvYcVR3+rmRMk8oGQ2
zlquslix7Ch2VY1gtguy67ZDTWlSwXXE4p8/9weavSYomHR7Sqq/3DNmTAOyHYZ2
m/NUZVo/oBlpywPCuhFk36uxrUku3FprFLRGAwvo0we1EnF8tw9YRLyl9crz6xVA
vR6vgf/CSiqmDdS2OCnGQ0/H6aKU/WhsXF839v2ty5jCI95yQoHthybVTd9EZfYj
nZpXe1dIMmJDb2eEoPzTlvm8/Na+0zQjvsk695t4wnP1xe4vicE8gl8SWW8KNOrP
23Hyhg42BwE3H2m5F8KqhdeE2XMbgD3GoBV+EMKaEVHE3FsLmHhAOMzgJQ/UEukH
CX9oHWcbUTmtcu+RAl33UaSSSl4p0wz4rZa+NzlpgqEzvUO5izfMeHPV1udV9tP+
e/wAP5+YAUt5T8+LoRI8
=KrQx
-----END PGP SIGNATURE-----
2
3
AFAIK, even if for no other reason, this patch will probably be
rejected because it patches configure, as opposed to just patching
configure.ac and requesting configure to be regenerated.
It might also have a better chance if broken up into several patches.
Damjan
On Wed, Feb 11, 2015 at 11:15 AM, Maarten Lankhorst <wine(a)mblankhorst.nl> wrote:
> This is a squash of all the winepulse related patches in repo.or.cz/w/wine/multimedia.git
>
> Signed-off-by: Maarten Lankhorst <wine(a)mblankhorst.nl>
> ---
> diff --git a/configure b/configure
> index abef0c3..51a420f 100755
> --- a/configure
> +++ b/configure
> @@ -653,6 +653,8 @@ OSS4_CFLAGS
> ALSA_LIBS
> GSTREAMER_LIBS
> GSTREAMER_CFLAGS
> +PULSEINCL
> +PULSELIBS
> GETTEXTPO_LIBS
> Z_LIBS
> FREETYPE_LIBS
> @@ -827,6 +829,7 @@ with_oss
> with_pcap
> with_png
> with_pthread
> +with_pulse
> with_sane
> with_tiff
> with_v4l
> @@ -1296,6 +1299,7 @@ enable_winemapi
> enable_winemp3_acm
> enable_wineoss_drv
> enable_wineps_drv
> +enable_winepulse_drv
> enable_wineqtdecoder
> enable_winex11_drv
> enable_wing32
> @@ -2130,6 +2134,7 @@ Optional Packages:
> --without-pcap do not use the Packet Capture library
> --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)
> @@ -3377,6 +3382,12 @@ if test "${with_pthread+set}" = set; then :
> fi
>
>
> +# Check whether --with-pulse was given.
> +if test "${with_pulse+set}" = set; then :
> + withval=$with_pulse;
> +fi
> +
> +
> # Check whether --with-sane was given.
> if test "${with_sane+set}" = set; then :
> withval=$with_sane;
> @@ -12363,6 +12374,87 @@ esac
> fi
> fi
>
> +PULSELIBS=""
> +
> +PULSEINCL=""
> +
> +if test "x$with_pulse" != "xno";
> +then
> + ac_save_CPPFLAGS="$CPPFLAGS"
> + if test "$PKG_CONFIG" != "false";
> + then
> + ac_pulse_libs="`$PKG_CONFIG --libs libpulse 2>/dev/null`"
> + ac_pulse_cflags="`$PKG_CONFIG --cflags-only-I libpulse 2>/dev/null`"
> +
> + CPPFLAGS="$CPPFLAGS $ac_pulse_cflags"
> + for ac_header in pulse/pulseaudio.h
> +do :
> + ac_fn_c_check_header_mongrel "$LINENO" "pulse/pulseaudio.h" "ac_cv_header_pulse_pulseaudio_h" "$ac_includes_default"
> +if test "x$ac_cv_header_pulse_pulseaudio_h" = xyes; then :
> + cat >>confdefs.h <<_ACEOF
> +#define HAVE_PULSE_PULSEAUDIO_H 1
> +_ACEOF
> + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pa_stream_is_corked in -lpulse" >&5
> +$as_echo_n "checking for pa_stream_is_corked in -lpulse... " >&6; }
> +if ${ac_cv_lib_pulse_pa_stream_is_corked+:} false; then :
> + $as_echo_n "(cached) " >&6
> +else
> + ac_check_lib_save_LIBS=$LIBS
> +LIBS="-lpulse $ac_pulse_libs $LIBS"
> +cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> +/* end confdefs.h. */
> +
> +/* Override any GCC internal prototype to avoid an error.
> + Use char because int might match the return type of a GCC
> + builtin and then its argument prototype would still apply. */
> +#ifdef __cplusplus
> +extern "C"
> +#endif
> +char pa_stream_is_corked ();
> +int
> +main ()
> +{
> +return pa_stream_is_corked ();
> + ;
> + return 0;
> +}
> +_ACEOF
> +if ac_fn_c_try_link "$LINENO"; then :
> + ac_cv_lib_pulse_pa_stream_is_corked=yes
> +else
> + ac_cv_lib_pulse_pa_stream_is_corked=no
> +fi
> +rm -f core conftest.err conftest.$ac_objext \
> + conftest$ac_exeext conftest.$ac_ext
> +LIBS=$ac_check_lib_save_LIBS
> +fi
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pulse_pa_stream_is_corked" >&5
> +$as_echo "$ac_cv_lib_pulse_pa_stream_is_corked" >&6; }
> +if test "x$ac_cv_lib_pulse_pa_stream_is_corked" = xyes; then :
> +
> +$as_echo "#define HAVE_PULSEAUDIO 1" >>confdefs.h
> +
> + PULSELIBS="$ac_pulse_libs"
> + PULSEINCL="$ac_pulse_cflags"
> +fi
> +
> +
> +fi
> +
> +done
> +
> + fi
> + CPPFLAGS="$ac_save_CPPFLAGS"
> +fi
> +if test "$ac_cv_lib_pulse_pa_stream_is_corked" != "yes"; then :
> + case "x$with_pulse" in
> + x) as_fn_append wine_warnings "|libpulse ${notice_platform}development files not found or too old, Pulse won't be supported." ;;
> + xno) ;;
> + *) as_fn_error $? "libpulse ${notice_platform}development files not found or too old, Pulse won't be supported.
> +This is an error since --with-pulse was requested." "$LINENO" 5 ;;
> +esac
> +fi
> +
> if test "x$with_gstreamer" != "xno"
> then
> if ${GSTREAMER_CFLAGS:+false} :; then :
> @@ -13674,12 +13766,13 @@ fi
>
> test -n "$ALSA_LIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no}
> test -n "$COREAUDIO_LIBS" || 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$ALSA_LIBS$COREAUDIO_LIBS" = "x" -a \
> +if test "x$ALSA_LIBS$COREAUDIO_LIBS$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
> @@ -17616,7 +17709,7 @@ wine_fn_config_dll windowscodecs enable_windowscodecs clean,implib
> wine_fn_config_test dlls/windowscodecs/tests windowscodecs_test
> wine_fn_config_dll windowscodecsext enable_windowscodecsext implib
> wine_fn_config_test dlls/windowscodecsext/tests windowscodecsext_test
> -wine_fn_config_dll winealsa.drv enable_winealsa_drv
> +wine_fn_config_dll winealsa.drv enable_winealsa_drv implib
> wine_fn_config_dll winecoreaudio.drv enable_winecoreaudio_drv
> wine_fn_config_lib winecrt0
> wine_fn_config_dll wined3d enable_wined3d implib
> @@ -17628,6 +17721,7 @@ wine_fn_config_dll winemp3.acm enable_winemp3_acm
> wine_fn_config_dll wineoss.drv enable_wineoss_drv
> wine_fn_config_dll wineps.drv enable_wineps_drv clean,po
> wine_fn_config_dll wineps16.drv16 enable_win16
> +wine_fn_config_dll winepulse.drv enable_winepulse_drv
> wine_fn_config_dll wineqtdecoder enable_wineqtdecoder
> wine_fn_config_dll winex11.drv enable_winex11_drv
> wine_fn_config_dll wing.dll16 enable_win16
> diff --git a/configure.ac b/configure.ac
> index 2e6cb4c..4146e8f 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -72,6 +72,7 @@ AC_ARG_WITH(pcap, AS_HELP_STRING([--without-pcap],[do not use the Packet Ca
> AC_ARG_WITH(png, AS_HELP_STRING([--without-png],[do not use PNG]))
> AC_ARG_WITH(pthread, AS_HELP_STRING([--without-pthread],[do not use the pthread library]),
> [if test "x$withval" = "xno"; then ac_cv_header_pthread_h=no; fi])
> +AC_ARG_WITH(pulse, AC_HELP_STRING([--without-pulse],[do not use PulseAudio sound support]))
> AC_ARG_WITH(sane, AS_HELP_STRING([--without-sane],[do not use SANE (scanner support)]))
> AC_ARG_WITH(tiff, AS_HELP_STRING([--without-tiff],[do not use TIFF]))
> AC_ARG_WITH(v4l, AS_HELP_STRING([--without-v4l],[do not use v4l1 (v4l support)]))
> @@ -1548,6 +1549,30 @@ then
> [GetText ${notice_platform}development files not found (or too old), po files can't be rebuilt.])
> fi
>
> +dnl **** Check for PulseAudio ****
> +AC_SUBST(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
> @@ -1766,13 +1791,14 @@ fi
> dnl **** Disable unsupported winmm drivers ****
> test -n "$ALSA_LIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no}
> test -n "$COREAUDIO_LIBS" || enable_winecoreaudio_drv=${enable_winecoreaudio_drv:-no}
> +test -n "$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$ALSA_LIBS$COREAUDIO_LIBS" = "x" -a \
> +if test "x$ALSA_LIBS$COREAUDIO_LIBS$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
> @@ -3327,7 +3353,7 @@ WINE_CONFIG_DLL(windowscodecs,,[clean,implib])
> WINE_CONFIG_TEST(dlls/windowscodecs/tests)
> WINE_CONFIG_DLL(windowscodecsext,,[implib])
> WINE_CONFIG_TEST(dlls/windowscodecsext/tests)
> -WINE_CONFIG_DLL(winealsa.drv)
> +WINE_CONFIG_DLL(winealsa.drv,,[implib])
> WINE_CONFIG_DLL(winecoreaudio.drv)
> WINE_CONFIG_LIB(winecrt0)
> WINE_CONFIG_DLL(wined3d,,[implib])
> @@ -3339,6 +3365,7 @@ WINE_CONFIG_DLL(winemp3.acm)
> WINE_CONFIG_DLL(wineoss.drv)
> WINE_CONFIG_DLL(wineps.drv,,[clean,po])
> WINE_CONFIG_DLL(wineps16.drv16,enable_win16)
> +WINE_CONFIG_DLL(winepulse.drv)
> WINE_CONFIG_DLL(wineqtdecoder)
> WINE_CONFIG_DLL(winex11.drv)
> WINE_CONFIG_DLL(wing.dll16,enable_win16)
> diff --git a/dlls/mmdevapi/main.c b/dlls/mmdevapi/main.c
> index 52cf6f1..aa4baa5 100644
> --- a/dlls/mmdevapi/main.c
> +++ b/dlls/mmdevapi/main.c
> @@ -113,7 +113,7 @@ static BOOL init_driver(void)
> {
> static const WCHAR drv_value[] = {'A','u','d','i','o',0};
>
> - static WCHAR default_list[] = {'a','l','s','a',',','o','s','s',',',
> + static WCHAR default_list[] = {'p','u','l','s','e',',','a','l','s','a',',','o','s','s',',',
> 'c','o','r','e','a','u','d','i','o',0};
>
> DriverFuncs driver;
> diff --git a/dlls/winepulse.drv/Makefile.in b/dlls/winepulse.drv/Makefile.in
> new file mode 100644
> index 0000000..3428329
> --- /dev/null
> +++ b/dlls/winepulse.drv/Makefile.in
> @@ -0,0 +1,8 @@
> +MODULE = winepulse.drv
> +IMPORTS = dxguid uuid winmm user32 advapi32 ole32
> +DELAYIMPORTS = winealsa.drv
> +EXTRALIBS = @PULSELIBS@ $(PTHREAD_LIBS)
> +EXTRAINCL = @PULSEINCL@
> +
> +C_SRCS = \
> + mmdevdrv.c
> diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
> new file mode 100644
> index 0000000..ab78d5a
> --- /dev/null
> +++ b/dlls/winepulse.drv/mmdevdrv.c
> @@ -0,0 +1,3182 @@
> +/*
> + * Copyright 2011-2012 Maarten Lankhorst
> + * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers
> + * Copyright 2011 Andrew Eikum for CodeWeavers
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
> + */
> +
> +#define NONAMELESSUNION
> +#define COBJMACROS
> +#define _GNU_SOURCE
> +
> +#include "config.h"
> +#include <poll.h>
> +#include <pthread.h>
> +
> +#include <stdarg.h>
> +#include <unistd.h>
> +#include <math.h>
> +#include <stdio.h>
> +#include <errno.h>
> +
> +#include <pulse/pulseaudio.h>
> +
> +#include "windef.h"
> +#include "winbase.h"
> +#include "winnls.h"
> +#include "winreg.h"
> +#include "wine/debug.h"
> +#include "wine/unicode.h"
> +#include "wine/list.h"
> +
> +#include "ole2.h"
> +#include "dshow.h"
> +#include "dsound.h"
> +#include "propsys.h"
> +
> +#include "initguid.h"
> +#include "ks.h"
> +#include "ksmedia.h"
> +#include "propkey.h"
> +#include "mmdeviceapi.h"
> +#include "audioclient.h"
> +#include "endpointvolume.h"
> +#include "audiopolicy.h"
> +
> +#include "wine/list.h"
> +
> +#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
> +
> +WINE_DEFAULT_DEBUG_CHANNEL(pulse);
> +
> +/* From <dlls/mmdevapi/mmdevapi.h> */
> +enum DriverPriority {
> + Priority_Unavailable = 0,
> + Priority_Low,
> + Priority_Neutral,
> + Priority_Preferred
> +};
> +
> +static const REFERENCE_TIME MinimumPeriod = 30000;
> +static const REFERENCE_TIME DefaultPeriod = 100000;
> +
> +static pa_context *pulse_ctx;
> +static pa_mainloop *pulse_ml;
> +
> +static HANDLE pulse_thread;
> +static pthread_mutex_t pulse_lock;
> +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 GUID pulse_render_guid =
> +{ 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
> +static GUID pulse_capture_guid =
> +{ 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
> +
> +BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
> +{
> + if (reason == DLL_PROCESS_ATTACH) {
> + pthread_mutexattr_t attr;
> +
> + DisableThreadLibraryCalls(dll);
> +
> + pthread_mutexattr_init(&attr);
> + pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
> +
> + if (pthread_mutex_init(&pulse_lock, &attr) != 0)
> + pthread_mutex_init(&pulse_lock, NULL);
> + } else if (reason == DLL_PROCESS_DETACH) {
> + if (pulse_thread)
> + SetThreadPriority(pulse_thread, 0);
> + 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);
> + }
> + 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;
> + IUnknown *marshal;
> + 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;
> +
> + 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 DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map) {
> + int i;
> + DWORD mask = 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: mask |= SPEAKER_FRONT_LEFT; break;
> + case PA_CHANNEL_POSITION_MONO:
> + case PA_CHANNEL_POSITION_FRONT_CENTER: mask |= SPEAKER_FRONT_CENTER; break;
> + case PA_CHANNEL_POSITION_FRONT_RIGHT: mask |= SPEAKER_FRONT_RIGHT; break;
> + case PA_CHANNEL_POSITION_REAR_LEFT: mask |= SPEAKER_BACK_LEFT; break;
> + case PA_CHANNEL_POSITION_REAR_CENTER: mask |= SPEAKER_BACK_CENTER; break;
> + case PA_CHANNEL_POSITION_REAR_RIGHT: mask |= SPEAKER_BACK_RIGHT; break;
> + case PA_CHANNEL_POSITION_LFE: mask |= SPEAKER_LOW_FREQUENCY; break;
> + case PA_CHANNEL_POSITION_SIDE_LEFT: mask |= SPEAKER_SIDE_LEFT; break;
> + case PA_CHANNEL_POSITION_SIDE_RIGHT: mask |= SPEAKER_SIDE_RIGHT; break;
> + case PA_CHANNEL_POSITION_TOP_CENTER: mask |= SPEAKER_TOP_CENTER; break;
> + case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: mask |= SPEAKER_TOP_FRONT_LEFT; break;
> + case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: mask |= SPEAKER_TOP_FRONT_CENTER; break;
> + case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
> + case PA_CHANNEL_POSITION_TOP_REAR_LEFT: mask |= SPEAKER_TOP_BACK_LEFT; break;
> + case PA_CHANNEL_POSITION_TOP_REAR_CENTER: mask |= SPEAKER_TOP_BACK_CENTER; break;
> + case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: mask |= SPEAKER_TOP_BACK_RIGHT; break;
> + case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
> + case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
> + }
> +
> + return mask;
> +}
> +
> +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;
> + 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 = pulse_channel_map_to_channel_mask(&map);
> +}
> +
> +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 silence_buffer(pa_sample_format_t format, BYTE *buffer, UINT32 bytes)
> +{
> + memset(buffer, format == PA_SAMPLE_U8 ? 0x80 : 0, bytes);
> +}
> +
> +static void dump_attr(const pa_buffer_attr *attr) {
> + TRACE("maxlength: %u\n", attr->maxlength);
> + TRACE("minreq: %u\n", attr->minreq);
> + TRACE("fragsize: %u\n", attr->fragsize);
> + TRACE("tlength: %u\n", attr->tlength);
> + TRACE("prebuf: %u\n", attr->prebuf);
> +}
> +
> +static void pulse_op_cb(pa_stream *s, int success, void *user) {
> + TRACE("Success: %i\n", success);
> + *(int*)user = success;
> + pthread_cond_signal(&pulse_cond);
> +}
> +
> +static void pulse_attr_update(pa_stream *s, void *user) {
> + const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s);
> + TRACE("New attributes or device moved:\n");
> + dump_attr(attr);
> +}
> +
> +static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
> +{
> + ACImpl *This = userdata;
> + UINT32 oldpad = This->pad;
> +
> + if (bytes < This->bufsize_bytes)
> + This->pad = This->bufsize_bytes - bytes;
> + else
> + This->pad = 0;
> +
> + if (oldpad == This->pad)
> + return;
> +
> + assert(oldpad > This->pad);
> +
> + This->clock_written += oldpad - This->pad;
> + TRACE("New pad: %zu (-%zu)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss));
> +
> + if (This->event)
> + SetEvent(This->event);
> +}
> +
> +static void pulse_underflow_callback(pa_stream *s, void *userdata)
> +{
> + 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)
> +{
> + TRACE("(Re)started playing\n");
> +}
> +
> +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;
> + size_t src_len, copy, rem = This->capture_period;
> + if (!(p = (ACPacket*)list_head(&This->packet_free_head))) {
> + p = (ACPacket*)list_head(&This->packet_filled_head);
> + if (!p->discont) {
> + 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) {
> + size_t 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: %zu, fragsize: %u\n", bytes, pa_stream_get_buffer_attr(s)->fragsize);
> + assert(bytes >= This->peek_ofs);
> + bytes -= This->peek_ofs;
> + if (bytes < This->capture_period)
> + 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);
> +
> + if (!This->stream) {
> + WARN("pa_stream_new returned error %i\n", pa_context_errno(pulse_ctx));
> + return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
> + }
> +
> + 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;
> + WCHAR *id;
> +
> + 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));
> + *keys = NULL;
> + if (!*ids)
> + return E_OUTOFMEMORY;
> +
> + (*ids)[0] = id = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
> + *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys));
> + if (!*keys || !id) {
> + HeapFree(GetProcessHeap(), 0, id);
> + HeapFree(GetProcessHeap(), 0, *keys);
> + HeapFree(GetProcessHeap(), 0, *ids);
> + *ids = NULL;
> + *keys = NULL;
> + return E_OUTOFMEMORY;
> + }
> + memcpy(id, defaultW, sizeof(defaultW));
> +
> + if (flow == eRender)
> + (*keys)[0] = pulse_render_guid;
> + else
> + (*keys)[0] = pulse_capture_guid;
> +
> + return S_OK;
> +}
> +
> +int WINAPI AUDDRV_GetPriority(void)
> +{
> + HRESULT hr;
> + pthread_mutex_lock(&pulse_lock);
> + hr = pulse_connect();
> + pthread_mutex_unlock(&pulse_lock);
> + return SUCCEEDED(hr) ? Priority_Preferred : Priority_Unavailable;
> +}
> +
> +HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
> +{
> + HRESULT hr;
> + ACImpl *This;
> + int i;
> + EDataFlow dataflow;
> +
> + 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;
> + for (i = 0; i < PA_CHANNELS_MAX; ++i)
> + This->vol[i] = 1.f;
> +
> + hr = CoCreateFreeThreadedMarshaler((IUnknown*)This, &This->marshal);
> + if (hr) {
> + HeapFree(GetProcessHeap(), 0, This);
> + return hr;
> + }
> + 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)
> +{
> + ACImpl *This = impl_from_IAudioClient(iface);
> +
> + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
> +
> + if (!ppv)
> + return E_POINTER;
> +
> + *ppv = NULL;
> + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
> + *ppv = iface;
> + if (*ppv) {
> + IUnknown_AddRef((IUnknown*)*ppv);
> + return S_OK;
> + }
> +
> + if (IsEqualIID(riid, &IID_IMarshal))
> + return IUnknown_QueryInterface(This->marshal, riid, ppv);
> +
> + WARN("Unknown interface %s\n", debugstr_guid(riid));
> + return E_NOINTERFACE;
> +}
> +
> +static ULONG WINAPI AudioClient_AddRef(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);
> + }
> + IUnknown_Release(This->marshal);
> + 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 KSAUDIO_SPEAKER_MONO;
> + case 2:
> + return KSAUDIO_SPEAKER_STEREO;
> + case 3:
> + return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
> + case 4:
> + return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
> + case 5:
> + return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
> + case 6:
> + return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
> + case 7:
> + return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
> + case 8:
> + return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
> + }
> + 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|SPEAKER_RESERVED)))
> + mask = get_channel_mask(fmt->nChannels);
> + for (j = 0; j < sizeof(pulse_pos_from_wfx)/sizeof(*pulse_pos_from_wfx) && i < fmt->nChannels; ++j) {
> + if (mask & (1 << j))
> + This->map.map[i++] = pulse_pos_from_wfx[j];
> + }
> +
> + /* Special case for mono since pulse appears to map it differently */
> + if (mask == SPEAKER_FRONT_CENTER)
> + This->map.map[0] = PA_CHANNEL_POSITION_MONO;
> +
> + if (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);
> + 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);
> + TRACE("Obtaining format returns %08x\n", hr);
> + dump_fmt(fmt);
> +
> + if (FAILED(hr))
> + goto exit;
> +
> + if (mode == AUDCLNT_SHAREMODE_SHARED) {
> + REFERENCE_TIME def = pulse_def_period[This->dataflow == eCapture];
> + REFERENCE_TIME min = pulse_min_period[This->dataflow == eCapture];
> +
> + /* Switch to low latency mode if below 2 default periods,
> + * which is 20 ms by default, this will increase the amount
> + * of interrupts but allows very low latency. In dsound I
> + * managed to get a total latency of ~8ms, which is well below
> + * default
> + */
> + if (duration < 2 * def)
> + period = min;
> + else
> + period = def;
> + if (duration < 2 * period)
> + duration = 2 * period;
> +
> + /* Uh oh, really low latency requested.. */
> + if (duration <= 2 * period)
> + period /= 2;
> + }
> + period_bytes = pa_frame_size(&This->ss) * MulDiv(period, This->ss.rate, 10000000);
> +
> + 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;
> + silence_buffer(This->ss.format, This->tmp_buffer, This->bufsize_bytes);
> + list_init(&This->packet_free_head);
> + list_init(&This->packet_filled_head);
> + for (i = 0; i < capture_packets; ++i, ++cur_packet) {
> + 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;
> + BOOL exclusive;
> +
> + TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
> +
> + if (!fmt)
> + return E_POINTER;
> +
> + if (out)
> + *out = NULL;
> +
> + if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
> + exclusive = 1;
> + out = NULL;
> + } else if (mode == AUDCLNT_SHAREMODE_SHARED) {
> + exclusive = 0;
> + if (!out)
> + return E_POINTER;
> + } else
> + return E_INVALIDARG;
> +
> + if (fmt->nChannels == 0)
> + return AUDCLNT_E_UNSUPPORTED_FORMAT;
> +
> + closest = clone_format(fmt);
> + if (!closest)
> + return E_OUTOFMEMORY;
> +
> + dump_fmt(fmt);
> +
> + switch (fmt->wFormatTag) {
> + case WAVE_FORMAT_EXTENSIBLE: {
> + WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest;
> +
> + if ((fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) &&
> + fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE)) ||
> + fmt->nBlockAlign != fmt->wBitsPerSample / 8 * fmt->nChannels ||
> + ext->Samples.wValidBitsPerSample > fmt->wBitsPerSample ||
> + fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) {
> + hr = E_INVALIDARG;
> + break;
> + }
> +
> + if (exclusive) {
> + UINT32 mask = 0, i, channels = 0;
> +
> + if (!(ext->dwChannelMask & (SPEAKER_ALL | SPEAKER_RESERVED))) {
> + for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) {
> + if (i & ext->dwChannelMask) {
> + mask |= i;
> + channels++;
> + }
> + }
> +
> + if (channels != fmt->nChannels || (ext->dwChannelMask & ~mask)) {
> + hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
> + break;
> + }
> + } else {
> + hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
> + break;
> + }
> + }
> +
> + if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
> + if (fmt->wBitsPerSample != 32) {
> + hr = E_INVALIDARG;
> + break;
> + }
> +
> + if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample) {
> + hr = S_FALSE;
> + ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
> + }
> + } else if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
> + if (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8) {
> + hr = E_INVALIDARG;
> + break;
> + }
> +
> + if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample &&
> + !(fmt->wBitsPerSample == 32 &&
> + ext->Samples.wValidBitsPerSample == 24)) {
> + hr = S_FALSE;
> + ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
> + break;
> + }
> + } else {
> + hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
> + break;
> + }
> +
> + break;
> + }
> +
> + case WAVE_FORMAT_ALAW:
> + case WAVE_FORMAT_MULAW:
> + if (fmt->wBitsPerSample != 8) {
> + hr = E_INVALIDARG;
> + break;
> + }
> + /* Fall-through */
> + case WAVE_FORMAT_IEEE_FLOAT:
> + if (fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->wBitsPerSample != 32) {
> + hr = E_INVALIDARG;
> + break;
> + }
> + /* Fall-through */
> + case WAVE_FORMAT_PCM:
> + if (fmt->wFormatTag == WAVE_FORMAT_PCM &&
> + (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8)) {
> + hr = E_INVALIDARG;
> + break;
> + }
> +
> + if (fmt->nChannels > 2) {
> + hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
> + break;
> + }
> + /*
> + * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be
> + * ignored, invalid values are happily accepted.
> + */
> + break;
> + default:
> + hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
> + break;
> + }
> +
> + if (exclusive && hr != S_OK) {
> + hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
> + CoTaskMemFree(closest);
> + } else if (hr != S_FALSE)
> + CoTaskMemFree(closest);
> + else
> + *out = closest;
> +
> + /* Winepulse does not currently support exclusive mode, if you know of an
> + * application that uses it, I will correct this..
> + */
> + if (hr == S_OK && exclusive)
> + return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
> +
> + TRACE("returning: %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;
> + }
> +
> + 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;
> + }
> + 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)
> +{
> + ACImpl *This = impl_from_IAudioRenderClient(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_IAudioRenderClient))
> + *ppv = iface;
> + if (*ppv) {
> + IUnknown_AddRef((IUnknown*)*ppv);
> + return S_OK;
> + }
> +
> + if (IsEqualIID(riid, &IID_IMarshal))
> + return IUnknown_QueryInterface(This->marshal, riid, ppv);
> +
> + WARN("Unknown interface %s\n", debugstr_guid(riid));
> + return E_NOINTERFACE;
> +}
> +
> +static ULONG WINAPI 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);
> + size_t avail, req, bytes = frames * pa_frame_size(&This->ss);
> + UINT32 pad;
> + 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 %zu 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 %zu/%u %u/%u\n", This, ret, req/pa_frame_size(&This->ss), frames, pad, This->bufsize_frames);
> + if (ret >= 0)
> + pa_stream_cancel_write(This->stream);
> + *data = This->tmp_buffer;
> + This->locked_ptr = NULL;
> + } else
> + *data = This->locked_ptr;
> + pthread_mutex_unlock(&pulse_lock);
> + return hr;
> +}
> +
> +static void pulse_free_noop(void *buf)
> +{
> +}
> +
> +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;
> + }
> +
> + This->locked = 0;
> + if (This->locked_ptr) {
> + if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
> + silence_buffer(This->ss.format, This->locked_ptr, written_bytes);
> + pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
> + } else {
> + if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
> + silence_buffer(This->ss.format, This->tmp_buffer, written_bytes);
> + pa_stream_write(This->stream, This->tmp_buffer, written_bytes, pulse_free_noop, 0, PA_SEEK_RELATIVE);
> + }
> +
> + This->pad += written_bytes;
> + This->locked_ptr = NULL;
> + TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
> + 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)
> +{
> + ACImpl *This = impl_from_IAudioCaptureClient(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_IAudioCaptureClient))
> + *ppv = iface;
> + if (*ppv) {
> + IUnknown_AddRef((IUnknown*)*ppv);
> + return S_OK;
> + }
> +
> + if (IsEqualIID(riid, &IID_IMarshal))
> + return IUnknown_QueryInterface(This->marshal, riid, ppv);
> +
> + WARN("Unknown interface %s\n", debugstr_guid(riid));
> + return E_NOINTERFACE;
> +}
> +
> +static ULONG WINAPI 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);
> +
> + TRACE("(%p)->(%p)\n", This, frames);
> + if (!frames)
> + return E_POINTER;
> +
> + pthread_mutex_lock(&pulse_lock);
> + ACImpl_GetCapturePad(This, NULL);
> + if (This->locked_ptr)
> + *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;
> + }
> +
> + if (IsEqualIID(riid, &IID_IMarshal))
> + return IUnknown_QueryInterface(This->marshal, riid, ppv);
> +
> + WARN("Unknown interface %s\n", debugstr_guid(riid));
> + return E_NOINTERFACE;
> +}
> +
> +static ULONG WINAPI 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)) {
> + if (This->share == AUDCLNT_SHAREMODE_SHARED)
> + *freq = This->ss.rate * pa_frame_size(&This->ss);
> + else
> + *freq = This->ss.rate;
> + }
> + pthread_mutex_unlock(&pulse_lock);
> + return hr;
> +}
> +
> +static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
> + UINT64 *qpctime)
> +{
> + ACImpl *This = impl_from_IAudioClock(iface);
> + 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->share == AUDCLNT_SHAREMODE_EXCLUSIVE)
> + *pos /= pa_frame_size(&This->ss);
> +
> + /* 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) && This->share == AUDCLNT_SHAREMODE_SHARED)
> + *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)
> +{
> + ACImpl *This = impl_from_IAudioStreamVolume(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_IAudioStreamVolume))
> + *ppv = iface;
> + if (*ppv) {
> + IUnknown_AddRef((IUnknown*)*ppv);
> + return S_OK;
> + }
> +
> + if (IsEqualIID(riid, &IID_IMarshal))
> + return IUnknown_QueryInterface(This->marshal, riid, ppv);
> +
> + WARN("Unknown interface %s\n", debugstr_guid(riid));
> + return E_NOINTERFACE;
> +}
> +
> +static ULONG WINAPI 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 HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
> + IAudioStreamVolume *iface, UINT32 count, const float *levels)
> +{
> + ACImpl *This = impl_from_IAudioStreamVolume(iface);
> + HRESULT hr;
> + int i;
> +
> + 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;
> +
> + 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);
> + HRESULT hr;
> + int i;
> +
> + 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;
> +
> + 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;
> +}
> +
> +static void pulse_phys_speakers_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata)
> +{
> + PROPVARIANT *pv = userdata;
> +
> + if (i)
> + pv->u.ulVal |= pulse_channel_map_to_channel_mask(&i->channel_map);
> +
> + pthread_cond_signal(&pulse_cond);
> +}
> +
> +HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
> +{
> + pa_operation *o;
> +
> + TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
> +
> + if (IsEqualGUID(guid, &pulse_render_guid) && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
> + /* For default Pulseaudio render device, OR together all of the
> + * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
> +
> + out->vt = VT_UI4;
> + out->u.ulVal = 0;
> +
> + pthread_mutex_lock(&pulse_lock);
> + o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, out);
> + if (o) {
> + while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
> + pthread_cond_wait(&pulse_cond, &pulse_lock);
> + pa_operation_unref(o);
> + }
> + pthread_mutex_unlock(&pulse_lock);
> + return out->u.ulVal ? S_OK : E_FAIL;
> + }
> +
> + return E_NOTIMPL;
> +}
> diff --git a/dlls/winepulse.drv/winepulse.drv.spec b/dlls/winepulse.drv/winepulse.drv.spec
> new file mode 100644
> index 0000000..7aeb175
> --- /dev/null
> +++ b/dlls/winepulse.drv/winepulse.drv.spec
> @@ -0,0 +1,11 @@
> +# 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
> +@ stdcall -private GetPropValue(ptr ptr ptr) AUDDRV_GetPropValue
> +
> +# WinMM driver functions
> +@ stdcall -private DriverProc(long long long long long) winealsa.drv.DriverProc
> +@ stdcall -private midMessage(long long long long long) winealsa.drv.midMessage
> +@ stdcall -private modMessage(long long long long long) winealsa.drv.modMessage
> diff --git a/include/config.h.in b/include/config.h.in
> index 4d7c7ac..0e44fd7 100644
> --- a/include/config.h.in
> +++ b/include/config.h.in
> @@ -687,6 +687,12 @@
> /* Define to 1 if you have the <pthread_np.h> header file. */
> #undef HAVE_PTHREAD_NP_H
>
> +/* Define if you have pulseaudio */
> +#undef HAVE_PULSEAUDIO
> +
> +/* Define to 1 if you have the <pulse/pulseaudio.h> header file. */
> +#undef HAVE_PULSE_PULSEAUDIO_H
> +
> /* Define to 1 if you have the <pwd.h> header file. */
> #undef HAVE_PWD_H
>
>
>
>
5
4
Re: ntdll/tests: Add tests for RtlIpv4StringToAddressEx, RtlIpv6StringToAddress (resend)
by Stefan Dösinger 16 Feb '15
by Stefan Dösinger 16 Feb '15
16 Feb '15
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Hi,
Please split the patch into one patch per tested function (ipv4ex, ipv6, ipv6ex).
Am 2015-02-13 um 22:35 schrieb Mark Jansen:
> + struct
> + {
> + PCSTR address;
> + NTSTATUS res;
> + int ip[4];
> + USHORT port;
> + } tests[] =
You can make this const (or even static const, not much difference though).
> + { "1.1.1.1:", STATUS_INVALID_PARAMETER, { 1, 1, 1, 1 }, 0xdead },
> + { "1.1.1.1+", STATUS_INVALID_PARAMETER, { 1, 1, 1, 1 }, 0xdead },
> + { "1.1.1.1:1", STATUS_SUCCESS, { 1, 1, 1, 1 }, 0x100 },
I recommend to add a few more tests:
"1.1.1.1" - without a port
"256.1.1.1:1" - out of range ip
"-1.1.1.1:1" - negative ip
"1.1.0.1" - There's a zero in it, should still be valid though.
Hex and octal IPs
> + const int testcount = sizeof(tests) / sizeof(tests[0]);
> + int i, Strict;
Technically i and testcount can be made unsigned. According to your function definition earlier in the file Strict should be a BOOLEAN. For consistency "Strict" should be renamed to lower case "strict".
> + ok(res == STATUS_INVALID_PARAMETER,
> + "[null address] res = 0x%08x, expected 0x%08x\n",
> + res, STATUS_INVALID_PARAMETER);
> ...
> + ok(res == tests[i].res,
> + "[%s] res = 0x%08x, expected 0x%08x\n",
> + tests[i].address, res, tests[i].res);
Indentation inconsistency in the line continuation.
> + expected_ip.S_un.S_un_b.s_b1 = tests[i].ip[0];
> + expected_ip.S_un.S_un_b.s_b2 = tests[i].ip[1];
> + expected_ip.S_un.S_un_b.s_b3 = tests[i].ip[2];
> + expected_ip.S_un.S_un_b.s_b4 = tests[i].ip[3];
Is there a reason why you didn't just put a IN_ADDR in the test structure? You'll probably need an extra curly brackets around each member of struct S_un_b and one for the union: {{1}, {1}, {1}, {1}}
Most of this applies in the same fashion to the ipv6 tests.
> +/* ipv6 addresses from https://github.com/beaugunderson/javascript-ipv6/tree/master/test/data */
This is licensed under MIT. IANAL, but I think it is ok to transfer to an LGPLv2 licensed project.
> +static void test_RtlIpv6StringToAddress(void)
> ...
> + struct
> + {
> + PCSTR address;
> + NTSTATUS res;
> + int terminator_offset;
> + int ip[8];
> + } tests[] =
> ...
> + struct
> + {
> + PCSTR address;
> + NTSTATUS res;
> + ULONG scope;
> + USHORT port;
> + int ip[8];
> + } tests[] =
Did you try to share the data for the ipv6 and ipv6ex test?
Unfortunately my ipv6-foo is too limited to say much about the tests in the table :-( .
> + /* sanity check */
> + ok(sizeof(ip._S6_un) == (sizeof(USHORT)* 8), "sizeof(ip._S6_un)\n");
I think we have compile-time asserts for things like this, but I don't know how they work.
> + res = pRtlIpv6StringToAddressExA("::", &ip, &scope, &port);
> + ok(res == STATUS_SUCCESS, "[validate] res = 0x%08x, expected STATUS_SUCCESS\n", res);
> + res = pRtlIpv6StringToAddressExA(NULL, &ip, &scope, &port);
> + ok(res == STATUS_INVALID_PARAMETER, "[null string] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res);
> + res = pRtlIpv6StringToAddressExA("::", NULL, &scope, &port);
> + ok(res == STATUS_INVALID_PARAMETER, "[null result] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res);
> + res = pRtlIpv6StringToAddressExA("::", &ip, NULL, &port);
> + ok(res == STATUS_INVALID_PARAMETER, "[null scope] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res);
> + res = pRtlIpv6StringToAddressExA("::", &ip, &scope, NULL);
> + ok(res == STATUS_INVALID_PARAMETER, "[null port] res = 0x%08x, expected STATUS_INVALID_PARAMETER\n", res);
Checking that the output variables remain unmodified in the ipv4 test was a good idea IMO. Please add it here as well. (It is somewhat redundant because the test table has invalid tests and checks if the output is untouched, but this wouldn't be the first place where we find strange error handling behavior...
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2
iQIcBAEBAgAGBQJU4Go3AAoJEN0/YqbEcdMwP/wP/j5pbxPuTt4dBpr/fxA/E/rv
H6+hoXqq3irBQWgIuU4sprfvR/VEcKYR7HnMvL8SX/aa7xvuFPSdZerjHGgpoz1R
SKlqg46X8HxIe6BvODUSpi90OrJ4UNmAutL2vhpFQVzSsblZ1xPkk3GKHnIubslC
fmUi9I91Vyk6Upvk4xI9FNyf7OddUcHkb+Xkw5qFCip/R4Mcad/lZ5lKq6pJWNhE
4VRIA19IlCshi0Z3CY1NM8ZdRCCg8Wi8MqP8jgBaHsR3yPfEoBa3qUjsB2ryhrlP
2ityxJSFgAl38OQ89kwwkL7KIxxrP7t8oDrjGpb76mPdnpWxRBNarmSSu+JAmDDg
jUlzsjftCifjTs2vZloDWbqqhEBY3a/SQ2tUHeIxuYplKpR/AN75WH7WxFOKpR1h
KgafrCMeCGi0s2dVymkqXNa8zluQ3nq/pC16nddelgXD4D3RMPwWBnEDY8bpspR7
n0SR2mMTDAt2mndeI8DXvSyyVE4umsl8sUCjWVpKE/lj2xKZjKyPWFVx8yv1omx+
EqF+RUA2Qld+HOCy7d2c00mPy0bNmthaj7T5tmQSjEVStmGgwSH0RytkrI1yqwU8
MyDotAIxZ3OAiHvMl6RHvq5pquWDM4FTJ+yMpdxiEKPIZ4dwMa5mkZvaCYWBB2Qv
NnxWITxzj7GvV1YH7U/5
=uuX8
-----END PGP SIGNATURE-----
3
3