diff --git a/wine/PKGBUILD b/wine/PKGBUILD index 36ef80c..dbab3de 100644 --- a/wine/PKGBUILD +++ b/wine/PKGBUILD @@ -1,15 +1,14 @@ # maintainer: Giuseppe Cala' pkgname=wine -pkgver=1.7.21 -pkgrel=2 +pkgver=1.7.22 +pkgrel=1 _pkgbasever=${pkgver/rc/-rc} -_pulsepatches=1.7.21 source=(http://ibiblio.org/pub/linux/system/emulators/$pkgname/$pkgname-$_pkgbasever.tar.bz2 30-win32-aliases.conf - http://dev.gentoo.org/~tetromino/distfiles/wine/winepulse-patches-$_pulsepatches.tar.bz2) + winemultimedia_1.7.22_20140712.patch) -md5sums=('ee43e69f135c46ad544bc35cd7e6681c' +md5sums=('519319364b0845ffcbeade4a5b98c0d4' '1ff4e467f59409272088d92173a0f801' 'c975b349bce41992905891e267ed61a6') @@ -23,61 +22,64 @@ install=wine.install depends=( fontconfig lib32-fontconfig - mesa lib32-mesa - libglapi lib32-libglapi libxcursor lib32-libxcursor libxrandr lib32-libxrandr libxdamage lib32-libxdamage - libxxf86dga lib32-libxxf86dga - alsa-lib lib32-alsa-lib - lcms lib32-lcms - mpg123 lib32-mpg123 - openal lib32-openal - libxml2 lib32-libxml2 libxi lib32-libxi gettext lib32-gettext - gstreamer0.10-base lib32-gstreamer0.10-base - libpulse lib32-libpulse + freetype2 lib32-freetype2 + glu lib32-glu + libsm lib32-libsm + gcc-libs lib32-gcc-libs desktop-file-utils ) - - makedepends=(autoconf ncurses bison perl fontforge flex prelink 'gcc>=4.5.0-2' 'gcc-multilib>=4.5.0-2' giflib lib32-giflib - libxpm lib32-libxpm libpng lib32-libpng + gnutls lib32-gnutls libxinerama lib32-libxinerama libxcomposite lib32-libxcomposite libxmu lib32-libxmu libxxf86vm lib32-libxxf86vm libxml2 lib32-libxml2 - libxslt lib32-libxslt libldap lib32-libldap lcms2 lib32-lcms2 mpg123 lib32-mpg123 openal lib32-openal - jack lib32-jack - libcups lib32-libcups - gnutls lib32-gnutls v4l-utils lib32-v4l-utils alsa-lib lib32-alsa-lib + mesa lib32-mesa + mesa-libgl lib32-mesa-libgl + libcl lib32-libcl + libxslt lib32-libxslt + jack lib32-jack samba opencl-headers ) optdepends=( - giflib lib32-giflib - libpng lib32-libpng - libldap lib32-libldap - jack lib32-jack - libcups lib32-libcups - gnutls lib32-gnutls - v4l-utils lib32-v4l-utils + giflib lib32-giflib + libpng lib32-libpng + libldap lib32-libldap + gnutls lib32-gnutls + lcms2 lib32-lcms2 + libxml2 lib32-libxml2 + mpg123 lib32-mpg123 + openal lib32-openal + v4l-utils lib32-v4l-utils + libpulse lib32-libpulse + alsa-plugins lib32-alsa-plugins + alsa-lib lib32-alsa-lib + libjpeg-turbo lib32-libjpeg-turbo + libxcomposite lib32-libxcomposite + libxinerama lib32-libxinerama + ncurses lib32-ncurses + libcl lib32-libcl + libxslt lib32-libxslt cups - samba - dosbox + samba dosbox ) prepare() { @@ -97,9 +99,7 @@ prepare() { sed -i 's|lncurses|lncursesw|g' "$srcdir/$pkgname/configure" cd $pkgname - for _patch in $(ls $srcdir/winepulse-patches-$_pulsepatches/*.patch) ; do - patch -Np1 -i $_patch - done + patch -Np1 -i ../winemultimedia_1.7.22_20140712.patch # These additional CPPFLAGS solve FS#27662 and FS#34195 export CPPFLAGS="${CPPFLAGS/-D_FORTIFY_SOURCE=2/} -D_FORTIFY_SOURCE=0" @@ -163,3 +163,6 @@ package() { ln -s ../conf.avail/30-win32-aliases.conf "$pkgdir/etc/fonts/conf.d/30-win32-aliases.conf" } +md5sums=('519319364b0845ffcbeade4a5b98c0d4' + '1ff4e467f59409272088d92173a0f801' + '3ef55a8345325b15144e2062b627b91f') diff --git a/wine/wine.install b/wine/wine.install index 949e511..0548b7f 100644 --- a/wine/wine.install +++ b/wine/wine.install @@ -1,23 +1,12 @@ -post_upgrade() { - update-desktop-database -q - echo "This wine package is wow64 enabled. This means it can run 32bit/64bit Windows apps on x86_64." - echo "If you are on x86_64, the default WINEARCH will be win64." - echo "This will cause a lot of Windows applications to malfunction even if they usually work in wine." - echo "Please create your ~/.wine with 'WINEARCH=win32 winecfg' if you are unsure and on x86_64." - echo "See the Arch wiki on wine for more information." - echo "" -} - post_install() { update-desktop-database -q - echo "This wine package is wow64 enabled. This means it can run 32bit/64bit Windows apps on x86_64." - echo "If you are on x86_64, the default WINEARCH will be win64." - echo "This will cause a lot of Windows applications to malfunction even if they usually work in wine." - echo "Please create your ~/.wine with 'WINEARCH=win32 winecfg' if you are unsure and on x86_64." - echo "See the Arch wiki on wine for more information." - echo "" + #echo "This wine package is wow64 enabled. This means it can run 32bit/64bit Windows apps on x86_64." + #echo "If you are on x86_64, the default WINEARCH will be win64." + #echo "This will cause a lot of Windows applications to malfunction even if they usually work in wine." + #echo "Please create your ~/.wine with 'WINEARCH=win32 winecfg' if you are unsure and on x86_64." + #echo "See the Arch wiki on wine for more information." } post_remove() { update-desktop-database -q -} \ No newline at end of file +} diff --git a/wine/winemultimedia_1.7.22_20140712.patch b/wine/winemultimedia_1.7.22_20140712.patch new file mode 100644 index 0000000..86b00c2 --- /dev/null +++ b/wine/winemultimedia_1.7.22_20140712.patch @@ -0,0 +1,6841 @@ +From b406908d798762a07d7cd0d02810ea46f30417ce Mon Sep 17 00:00:00 2001 +From: John Schoenick +Date: Sat, 12 Jul 2014 13:32:45 -0700 +Subject: [PATCH] wine-multimedia for wine 1.7.22 + +Squashed commit of the following: + +commit 324c919d453ddcad3e505f3a7682936afc00315c +Author: Mark Harmstone +Date: Sat May 24 10:20:47 2014 +0200 + + winepulse: handle stream create failing correctly + +commit 36b071ed8d9586ec783d5103738f633a5ce1658d +Author: Maarten Lankhorst +Date: Sat May 24 10:20:47 2014 +0200 + + dsound: fixup IDirectSoundCaptureBuffer_QueryInterface + + Don't expose v8, and actually respond to iunknown. + +commit b95c53699cda97b1e37dfd10a73197c89fd5c366 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:46 2014 +0200 + + Revert "winmm: Increase mmdevapi buffer sizes." + + This reverts commit fdfff7cf6013a030f2b240b13b02831e502d49e7. + + You've got to be kidding me, 1 second latency in winmm because + of a driver not used on linux? If the 'upstream' audio devs + spent less time on workarounds and more on fixing drivers, + this kind of crap wouldn't be needed. + + I cannot believe that winecoreaudio requires .5 second latency, + their api allows for very low latency, so it's probably + winecoreaudio screwing things up instead. + +commit 9c2ed8b6547526555a2abd685728efc470a6a918 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:46 2014 +0200 + + TESTING -- override pthreads to fix gstreamer v2 + + I believe the code is ready and will work properly now in all cases. + but please test before cherry picking this patch, and report + success or failure to me please. + + Changes since v1: + - Call pthread_yield to make sure that we link against libpthread. + This fixes the build on saucy. + +commit 23e5b3d03ee065cd86fb3a2f9cbec7381bdf1396 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:46 2014 +0200 + + winepulse: add support for IMarshal + + Fixes bug 32161 for winepulse. Based On Jeff Klein's patches for the + other drivers. + +commit 3546c02b884f5009daab652b93ec53ded1bc9725 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:46 2014 +0200 + + winepulse: use a pi-mutex for serialization. + + The winepulse thread is realtime, to prevent blocking it indefinitely + use priority inheritance. Only initialize and release are potentially + unsafe, the rest should be ok with -rt. + +commit 72b20348ec2ce540468a5051b8b089d75ef819ef +Author: Maarten Lankhorst +Date: Sat May 24 10:20:46 2014 +0200 + + dsound: kill unconditional memory allocation in mixing thread + + Unfortunately this doesn't get rid of the memory allocation entirely, + but it will decrease the chance of underruns due to locking immensely. + + Signed-off-by: Maarten Lankhorst + +commit b90fc84ebb6f18267628e7a1b8eec5a896587f21 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:46 2014 +0200 + + dsound: remove state machine from render buffer + + .. wat + +commit 2ea58a009384645445c8cf7865ae8c43478b124c +Author: Maarten Lankhorst +Date: Sat May 24 10:20:46 2014 +0200 + + dsound: fixup DSOUND_WaveQueue checks + + If you're going to be silly, you should go all the way. :P + +commit 78621985136b5624677b0a85e4535a010362396b +Author: Maarten Lankhorst +Date: Sat May 24 10:20:46 2014 +0200 + + dsound: fix format handling on invalid format to never fail + + For the users still on oss4 this is probably useful. This is more of + a theoretical concern than practical, since nobody uses primary mode. + And even if someone did, they would have to find a format that was + unsupported, like IEEE float would probably be the easiest to trigger. + + This patch now forces everything to a single call to DSOUND_ReopenDevice, + which will either fail and keep previous state, or succeed. + +commit 7651cdf5babcf4343ee08abf03bff7b44ff05a8c +Author: Maarten Lankhorst +Date: Sat May 24 10:20:46 2014 +0200 + + dsound: mix float natively + +commit 88d429e4383a723f590877998b64ba5cf5fb97d5 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:46 2014 +0200 + + dsound: rework ugly mixer logic + +commit 1ad2303cc86af1c9bdc290821177fcb07ae9a5a8 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:45 2014 +0200 + + winepulse v23: fixup a invalid free in mmdevapi + + array members of ids should be dynamically allocated, judging from valgrind output. + +commit 8c3ad5783376246d222e33395e8e28da933395b9 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:45 2014 +0200 + + winepulse: fix unneeded free in write.. + +commit 7d41010fbd4445856293dfa83ab2c391326b0e42 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:45 2014 +0200 + + winepulse v21: return early if padding didn't update + +commit 18a8f3794d1828d2ebae4de9d0b911e0c5c7dbe5 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:45 2014 +0200 + + winepulse: fixup IsFormatSupported calls + +commit c831afd2f2674f4abba830e87ffeb911df1388f5 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:45 2014 +0200 + + winepulse v20: fix the checks in IsFormatSupported + + Thanks to DGhost001 for reporting and isolating the issue. + +commit a3b383a5ed0221812e16a9ff83852d57c51b9ff9 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:45 2014 +0200 + + winepulse: disable the setevent part of the latency hack + + If you get playback glitches in skyrim or other games as a result of + this patch, PLEASE REPORT TO ME! + +commit b93ed79dab2976417c306b44576c3f8ed8daa8fa +Author: Maarten Lankhorst +Date: Sat May 24 10:20:45 2014 +0200 + + winepulse: remove bogus SetEvent from pulse_started_callback + +commit ee2d5abb46b6fd0967ffdb99798bd24c0b9ba65f +Author: Maarten Lankhorst +Date: Sat May 24 10:20:45 2014 +0200 + + winepulse: drop realtime priority before thread destruction + + prevents having to handle a kernel RT Watchdog Timeout. + +commit 9361e6798401ab3650fd74d8ec410475b5dae36b +Author: Maarten Lankhorst +Date: Sat May 24 10:20:45 2014 +0200 + + winepulse: Fix low latency support + + Some games request a 20 ms buffer and will only fill 20 ms. + Since 10 ms periods are too long in this case for winepulse, + fill change period size to 5 ms and force a trigger if + there's still data left to fill. + +commit b426d9e516cf13b0bc1af1236f37923c4b741b71 +Author: Juergen Tretthahn +Date: Sat May 24 10:20:44 2014 +0200 + + winepulse: API Compatibility with 1.5.2 onward, v2 + + V2: Add winepulse.drv.spec to commit too + V1: Original version + + Commit e87cb774d131963d2642d075977571585ec5da8d changed the driver api + leave this commit out to build for builds prior to this + + Not needed for: 1.5.1, 1.5 and 1.4 builds + +commit 24ad9ec72bdde9f552edf720dfb8ddb296b78308 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:44 2014 +0200 + + winepulse v18: Latency and compilation improvements + + Changes since v17: + - Remove clock_pulse interpolation + * Couldn't work, sadly + - Allow 2 * MinimumPeriod for shared buffers + - Fix all compiler warnings when compiling with 64-bits + - Dynamically select low latency mode if less than 2 default periods are request + * This requires the rtkit patch to be useful + +commit e2f8019b05427532961356cdaf40cdbb0cb392f7 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:44 2014 +0200 + + winepulse v17: Fix winmm tests + + Handle dwChannelMask = SPEAKER_ALL better so WAVE_FORMAT_EXTENSIBLE tests pass too + +commit 15983e6eaf216927c7bed825ab846ae377a33156 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:44 2014 +0200 + + winepulse v16: Add official warning wine doesn't want to support winepulse + + And give an alternative place to ask for support. + I wish it didn't have to come to this. + +commit d2b1b8b1979b799fd87b08bda9b0e77821681f2e +Author: Maarten Lankhorst +Date: Sat May 24 10:20:44 2014 +0200 + + winepulse v15: Add support for missing formats, and silence an error for missing format tags + +commit 2ad5775f86fe0282901cf65ee36fe1b806312591 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:44 2014 +0200 + + winepulse v12 + + Changes since v11: + - Fix incorrect assertions which may fail on moving a capture device + - Whitespace apply fixes + + Changes since v10: + - Make some members static + - Fix small memory leak in GetService + +commit cf6cf4117deb96c6c3d0ae9f0a655866021b32da +Author: Maarten Lankhorst +Date: Sat May 24 10:20:44 2014 +0200 + + fix fdels trailing whitespaces + + Happy? :P + +commit ff821e5994ae1ecba9593f766e7deea2b0ee5fc9 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:44 2014 +0200 + + winepulse: Add session support + + --- + Copied verbatim from winealsa + +commit ebd14a44da5d2e918e6992e7119a9dd7969a13de +Author: Maarten Lankhorst +Date: Sat May 24 10:20:44 2014 +0200 + + winepulse: Add audiostreamvolume + + --- + Pulse allows streams to set volume, but for various reasons it's + better off being disabled by default. + + It can be enabled with HKCU\Software\Wine\Pulse\StreamVol=0x1 + +commit 7bd1f84624013c656e0275247a83ea3cb386feca +Author: Maarten Lankhorst +Date: Sat May 24 10:20:44 2014 +0200 + + winepulse: Add IAudioClock and IAudioClock2 + +commit fea2f362a74cf8b309facef0558f10eea1ab234f +Author: Maarten Lankhorst +Date: Sat May 24 10:20:43 2014 +0200 + + winepulse: Add IAudioRenderClient and IAudioCaptureClient + +commit 47e1cf987c4f010f06adb75b2b554bf7e4a71211 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:43 2014 +0200 + + winepulse: Add audioclient + + --- + Without AudioRenderClient and AudioCaptureClient it won't work, + but it's easier to review + +commit b11592830890f0f5dcfd2f4747d6bd9356d5618a +Author: Maarten Lankhorst +Date: Sat May 24 10:20:43 2014 +0200 + + winepulse: Add format and period probing + +commit 8b1ac77e1a5eff08a0d38975ba5e57f1ccb363c6 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:43 2014 +0200 + + winepulse: Add initial stub for pulseaudio support + + --- + Just the basic of initialization and pulseaudio mainloop support is + added here. + +commit 9ea97bb103cc71446810810f8d5593a8d7e4c99c +Author: Maarten Lankhorst +Date: Sat May 24 10:20:43 2014 +0200 + + winmm: Load winealsa if winepulse is found + + Fixes midi on winepulse + +commit 21d468a3d66605ace55d07e215cf9df0327d350d +Author: Maarten Lankhorst +Date: Sat May 24 10:20:43 2014 +0200 + + mmdevapi: be stricter about tests + + Dont care about broken drivers.. + +commit 27fdbad8391fee7a4c8fbd45b8d5e93b008dd359 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:43 2014 +0200 + + rtkit: add SIGXCPU handling to wineserver + + This is dependent on getting the correct server_pid in the previous commit. + Processes now forward SIGXCPU to wineserver, who will attempt to downgrade + all threads that were set to realtime priority by avrt first, and if another + SIGXCPU is received or none were found it will downgrade all realtime threads. + + Special-thanks-to: tizbac + +commit 0480aaaa8e7da26e8ea5ab68290ac0ed4515fbbd +Author: Maarten Lankhorst +Date: Sat May 24 10:20:43 2014 +0200 + + server: Bump priority on server to process messages faster + +commit 5a838e231c27328d1b0777667e7adb20eb1fe5a1 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:43 2014 +0200 + + avrt: Add realtime to stub + +commit 03f9a8631d0519c0ac205fde7430910ea7f78df7 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:42 2014 +0200 + + server: Use rtkit to set realtime priority, try 4 + +commit 8e269bc3b9adba17f3e0187b33b30abd1a1eb89b +Author: Maarten Lankhorst +Date: Sat May 24 10:20:42 2014 +0200 + + valgrind prevent crash hack + +commit 0d57845a8a5ef48616b98296e336332ad12ab3c9 +Author: Maarten Lankhorst +Date: Sat May 24 10:20:42 2014 +0200 + + winemenubuilder: silence an err +--- + configure | 99 +- + configure.ac | 32 +- + dlls/avrt/main.c | 1 + + dlls/dsound/capture.c | 9 +- + dlls/dsound/dsound.c | 52 +- + dlls/dsound/dsound_convert.c | 18 - + dlls/dsound/dsound_main.c | 8 +- + dlls/dsound/dsound_private.h | 13 +- + dlls/dsound/mixer.c | 305 +-- + dlls/dsound/primary.c | 455 ++-- + dlls/mmdevapi/main.c | 2 +- + dlls/mmdevapi/tests/capture.c | 8 +- + dlls/mmdevapi/tests/render.c | 243 ++- + dlls/ntdll/ntdll_misc.h | 3 + + dlls/ntdll/server.c | 94 +- + dlls/ntdll/thread.c | 261 ++- + dlls/winegstreamer/glibthread.c | 13 + + dlls/winepulse.drv/Makefile.in | 7 + + dlls/winepulse.drv/mmdevdrv.c | 3227 ++++++++++++++++++++++++++++ + dlls/winepulse.drv/winepulse.drv.spec | 5 + + dlls/winmm/lolvldrv.c | 5 +- + dlls/winmm/waveform.c | 4 +- + libs/wine/loader.c | 46 +- + libs/wine/wine.map | 6 + + loader/Makefile.in | 2 +- + loader/main.c | 41 + + programs/winemenubuilder/winemenubuilder.c | 2 +- + server/Makefile.in | 2 + + server/main.c | 4 + + server/rtkit.c | 292 +++ + server/thread.c | 36 +- + server/thread.h | 2 + + 32 files changed, 4649 insertions(+), 648 deletions(-) + create mode 100644 dlls/winepulse.drv/Makefile.in + create mode 100644 dlls/winepulse.drv/mmdevdrv.c + create mode 100644 dlls/winepulse.drv/winepulse.drv.spec + create mode 100644 server/rtkit.c + +diff --git a/configure b/configure +index d7efbfd..011b8a3 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 +@@ -824,6 +826,7 @@ with_osmesa + with_oss + with_png + with_pthread ++with_pulse + with_sane + with_tiff + with_v4l +@@ -1263,6 +1266,7 @@ enable_winemapi + enable_winemp3_acm + enable_wineoss_drv + enable_wineps_drv ++enable_winepulse_drv + enable_wineqtdecoder + enable_winex11_drv + enable_wing32 +@@ -2092,6 +2096,7 @@ Optional Packages: + --without-oss do not use the OSS sound support + --without-png do not use PNG + --without-pthread do not use the pthread library ++ --without-pulse do not use PulseAudio sound support + --without-sane do not use SANE (scanner support) + --without-tiff do not use TIFF + --without-v4l do not use v4l1 (v4l support) +@@ -3333,6 +3338,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; +@@ -12105,6 +12116,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 : +@@ -13416,12 +13508,14 @@ 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 \ ++if test "x$ALSALIBS$COREAUDIO$PULSELIBS" = "x" -a \ + "x$ac_cv_member_oss_sysinfo_numaudioengines" != xyes -a \ +- "x$with_alsa$with_coreaudio$with_oss" != xnonono ++ "x$with_alsa$with_coreaudio$with_oss$with_pulse" != xnononono + then + as_fn_append wine_warnings "|No sound system was found. Windows applications will be silent." + fi +@@ -17265,6 +17359,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 c913f9f..6539100 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -70,6 +70,7 @@ AC_ARG_WITH(oss, AS_HELP_STRING([--without-oss],[do not use the OSS sound + 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)])) +@@ -1518,6 +1519,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 +@@ -1736,13 +1761,15 @@ 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 \ ++if test "x$ALSALIBS$COREAUDIO$PULSELIBS" = "x" -a \ + "x$ac_cv_member_oss_sysinfo_numaudioengines" != xyes -a \ +- "x$with_alsa$with_coreaudio$with_oss" != xnonono ++ "x$with_alsa$with_coreaudio$with_oss$with_pulse" != xnononono + then + WINE_WARNING([No sound system was found. Windows applications will be silent.]) + fi +@@ -3239,6 +3266,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/avrt/main.c b/dlls/avrt/main.c +index 8a11025..4e29abf 100644 +--- a/dlls/avrt/main.c ++++ b/dlls/avrt/main.c +@@ -80,6 +80,7 @@ HANDLE WINAPI AvSetMmThreadCharacteristicsW(LPCWSTR TaskName, LPDWORD TaskIndex) + SetLastError(ERROR_INVALID_HANDLE); + return NULL; + } ++ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL-1); + return (HANDLE)0x12345678; + } + +diff --git a/dlls/dsound/capture.c b/dlls/dsound/capture.c +index 17b1a04..d997fdc 100644 +--- a/dlls/dsound/capture.c ++++ b/dlls/dsound/capture.c +@@ -51,7 +51,7 @@ typedef struct IDirectSoundCaptureBufferImpl + IDirectSoundCaptureBuffer8 IDirectSoundCaptureBuffer8_iface; + IDirectSoundNotify IDirectSoundNotify_iface; + LONG numIfaces; /* "in use interfaces" refcount */ +- LONG ref, refn; ++ LONG ref, refn, has_dsc8; + /* IDirectSoundCaptureBuffer fields */ + DirectSoundCaptureDevice *device; + DSCBUFFERDESC *pdscbd; +@@ -241,8 +241,9 @@ static HRESULT WINAPI IDirectSoundCaptureBufferImpl_QueryInterface(IDirectSoundC + + *ppobj = NULL; + +- if ( IsEqualGUID( &IID_IDirectSoundCaptureBuffer, riid ) || +- IsEqualGUID( &IID_IDirectSoundCaptureBuffer8, riid ) ) { ++ if ( IsEqualIID( &IID_IUnknown, riid ) || ++ IsEqualIID( &IID_IDirectSoundCaptureBuffer, riid ) || ++ (This->has_dsc8 && IsEqualIID( &IID_IDirectSoundCaptureBuffer8, riid )) ) { + IDirectSoundCaptureBuffer8_AddRef(iface); + *ppobj = iface; + return S_OK; +@@ -1234,6 +1235,8 @@ static HRESULT WINAPI IDirectSoundCaptureImpl_CreateCaptureBuffer(IDirectSoundCa + + if (hr != DS_OK) + WARN("IDirectSoundCaptureBufferImpl_Create failed\n"); ++ else ++ This->device->capture_buffer->has_dsc8 = This->has_dsc8; + + return hr; + } +diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c +index 986168b..a6fc671 100644 +--- a/dlls/dsound/dsound.c ++++ b/dlls/dsound/dsound.c +@@ -306,7 +306,6 @@ static HRESULT WINAPI IDirectSound8Impl_SetCooperativeLevel(IDirectSound8 *iface + { + IDirectSoundImpl *This = impl_from_IDirectSound8(iface); + DirectSoundDevice *device = This->device; +- DWORD oldlevel; + HRESULT hr = S_OK; + + TRACE("(%p,%p,%s)\n", This, hwnd, dumpCooperativeLevel(level)); +@@ -323,15 +322,10 @@ static HRESULT WINAPI IDirectSound8Impl_SetCooperativeLevel(IDirectSound8 *iface + + RtlAcquireResourceExclusive(&device->buffer_list_lock, TRUE); + EnterCriticalSection(&device->mixlock); +- oldlevel = device->priolevel; +- device->priolevel = level; +- if ((level == DSSCL_WRITEPRIMARY) != (oldlevel == DSSCL_WRITEPRIMARY)) { ++ if ((level == DSSCL_WRITEPRIMARY) != (device->priolevel == DSSCL_WRITEPRIMARY)) + hr = DSOUND_ReopenDevice(device, level == DSSCL_WRITEPRIMARY); +- if (FAILED(hr)) +- device->priolevel = oldlevel; +- else +- DSOUND_PrimaryOpen(device); +- } ++ if (SUCCEEDED(hr)) ++ device->priolevel = level; + LeaveCriticalSection(&device->mixlock); + RtlReleaseResource(&device->buffer_list_lock); + return hr; +@@ -602,7 +596,7 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + + device->ref = 1; + device->priolevel = DSSCL_NORMAL; +- device->state = STATE_STOPPED; ++ device->stopped = 1; + device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE); + + /* 3D listener initial parameters */ +@@ -623,28 +617,23 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice) + device->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR; + device->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR; + +- device->prebuf = ds_snd_queue_max; + device->guid = GUID_NULL; + + /* Set default wave format (may need it for waveOutOpen) */ +- device->pwfx = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEFORMATEXTENSIBLE)); + device->primary_pwfx = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEFORMATEXTENSIBLE)); +- if (!device->pwfx || !device->primary_pwfx) { ++ if (!device->primary_pwfx) { + WARN("out of memory\n"); +- HeapFree(GetProcessHeap(),0,device->primary_pwfx); +- HeapFree(GetProcessHeap(),0,device->pwfx); + HeapFree(GetProcessHeap(),0,device); + return DSERR_OUTOFMEMORY; + } + +- device->pwfx->wFormatTag = WAVE_FORMAT_PCM; +- device->pwfx->nSamplesPerSec = 22050; +- device->pwfx->wBitsPerSample = 8; +- device->pwfx->nChannels = 2; +- device->pwfx->nBlockAlign = device->pwfx->wBitsPerSample * device->pwfx->nChannels / 8; +- device->pwfx->nAvgBytesPerSec = device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign; +- device->pwfx->cbSize = 0; +- memcpy(device->primary_pwfx, device->pwfx, sizeof(*device->pwfx)); ++ device->primary_pwfx->wFormatTag = WAVE_FORMAT_PCM; ++ device->primary_pwfx->nSamplesPerSec = 22050; ++ device->primary_pwfx->wBitsPerSample = 8; ++ device->primary_pwfx->nChannels = 2; ++ device->primary_pwfx->nBlockAlign = 2; ++ device->primary_pwfx->nAvgBytesPerSec = 44100; ++ device->primary_pwfx->cbSize = 0; + + InitializeCriticalSection(&(device->mixlock)); + device->mixlock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DirectSoundDevice.mixlock"); +@@ -693,17 +682,17 @@ ULONG DirectSoundDevice_Release(DirectSoundDevice * device) + if (hr != DS_OK) + WARN("DSOUND_PrimaryDestroy failed\n"); + +- if(device->client) ++ if(device->client) { ++ IAudioClient_Stop(device->client); + IAudioClient_Release(device->client); ++ } + if(device->render) + IAudioRenderClient_Release(device->render); +- if(device->clock) +- IAudioClock_Release(device->clock); + if(device->volume) + IAudioStreamVolume_Release(device->volume); + + HeapFree(GetProcessHeap(), 0, device->tmp_buffer); +- HeapFree(GetProcessHeap(), 0, device->mix_buffer); ++ HeapFree(GetProcessHeap(), 0, device->cp_buffer); + HeapFree(GetProcessHeap(), 0, device->buffer); + RtlDeleteResource(&device->buffer_list_lock); + device->mixlock.DebugInfo->Spare[0] = 0; +@@ -789,6 +778,7 @@ HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcG + device->mmdevice = mmdevice; + device->guid = devGUID; + device->sleepev = CreateEventW(0, 0, 0, 0); ++ device->buflen = ds_hel_buflen; + + hr = DSOUND_ReopenDevice(device, FALSE); + if (FAILED(hr)) +@@ -844,12 +834,8 @@ HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcG + + ZeroMemory(&device->volpan, sizeof(device->volpan)); + +- hr = DSOUND_PrimaryCreate(device); +- if (hr == DS_OK) { +- device->thread = CreateThread(0, 0, DSOUND_mixthread, device, 0, 0); +- SetThreadPriority(device->thread, THREAD_PRIORITY_TIME_CRITICAL); +- } else +- WARN("DSOUND_PrimaryCreate failed: %08x\n", hr); ++ device->thread = CreateThread(0, 0, DSOUND_mixthread, device, 0, 0); ++ SetThreadPriority(device->thread, THREAD_PRIORITY_TIME_CRITICAL); + + *ppDevice = device; + list_add_tail(&DSOUND_renderers, &device->entry); +diff --git a/dlls/dsound/dsound_convert.c b/dlls/dsound/dsound_convert.c +index d3d686a..d410d5e 100644 +--- a/dlls/dsound/dsound_convert.c ++++ b/dlls/dsound/dsound_convert.c +@@ -222,27 +222,9 @@ static void norm32(float *src, INT *dst, unsigned len) + } + } + +-static void normieee32(float *src, float *dst, unsigned len) +-{ +- TRACE("%p - %p %d\n", src, dst, len); +- len /= 4; +- while (len--) +- { +- if(*src > 1) +- *dst = 1; +- else if(*src < -1) +- *dst = -1; +- else +- *dst = *src; +- ++dst; +- ++src; +- } +-} +- + const normfunc normfunctions[5] = { + (normfunc)norm8, + (normfunc)norm16, + (normfunc)norm24, + (normfunc)norm32, +- (normfunc)normieee32 + }; +diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c +index 7a16366..4f533b4 100644 +--- a/dlls/dsound/dsound_main.c ++++ b/dlls/dsound/dsound_main.c +@@ -91,8 +91,7 @@ GUID DSOUND_capture_guids[MAXWAVEDRIVERS]; + WCHAR wine_vxd_drv[] = { 'w','i','n','e','m','m','.','v','x','d', 0 }; + + /* All default settings, you most likely don't want to touch these, see wiki on UsefulRegistryKeys */ +-int ds_hel_buflen = 32768 * 2; +-int ds_snd_queue_max = 10; ++int ds_hel_buflen = 32768; + static HINSTANCE instance; + + /* +@@ -145,15 +144,10 @@ void setup_dsound_options(void) + if (!get_config_key( hkey, appkey, "HelBuflen", buffer, MAX_PATH )) + ds_hel_buflen = atoi(buffer); + +- if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH )) +- ds_snd_queue_max = atoi(buffer); +- +- + if (appkey) RegCloseKey( appkey ); + if (hkey) RegCloseKey( hkey ); + + TRACE("ds_hel_buflen = %d\n", ds_hel_buflen); +- TRACE("ds_snd_queue_max = %d\n", ds_snd_queue_max); + } + + static const char * get_device_id(LPCGUID pGuid) +diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h +index a8f5f42..b2342e3 100644 +--- a/dlls/dsound/dsound_private.h ++++ b/dlls/dsound/dsound_private.h +@@ -31,7 +31,6 @@ + #include "wine/list.h" + + extern int ds_hel_buflen DECLSPEC_HIDDEN; +-extern int ds_snd_queue_max DECLSPEC_HIDDEN; + + /***************************************************************************** + * Predeclare the interface implementation structures +@@ -70,18 +69,16 @@ struct DirectSoundDevice + DSCAPS drvcaps; + DWORD priolevel, sleeptime; + PWAVEFORMATEX pwfx, primary_pwfx; +- UINT playing_offs_bytes, in_mmdev_bytes, prebuf; +- DWORD fraglen; + LPBYTE buffer; +- DWORD writelead, buflen, state, playpos, mixpos; ++ DWORD writelead, buflen, aclen, fraglen, playpos, pad, stopped; + int nrofbuffers; + IDirectSoundBufferImpl** buffers; + RTL_RWLOCK buffer_list_lock; + CRITICAL_SECTION mixlock; + IDirectSoundBufferImpl *primary; + DWORD speaker_config; +- float *mix_buffer, *tmp_buffer; +- DWORD tmp_buffer_len, mix_buffer_len; ++ float *tmp_buffer, *cp_buffer; ++ DWORD tmp_buffer_len, mix_buffer_len, cp_buffer_len; + + DSVOLUMEPAN volpan; + +@@ -93,7 +90,6 @@ struct DirectSoundDevice + + IMMDevice *mmdevice; + IAudioClient *client; +- IAudioClock *clock; + IAudioStreamVolume *volume; + IAudioRenderClient *render; + +@@ -202,14 +198,11 @@ HRESULT IDirectSoundImpl_Create(IUnknown *outer_unk, REFIID riid, void **ppv, BO + + /* primary.c */ + +-HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device) DECLSPEC_HIDDEN; + HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device) DECLSPEC_HIDDEN; + HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device) DECLSPEC_HIDDEN; + HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device) DECLSPEC_HIDDEN; +-HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos) DECLSPEC_HIDDEN; + LPWAVEFORMATEX DSOUND_CopyFormat(LPCWAVEFORMATEX wfex) DECLSPEC_HIDDEN; + HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave) DECLSPEC_HIDDEN; +-HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device) DECLSPEC_HIDDEN; + HRESULT primarybuffer_create(DirectSoundDevice *device, IDirectSoundBufferImpl **ppdsb, + const DSBUFFERDESC *dsbd) DECLSPEC_HIDDEN; + void primarybuffer_destroy(IDirectSoundBufferImpl *This) DECLSPEC_HIDDEN; +diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c +index 43b7dca..007180c 100644 +--- a/dlls/dsound/mixer.c ++++ b/dlls/dsound/mixer.c +@@ -251,7 +251,8 @@ static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, float *f + { + UINT i, channel; + UINT istride = dsb->pwfx->nBlockAlign; +- UINT ostride = dsb->device->pwfx->nChannels * sizeof(float); ++ DirectSoundDevice *dev = dsb->device; ++ UINT ostride = dev->pwfx->nChannels * sizeof(float); + + float freqAdjust = dsb->freqAdjust; + float freqAcc_start = *freqAcc; +@@ -262,18 +263,29 @@ static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, float *f + + UINT fir_cachesize = (fir_len + dsbfirstep - 2) / dsbfirstep; + UINT required_input = max_ipos + fir_cachesize; ++ float *intermediate, *fir_copy, *itmp; ++ ++ DWORD len = required_input * channels; ++ len += fir_cachesize; ++ len *= sizeof(float); ++ ++ if (!dev->cp_buffer) { ++ dev->cp_buffer = HeapAlloc(GetProcessHeap(), 0, len); ++ dev->cp_buffer_len = len; ++ } else if (len > dev->cp_buffer_len) { ++ dev->cp_buffer = HeapReAlloc(GetProcessHeap(), 0, dev->cp_buffer, len); ++ dev->cp_buffer_len = len; ++ } + +- float* intermediate = HeapAlloc(GetProcessHeap(), 0, +- sizeof(float) * required_input * channels); ++ fir_copy = dev->cp_buffer; ++ intermediate = fir_copy + fir_cachesize; + +- float* fir_copy = HeapAlloc(GetProcessHeap(), 0, +- sizeof(float) * fir_cachesize); + + /* Important: this buffer MUST be non-interleaved + * if you want -msse3 to have any effect. + * This is good for CPU cache effects, too. + */ +- float* itmp = intermediate; ++ itmp = intermediate; + for (channel = 0; channel < channels; channel++) + for (i = 0; i < required_input; i++) + *(itmp++) = get_current_sample(dsb, +@@ -309,9 +321,6 @@ static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, float *f + freqAcc_end -= (int)freqAcc_end; + *freqAcc = freqAcc_end; + +- HeapFree(GetProcessHeap(), 0, fir_copy); +- HeapFree(GetProcessHeap(), 0, intermediate); +- + return max_ipos; + } + +@@ -427,7 +436,7 @@ static void DSOUND_MixerVol(const IDirectSoundBufferImpl *dsb, INT frames) + * writepos = position (offset) in device buffer to write at + * fraglen = number of bytes to mix + */ +-static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen) ++static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, float *mix_buffer, DWORD writepos, DWORD fraglen) + { + INT len = fraglen; + float *ibuf; +@@ -452,7 +461,7 @@ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWO + /* Apply volume if needed */ + DSOUND_MixerVol(dsb, frames); + +- mixieee32(ibuf, dsb->device->mix_buffer, frames * dsb->device->pwfx->nChannels); ++ mixieee32(ibuf, mix_buffer, frames * dsb->device->pwfx->nChannels); + + /* check for notification positions */ + if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY && +@@ -476,7 +485,7 @@ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWO + * + * Returns: the number of bytes beyond the writepos that were mixed. + */ +-static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mixlen) ++static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, float *mix_buffer, DWORD writepos, DWORD mixlen) + { + DWORD primary_done = 0; + +@@ -503,7 +512,7 @@ static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mi + /* First try to mix to the end of the buffer if possible + * Theoretically it would allow for better optimization + */ +- primary_done += DSOUND_MixInBuffer(dsb, writepos, mixlen); ++ primary_done += DSOUND_MixInBuffer(dsb, mix_buffer, writepos, mixlen); + + TRACE("total mixed data=%d\n", primary_done); + +@@ -518,14 +527,12 @@ static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mi + * writepos = the current safe-to-write position in the primary buffer + * mixlen = the maximum amount to mix into the primary buffer + * (beyond the current writepos) +- * recover = true if the sound device may have been reset and the write +- * position in the device buffer changed + * all_stopped = reports back if all buffers have stopped + * + * Returns: the length beyond the writepos that was mixed to. + */ + +-static void DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos, DWORD mixlen, BOOL recover, BOOL *all_stopped) ++static void DSOUND_MixToPrimary(const DirectSoundDevice *device, float *mix_buffer, DWORD writepos, DWORD mixlen, BOOL *all_stopped) + { + INT i; + IDirectSoundBufferImpl *dsb; +@@ -533,7 +540,7 @@ static void DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos, + /* unless we find a running buffer, all have stopped */ + *all_stopped = TRUE; + +- TRACE("(%d,%d,%d)\n", writepos, mixlen, recover); ++ TRACE("(%d,%d)\n", writepos, mixlen); + for (i = 0; i < device->nrofbuffers; i++) { + dsb = device->buffers[i]; + +@@ -553,7 +560,7 @@ static void DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos, + dsb->state = STATE_PLAYING; + + /* mix next buffer into the main buffer */ +- DSOUND_MixOne(dsb, writepos, mixlen); ++ DSOUND_MixOne(dsb, mix_buffer, writepos, mixlen); + + *all_stopped = FALSE; + } +@@ -572,86 +579,29 @@ static void DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos, + * Returns: None + */ + +-static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force) ++static void DSOUND_WaveQueue(DirectSoundDevice *device, LPBYTE pos, DWORD bytes) + { +- DWORD prebuf_frames, prebuf_bytes, read_offs_bytes; + BYTE *buffer; + HRESULT hr; + + TRACE("(%p)\n", device); + +- read_offs_bytes = (device->playing_offs_bytes + device->in_mmdev_bytes) % device->buflen; +- +- TRACE("read_offs_bytes = %u, playing_offs_bytes = %u, in_mmdev_bytes: %u, prebuf = %u\n", +- read_offs_bytes, device->playing_offs_bytes, device->in_mmdev_bytes, device->prebuf); +- +- if (!force) +- { +- if(device->mixpos < device->playing_offs_bytes) +- prebuf_bytes = device->mixpos + device->buflen - device->playing_offs_bytes; +- else +- prebuf_bytes = device->mixpos - device->playing_offs_bytes; +- } +- else +- /* buffer the maximum amount of frags */ +- prebuf_bytes = device->prebuf * device->fraglen; +- +- /* limit to the queue we have left */ +- if(device->in_mmdev_bytes + prebuf_bytes > device->prebuf * device->fraglen) +- prebuf_bytes = device->prebuf * device->fraglen - device->in_mmdev_bytes; +- +- TRACE("prebuf_bytes = %u\n", prebuf_bytes); +- +- if(!prebuf_bytes) +- return; +- +- if(prebuf_bytes + read_offs_bytes > device->buflen){ +- DWORD chunk_bytes = device->buflen - read_offs_bytes; +- prebuf_frames = chunk_bytes / device->pwfx->nBlockAlign; +- prebuf_bytes -= chunk_bytes; +- }else{ +- prebuf_frames = prebuf_bytes / device->pwfx->nBlockAlign; +- prebuf_bytes = 0; +- } +- +- hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer); ++ hr = IAudioRenderClient_GetBuffer(device->render, bytes / device->pwfx->nBlockAlign, &buffer); + if(FAILED(hr)){ + WARN("GetBuffer failed: %08x\n", hr); + return; + } + +- memcpy(buffer, device->buffer + read_offs_bytes, +- prebuf_frames * device->pwfx->nBlockAlign); ++ memcpy(buffer, pos, bytes); + +- hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0); +- if(FAILED(hr)){ +- WARN("ReleaseBuffer failed: %08x\n", hr); ++ hr = IAudioRenderClient_ReleaseBuffer(device->render, bytes / device->pwfx->nBlockAlign, 0); ++ if(FAILED(hr)) { ++ ERR("ReleaseBuffer failed: %08x\n", hr); ++ IAudioRenderClient_ReleaseBuffer(device->render, 0, 0); + return; + } + +- device->in_mmdev_bytes += prebuf_frames * device->pwfx->nBlockAlign; +- +- /* check if anything wrapped */ +- if(prebuf_bytes > 0){ +- prebuf_frames = prebuf_bytes / device->pwfx->nBlockAlign; +- +- hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer); +- if(FAILED(hr)){ +- WARN("GetBuffer failed: %08x\n", hr); +- return; +- } +- +- memcpy(buffer, device->buffer, prebuf_frames * device->pwfx->nBlockAlign); +- +- hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0); +- if(FAILED(hr)){ +- WARN("ReleaseBuffer failed: %08x\n", hr); +- return; +- } +- device->in_mmdev_bytes += prebuf_frames * device->pwfx->nBlockAlign; +- } +- +- TRACE("in_mmdev_bytes now = %i\n", device->in_mmdev_bytes); ++ device->pad += bytes; + } + + /** +@@ -664,12 +614,12 @@ static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force) + * secondary->buffer (secondary format) + * =[Resample]=> device->tmp_buffer (float format) + * =[Volume]=> device->tmp_buffer (float format) +- * =[Mix]=> device->mix_buffer (float format) +- * =[Reformat]=> device->buffer (device format) ++ * =[Reformat]=> device->buffer (device format, skipped on float) + */ + static void DSOUND_PerformMix(DirectSoundDevice *device) + { +- UINT32 pad, to_mix_frags, to_mix_bytes; ++ UINT32 pad, maxq, writepos; ++ DWORD block; + HRESULT hr; + + TRACE("(%p)\n", device); +@@ -683,161 +633,68 @@ static void DSOUND_PerformMix(DirectSoundDevice *device) + LeaveCriticalSection(&device->mixlock); + return; + } +- +- to_mix_frags = device->prebuf - (pad * device->pwfx->nBlockAlign + device->fraglen - 1) / device->fraglen; +- +- to_mix_bytes = to_mix_frags * device->fraglen; +- +- if(device->in_mmdev_bytes > 0){ +- DWORD delta_bytes = min(to_mix_bytes, device->in_mmdev_bytes); +- device->in_mmdev_bytes -= delta_bytes; +- device->playing_offs_bytes += delta_bytes; +- device->playing_offs_bytes %= device->buflen; ++ block = device->pwfx->nBlockAlign; ++ pad *= block; ++ device->playpos += device->pad - pad; ++ device->playpos %= device->buflen; ++ device->pad = pad; ++ ++ maxq = device->aclen - pad; ++ if(!maxq){ ++ /* nothing to do! */ ++ LeaveCriticalSection(&device->mixlock); ++ return; + } ++ if (maxq > device->fraglen * 3) ++ maxq = device->fraglen * 3; ++ ++ writepos = (device->playpos + pad) % device->buflen; + + if (device->priolevel != DSSCL_WRITEPRIMARY) { +- BOOL recover = FALSE, all_stopped = FALSE; +- DWORD playpos, writepos, writelead, maxq, prebuff_max, prebuff_left, size1, size2; +- LPVOID buf1, buf2; ++ BOOL all_stopped = FALSE; + int nfiller; ++ void *buffer = NULL; + + /* the sound of silence */ + nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0; + +- /* get the position in the primary buffer */ +- if (DSOUND_PrimaryGetPosition(device, &playpos, &writepos) != 0){ +- LeaveCriticalSection(&(device->mixlock)); +- return; +- } +- +- TRACE("primary playpos=%d, writepos=%d, clrpos=%d, mixpos=%d, buflen=%d\n", +- playpos,writepos,device->playpos,device->mixpos,device->buflen); +- assert(device->playpos < device->buflen); +- +- /* calc maximum prebuff */ +- prebuff_max = (device->prebuf * device->fraglen); +- +- /* check how close we are to an underrun. It occurs when the writepos overtakes the mixpos */ +- prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos); +- writelead = DSOUND_BufPtrDiff(device->buflen, writepos, playpos); +- + /* check for underrun. underrun occurs when the write position passes the mix position + * also wipe out just-played sound data */ +- if((prebuff_left > prebuff_max) || (device->state == STATE_STOPPED) || (device->state == STATE_STARTING)){ +- if (device->state == STATE_STOPPING || device->state == STATE_PLAYING) +- WARN("Probable buffer underrun\n"); +- else TRACE("Buffer starting or buffer underrun\n"); +- +- /* recover mixing for all buffers */ +- recover = TRUE; +- +- /* reset mix position to write position */ +- device->mixpos = writepos; +- +- ZeroMemory(device->buffer, device->buflen); +- } else if (playpos < device->playpos) { +- buf1 = device->buffer + device->playpos; +- buf2 = device->buffer; +- size1 = device->buflen - device->playpos; +- size2 = playpos; +- FillMemory(buf1, size1, nfiller); +- if (playpos && (!buf2 || !size2)) +- FIXME("%d: (%d, %d)=>(%d, %d) There should be an additional buffer here!!\n", __LINE__, device->playpos, device->mixpos, playpos, writepos); +- FillMemory(buf2, size2, nfiller); +- } else { +- buf1 = device->buffer + device->playpos; +- buf2 = NULL; +- size1 = playpos - device->playpos; +- size2 = 0; +- FillMemory(buf1, size1, nfiller); +- } +- device->playpos = playpos; +- +- /* find the maximum we can prebuffer from current write position */ +- maxq = (writelead < prebuff_max) ? (prebuff_max - writelead) : 0; +- +- TRACE("prebuff_left = %d, prebuff_max = %dx%d=%d, writelead=%d\n", +- prebuff_left, device->prebuf, device->fraglen, prebuff_max, writelead); +- +- ZeroMemory(device->mix_buffer, device->mix_buffer_len); ++ if (!pad) ++ WARN("Probable buffer underrun\n"); + +- /* do the mixing */ +- DSOUND_MixToPrimary(device, writepos, maxq, recover, &all_stopped); +- +- if (maxq + writepos > device->buflen) +- { +- DWORD todo = device->buflen - writepos; +- DWORD offs_float = (todo / device->pwfx->nBlockAlign) * device->pwfx->nChannels; +- device->normfunction(device->mix_buffer, device->buffer + writepos, todo); +- device->normfunction(device->mix_buffer + offs_float, device->buffer, maxq - todo); +- } +- else +- device->normfunction(device->mix_buffer, device->buffer + writepos, maxq); +- +- /* update the mix position, taking wrap-around into account */ +- device->mixpos = writepos + maxq; +- device->mixpos %= device->buflen; +- +- /* update prebuff left */ +- prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos); +- +- /* check if have a whole fragment */ +- if (prebuff_left >= device->fraglen){ +- +- /* update the wave queue */ +- DSOUND_WaveQueue(device, FALSE); +- +- /* buffers are full. start playing if applicable */ +- if(device->state == STATE_STARTING){ +- TRACE("started primary buffer\n"); +- if(DSOUND_PrimaryPlay(device) != DS_OK){ +- WARN("DSOUND_PrimaryPlay failed\n"); +- } +- else{ +- /* we are playing now */ +- device->state = STATE_PLAYING; +- } +- } +- +- /* buffers are full. start stopping if applicable */ +- if(device->state == STATE_STOPPED){ +- TRACE("restarting primary buffer\n"); +- if(DSOUND_PrimaryPlay(device) != DS_OK){ +- WARN("DSOUND_PrimaryPlay failed\n"); +- } +- else{ +- /* start stopping again. as soon as there is no more data, it will stop */ +- device->state = STATE_STOPPING; +- } +- } ++ hr = IAudioRenderClient_GetBuffer(device->render, maxq / block, (void*)&buffer); ++ if(FAILED(hr)){ ++ WARN("GetBuffer failed: %08x\n", hr); ++ LeaveCriticalSection(&device->mixlock); ++ return; + } + +- /* if device was stopping, its for sure stopped when all buffers have stopped */ +- else if((all_stopped == TRUE) && (device->state == STATE_STOPPING)){ +- TRACE("All buffers have stopped. Stopping primary buffer\n"); +- device->state = STATE_STOPPED; +- +- /* stop the primary buffer now */ +- DSOUND_PrimaryStop(device); +- } ++ memset(buffer, nfiller, maxq); + +- } else if (device->state != STATE_STOPPED) { ++ if (!device->normfunction) ++ DSOUND_MixToPrimary(device, buffer, writepos, maxq, &all_stopped); ++ else { + +- DSOUND_WaveQueue(device, TRUE); ++ /* do the mixing */ ++ DSOUND_MixToPrimary(device, (float*)device->buffer, writepos, maxq, &all_stopped); + +- /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */ +- if (device->state == STATE_STARTING) { +- if (DSOUND_PrimaryPlay(device) != DS_OK) +- WARN("DSOUND_PrimaryPlay failed\n"); +- else +- device->state = STATE_PLAYING; +- } +- else if (device->state == STATE_STOPPING) { +- if (DSOUND_PrimaryStop(device) != DS_OK) +- WARN("DSOUND_PrimaryStop failed\n"); +- else +- device->state = STATE_STOPPED; ++ device->normfunction(device->buffer, buffer, maxq); + } ++ ++ hr = IAudioRenderClient_ReleaseBuffer(device->render, maxq / block, 0); ++ if(FAILED(hr)) ++ ERR("ReleaseBuffer failed: %08x\n", hr); ++ ++ device->pad += maxq; ++ } else if (!device->stopped) { ++ if (maxq > device->buflen) ++ maxq = device->buflen; ++ if (writepos + maxq > device->buflen) { ++ DSOUND_WaveQueue(device, device->buffer + writepos, device->buflen - writepos); ++ DSOUND_WaveQueue(device, device->buffer, writepos + maxq - device->buflen); ++ } else ++ DSOUND_WaveQueue(device, device->buffer + writepos, maxq); + } + + LeaveCriticalSection(&(device->mixlock)); +diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c +index a824477..8f3580a 100644 +--- a/dlls/dsound/primary.c ++++ b/dlls/dsound/primary.c +@@ -40,24 +40,6 @@ + + WINE_DEFAULT_DEBUG_CHANNEL(dsound); + +-static DWORD DSOUND_fraglen(DirectSoundDevice *device) +-{ +- REFERENCE_TIME period; +- HRESULT hr; +- DWORD ret; +- +- hr = IAudioClient_GetDevicePeriod(device->client, &period, NULL); +- if(FAILED(hr)){ +- /* just guess at 10ms */ +- WARN("GetDevicePeriod failed: %08x\n", hr); +- ret = MulDiv(device->pwfx->nBlockAlign, device->pwfx->nSamplesPerSec, 100); +- }else +- ret = MulDiv(device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign, period, 10000000); +- +- ret -= ret % device->pwfx->nBlockAlign; +- return ret; +-} +- + static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, + BOOL forcewave, WAVEFORMATEX **wfx) + { +@@ -146,17 +128,8 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client + return S_OK; + } + +-HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave) ++static void DSOUND_ReleaseDevice(DirectSoundDevice *device) + { +- UINT prebuf_frames; +- REFERENCE_TIME prebuf_rt; +- WAVEFORMATEX *wfx = NULL; +- HRESULT hres; +- REFERENCE_TIME period; +- DWORD period_ms; +- +- TRACE("(%p, %d)\n", device, forcewave); +- + if(device->client){ + IAudioClient_Release(device->client); + device->client = NULL; +@@ -165,166 +138,88 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave) + IAudioRenderClient_Release(device->render); + device->render = NULL; + } +- if(device->clock){ +- IAudioClock_Release(device->clock); +- device->clock = NULL; +- } + if(device->volume){ + IAudioStreamVolume_Release(device->volume); + device->volume = NULL; + } + +- hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient, +- CLSCTX_INPROC_SERVER, NULL, (void **)&device->client); +- if(FAILED(hres)) { +- WARN("Activate failed: %08x\n", hres); +- return hres; +- } +- +- hres = DSOUND_WaveFormat(device, device->client, forcewave, &wfx); +- if (FAILED(hres)) { +- IAudioClient_Release(device->client); +- device->client = NULL; +- return hres; +- } +- HeapFree(GetProcessHeap(), 0, device->pwfx); +- device->pwfx = wfx; +- +- prebuf_frames = device->prebuf * DSOUND_fraglen(device) / device->pwfx->nBlockAlign; +- prebuf_rt = (10000000 * (UINT64)prebuf_frames) / device->pwfx->nSamplesPerSec; +- +- hres = IAudioClient_Initialize(device->client, +- AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST | +- AUDCLNT_STREAMFLAGS_EVENTCALLBACK, prebuf_rt, 0, device->pwfx, NULL); +- if(FAILED(hres)){ +- IAudioClient_Release(device->client); +- device->client = NULL; +- WARN("Initialize failed: %08x\n", hres); +- return hres; +- } +- IAudioClient_SetEventHandle(device->client, device->sleepev); +- +- hres = IAudioClient_GetService(device->client, &IID_IAudioRenderClient, +- (void**)&device->render); +- if(FAILED(hres)){ +- IAudioClient_Release(device->client); +- device->client = NULL; +- WARN("GetService failed: %08x\n", hres); +- return hres; +- } +- +- hres = IAudioClient_GetService(device->client, &IID_IAudioClock, +- (void**)&device->clock); +- if(FAILED(hres)){ +- IAudioClient_Release(device->client); +- IAudioRenderClient_Release(device->render); +- device->client = NULL; +- device->render = NULL; +- WARN("GetService failed: %08x\n", hres); +- return hres; ++ if (device->pad) { ++ device->playpos += device->pad; ++ device->playpos %= device->buflen; ++ device->pad = 0; + } +- +- hres = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume, +- (void**)&device->volume); +- if(FAILED(hres)){ +- IAudioClient_Release(device->client); +- IAudioRenderClient_Release(device->render); +- IAudioClock_Release(device->clock); +- device->client = NULL; +- device->render = NULL; +- device->clock = NULL; +- WARN("GetService failed: %08x\n", hres); +- return hres; +- } +- +- /* Now kick off the timer so the event fires periodically */ +- hres = IAudioClient_Start(device->client); +- if (FAILED(hres)) +- WARN("starting failed with %08x\n", hres); +- +- hres = IAudioClient_GetStreamLatency(device->client, &period); +- if (FAILED(hres)) { +- WARN("GetStreamLatency failed with %08x\n", hres); +- period_ms = 10; +- } else +- period_ms = (period + 9999) / 10000; +- TRACE("period %u ms fraglen %u prebuf %u\n", period_ms, device->fraglen, device->prebuf); +- +- if (period_ms < 3) +- device->sleeptime = 5; +- else +- device->sleeptime = period_ms * 5 / 2; +- +- return S_OK; + } + +-HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device) ++static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device, WAVEFORMATEX *wfx, DWORD aclen, BOOL forcewave) + { +- IDirectSoundBufferImpl** dsb = device->buffers; +- LPBYTE newbuf; +- int i; +- +- TRACE("(%p)\n", device); ++ IDirectSoundBufferImpl** dsb = device->buffers; ++ LPBYTE newbuf; ++ DWORD new_buflen; ++ BOOL mixfloat = FALSE; ++ int i; + +- device->fraglen = DSOUND_fraglen(device); +- +- /* on original windows, the buffer it set to a fixed size, no matter what the settings are. +- on windows this size is always fixed (tested on win-xp) */ +- if (!device->buflen) +- device->buflen = ds_hel_buflen; +- device->buflen -= device->buflen % device->pwfx->nBlockAlign; +- while(device->buflen < device->fraglen * device->prebuf){ +- device->buflen += ds_hel_buflen; +- device->buflen -= device->buflen % device->pwfx->nBlockAlign; +- } ++ TRACE("(%p)\n", device); + +- HeapFree(GetProcessHeap(), 0, device->mix_buffer); +- device->mix_buffer_len = (device->buflen / (device->pwfx->wBitsPerSample / 8)) * sizeof(float); +- device->mix_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, device->mix_buffer_len); +- if (!device->mix_buffer) +- return DSERR_OUTOFMEMORY; ++ new_buflen = device->buflen; ++ new_buflen -= new_buflen % wfx->nBlockAlign; + +- if (device->state == STATE_PLAYING) device->state = STATE_STARTING; +- else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED; ++ if (wfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT || ++ (wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE && ++ IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)wfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) ++ mixfloat = TRUE; + + /* reallocate emulated primary buffer */ +- if (device->buffer) +- newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, device->buflen); +- else +- newbuf = HeapAlloc(GetProcessHeap(),0, device->buflen); ++ if (forcewave) { ++ if (device->buffer) ++ newbuf = HeapReAlloc(GetProcessHeap(), 0, device->buffer, new_buflen); ++ else ++ newbuf = HeapAlloc(GetProcessHeap(), 0, new_buflen); ++ ++ if (!newbuf) { ++ ERR("failed to allocate primary buffer\n"); ++ return DSERR_OUTOFMEMORY; ++ } ++ device->mix_buffer_len = 0; ++ } else if (!mixfloat) { ++ DWORD alloc_len = aclen / (wfx->nBlockAlign / 8) * sizeof(float); + +- if (!newbuf) { +- ERR("failed to allocate primary buffer\n"); +- return DSERR_OUTOFMEMORY; +- /* but the old buffer might still exist and must be re-prepared */ +- } ++ if (device->buffer) ++ newbuf = HeapReAlloc(GetProcessHeap(), 0, device->buffer, alloc_len); ++ else ++ newbuf = HeapAlloc(GetProcessHeap(), 0, alloc_len); + +- device->writelead = (device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign; ++ if (!newbuf) { ++ ERR("failed to allocate primary buffer\n"); ++ return DSERR_OUTOFMEMORY; ++ } ++ device->mix_buffer_len = alloc_len; ++ } else { ++ HeapFree(GetProcessHeap(), 0, device->buffer); ++ newbuf = NULL; ++ device->mix_buffer_len = 0; ++ } + + device->buffer = newbuf; ++ device->buflen = new_buflen; ++ HeapFree(GetProcessHeap(), 0, device->pwfx); ++ device->pwfx = wfx; ++ ++ device->writelead = (wfx->nSamplesPerSec / 100) * wfx->nBlockAlign; + + TRACE("buflen: %u, fraglen: %u, mix_buffer_len: %u\n", +- device->buflen, device->fraglen, device->mix_buffer_len); ++ device->buflen, device->fraglen, device->mix_buffer_len); + +- if(device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT || +- (device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE && +- IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)device->pwfx)->SubFormat, +- &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) +- device->normfunction = normfunctions[4]; ++ if (!forcewave && !mixfloat) ++ device->normfunction = normfunctions[wfx->nBlockAlign/8 - 1]; + else +- device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1]; ++ device->normfunction = NULL; + +- FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0); +- FillMemory(device->mix_buffer, device->mix_buffer_len, 0); ++ if (device->mix_buffer_len) ++ FillMemory(device->buffer, device->mix_buffer_len, 0); ++ else if (device->buffer) ++ FillMemory(device->buffer, device->buflen, (wfx->wBitsPerSample == 8) ? 128 : 0); + device->playpos = 0; + +- if (device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT || +- (device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE && +- IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)device->pwfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) +- device->normfunction = normfunctions[4]; +- else +- device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1]; +- + for (i = 0; i < device->nrofbuffers; i++) { + RtlAcquireResourceExclusive(&dsb[i]->lock, TRUE); + DSOUND_RecalcFormat(dsb[i]); +@@ -334,38 +229,103 @@ HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device) + return DS_OK; + } + +- +-static void DSOUND_PrimaryClose(DirectSoundDevice *device) ++HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave) + { +- HRESULT hr; ++ HRESULT hres; ++ REFERENCE_TIME period; ++ UINT32 frames; ++ DWORD period_ms; ++ IAudioClient *client = NULL; ++ IAudioRenderClient *render = NULL; ++ IAudioStreamVolume *volume = NULL; ++ DWORD fraglen, aclen; ++ WAVEFORMATEX *wfx = NULL; + +- TRACE("(%p)\n", device); ++ TRACE("(%p, %d)\n", device, forcewave); + +- if(device->client){ +- hr = IAudioClient_Stop(device->client); +- if(FAILED(hr)) +- WARN("Stop failed: %08x\n", hr); ++ hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient, ++ CLSCTX_INPROC_SERVER, NULL, (void **)&client); ++ if(FAILED(hres)){ ++ WARN("Activate failed: %08x\n", hres); ++ return hres; + } + +- /* clear the queue */ +- device->in_mmdev_bytes = 0; +-} ++ hres = DSOUND_WaveFormat(device, client, forcewave, &wfx); ++ if (FAILED(hres)) { ++ IAudioClient_Release(client); ++ return hres; ++ } + +-HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device) +-{ +- HRESULT err = DS_OK; +- TRACE("(%p)\n", device); ++ hres = IAudioClient_Initialize(client, ++ AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST | ++ AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 800000, 0, wfx, NULL); ++ if(FAILED(hres)){ ++ IAudioClient_Release(client); ++ ERR("Initialize failed: %08x\n", hres); ++ return hres; ++ } + +- device->buflen = ds_hel_buflen; +- err = DSOUND_PrimaryOpen(device); ++ IAudioClient_SetEventHandle(client, device->sleepev); + +- if (err != DS_OK) { +- WARN("DSOUND_PrimaryOpen failed\n"); +- return err; +- } ++ hres = IAudioClient_GetService(client, &IID_IAudioRenderClient, (void**)&render); ++ if(FAILED(hres)) ++ goto err_service; + +- device->state = STATE_STOPPED; +- return DS_OK; ++ hres = IAudioClient_GetService(client, &IID_IAudioStreamVolume, (void**)&volume); ++ if(FAILED(hres)) ++ goto err_service; ++ ++ /* Now kick off the timer so the event fires periodically */ ++ hres = IAudioClient_Start(client); ++ if (FAILED(hres)) { ++ WARN("Start failed with %08x\n", hres); ++ goto err; ++ } ++ hres = IAudioClient_GetStreamLatency(client, &period); ++ if (FAILED(hres)) { ++ WARN("GetStreamLatency failed with %08x\n", hres); ++ goto err; ++ } ++ hres = IAudioClient_GetBufferSize(client, &frames); ++ if (FAILED(hres)) { ++ WARN("GetBufferSize failed with %08x\n", hres); ++ goto err; ++ } ++ ++ period_ms = (period + 9999) / 10000; ++ fraglen = MulDiv(wfx->nSamplesPerSec, period, 10000000) * wfx->nBlockAlign; ++ aclen = frames * wfx->nBlockAlign; ++ TRACE("period %u ms fraglen %u buflen %u\n", period_ms, fraglen, aclen); ++ ++ hres = DSOUND_PrimaryOpen(device, wfx, aclen, forcewave); ++ if(FAILED(hres)) ++ goto err; ++ ++ DSOUND_ReleaseDevice(device); ++ device->client = client; ++ device->render = render; ++ device->volume = volume; ++ device->fraglen = fraglen; ++ device->aclen = aclen; ++ ++ if (period_ms < 3) ++ device->sleeptime = 5; ++ else ++ device->sleeptime = period_ms * 5 / 2; ++ ++ return S_OK; ++ ++err_service: ++ WARN("GetService failed: %08x\n", hres); ++err: ++ if (volume) ++ IAudioStreamVolume_Release(volume); ++ if (render) ++ IAudioRenderClient_Release(render); ++ if (client) ++ IAudioClient_Release(client); ++ HeapFree(GetProcessHeap(), 0, wfx); ++ return hres; + } + + HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device) +@@ -375,8 +335,6 @@ HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device) + /* **** */ + EnterCriticalSection(&(device->mixlock)); + +- DSOUND_PrimaryClose(device); +- + if(device->primary && (device->primary->ref || device->primary->numIfaces)) + WARN("Destroying primary buffer while references held (%u %u)\n", device->primary->ref, device->primary->numIfaces); + +@@ -393,62 +351,19 @@ HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device) + return DS_OK; + } + +-HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device) +-{ +- HRESULT hr; +- +- TRACE("(%p)\n", device); +- +- hr = IAudioClient_Start(device->client); +- if(FAILED(hr) && hr != AUDCLNT_E_NOT_STOPPED){ +- WARN("Start failed: %08x\n", hr); +- return hr; +- } +- +- return DS_OK; +-} +- +-HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device) +-{ +- HRESULT hr; +- +- TRACE("(%p)\n", device); +- +- hr = IAudioClient_Stop(device->client); +- if(FAILED(hr)){ +- WARN("Stop failed: %08x\n", hr); +- return hr; +- } +- +- return DS_OK; +-} +- +-HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos) +-{ +- TRACE("(%p,%p,%p)\n", device, playpos, writepos); +- +- /* check if playpos was requested */ +- if (playpos) +- *playpos = device->playing_offs_bytes; +- +- /* check if writepos was requested */ +- if (writepos) +- /* the writepos is the first non-queued position */ +- *writepos = (device->playing_offs_bytes + device->in_mmdev_bytes) % device->buflen; +- +- TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount()); +- return DS_OK; +-} +- + WAVEFORMATEX *DSOUND_CopyFormat(const WAVEFORMATEX *wfex) + { + WAVEFORMATEX *pwfx; + if(wfex->wFormatTag == WAVE_FORMAT_PCM){ + pwfx = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEX)); ++ if (!pwfx) ++ return NULL; + CopyMemory(pwfx, wfex, sizeof(PCMWAVEFORMAT)); + pwfx->cbSize = 0; + }else{ + pwfx = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEX) + wfex->cbSize); ++ if (!pwfx) ++ return NULL; + CopyMemory(pwfx, wfex, sizeof(WAVEFORMATEX) + wfex->cbSize); + } + +@@ -465,7 +380,6 @@ HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passe + HRESULT err = S_OK; + WAVEFORMATEX *old_fmt; + WAVEFORMATEXTENSIBLE *fmtex, *passed_fmtex = (WAVEFORMATEXTENSIBLE*)passed_fmt; +- BOOL forced = (device->priolevel == DSSCL_WRITEPRIMARY); + + TRACE("(%p,%p)\n", device, passed_fmt); + +@@ -515,28 +429,20 @@ HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passe + fmtex->Samples.wValidBitsPerSample = fmtex->Format.wBitsPerSample; + } + +- DSOUND_PrimaryClose(device); +- +- err = DSOUND_ReopenDevice(device, forced); ++ err = DSOUND_ReopenDevice(device, TRUE); + if (FAILED(err)) { + ERR("No formats could be opened\n"); +- goto done; +- } +- +- err = DSOUND_PrimaryOpen(device); +- if (err != DS_OK) { +- ERR("DSOUND_PrimaryOpen failed\n"); +- goto done; +- } +- +-done: +- if (err != DS_OK) ++ HeapFree(GetProcessHeap(), 0, device->primary_pwfx); + device->primary_pwfx = old_fmt; +- else ++ } else + HeapFree(GetProcessHeap(), 0, old_fmt); + } else { +- HeapFree(GetProcessHeap(), 0, device->primary_pwfx); +- device->primary_pwfx = DSOUND_CopyFormat(passed_fmt); ++ WAVEFORMATEX *wfx = DSOUND_CopyFormat(passed_fmt); ++ if (wfx) { ++ HeapFree(GetProcessHeap(), 0, device->primary_pwfx); ++ device->primary_pwfx = wfx; ++ } else ++ err = DSERR_OUTOFMEMORY; + } + + out: +@@ -706,16 +612,7 @@ static HRESULT WINAPI PrimaryBufferImpl_Play(IDirectSoundBuffer *iface, DWORD re + return DSERR_INVALIDPARAM; + } + +- /* **** */ +- EnterCriticalSection(&(device->mixlock)); +- +- if (device->state == STATE_STOPPED) +- device->state = STATE_STARTING; +- else if (device->state == STATE_STOPPING) +- device->state = STATE_PLAYING; +- +- LeaveCriticalSection(&(device->mixlock)); +- /* **** */ ++ device->stopped = 0; + + return DS_OK; + } +@@ -726,16 +623,7 @@ static HRESULT WINAPI PrimaryBufferImpl_Stop(IDirectSoundBuffer *iface) + DirectSoundDevice *device = This->device; + TRACE("(%p)\n", iface); + +- /* **** */ +- EnterCriticalSection(&(device->mixlock)); +- +- if (device->state == STATE_PLAYING) +- device->state = STATE_STOPPING; +- else if (device->state == STATE_STARTING) +- device->state = STATE_STOPPED; +- +- LeaveCriticalSection(&(device->mixlock)); +- /* **** */ ++ device->stopped = 1; + + return DS_OK; + } +@@ -781,7 +669,9 @@ static ULONG WINAPI PrimaryBufferImpl_Release(IDirectSoundBuffer *iface) + static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(IDirectSoundBuffer *iface, + DWORD *playpos, DWORD *writepos) + { +- HRESULT hres; ++ HRESULT hres = DS_OK; ++ UINT32 pad = 0; ++ UINT32 mixpos; + IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface); + DirectSoundDevice *device = This->device; + TRACE("(%p,%p,%p)\n", iface, playpos, writepos); +@@ -789,17 +679,23 @@ static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(IDirectSoundBuffer *i + /* **** */ + EnterCriticalSection(&(device->mixlock)); + +- hres = DSOUND_PrimaryGetPosition(device, playpos, writepos); ++ if (device->client) ++ hres = IAudioClient_GetCurrentPadding(device->client, &pad); + if (hres != DS_OK) { +- WARN("DSOUND_PrimaryGetPosition failed\n"); ++ WARN("IAudioClient_GetCurrentPadding failed\n"); + LeaveCriticalSection(&(device->mixlock)); + return hres; + } ++ mixpos = (device->playpos + pad * device->pwfx->nBlockAlign) % device->buflen; ++ if (playpos) ++ *playpos = mixpos; + if (writepos) { +- if (device->state != STATE_STOPPED) ++ *writepos = mixpos; ++ if (!device->stopped) { + /* apply the documented 10ms lead to writepos */ + *writepos += device->writelead; +- while (*writepos >= device->buflen) *writepos -= device->buflen; ++ *writepos %= device->buflen; ++ } + } + + LeaveCriticalSection(&(device->mixlock)); +@@ -821,8 +717,7 @@ static HRESULT WINAPI PrimaryBufferImpl_GetStatus(IDirectSoundBuffer *iface, DWO + } + + *status = 0; +- if ((device->state == STATE_STARTING) || +- (device->state == STATE_PLAYING)) ++ if (!device->stopped) + *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING; + + TRACE("status=%x\n", *status); +diff --git a/dlls/mmdevapi/main.c b/dlls/mmdevapi/main.c +index 447813f..b9ae99e 100644 +--- a/dlls/mmdevapi/main.c ++++ b/dlls/mmdevapi/main.c +@@ -110,7 +110,7 @@ static BOOL init_driver(void) + { + static const WCHAR drv_value[] = {'A','u','d','i','o',0}; + +- static WCHAR default_list[] = {'a','l','s','a',',','o','s','s',',', ++ static WCHAR default_list[] = {'p','u','l','s','e',',','a','l','s','a',',','o','s','s',',', + 'c','o','r','e','a','u','d','i','o',0}; + + DriverFuncs driver; +diff --git a/dlls/mmdevapi/tests/capture.c b/dlls/mmdevapi/tests/capture.c +index 23075ea..5be8a8d 100644 +--- a/dlls/mmdevapi/tests/capture.c ++++ b/dlls/mmdevapi/tests/capture.c +@@ -241,7 +241,7 @@ static void test_capture(IAudioClient *ac, HANDLE handle, WAVEFORMATEX *wfx) + ok(hr == S_OK, "Valid IAudioCaptureClient_GetBuffer returns %08x\n", hr); + ok(frames2 == frames, "GetBuffer after ReleaseBuffer(0) %u/%u\n", frames2, frames); + ok(pos2 == pos, "Position after ReleaseBuffer(0) %u/%u\n", (UINT)pos2, (UINT)pos); +- todo_wine ok(qpc2 == qpc, "HPC after ReleaseBuffer(0) %u vs. %u\n", (UINT)qpc2, (UINT)qpc); ++ ok(qpc2 == qpc, "HPC after ReleaseBuffer(0) %u vs. %u\n", (UINT)qpc2, (UINT)qpc); + } + + /* trace after the GCP test because log output to MS-DOS console disturbs timing */ +@@ -304,13 +304,13 @@ static void test_capture(IAudioClient *ac, HANDLE handle, WAVEFORMATEX *wfx) + + if(hr == S_OK){ + /* The discontinuity is reported here, but is this an old or new packet? */ +- todo_wine ok(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY, "expect DISCONTINUITY %x\n", flags); ++ ok(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY, "expect DISCONTINUITY %x\n", flags); + ok(pad == next, "GCP %u vs. BufferSize %u\n", (UINT32)pad, next); + + /* Native's position is one period further than what we read. + * Perhaps that's precisely the meaning of DATA_DISCONTINUITY: + * signal when the position jump left a gap. */ +- todo_wine ok(pos == sum + frames, "Position %u gap %d\n", ++ ok(pos == sum + frames, "Position %u gap %d\n", + (UINT)pos, (UINT)pos - sum); + if(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) + sum = pos; +@@ -388,7 +388,7 @@ static void test_capture(IAudioClient *ac, HANDLE handle, WAVEFORMATEX *wfx) + /* Only PulseAudio goes here; despite snd_pcm_drop it manages + * to fill GetBufferSize with a single snd_pcm_read */ + trace("Test marked todo: only PulseAudio gets here\n"); +- todo_wine ok(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY, "expect DISCONTINUITY %x\n", flags); ++ ok(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY, "expect DISCONTINUITY %x\n", flags); + /* Reset zeroes padding, not the position */ + ok(pos >= sum, "Position %u last %u\n", (UINT)pos, sum); + /*sum = pos; check after next GetBuffer */ +diff --git a/dlls/mmdevapi/tests/render.c b/dlls/mmdevapi/tests/render.c +index 44472a6..c9e4636 100644 +--- a/dlls/mmdevapi/tests/render.c ++++ b/dlls/mmdevapi/tests/render.c +@@ -467,6 +467,169 @@ static void test_formats(AUDCLNT_SHAREMODE mode) + } + } + ++static void test_formats2(void) ++{ ++ IAudioClient *ac; ++ HRESULT hr; ++ WAVEFORMATEX *pwfx, *pwfx2; ++ WAVEFORMATEXTENSIBLE *pwfe, wfe, *pwfe2; ++ ++ hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, ++ NULL, (void**)&ac); ++ ++ ok(hr == S_OK, "Activation failed with %08x\n", hr); ++ if (hr != S_OK) ++ return; ++ ++ hr = IAudioClient_GetMixFormat(ac, &pwfx); ++ ok(hr == S_OK, "GetMixFormat failed: %08x\n", hr); ++ if (hr != S_OK) ++ return; ++ ++ ok(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE, "Invalid wFormatTag\n"); ++ if (pwfx->wFormatTag != WAVE_FORMAT_EXTENSIBLE) { ++ CoTaskMemFree(pwfx); ++ return; ++ } ++ ++ pwfe = (WAVEFORMATEXTENSIBLE*)pwfx; ++ ok(pwfe->Samples.wValidBitsPerSample, "wValidBitsPerSample should be non-zero\n"); ++ ++ if (pwfx->nChannels > 2) { ++ trace("Limiting channels to 2\n"); ++ pwfx->nChannels = 2; ++ pwfx->nBlockAlign = pwfx->wBitsPerSample / 8 * pwfx->nChannels; ++ pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; ++ pwfe->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; ++ } ++ ++ wfe = *pwfe; ++ pwfx->nAvgBytesPerSec = pwfx->nBlockAlign = 0; ++ ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL); ++ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, ++ "Exclusive IsFormatSupported with nAvgBytesPerSec=0 and nBlockAlign=0 returned %08x\n", hr); ++ ++ pwfx2 = NULL; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); ++ ok((hr == E_INVALIDARG || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && !pwfx2, ++ "Shared IsFormatSupported with nAvgBytesPerSec=0 and nBlockAlign=0 returned %08x %p\n", hr, pwfx2); ++ CoTaskMemFree(pwfx2); ++ ++ pwfx->wFormatTag = WAVE_FORMAT_PCM; ++ pwfx2 = NULL; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); ++ ok((hr == S_OK || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && !pwfx2, ++ "Shared IsFormatSupported with nAvgBytesPerSec=0 and nBlockAlign=0 returned %08x %p\n", hr, pwfx2); ++ CoTaskMemFree(pwfx2); ++ ++ *pwfe = wfe; ++ pwfe->dwChannelMask = 0; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL); ++ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, ++ "Exclusive IsFormatSupported with dwChannelMask=0 returned %08x\n", hr); ++ ++ pwfx2 = NULL; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); ++ ok(hr == S_OK, ++ "Shared IsFormatSupported with dwChannelMask=0 returned %08x\n", hr); ++ CoTaskMemFree(pwfx2); ++ ++ ++ pwfe->dwChannelMask = 0x3ffff; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL); ++ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, ++ "Exclusive IsFormatSupported with dwChannelMask=0x3ffff returned %08x\n", hr); ++ ++ pwfx2 = NULL; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); ++ ok(hr == S_OK && !pwfx2, ++ "Shared IsFormatSupported with dwChannelMask=0x3ffff returned %08x %p\n", hr, pwfx2); ++ CoTaskMemFree(pwfx2); ++ ++ ++ pwfe->dwChannelMask = 0x40000000; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL); ++ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, ++ "Exclusive IsFormatSupported with dwChannelMask=0x40000000 returned %08x\n", hr); ++ ++ pwfx2 = NULL; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); ++ ok(hr == S_OK && !pwfx2, ++ "Shared IsFormatSupported with dwChannelMask=0x40000000 returned %08x %p\n", hr, pwfx2); ++ CoTaskMemFree(pwfx2); ++ ++ pwfe->dwChannelMask = SPEAKER_ALL | SPEAKER_RESERVED; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL); ++ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, ++ "Exclusive IsFormatSupported with dwChannelMask=SPEAKER_ALL | SPEAKER_RESERVED returned %08x\n", hr); ++ ++ pwfx2 = NULL; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); ++ ok(hr == S_OK && !pwfx2, ++ "Shared IsFormatSupported with dwChannelMask=SPEAKER_ALL | SPEAKER_RESERVED returned %08x %p\n", hr, pwfx2); ++ CoTaskMemFree(pwfx2); ++ ++ *pwfe = wfe; ++ pwfe->Samples.wValidBitsPerSample = 0; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL); ++ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, ++ "Exclusive IsFormatSupported with wValidBitsPerSample=0 returned %08x\n", hr); ++ ++ pwfx2 = NULL; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); ++ ok((hr == S_FALSE || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && pwfx2, ++ "Shared IsFormatSupported with wValidBitsPerSample=0 returned %08x %p\n", hr, pwfx2); ++ if (pwfx2) { ++ pwfe2 = (WAVEFORMATEXTENSIBLE*)pwfx2; ++ ok(pwfe2->Samples.wValidBitsPerSample == pwfx->wBitsPerSample, ++ "Shared IsFormatSupported had wValidBitsPerSample set to %u, not %u\n", ++ pwfe2->Samples.wValidBitsPerSample, pwfx->wBitsPerSample); ++ CoTaskMemFree(pwfx2); ++ } ++ ++ pwfx2 = NULL; ++ pwfe->Samples.wValidBitsPerSample = pwfx->wBitsPerSample + 1; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); ++ ok((hr == E_INVALIDARG || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && !pwfx2, ++ "Shared IsFormatSupported with wValidBitsPerSample += 1 returned %08x %p\n", hr, pwfx2); ++ ++ *pwfe = wfe; ++ memset(&pwfe->SubFormat, 0xff, 16); ++ pwfx2 = NULL; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); ++ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT && !pwfx2, ++ "Shared IsFormatSupported with SubFormat=-1 returned %08x %p\n", hr, pwfx2); ++ CoTaskMemFree(pwfx2); ++ ++ *pwfe = wfe; ++ pwfx2 = NULL; ++ pwfe->Samples.wValidBitsPerSample = pwfx->wBitsPerSample = 256; ++ pwfx->nBlockAlign = pwfx->wBitsPerSample / 8 * pwfx->nChannels; ++ pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); ++ ok((hr == E_INVALIDARG || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && !pwfx2, ++ "Shared IsFormatSupported with wBitsPerSample=256 returned %08x %p\n", hr, pwfx2); ++ CoTaskMemFree(pwfx2); ++ ++ *pwfe = wfe; ++ pwfx2 = NULL; ++ pwfe->Samples.wValidBitsPerSample = pwfx->wBitsPerSample - 1; ++ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2); ++ ok(hr == S_FALSE && pwfx2, ++ "Shared IsFormatSupported with wValidBitsPerSample-=1 returned %08x %p\n", hr, pwfx2); ++ if (pwfx2) { ++ pwfe2 = (WAVEFORMATEXTENSIBLE*)pwfx2; ++ ok(pwfe2->Samples.wValidBitsPerSample == pwfx->wBitsPerSample, ++ "Shared IsFormatSupported had wValidBitsPerSample set to %u, not %u\n", ++ pwfe2->Samples.wValidBitsPerSample, pwfx->wBitsPerSample); ++ CoTaskMemFree(pwfx2); ++ } ++ ++ CoTaskMemFree(pwfx); ++ IAudioClient_Release(ac); ++} ++ + static void test_references(void) + { + IAudioClient *ac; +@@ -983,7 +1146,7 @@ static void test_clock(int share) + ok(hr == S_OK, "GetBuffer failed: %08x\n", hr); + trace("data at %p\n", data); + +- hr = IAudioRenderClient_ReleaseBuffer(arc, avail, winetest_debug>2 ? ++ hr = IAudioRenderClient_ReleaseBuffer(arc, avail, winetest_interactive ? + wave_generate_tone(pwfx, data, avail) : AUDCLNT_BUFFERFLAGS_SILENT); + ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr); + if(hr == S_OK) sum += avail; +@@ -1020,8 +1183,7 @@ static void test_clock(int share) + ok(hr == S_OK, "GetPosition failed: %08x\n", hr); + ok(pos >= last, "Position %u vs. last %u\n", (UINT)pos,(UINT)last); + last = pos; +- if(/*share &&*/ winetest_debug>1) todo_wine +- ok(pos*1000/freq <= slept*1.1, "Position %u too far after stop %ums\n", (UINT)pos, slept); ++ ok(pos*1000/freq <= slept*1.1, "Position %u too far after stop %ums\n", (UINT)pos, slept); + + hr = IAudioClient_Start(ac); /* #2 */ + ok(hr == S_OK, "Start failed: %08x\n", hr); +@@ -1054,12 +1216,11 @@ static void test_clock(int share) + ok(pos * pwfx->nSamplesPerSec <= sum * freq, "Position %u > written %u\n", (UINT)pos, sum); + /* Prove that Stop must not drop frames (in shared mode). */ + ok(pad ? pos > last : pos >= last, "Position %u vs. last %u\n", (UINT)pos,(UINT)last); +- if (share && pad > 0 && winetest_debug>1) todo_wine ++ if (share && pad > 0) + ok(pos*1000/freq <= slept*1.1, "Position %u too far after playing %ums\n", (UINT)pos, slept); + /* in exclusive mode, testbot's w7 machines yield pos > sum-pad */ +- if(/*share &&*/ winetest_debug>1) +- ok(pos * pwfx->nSamplesPerSec == (sum-pad) * freq, +- "Position %u after stop vs. %u padding\n", (UINT)pos, pad); ++ ok(pos * pwfx->nSamplesPerSec == (sum-pad) * freq, ++ "Position %u after stop vs. %u padding\n", (UINT)pos, pad); + last = pos; + + Sleep(100); +@@ -1087,7 +1248,7 @@ static void test_clock(int share) + ok(hr == S_OK, "GetBuffer failed: %08x\n", hr); + trace("data at %p\n", data); + +- hr = IAudioRenderClient_ReleaseBuffer(arc, avail, winetest_debug>2 ? ++ hr = IAudioRenderClient_ReleaseBuffer(arc, avail, winetest_interactive ? + wave_generate_tone(pwfx, data, avail) : AUDCLNT_BUFFERFLAGS_SILENT); + ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr); + if(hr == S_OK) sum += avail; +@@ -1112,10 +1273,7 @@ static void test_clock(int share) + trace("position %u past %ums sleep #3\n", (UINT)pos, slept); + ok(pos > last, "Position %u vs. last %u\n", (UINT)pos,(UINT)last); + ok(pos * pwfx->nSamplesPerSec <= sum * freq, "Position %u > written %u\n", (UINT)pos, sum); +- if (winetest_debug>1) +- ok(pos*1000/freq <= slept*1.1, "Position %u too far after playing %ums\n", (UINT)pos, slept); +- else +- skip("Rerun with WINETEST_DEBUG=2 for GetPosition tests.\n"); ++ ok(pos*1000/freq <= slept*1.1, "Position %u too far after playing %ums\n", (UINT)pos, slept); + last = pos; + + hr = IAudioClient_Reset(ac); +@@ -1133,11 +1291,10 @@ static void test_clock(int share) + ok(pos >= last, "Position %u vs. last %u\n", (UINT)pos,(UINT)last); + ok(pcpos > pcpos0, "pcpos should increase\n"); + ok(pos * pwfx->nSamplesPerSec <= sum * freq, "Position %u > written %u\n", (UINT)pos, sum); +- if (pad > 0 && winetest_debug>1) todo_wine ++ if (pad > 0) + ok(pos*1000/freq <= slept*1.1, "Position %u too far after stop %ums\n", (UINT)pos, slept); +- if(winetest_debug>1) +- ok(pos * pwfx->nSamplesPerSec == (sum-pad) * freq, +- "Position %u after stop vs. %u padding\n", (UINT)pos, pad); ++ ok(pos * pwfx->nSamplesPerSec == (sum-pad) * freq, ++ "Position %u after stop vs. %u padding\n", (UINT)pos, pad); + last = pos; + + /* Begin the big loop */ +@@ -1160,19 +1317,17 @@ static void test_clock(int share) + ok(hr == S_OK, "GetBuffer failed: %08x\n", hr); + trace("data at %p for prefill %u\n", data, avail); + +- if (winetest_debug>2) { +- hr = IAudioClient_Stop(ac); +- ok(hr == S_OK, "Stop failed: %08x\n", hr); ++ hr = IAudioClient_Stop(ac); ++ ok(hr == S_OK, "Stop failed: %08x\n", hr); + +- Sleep(20); +- slept += 20; ++ Sleep(20); ++ slept += 20; + +- hr = IAudioClient_Reset(ac); +- ok(hr == AUDCLNT_E_BUFFER_OPERATION_PENDING, "Reset failed: %08x\n", hr); ++ hr = IAudioClient_Reset(ac); ++ ok(hr == AUDCLNT_E_BUFFER_OPERATION_PENDING, "Reset failed: %08x\n", hr); + +- hr = IAudioClient_Start(ac); +- ok(hr == S_OK, "Start failed: %08x\n", hr); +- } ++ hr = IAudioClient_Start(ac); ++ ok(hr == S_OK, "Start failed: %08x\n", hr); + + /* Despite passed time, data must still point to valid memory... */ + hr = IAudioRenderClient_ReleaseBuffer(arc, avail, +@@ -1217,14 +1372,13 @@ static void test_clock(int share) + trace("padding %u position %u/%u slept %ums iteration %d\n", pad, (UINT)pos, sum-pad, slept, i); + ok(pad ? pos > last : pos >= last, "No position increase at iteration %d\n", i); + ok(pos * pwfx->nSamplesPerSec <= sum * freq, "Position %u > written %u\n", (UINT)pos, sum); +- if (winetest_debug>1) { +- /* Padding does not lag behind by much */ +- ok(pos * pwfx->nSamplesPerSec <= (sum-pad+fragment) * freq, "Position %u > written %u\n", (UINT)pos, sum); +- ok(pos*1000/freq <= slept*1.1, "Position %u too far after %ums\n", (UINT)pos, slept); +- if (pad) /* not in case of underrun */ +- ok((pos-last)*1000/freq >= 90 && 110 >= (pos-last)*1000/freq, +- "Position delta %ld not regular\n", (long)(pos-last)); +- } ++ ++ /* Padding does not lag behind by much */ ++ ok(pos * pwfx->nSamplesPerSec <= (sum-pad+fragment) * freq, "Position %u > written %u\n", (UINT)pos, sum); ++ ok(pos*1000/freq <= slept*1.1, "Position %u too far after %ums\n", (UINT)pos, slept); ++ if (pad) /* not in case of underrun */ ++ ok((pos-last)*1000/freq >= 90 && 110 >= (pos-last)*1000/freq, ++ "Position delta %ld not regular\n", (long)(pos-last)); + last = pos; + + hr = IAudioClient_GetStreamLatency(ac, &t1); +@@ -1237,7 +1391,7 @@ static void test_clock(int share) + /* ok(hr == AUDCLNT_E_BUFFER_TOO_LARGE || (hr == S_OK && i==0) without todo_wine */ + ok(hr == S_OK || hr == AUDCLNT_E_BUFFER_TOO_LARGE, + "GetBuffer large (%u) failed: %08x\n", avail, hr); +- if(hr == S_OK && i) todo_wine ok(FALSE, "GetBuffer large (%u) at iteration %d\n", avail, i); ++ if(hr == S_OK && i) ok(FALSE, "GetBuffer large (%u) at iteration %d\n", avail, i); + /* Only the first iteration should allow that large a buffer + * as prefill was drained during the first 350+100ms sleep. + * Afterwards, only 100ms of data should find room per iteration. */ +@@ -2089,7 +2243,7 @@ static void test_worst_case(void) + hr = IAudioClock_GetFrequency(acl, &freq); + ok(hr == S_OK, "GetFrequency failed: %08x\n", hr); + +- for(j = 0; j <= (winetest_interactive ? 9 : 2); j++){ ++ for(j = 0; j < 10; j++){ + sum = 0; + trace("Should play %ums continuous tone with fragment size %u.\n", + (ULONG)(defp/100), fragment); +@@ -2098,15 +2252,13 @@ static void test_worst_case(void) + ok(hr == S_OK, "GetPosition failed: %08x\n", hr); + + /* XAudio2 prefills one period, play without it */ +- if(winetest_debug>2){ +- hr = IAudioRenderClient_GetBuffer(arc, fragment, &data); +- ok(hr == S_OK, "GetBuffer failed: %08x\n", hr); +- +- hr = IAudioRenderClient_ReleaseBuffer(arc, fragment, AUDCLNT_BUFFERFLAGS_SILENT); +- ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr); +- if(hr == S_OK) +- sum += fragment; +- } ++ hr = IAudioRenderClient_GetBuffer(arc, fragment, &data); ++ ok(hr == S_OK, "GetBuffer failed: %08x\n", hr); ++ ++ hr = IAudioRenderClient_ReleaseBuffer(arc, fragment, AUDCLNT_BUFFERFLAGS_SILENT); ++ ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr); ++ if(hr == S_OK) ++ sum += fragment; + + hr = IAudioClient_Start(ac); + ok(hr == S_OK, "Start failed: %08x\n", hr); +@@ -2256,6 +2408,7 @@ START_TEST(render) + test_audioclient(); + test_formats(AUDCLNT_SHAREMODE_EXCLUSIVE); + test_formats(AUDCLNT_SHAREMODE_SHARED); ++ test_formats2(); + test_references(); + test_marshal(); + trace("Output to a MS-DOS console is particularly slow and disturbs timing.\n"); +diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h +index 4370084..1af819b 100644 +--- a/dlls/ntdll/ntdll_misc.h ++++ b/dlls/ntdll/ntdll_misc.h +@@ -28,6 +28,7 @@ + #include "winnt.h" + #include "winternl.h" + #include "wine/server.h" ++#include "wine/list.h" + + #define MAX_NT_PATH_LENGTH 277 + +@@ -235,6 +236,8 @@ struct ntdll_thread_data + WINE_VM86_TEB_INFO vm86; /* 1fc vm86 private data */ + void *exit_frame; /* 204 exit frame pointer */ + #endif ++ struct list entry; ++ BOOL detached; + }; + + static inline struct ntdll_thread_data *ntdll_get_thread_data(void) +diff --git a/dlls/ntdll/server.c b/dlls/ntdll/server.c +index f3c6b38..8c48fd2 100644 +--- a/dlls/ntdll/server.c ++++ b/dlls/ntdll/server.c +@@ -79,6 +79,9 @@ + #include "ntdll_misc.h" + + WINE_DEFAULT_DEBUG_CHANNEL(server); ++WINE_DECLARE_DEBUG_CHANNEL(winediag); ++WINE_DECLARE_DEBUG_CHANNEL(tid); ++WINE_DECLARE_DEBUG_CHANNEL(timestamp); + + /* Some versions of glibc don't define this */ + #ifndef SCM_RIGHTS +@@ -1328,6 +1331,88 @@ static int get_unix_tid(void) + } + + ++#ifdef SIGXCPU ++static int convert_tidtostr_r(char *s, unsigned hex) ++{ ++ int i; ++ char *start = s; ++ ++ for (i = 0; i < 8; ++i) { ++ unsigned c = hex >> (28 - 4 * i); ++ ++ if (c || i >= 4) ++ { /* last 4 digits always printed */ ++ c &= 0xf; ++ ++ if (c < 10) ++ *s++ = '0' + c; ++ else ++ *s++ = 'a' + c - 10; ++ } ++ } ++ ++ *s++ = ':'; ++ return s - start; ++} ++ ++static int convert_stamptostr_r(char *s, unsigned stamp) ++{ ++ unsigned high = stamp / 1000, low = stamp % 1000, i; ++ char *start = s; ++ ++ for (i = 1000000; i; i /= 10) { ++ if (high >= i) ++ *s++ = '0' + (high / i) % 10; ++ else if (i <= 100) ++ *s++ = ' '; ++ } ++ ++ *s++ = '.'; ++ ++ for (i = 100; i; i /= 10) ++ *s++ = '0' + (low / i) % 10; ++ ++ *s++ = ':'; ++ ++ return s - start; ++} ++ ++ ++static const char throttle_str[] = ++"fixme:winediag:sigxcpu_handler realtime priority was throttled due to program exceeding time limit\n"; ++ ++static void sigxcpu_handler( int sig ) ++{ ++ char temp[16]; ++ int old_errno = errno, ret; ++ ++ if (server_pid > 0) ++ kill(server_pid, SIGXCPU); ++ else { ++ /* uh oh, somehow init failed to get server_pid */ ++ struct sched_param parm; ++ memset(&parm, 0, sizeof(parm)); ++ sched_setscheduler(0, SCHED_OTHER | SCHED_RESET_ON_FORK, &parm); ++ } ++ ++ if (FIXME_ON(winediag)) { ++ if (TRACE_ON(timestamp)) { ++ ret = convert_stamptostr_r(temp, NtGetTickCount()); ++ write(2, temp, ret); ++ } ++ ++ if (TRACE_ON(tid)) { ++ ret = convert_tidtostr_r(temp, GetCurrentThreadId()); ++ write(2, temp, ret); ++ } ++ ++ write(2, throttle_str, sizeof(throttle_str)-1); ++ } ++ ++ errno = old_errno; ++} ++#endif ++ + /*********************************************************************** + * server_init_process + * +@@ -1337,6 +1422,14 @@ void server_init_process(void) + { + obj_handle_t version; + const char *env_socket = getenv( "WINESERVERSOCKET" ); ++#ifdef SIGXCPU ++ struct sigaction sa; ++ ++ sa.sa_handler = sigxcpu_handler; ++ sigemptyset(&sa.sa_mask); ++ sa.sa_flags = 0; ++ sigaction( SIGXCPU, &sa, NULL ); ++#endif + + server_pid = -1; + if (env_socket) +@@ -1420,7 +1513,6 @@ NTSTATUS server_init_process_done(void) + return status; + } + +- + /*********************************************************************** + * server_init_thread + * +diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c +index c8461b0..f133db3 100644 +--- a/dlls/ntdll/thread.c ++++ b/dlls/ntdll/thread.c +@@ -33,6 +33,7 @@ + #ifdef HAVE_SYS_SYSCALL_H + #include + #endif ++#include + + #define NONAMELESSUNION + #include "ntstatus.h" +@@ -58,6 +59,7 @@ struct startup_info + TEB *teb; + PRTL_THREAD_START_ROUTINE entry_point; + void *entry_arg; ++ BOOL native_thread; + }; + + static PEB *peb; +@@ -202,6 +204,78 @@ static ULONG get_dyld_image_info_addr(void) + } + #endif /* __APPLE__ */ + ++#ifdef __linux__ ++extern typeof(pthread_create) *__glob_pthread_create, *call_pthread_create; ++extern typeof(pthread_join) *__glob_pthread_join, *call_pthread_join; ++extern typeof(pthread_detach) *__glob_pthread_detach, *call_pthread_detach; ++ ++static typeof(pthread_create) __hook_pthread_create; ++static typeof(pthread_join) __hook_pthread_join; ++static typeof(pthread_detach) __hook_pthread_detach; ++ ++static pthread_mutex_t thread_lock; ++ ++static void thread_wrap_init(void) ++{ ++ pthread_mutexattr_t attr; ++ pthread_mutexattr_init(&attr); ++ pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST); ++ pthread_mutex_init(&thread_lock, &attr); ++ pthread_mutexattr_destroy(&attr); ++ ++ call_pthread_create = __hook_pthread_create; ++ call_pthread_join = __hook_pthread_join; ++ call_pthread_detach = __hook_pthread_detach; ++} ++ ++static TEB *dead_teb; ++static struct list active_list = LIST_INIT(active_list); ++ ++static void take_thread_lock(void) ++{ ++ int ret = pthread_mutex_lock(&thread_lock); ++ if (ret == EOWNERDEAD) ++ pthread_mutex_consistent(&thread_lock); ++} ++ ++static void detach_thread_unlock(TEB *own_teb) ++{ ++ struct ntdll_thread_data *thread_data; ++ TEB *teb = dead_teb; ++ ++ dead_teb = own_teb; ++ ++ pthread_mutex_unlock(&thread_lock); ++ if (!teb) ++ return; ++ ++ thread_data = (struct ntdll_thread_data *)teb->SpareBytes1; ++ __glob_pthread_join(thread_data->pthread_id, NULL); ++ signal_free_thread(teb); ++} ++ ++static void reap_thread(TEB *teb) ++{ ++ struct ntdll_thread_data *thread_data = (struct ntdll_thread_data *)teb->SpareBytes1; ++ take_thread_lock(); ++ if (thread_data->detached) ++ detach_thread_unlock(teb); ++ else { ++ /* ++ * Do not unlock, wait until the thread is thoroughly dead. ++ * This prevents a race condition where detach is called ++ * after the thread has not finished dying yet. ++ */ ++ } ++} ++ ++#else ++#define __glob_pthread_create pthread_create ++#define __glob_pthread_join pthread_join ++#define __glob_pthread_detach pthread_detach ++#define thread_wrap_init() ++#endif ++ + /*********************************************************************** + * thread_init + * +@@ -220,6 +294,7 @@ HANDLE thread_init(void) + struct ntdll_thread_data *thread_data; + static struct debug_info debug_info; /* debug info for initial thread */ + ++ thread_wrap_init(); + virtual_init(); + + /* reserve space for shared user data */ +@@ -349,14 +424,12 @@ void terminate_thread( int status ) + pthread_exit( UIntToPtr(status) ); + } + +- +-/*********************************************************************** +- * exit_thread +- */ +-void exit_thread( int status ) ++static void exit_thread_common( int status ) + { ++#ifndef __linux__ + static void *prev_teb; + TEB *teb; ++#endif + + if (status) /* send the exit code to the server (0 is already the default) */ + { +@@ -380,24 +453,158 @@ void exit_thread( int status ) + + pthread_sigmask( SIG_BLOCK, &server_block_set, NULL ); + ++#ifndef __linux__ + if ((teb = interlocked_xchg_ptr( &prev_teb, NtCurrentTeb() ))) + { + struct ntdll_thread_data *thread_data = (struct ntdll_thread_data *)teb->SpareBytes1; + + if (thread_data->pthread_id) + { +- pthread_join( thread_data->pthread_id, NULL ); ++ __glob_pthread_join( thread_data->pthread_id, NULL ); + signal_free_thread( teb ); + } + } ++#else ++ reap_thread(NtCurrentTeb()); ++#endif + + close( ntdll_get_thread_data()->wait_fd[0] ); + close( ntdll_get_thread_data()->wait_fd[1] ); + close( ntdll_get_thread_data()->reply_fd ); + close( ntdll_get_thread_data()->request_fd ); ++} ++ ++void exit_thread( int status ) ++{ ++ exit_thread_common(status); + pthread_exit( UIntToPtr(status) ); + } + ++#ifdef __linux__ ++ ++struct unix_arg { ++ void *(*start)(void *); ++ void *arg; ++}; ++ ++/* dummy used for comparison */ ++static DWORD native_unix_start; ++ ++static void call_native_cleanup(void *arg) ++{ ++ RtlFreeThreadActivationContextStack(); ++ exit_thread_common(0); ++} ++ ++static int ++__hook_pthread_create(pthread_t *thread, const pthread_attr_t *attr, ++ void *(*start_routine) (void *), void *parm) ++{ ++ NTSTATUS ret; ++ size_t stack = 8 * 1024 * 1024; ++ struct unix_arg arg; ++ arg.start = start_routine; ++ arg.arg = parm; ++ ++ TRACE("Overriding thread creation!\n"); ++ if (attr) { ++ static int once; ++ if (!once++) ++ FIXME("most thread attributes ignored!\n"); ++ else ++ WARN("most thread attributes ignored!\n"); ++ ++ pthread_attr_getstacksize(attr, &stack); ++ } ++ ++ ret = RtlCreateUserThread( NtCurrentProcess(), NULL, FALSE, NULL, stack, 0, (void*)&native_unix_start, &arg, NULL, (void*)thread ); ++ if (ret != STATUS_SUCCESS) ++ FIXME("ret: %08x\n", ret); ++ switch (ret) { ++ case STATUS_SUCCESS: ++ return 0; ++ case STATUS_NO_MEMORY: ++ return ENOMEM; ++ case STATUS_TOO_MANY_OPENED_FILES: ++ return EMFILE; ++ default: ++ ERR("Unhandled ntstatus %08x\n", ret); ++ return ENOMEM; ++ } ++} ++ ++static int __hook_pthread_detach(pthread_t thread) ++{ ++ struct ntdll_thread_data *thread_data; ++ TEB *teb = NULL; ++ ++ if (pthread_equal(thread, pthread_self())) { ++ ntdll_get_thread_data()->detached = 1; ++ return 0; ++ } ++ ++ take_thread_lock(); ++ LIST_FOR_EACH_ENTRY(thread_data, &active_list, typeof(*thread_data), entry) { ++ if (pthread_equal(thread_data->pthread_id, thread)) { ++ teb = CONTAINING_RECORD(thread_data, typeof(*teb), SpareBytes1); ++ ++ list_remove(&thread_data->entry); ++ if (!pthread_tryjoin_np(thread, NULL)) { ++ detach_thread_unlock(NULL); ++ signal_free_thread(teb); ++ return 0; ++ } ++ thread_data->detached = 1; ++ break; ++ } ++ } ++ detach_thread_unlock(NULL); ++ return teb ? 0 : ESRCH; ++} ++ ++static int __hook_pthread_join(pthread_t thread, void **retval) ++{ ++ struct ntdll_thread_data *thread_data, *t2; ++ int ret = ESRCH; ++ ++ if (pthread_equal(thread, pthread_self())) ++ return EDEADLK; ++ ++ take_thread_lock(); ++ LIST_FOR_EACH_ENTRY(thread_data, &active_list, typeof(*thread_data), entry) { ++ TEB *teb = CONTAINING_RECORD(thread_data, typeof(*teb), SpareBytes1); ++ ++ if (pthread_equal(thread, thread_data->pthread_id)) { ++ ++ ret = pthread_tryjoin_np(thread, retval); ++ if (!ret) ++ goto free; ++ detach_thread_unlock(NULL); ++ ++ ret = __glob_pthread_join(thread, retval); ++ if (ret) ++ return ret; ++ ++ take_thread_lock(); ++ /* Check if someone else freed the thread yet */ ++ LIST_FOR_EACH_ENTRY(t2, &active_list, typeof(*thread_data), entry) ++ if (t2 == thread_data) ++ goto free; ++ break; ++ ++free: ++ list_remove(&thread_data->entry); ++ detach_thread_unlock(NULL); ++ signal_free_thread(teb); ++ return 0; ++ } ++ } ++ ++ detach_thread_unlock(NULL); ++ return ret; ++} ++ ++#endif + + /*********************************************************************** + * start_thread +@@ -426,9 +633,26 @@ static void start_thread( struct startup_info *info ) + if (TRACE_ON(relay)) + DPRINTF( "%04x:Starting thread proc %p (arg=%p)\n", GetCurrentThreadId(), func, arg ); + +- call_thread_entry_point( (LPTHREAD_START_ROUTINE)func, arg ); +-} ++#ifdef __linux__ ++ if (info->native_thread) { ++ void *(*start)(void*) = (void*)func; + ++ thread_data->detached = 0; ++ ++ take_thread_lock(); ++ list_add_tail(&active_list, &thread_data->entry); ++ detach_thread_unlock(NULL); ++ ++ FIXME("Started native thread %08x\n", GetCurrentThreadId()); ++ pthread_cleanup_push(call_native_cleanup, NULL); ++ pthread_exit(start(arg)); ++ pthread_cleanup_pop(1); ++ return; ++ } ++ thread_data->detached = 1; ++#endif ++ call_thread_entry_point( (LPTHREAD_START_ROUTINE)func, arg ); ++} + + /*********************************************************************** + * RtlCreateUserThread (NTDLL.@) +@@ -524,8 +748,18 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR * + + info = (struct startup_info *)(teb + 1); + info->teb = teb; +- info->entry_point = start; +- info->entry_arg = param; ++#ifdef __linux__ ++ info->native_thread = (void*)start == (void*)&native_unix_start; ++ if (info->native_thread) { ++ struct unix_arg *arg = param; ++ info->entry_point = (void*)arg->start; ++ info->entry_arg = arg->arg; ++ } else ++#endif ++ { ++ info->entry_point = start; ++ info->entry_arg = param; ++ } + + thread_data = (struct ntdll_thread_data *)teb->SpareBytes1; + thread_data->request_fd = request_pipe[1]; +@@ -540,7 +774,7 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR * + (char *)teb->Tib.StackBase - (char *)teb->DeallocationStack ); + pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ); /* force creating a kernel thread */ + interlocked_xchg_add( &nb_threads, 1 ); +- if (pthread_create( &pthread_id, &attr, (void * (*)(void *))start_thread, info )) ++ if (__glob_pthread_create( &pthread_id, &attr, (void * (*)(void *))start_thread, info )) + { + interlocked_xchg_add( &nb_threads, -1 ); + pthread_attr_destroy( &attr ); +@@ -550,6 +784,11 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR * + pthread_attr_destroy( &attr ); + pthread_sigmask( SIG_SETMASK, &sigset, NULL ); + ++#ifdef __linux__ ++ if ((void*)start == (void*)&native_unix_start && id) ++ *(pthread_t*)id = pthread_id; ++ else ++#endif + if (id) id->UniqueThread = ULongToHandle(tid); + if (handle_ptr) *handle_ptr = handle; + else NtClose( handle ); +diff --git a/dlls/winegstreamer/glibthread.c b/dlls/winegstreamer/glibthread.c +index 0d829a0..46e22f4 100644 +--- a/dlls/winegstreamer/glibthread.c ++++ b/dlls/winegstreamer/glibthread.c +@@ -43,6 +43,7 @@ + #include + #include + ++#if 0 + #include "windef.h" + #include "winbase.h" + #include "winnls.h" +@@ -388,3 +389,15 @@ void g_thread_impl_init (void) + g_thread_self_tls = TlsAlloc (); + g_thread_init(&g_thread_functions_for_glib_use_default); + } ++ ++#else ++ ++void g_thread_impl_init (void) ++{ ++ static gboolean beenhere = FALSE; ++ ++ if (!beenhere++) ++ g_thread_init(NULL); ++} ++ ++#endif +diff --git a/dlls/winepulse.drv/Makefile.in b/dlls/winepulse.drv/Makefile.in +new file mode 100644 +index 0000000..158bbc0 +--- /dev/null ++++ b/dlls/winepulse.drv/Makefile.in +@@ -0,0 +1,7 @@ ++MODULE = winepulse.drv ++IMPORTS = dxguid uuid winmm user32 advapi32 ole32 ++EXTRALIBS = @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..e755e8a +--- /dev/null ++++ b/dlls/winepulse.drv/mmdevdrv.c +@@ -0,0 +1,3227 @@ ++/* ++ * Copyright 2011-2012 Maarten Lankhorst ++ * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers ++ * Copyright 2011 Andrew Eikum for CodeWeavers ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA ++ * ++ * Pulseaudio driver support.. hell froze over ++ */ ++ ++#define NONAMELESSUNION ++#define COBJMACROS ++#define _GNU_SOURCE ++ ++#include "config.h" ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "windef.h" ++#include "winbase.h" ++#include "winnls.h" ++#include "winreg.h" ++#include "wine/debug.h" ++#include "wine/unicode.h" ++#include "wine/list.h" ++ ++#include "ole2.h" ++#include "dshow.h" ++#include "dsound.h" ++#include "propsys.h" ++ ++#include "initguid.h" ++#include "ks.h" ++#include "ksmedia.h" ++#include "mmdeviceapi.h" ++#include "audioclient.h" ++#include "endpointvolume.h" ++#include "audiopolicy.h" ++ ++#include "wine/list.h" ++ ++#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER) ++ ++WINE_DEFAULT_DEBUG_CHANNEL(pulse); ++WINE_DECLARE_DEBUG_CHANNEL(winediag); ++ ++static const REFERENCE_TIME MinimumPeriod = 30000; ++static const REFERENCE_TIME DefaultPeriod = 100000; ++ ++static pa_context *pulse_ctx; ++static pa_mainloop *pulse_ml; ++ ++static HANDLE pulse_thread; ++static pthread_mutex_t pulse_lock; ++static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER; ++static struct list g_sessions = LIST_INIT(g_sessions); ++ ++/* Mixer format + period times */ ++static WAVEFORMATEXTENSIBLE pulse_fmt[2]; ++static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2]; ++ ++static DWORD pulse_stream_volume; ++ ++const WCHAR pulse_keyW[] = {'S','o','f','t','w','a','r','e','\\', ++ 'W','i','n','e','\\','P','u','l','s','e',0}; ++const WCHAR pulse_streamW[] = { 'S','t','r','e','a','m','V','o','l',0 }; ++ ++static GUID pulse_render_guid = ++{ 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } }; ++static GUID pulse_capture_guid = ++{ 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } }; ++ ++static HANDLE warn_once; ++ ++BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) ++{ ++ if (reason == DLL_PROCESS_ATTACH) { ++ HKEY key; ++ pthread_mutexattr_t attr; ++ ++ if (RegOpenKeyW(HKEY_CURRENT_USER, pulse_keyW, &key) == ERROR_SUCCESS) { ++ DWORD size = sizeof(pulse_stream_volume); ++ RegQueryValueExW(key, pulse_streamW, 0, NULL, ++ (BYTE*)&pulse_stream_volume, &size); ++ RegCloseKey(key); ++ } ++ DisableThreadLibraryCalls(dll); ++ ++ 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); ++ if (warn_once) ++ CloseHandle(warn_once); ++ } ++ return TRUE; ++} ++ ++typedef struct ACImpl ACImpl; ++ ++typedef struct _AudioSession { ++ GUID guid; ++ struct list clients; ++ ++ IMMDevice *device; ++ ++ float master_vol; ++ UINT32 channel_count; ++ float *channel_vols; ++ BOOL mute; ++ ++ struct list entry; ++} AudioSession; ++ ++typedef struct _AudioSessionWrapper { ++ IAudioSessionControl2 IAudioSessionControl2_iface; ++ IChannelAudioVolume IChannelAudioVolume_iface; ++ ISimpleAudioVolume ISimpleAudioVolume_iface; ++ ++ LONG ref; ++ ++ ACImpl *client; ++ AudioSession *session; ++} AudioSessionWrapper; ++ ++typedef struct _ACPacket { ++ struct list entry; ++ UINT64 qpcpos; ++ BYTE *data; ++ UINT32 discont; ++} ACPacket; ++ ++struct ACImpl { ++ IAudioClient IAudioClient_iface; ++ IAudioRenderClient IAudioRenderClient_iface; ++ IAudioCaptureClient IAudioCaptureClient_iface; ++ IAudioClock IAudioClock_iface; ++ IAudioClock2 IAudioClock2_iface; ++ IAudioStreamVolume IAudioStreamVolume_iface; ++ 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 void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) { ++ WAVEFORMATEX *wfx = &fmt->Format; ++ pa_stream *stream; ++ pa_channel_map map; ++ pa_sample_spec ss; ++ pa_buffer_attr attr; ++ int ret, i; ++ unsigned int length = 0; ++ ++ pa_channel_map_init_auto(&map, 2, PA_CHANNEL_MAP_ALSA); ++ ss.rate = 48000; ++ ss.format = PA_SAMPLE_FLOAT32LE; ++ ss.channels = map.channels; ++ ++ attr.maxlength = -1; ++ attr.tlength = -1; ++ attr.minreq = attr.fragsize = pa_frame_size(&ss); ++ attr.prebuf = 0; ++ ++ stream = pa_stream_new(pulse_ctx, "format test stream", &ss, &map); ++ if (stream) ++ pa_stream_set_state_callback(stream, pulse_stream_state, NULL); ++ if (!stream) ++ ret = -1; ++ else if (render) ++ ret = pa_stream_connect_playback(stream, NULL, &attr, ++ PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS, NULL, NULL); ++ else ++ ret = pa_stream_connect_record(stream, NULL, &attr, PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS); ++ if (ret >= 0) { ++ while (pa_stream_get_state(stream) == PA_STREAM_CREATING) ++ pthread_cond_wait(&pulse_cond, &pulse_lock); ++ if (pa_stream_get_state(stream) == PA_STREAM_READY) { ++ ss = *pa_stream_get_sample_spec(stream); ++ map = *pa_stream_get_channel_map(stream); ++ if (render) ++ length = pa_stream_get_buffer_attr(stream)->minreq; ++ else ++ length = pa_stream_get_buffer_attr(stream)->fragsize; ++ pa_stream_disconnect(stream); ++ while (pa_stream_get_state(stream) == PA_STREAM_READY) ++ pthread_cond_wait(&pulse_cond, &pulse_lock); ++ } ++ } ++ if (stream) ++ pa_stream_unref(stream); ++ if (length) ++ pulse_def_period[!render] = pulse_min_period[!render] = pa_bytes_to_usec(10 * length, &ss); ++ else ++ pulse_min_period[!render] = MinimumPeriod; ++ if (pulse_def_period[!render] <= DefaultPeriod) ++ pulse_def_period[!render] = DefaultPeriod; ++ ++ wfx->wFormatTag = WAVE_FORMAT_EXTENSIBLE; ++ wfx->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); ++ wfx->nChannels = ss.channels; ++ wfx->wBitsPerSample = 8 * pa_sample_size_of_format(ss.format); ++ wfx->nSamplesPerSec = ss.rate; ++ wfx->nBlockAlign = pa_frame_size(&ss); ++ wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign; ++ if (ss.format != PA_SAMPLE_S24_32LE) ++ fmt->Samples.wValidBitsPerSample = wfx->wBitsPerSample; ++ else ++ fmt->Samples.wValidBitsPerSample = 24; ++ if (ss.format == PA_SAMPLE_FLOAT32LE) ++ fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; ++ else ++ fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; ++ ++ fmt->dwChannelMask = 0; ++ for (i = 0; i < map.channels; ++i) ++ switch (map.map[i]) { ++ default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(map.map[i])); break; ++ case PA_CHANNEL_POSITION_FRONT_LEFT: fmt->dwChannelMask |= SPEAKER_FRONT_LEFT; break; ++ case PA_CHANNEL_POSITION_MONO: ++ case PA_CHANNEL_POSITION_FRONT_CENTER: fmt->dwChannelMask |= SPEAKER_FRONT_CENTER; break; ++ case PA_CHANNEL_POSITION_FRONT_RIGHT: fmt->dwChannelMask |= SPEAKER_FRONT_RIGHT; break; ++ case PA_CHANNEL_POSITION_REAR_LEFT: fmt->dwChannelMask |= SPEAKER_BACK_LEFT; break; ++ case PA_CHANNEL_POSITION_REAR_CENTER: fmt->dwChannelMask |= SPEAKER_BACK_CENTER; break; ++ case PA_CHANNEL_POSITION_REAR_RIGHT: fmt->dwChannelMask |= SPEAKER_BACK_RIGHT; break; ++ case PA_CHANNEL_POSITION_LFE: fmt->dwChannelMask |= SPEAKER_LOW_FREQUENCY; break; ++ case PA_CHANNEL_POSITION_SIDE_LEFT: fmt->dwChannelMask |= SPEAKER_SIDE_LEFT; break; ++ case PA_CHANNEL_POSITION_SIDE_RIGHT: fmt->dwChannelMask |= SPEAKER_SIDE_RIGHT; break; ++ case PA_CHANNEL_POSITION_TOP_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_CENTER; break; ++ case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_LEFT; break; ++ case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_CENTER; break; ++ case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_RIGHT; break; ++ case PA_CHANNEL_POSITION_TOP_REAR_LEFT: fmt->dwChannelMask |= SPEAKER_TOP_BACK_LEFT; break; ++ case PA_CHANNEL_POSITION_TOP_REAR_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_BACK_CENTER; break; ++ case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: fmt->dwChannelMask |= SPEAKER_TOP_BACK_RIGHT; break; ++ } ++} ++ ++static HRESULT pulse_connect(void) ++{ ++ int len; ++ WCHAR path[PATH_MAX], *name; ++ char *str; ++ ++ if (!pulse_thread) ++ { ++ if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL))) ++ { ++ ERR("Failed to create mainloop thread."); ++ return E_FAIL; ++ } ++ SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL); ++ pthread_cond_wait(&pulse_cond, &pulse_lock); ++ } ++ ++ if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx))) ++ return S_OK; ++ if (pulse_ctx) ++ pa_context_unref(pulse_ctx); ++ ++ GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*path)); ++ name = strrchrW(path, '\\'); ++ if (!name) ++ name = path; ++ else ++ name++; ++ len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL); ++ str = pa_xmalloc(len); ++ WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL); ++ TRACE("Name: %s\n", str); ++ pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str); ++ pa_xfree(str); ++ if (!pulse_ctx) { ++ ERR("Failed to create context\n"); ++ return E_FAIL; ++ } ++ ++ pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL); ++ ++ TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION); ++ if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0) ++ goto fail; ++ ++ /* Wait for connection */ ++ while (pthread_cond_wait(&pulse_cond, &pulse_lock)) { ++ pa_context_state_t state = pa_context_get_state(pulse_ctx); ++ ++ if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) ++ goto fail; ++ ++ if (state == PA_CONTEXT_READY) ++ break; ++ } ++ ++ TRACE("Connected to server %s with protocol version: %i.\n", ++ pa_context_get_server(pulse_ctx), ++ pa_context_get_server_protocol_version(pulse_ctx)); ++ pulse_probe_settings(1, &pulse_fmt[0]); ++ pulse_probe_settings(0, &pulse_fmt[1]); ++ return S_OK; ++ ++fail: ++ pa_context_unref(pulse_ctx); ++ pulse_ctx = NULL; ++ return E_FAIL; ++} ++ ++static void pulse_contextcallback(pa_context *c, void *userdata) { ++ switch (pa_context_get_state(c)) { ++ default: ++ FIXME("Unhandled state: %i\n", pa_context_get_state(c)); ++ case PA_CONTEXT_CONNECTING: ++ case PA_CONTEXT_UNCONNECTED: ++ case PA_CONTEXT_AUTHORIZING: ++ case PA_CONTEXT_SETTING_NAME: ++ case PA_CONTEXT_TERMINATED: ++ TRACE("State change to %i\n", pa_context_get_state(c)); ++ return; ++ ++ case PA_CONTEXT_READY: ++ TRACE("Ready\n"); ++ break; ++ ++ case PA_CONTEXT_FAILED: ++ ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c))); ++ break; ++ } ++ pthread_cond_signal(&pulse_cond); ++} ++ ++static HRESULT pulse_stream_valid(ACImpl *This) { ++ if (!This->stream) ++ return AUDCLNT_E_NOT_INITIALIZED; ++ if (!This->stream || pa_stream_get_state(This->stream) != PA_STREAM_READY) ++ return AUDCLNT_E_DEVICE_INVALIDATED; ++ return S_OK; ++} ++ ++static void dump_attr(const pa_buffer_attr *attr) { ++ TRACE("maxlength: %u\n", attr->maxlength); ++ TRACE("minreq: %u\n", attr->minreq); ++ TRACE("fragsize: %u\n", attr->fragsize); ++ TRACE("tlength: %u\n", attr->tlength); ++ TRACE("prebuf: %u\n", attr->prebuf); ++} ++ ++static void pulse_op_cb(pa_stream *s, int success, void *user) { ++ TRACE("Success: %i\n", success); ++ *(int*)user = success; ++ pthread_cond_signal(&pulse_cond); ++} ++ ++static void pulse_ctx_op_cb(pa_context *c, int success, void *user) { ++ TRACE("Success: %i\n", success); ++ *(int*)user = success; ++ pthread_cond_signal(&pulse_cond); ++} ++ ++static void pulse_attr_update(pa_stream *s, void *user) { ++ const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s); ++ TRACE("New attributes or device moved:\n"); ++ dump_attr(attr); ++} ++ ++static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata) ++{ ++ ACImpl *This = userdata; ++ 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; ++ if (getenv("WINENOPULSE")) { ++ FIXME_(winediag)("winepulse has been temporarily disabled through the environment\n"); ++ return 0; ++ } ++ pthread_mutex_lock(&pulse_lock); ++ hr = pulse_connect(); ++ pthread_mutex_unlock(&pulse_lock); ++ return SUCCEEDED(hr) ? 3 : 0; ++} ++ ++HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out) ++{ ++ HRESULT hr; ++ ACImpl *This; ++ int i; ++ EDataFlow dataflow; ++ ++ /* Give one visible warning per session ++ * Sadly wine has chosen not to accept the winepulse patch, so support ourselves ++ */ ++ if (!warn_once && (warn_once = CreateEventA(0, 0, 0, "__winepulse_warn_event")) && GetLastError() != ERROR_ALREADY_EXISTS) { ++ FIXME_(winediag)("Winepulse is not officially supported by the wine project\n"); ++ FIXME_(winediag)("For sound related feedback and support, please visit http://ubuntuforums.org/showthread.php?t=1960599\n"); ++ } else { ++ WARN_(winediag)("Winepulse is not officially supported by the wine project\n"); ++ WARN_(winediag)("For sound related feedback and support, please visit http://ubuntuforums.org/showthread.php?t=1960599\n"); ++ } ++ ++ TRACE("%s %p %p\n", debugstr_guid(guid), dev, out); ++ if (IsEqualGUID(guid, &pulse_render_guid)) ++ dataflow = eRender; ++ else if (IsEqualGUID(guid, &pulse_capture_guid)) ++ dataflow = eCapture; ++ else ++ return E_UNEXPECTED; ++ ++ *out = NULL; ++ pthread_mutex_lock(&pulse_lock); ++ hr = pulse_connect(); ++ pthread_mutex_unlock(&pulse_lock); ++ if (FAILED(hr)) ++ return hr; ++ ++ This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This)); ++ if (!This) ++ return E_OUTOFMEMORY; ++ ++ This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl; ++ This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl; ++ This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl; ++ This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl; ++ This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl; ++ This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl; ++ This->dataflow = dataflow; ++ This->parent = dev; ++ 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 SPEAKER_FRONT_CENTER; ++ case 2: ++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; ++ case 3: ++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | ++ SPEAKER_LOW_FREQUENCY; ++ case 4: ++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | ++ SPEAKER_BACK_RIGHT; ++ case 5: ++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | ++ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY; ++ case 6: ++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | ++ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER; ++ case 7: ++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | ++ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER | ++ SPEAKER_BACK_CENTER; ++ case 8: ++ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | ++ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER | ++ SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; ++ } ++ FIXME("Unknown speaker configuration: %u\n", channels); ++ return 0; ++} ++ ++static void session_init_vols(AudioSession *session, UINT channels) ++{ ++ if (session->channel_count < channels) { ++ UINT i; ++ ++ if (session->channel_vols) ++ session->channel_vols = HeapReAlloc(GetProcessHeap(), 0, ++ session->channel_vols, sizeof(float) * channels); ++ else ++ session->channel_vols = HeapAlloc(GetProcessHeap(), 0, ++ sizeof(float) * channels); ++ if (!session->channel_vols) ++ return; ++ ++ for(i = session->channel_count; i < channels; ++i) ++ session->channel_vols[i] = 1.f; ++ ++ session->channel_count = channels; ++ } ++} ++ ++static AudioSession *create_session(const GUID *guid, IMMDevice *device, ++ UINT num_channels) ++{ ++ AudioSession *ret; ++ ++ ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession)); ++ if (!ret) ++ return NULL; ++ ++ memcpy(&ret->guid, guid, sizeof(GUID)); ++ ++ ret->device = device; ++ ++ list_init(&ret->clients); ++ ++ list_add_head(&g_sessions, &ret->entry); ++ ++ session_init_vols(ret, num_channels); ++ ++ ret->master_vol = 1.f; ++ ++ return ret; ++} ++ ++/* if channels == 0, then this will return or create a session with ++ * matching dataflow and GUID. otherwise, channels must also match */ ++static HRESULT get_audio_session(const GUID *sessionguid, ++ IMMDevice *device, UINT channels, AudioSession **out) ++{ ++ AudioSession *session; ++ ++ if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) { ++ *out = create_session(&GUID_NULL, device, channels); ++ if (!*out) ++ return E_OUTOFMEMORY; ++ ++ return S_OK; ++ } ++ ++ *out = NULL; ++ LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) { ++ if (session->device == device && ++ IsEqualGUID(sessionguid, &session->guid)) { ++ session_init_vols(session, channels); ++ *out = session; ++ break; ++ } ++ } ++ ++ if (!*out) { ++ *out = create_session(sessionguid, device, channels); ++ if (!*out) ++ return E_OUTOFMEMORY; ++ } ++ ++ return S_OK; ++} ++ ++static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt) ++{ ++ pa_channel_map_init(&This->map); ++ This->ss.rate = fmt->nSamplesPerSec; ++ This->ss.format = PA_SAMPLE_INVALID; ++ switch(fmt->wFormatTag) { ++ case WAVE_FORMAT_IEEE_FLOAT: ++ if (!fmt->nChannels || fmt->nChannels > 2 || fmt->wBitsPerSample != 32) ++ break; ++ This->ss.format = PA_SAMPLE_FLOAT32LE; ++ pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA); ++ break; ++ case WAVE_FORMAT_PCM: ++ if (!fmt->nChannels || fmt->nChannels > 2) ++ break; ++ if (fmt->wBitsPerSample == 8) ++ This->ss.format = PA_SAMPLE_U8; ++ else if (fmt->wBitsPerSample == 16) ++ This->ss.format = PA_SAMPLE_S16LE; ++ else ++ return AUDCLNT_E_UNSUPPORTED_FORMAT; ++ pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA); ++ break; ++ case WAVE_FORMAT_EXTENSIBLE: { ++ WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)fmt; ++ DWORD mask = wfe->dwChannelMask; ++ DWORD i = 0, j; ++ if (fmt->cbSize != (sizeof(*wfe) - sizeof(*fmt)) && fmt->cbSize != sizeof(*wfe)) ++ break; ++ if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) && ++ (!wfe->Samples.wValidBitsPerSample || wfe->Samples.wValidBitsPerSample == 32) && ++ fmt->wBitsPerSample == 32) ++ This->ss.format = PA_SAMPLE_FLOAT32LE; ++ else if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) { ++ DWORD valid = wfe->Samples.wValidBitsPerSample; ++ if (!valid) ++ valid = fmt->wBitsPerSample; ++ if (!valid || valid > fmt->wBitsPerSample) ++ break; ++ switch (fmt->wBitsPerSample) { ++ case 8: ++ if (valid == 8) ++ This->ss.format = PA_SAMPLE_U8; ++ break; ++ case 16: ++ if (valid == 16) ++ This->ss.format = PA_SAMPLE_S16LE; ++ break; ++ case 24: ++ if (valid == 24) ++ This->ss.format = PA_SAMPLE_S24LE; ++ break; ++ case 32: ++ if (valid == 24) ++ This->ss.format = PA_SAMPLE_S24_32LE; ++ else if (valid == 32) ++ This->ss.format = PA_SAMPLE_S32LE; ++ break; ++ default: ++ return AUDCLNT_E_UNSUPPORTED_FORMAT; ++ } ++ } ++ This->map.channels = fmt->nChannels; ++ if (!mask || (mask & (SPEAKER_ALL|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); ++ dump_fmt(fmt); ++ return AUDCLNT_E_UNSUPPORTED_FORMAT; ++ } ++ return S_OK; ++} ++ ++static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface, ++ AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration, ++ REFERENCE_TIME period, const WAVEFORMATEX *fmt, ++ const GUID *sessionguid) ++{ ++ ACImpl *This = impl_from_IAudioClient(iface); ++ HRESULT hr = S_OK; ++ UINT period_bytes; ++ ++ TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags, ++ wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid)); ++ ++ if (!fmt) ++ return E_POINTER; ++ ++ if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE) ++ return AUDCLNT_E_NOT_INITIALIZED; ++ if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) ++ return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED; ++ ++ if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS | ++ AUDCLNT_STREAMFLAGS_LOOPBACK | ++ AUDCLNT_STREAMFLAGS_EVENTCALLBACK | ++ AUDCLNT_STREAMFLAGS_NOPERSIST | ++ AUDCLNT_STREAMFLAGS_RATEADJUST | ++ AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED | ++ AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE | ++ AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)) { ++ TRACE("Unknown flags: %08x\n", flags); ++ return E_INVALIDARG; ++ } ++ ++ pthread_mutex_lock(&pulse_lock); ++ if (This->stream) { ++ pthread_mutex_unlock(&pulse_lock); ++ return AUDCLNT_E_ALREADY_INITIALIZED; ++ } ++ ++ hr = pulse_spec_from_waveformat(This, fmt); ++ if (FAILED(hr)) ++ goto exit; ++ ++ if (mode == AUDCLNT_SHAREMODE_SHARED) { ++ 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; ++ memset(This->tmp_buffer, This->ss.format == PA_SAMPLE_U8 ? 0x80 : 0, This->bufsize_bytes); ++ list_init(&This->packet_free_head); ++ list_init(&This->packet_filled_head); ++ for (i = 0; i < capture_packets; ++i, ++cur_packet) { ++ list_add_tail(&This->packet_free_head, &cur_packet->entry); ++ cur_packet->data = data; ++ data += This->capture_period; ++ } ++ assert(!This->capture_period || This->bufsize_bytes == This->capture_period * capture_packets); ++ assert(!capture_packets || data - This->bufsize_bytes == This->tmp_buffer); ++ } ++ } ++ if (SUCCEEDED(hr)) ++ hr = get_audio_session(sessionguid, This->parent, fmt->nChannels, &This->session); ++ if (SUCCEEDED(hr)) ++ list_add_tail(&This->session->clients, &This->entry); ++ ++exit: ++ if (FAILED(hr)) { ++ HeapFree(GetProcessHeap(), 0, This->tmp_buffer); ++ This->tmp_buffer = NULL; ++ if (This->stream) { ++ pa_stream_disconnect(This->stream); ++ pa_stream_unref(This->stream); ++ This->stream = NULL; ++ } ++ } ++ pthread_mutex_unlock(&pulse_lock); ++ return hr; ++} ++ ++static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface, ++ UINT32 *out) ++{ ++ ACImpl *This = impl_from_IAudioClient(iface); ++ HRESULT hr; ++ ++ TRACE("(%p)->(%p)\n", This, out); ++ ++ if (!out) ++ return E_POINTER; ++ ++ pthread_mutex_lock(&pulse_lock); ++ hr = pulse_stream_valid(This); ++ if (SUCCEEDED(hr)) ++ *out = This->bufsize_frames; ++ pthread_mutex_unlock(&pulse_lock); ++ ++ return hr; ++} ++ ++static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface, ++ REFERENCE_TIME *latency) ++{ ++ ACImpl *This = impl_from_IAudioClient(iface); ++ const pa_buffer_attr *attr; ++ REFERENCE_TIME lat; ++ HRESULT hr; ++ ++ TRACE("(%p)->(%p)\n", This, latency); ++ ++ if (!latency) ++ return E_POINTER; ++ ++ pthread_mutex_lock(&pulse_lock); ++ hr = pulse_stream_valid(This); ++ if (FAILED(hr)) { ++ pthread_mutex_unlock(&pulse_lock); ++ return hr; ++ } ++ attr = pa_stream_get_buffer_attr(This->stream); ++ if (This->dataflow == eRender) ++ lat = attr->minreq / pa_frame_size(&This->ss); ++ else ++ lat = attr->fragsize / pa_frame_size(&This->ss); ++ *latency = 10000000; ++ *latency *= lat; ++ *latency /= This->ss.rate; ++ pthread_mutex_unlock(&pulse_lock); ++ TRACE("Latency: %u ms\n", (DWORD)(*latency / 10000)); ++ return S_OK; ++} ++ ++static void ACImpl_GetRenderPad(ACImpl *This, UINT32 *out) ++{ ++ *out = This->pad / pa_frame_size(&This->ss); ++} ++ ++static void ACImpl_GetCapturePad(ACImpl *This, UINT32 *out) ++{ ++ ACPacket *packet = This->locked_ptr; ++ if (!packet && !list_empty(&This->packet_filled_head)) { ++ packet = (ACPacket*)list_head(&This->packet_filled_head); ++ This->locked_ptr = packet; ++ list_remove(&packet->entry); ++ } ++ if (out) ++ *out = This->pad / pa_frame_size(&This->ss); ++} ++ ++static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface, ++ UINT32 *out) ++{ ++ ACImpl *This = impl_from_IAudioClient(iface); ++ HRESULT hr; ++ ++ TRACE("(%p)->(%p)\n", This, out); ++ ++ if (!out) ++ return E_POINTER; ++ ++ pthread_mutex_lock(&pulse_lock); ++ hr = pulse_stream_valid(This); ++ if (FAILED(hr)) { ++ pthread_mutex_unlock(&pulse_lock); ++ return hr; ++ } ++ ++ if (This->dataflow == eRender) ++ ACImpl_GetRenderPad(This, out); ++ else ++ ACImpl_GetCapturePad(This, out); ++ pthread_mutex_unlock(&pulse_lock); ++ ++ TRACE("%p Pad: %u ms (%u)\n", This, MulDiv(*out, 1000, This->ss.rate), *out); ++ return S_OK; ++} ++ ++static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface, ++ AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt, ++ WAVEFORMATEX **out) ++{ ++ ACImpl *This = impl_from_IAudioClient(iface); ++ HRESULT hr = S_OK; ++ WAVEFORMATEX *closest = NULL; ++ 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); ++// UINT32 period; ++ ++ TRACE("(%p)->(%u, %x)\n", This, written_frames, flags); ++ ++ pthread_mutex_lock(&pulse_lock); ++ if (!This->locked || !written_frames) { ++ if (This->locked_ptr) ++ pa_stream_cancel_write(This->stream); ++ This->locked = 0; ++ This->locked_ptr = NULL; ++ pthread_mutex_unlock(&pulse_lock); ++ return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK; ++ } ++ ++ if (This->locked < written_frames) { ++ pthread_mutex_unlock(&pulse_lock); ++ return AUDCLNT_E_INVALID_SIZE; ++ } ++ ++ if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { ++ if (This->ss.format == PA_SAMPLE_U8) ++ memset(This->tmp_buffer, 128, written_bytes); ++ else ++ memset(This->tmp_buffer, 0, written_bytes); ++ } ++ ++ This->locked = 0; ++ if (This->locked_ptr) ++ pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE); ++ else ++ pa_stream_write(This->stream, This->tmp_buffer, written_bytes, 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); ++ ++// period = pa_stream_get_buffer_attr(This->stream)->minreq; ++ /* Require a minimum of 3 periods filled, if possible */ ++// if (This->event && This->pad + period <= This->bufsize_bytes && This->pad < period * 3) ++// SetEvent(This->event); ++ pthread_mutex_unlock(&pulse_lock); ++ return S_OK; ++} ++ ++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); ++ ACPacket *p; ++ ++ TRACE("(%p)->(%p)\n", This, frames); ++ if (!frames) ++ return E_POINTER; ++ ++ pthread_mutex_lock(&pulse_lock); ++ ACImpl_GetCapturePad(This, NULL); ++ p = This->locked_ptr; ++ if (p) ++ *frames = This->capture_period / pa_frame_size(&This->ss); ++ else ++ *frames = 0; ++ pthread_mutex_unlock(&pulse_lock); ++ return S_OK; ++} ++ ++static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl = ++{ ++ AudioCaptureClient_QueryInterface, ++ AudioCaptureClient_AddRef, ++ AudioCaptureClient_Release, ++ AudioCaptureClient_GetBuffer, ++ AudioCaptureClient_ReleaseBuffer, ++ AudioCaptureClient_GetNextPacketSize ++}; ++ ++static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface, ++ REFIID riid, void **ppv) ++{ ++ ACImpl *This = impl_from_IAudioClock(iface); ++ ++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); ++ ++ if (!ppv) ++ return E_POINTER; ++ *ppv = NULL; ++ ++ if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock)) ++ *ppv = iface; ++ else if (IsEqualIID(riid, &IID_IAudioClock2)) ++ *ppv = &This->IAudioClock2_iface; ++ if (*ppv) { ++ IUnknown_AddRef((IUnknown*)*ppv); ++ return S_OK; ++ } ++ ++ 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)) ++ *freq = This->ss.rate * pa_frame_size(&This->ss); ++ pthread_mutex_unlock(&pulse_lock); ++ return hr; ++} ++ ++static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos, ++ UINT64 *qpctime) ++{ ++ ACImpl *This = impl_from_IAudioClock(iface); ++ 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; ++ ++ /* Make time never go backwards */ ++ if (*pos < This->clock_lastpos) ++ *pos = This->clock_lastpos; ++ else ++ This->clock_lastpos = *pos; ++ pthread_mutex_unlock(&pulse_lock); ++ ++ TRACE("%p Position: %u\n", This, (unsigned)*pos); ++ ++ if (qpctime) { ++ LARGE_INTEGER stamp, freq; ++ QueryPerformanceCounter(&stamp); ++ QueryPerformanceFrequency(&freq); ++ *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart; ++ } ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface, ++ DWORD *chars) ++{ ++ ACImpl *This = impl_from_IAudioClock(iface); ++ ++ TRACE("(%p)->(%p)\n", This, chars); ++ ++ if (!chars) ++ return E_POINTER; ++ ++ *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ; ++ ++ return S_OK; ++} ++ ++static const IAudioClockVtbl AudioClock_Vtbl = ++{ ++ AudioClock_QueryInterface, ++ AudioClock_AddRef, ++ AudioClock_Release, ++ AudioClock_GetFrequency, ++ AudioClock_GetPosition, ++ AudioClock_GetCharacteristics ++}; ++ ++static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface, ++ REFIID riid, void **ppv) ++{ ++ ACImpl *This = impl_from_IAudioClock2(iface); ++ return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv); ++} ++ ++static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface) ++{ ++ ACImpl *This = impl_from_IAudioClock2(iface); ++ return IAudioClient_AddRef(&This->IAudioClient_iface); ++} ++ ++static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface) ++{ ++ ACImpl *This = impl_from_IAudioClock2(iface); ++ return IAudioClient_Release(&This->IAudioClient_iface); ++} ++ ++static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface, ++ UINT64 *pos, UINT64 *qpctime) ++{ ++ ACImpl *This = impl_from_IAudioClock2(iface); ++ HRESULT hr = AudioClock_GetPosition(&This->IAudioClock_iface, pos, qpctime); ++ if (SUCCEEDED(hr)) ++ *pos /= pa_frame_size(&This->ss); ++ return hr; ++} ++ ++static const IAudioClock2Vtbl AudioClock2_Vtbl = ++{ ++ AudioClock2_QueryInterface, ++ AudioClock2_AddRef, ++ AudioClock2_Release, ++ AudioClock2_GetDevicePosition ++}; ++ ++static HRESULT WINAPI AudioStreamVolume_QueryInterface( ++ IAudioStreamVolume *iface, REFIID riid, void **ppv) ++{ ++ 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 void pulse_sink_input_info_cb(pa_context *c, const pa_sink_input_info *info, int eol, void *data) ++{ ++ struct pulse_info_cb_data *d = data; ++ int i; ++ if (eol) ++ return; ++ for (i = 0; i < d->n; ++i) ++ d->levels[i] = (float)info->volume.values[i] / (float)PA_VOLUME_NORM; ++ pthread_cond_signal(&pulse_cond); ++} ++ ++static void pulse_source_info_cb(pa_context *c, const pa_source_info *info, int eol, void *data) ++{ ++ struct pulse_info_cb_data *d = data; ++ int i; ++ if (eol) ++ return; ++ for (i = 0; i < d->n; ++i) ++ d->levels[i] = (float)info->volume.values[i] / (float)PA_VOLUME_NORM; ++ pthread_cond_signal(&pulse_cond); ++} ++ ++static HRESULT WINAPI AudioStreamVolume_SetAllVolumes( ++ IAudioStreamVolume *iface, UINT32 count, const float *levels) ++{ ++ ACImpl *This = impl_from_IAudioStreamVolume(iface); ++ pa_operation *o; ++ HRESULT hr; ++ int success = 0, i; ++ pa_cvolume cv; ++ ++ TRACE("(%p)->(%d, %p)\n", This, count, levels); ++ ++ if (!levels) ++ return E_POINTER; ++ ++ if (count != This->ss.channels) ++ return E_INVALIDARG; ++ ++ pthread_mutex_lock(&pulse_lock); ++ hr = pulse_stream_valid(This); ++ if (FAILED(hr)) ++ goto out; ++ ++ if (pulse_stream_volume) { ++ cv.channels = count; ++ for (i = 0; i < cv.channels; ++i) ++ cv.values[i] = levels[i] * (float)PA_VOLUME_NORM; ++ if (This->dataflow == eRender) ++ o = pa_context_set_sink_input_volume(pulse_ctx, pa_stream_get_index(This->stream), &cv, pulse_ctx_op_cb, &success); ++ else ++ o = pa_context_set_source_volume_by_index(pulse_ctx, pa_stream_get_device_index(This->stream), &cv, pulse_ctx_op_cb, &success); ++ if (o) { ++ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) ++ pthread_cond_wait(&pulse_cond, &pulse_lock); ++ pa_operation_unref(o); ++ } ++ if (!success) ++ hr = AUDCLNT_E_BUFFER_ERROR; ++ } else { ++ int i; ++ for (i = 0; i < count; ++i) ++ This->vol[i] = levels[i]; ++ } ++ ++out: ++ pthread_mutex_unlock(&pulse_lock); ++ return hr; ++} ++ ++static HRESULT WINAPI AudioStreamVolume_GetAllVolumes( ++ IAudioStreamVolume *iface, UINT32 count, float *levels) ++{ ++ ACImpl *This = impl_from_IAudioStreamVolume(iface); ++ pa_operation *o; ++ HRESULT hr; ++ struct pulse_info_cb_data info; ++ ++ TRACE("(%p)->(%d, %p)\n", This, count, levels); ++ ++ if (!levels) ++ return E_POINTER; ++ ++ if (count != This->ss.channels) ++ return E_INVALIDARG; ++ ++ pthread_mutex_lock(&pulse_lock); ++ hr = pulse_stream_valid(This); ++ if (FAILED(hr)) ++ goto out; ++ ++ if (pulse_stream_volume) { ++ info.n = count; ++ info.levels = levels; ++ if (This->dataflow == eRender) ++ o = pa_context_get_sink_input_info(pulse_ctx, pa_stream_get_index(This->stream), pulse_sink_input_info_cb, &info); ++ else ++ o = pa_context_get_source_info_by_index(pulse_ctx, pa_stream_get_device_index(This->stream), pulse_source_info_cb, &info); ++ if (o) { ++ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) ++ pthread_cond_wait(&pulse_cond, &pulse_lock); ++ pa_operation_unref(o); ++ } else ++ hr = AUDCLNT_E_BUFFER_ERROR; ++ } else { ++ int i; ++ for (i = 0; i < count; ++i) ++ levels[i] = This->vol[i]; ++ } ++ ++out: ++ pthread_mutex_unlock(&pulse_lock); ++ return hr; ++} ++ ++static HRESULT WINAPI AudioStreamVolume_SetChannelVolume( ++ IAudioStreamVolume *iface, UINT32 index, float level) ++{ ++ ACImpl *This = impl_from_IAudioStreamVolume(iface); ++ HRESULT hr; ++ float volumes[PA_CHANNELS_MAX]; ++ ++ TRACE("(%p)->(%d, %f)\n", This, index, level); ++ ++ if (level < 0.f || level > 1.f) ++ return E_INVALIDARG; ++ ++ if (index >= This->ss.channels) ++ return E_INVALIDARG; ++ ++ hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes); ++ volumes[index] = level; ++ if (SUCCEEDED(hr)) ++ hr = AudioStreamVolume_SetAllVolumes(iface, This->ss.channels, volumes); ++ return hr; ++} ++ ++static HRESULT WINAPI AudioStreamVolume_GetChannelVolume( ++ IAudioStreamVolume *iface, UINT32 index, float *level) ++{ ++ ACImpl *This = impl_from_IAudioStreamVolume(iface); ++ float volumes[PA_CHANNELS_MAX]; ++ HRESULT hr; ++ ++ TRACE("(%p)->(%d, %p)\n", This, index, level); ++ ++ if (!level) ++ return E_POINTER; ++ ++ if (index >= This->ss.channels) ++ return E_INVALIDARG; ++ ++ hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes); ++ if (SUCCEEDED(hr)) ++ *level = volumes[index]; ++ return hr; ++} ++ ++static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl = ++{ ++ AudioStreamVolume_QueryInterface, ++ AudioStreamVolume_AddRef, ++ AudioStreamVolume_Release, ++ AudioStreamVolume_GetChannelCount, ++ AudioStreamVolume_SetChannelVolume, ++ AudioStreamVolume_GetChannelVolume, ++ AudioStreamVolume_SetAllVolumes, ++ AudioStreamVolume_GetAllVolumes ++}; ++ ++static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client) ++{ ++ AudioSessionWrapper *ret; ++ ++ ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ++ sizeof(AudioSessionWrapper)); ++ if (!ret) ++ return NULL; ++ ++ ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl; ++ ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl; ++ ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl; ++ ++ ret->ref = !client; ++ ++ ret->client = client; ++ if (client) { ++ ret->session = client->session; ++ AudioClient_AddRef(&client->IAudioClient_iface); ++ } ++ ++ return ret; ++} ++ ++static HRESULT WINAPI AudioSessionControl_QueryInterface( ++ IAudioSessionControl2 *iface, REFIID riid, void **ppv) ++{ ++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); ++ ++ if (!ppv) ++ return E_POINTER; ++ *ppv = NULL; ++ ++ if (IsEqualIID(riid, &IID_IUnknown) || ++ IsEqualIID(riid, &IID_IAudioSessionControl) || ++ IsEqualIID(riid, &IID_IAudioSessionControl2)) ++ *ppv = iface; ++ if (*ppv) { ++ IUnknown_AddRef((IUnknown*)*ppv); ++ return S_OK; ++ } ++ ++ WARN("Unknown interface %s\n", debugstr_guid(riid)); ++ return E_NOINTERFACE; ++} ++ ++static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ULONG ref; ++ ref = InterlockedIncrement(&This->ref); ++ TRACE("(%p) Refcount now %u\n", This, ref); ++ return ref; ++} ++ ++static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ULONG ref; ++ ref = InterlockedDecrement(&This->ref); ++ TRACE("(%p) Refcount now %u\n", This, ref); ++ if (!ref) { ++ if (This->client) { ++ This->client->session_wrapper = NULL; ++ AudioClient_Release(&This->client->IAudioClient_iface); ++ } ++ HeapFree(GetProcessHeap(), 0, This); ++ } ++ return ref; ++} ++ ++static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface, ++ AudioSessionState *state) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ACImpl *client; ++ ++ TRACE("(%p)->(%p)\n", This, state); ++ ++ if (!state) ++ return NULL_PTR_ERR; ++ ++ pthread_mutex_lock(&pulse_lock); ++ if (list_empty(&This->session->clients)) { ++ *state = AudioSessionStateExpired; ++ goto out; ++ } ++ LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry) { ++ if (client->started) { ++ *state = AudioSessionStateActive; ++ goto out; ++ } ++ } ++ *state = AudioSessionStateInactive; ++ ++out: ++ pthread_mutex_unlock(&pulse_lock); ++ return S_OK; ++} ++ ++static HRESULT WINAPI AudioSessionControl_GetDisplayName( ++ IAudioSessionControl2 *iface, WCHAR **name) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ FIXME("(%p)->(%p) - stub\n", This, name); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI AudioSessionControl_SetDisplayName( ++ IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session)); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI AudioSessionControl_GetIconPath( ++ IAudioSessionControl2 *iface, WCHAR **path) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ FIXME("(%p)->(%p) - stub\n", This, path); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI AudioSessionControl_SetIconPath( ++ IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session)); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI AudioSessionControl_GetGroupingParam( ++ IAudioSessionControl2 *iface, GUID *group) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ FIXME("(%p)->(%p) - stub\n", This, group); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI AudioSessionControl_SetGroupingParam( ++ IAudioSessionControl2 *iface, const GUID *group, const GUID *session) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group), ++ debugstr_guid(session)); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification( ++ IAudioSessionControl2 *iface, IAudioSessionEvents *events) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ FIXME("(%p)->(%p) - stub\n", This, events); ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification( ++ IAudioSessionControl2 *iface, IAudioSessionEvents *events) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ FIXME("(%p)->(%p) - stub\n", This, events); ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier( ++ IAudioSessionControl2 *iface, WCHAR **id) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ FIXME("(%p)->(%p) - stub\n", This, id); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier( ++ IAudioSessionControl2 *iface, WCHAR **id) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ FIXME("(%p)->(%p) - stub\n", This, id); ++ ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI AudioSessionControl_GetProcessId( ++ IAudioSessionControl2 *iface, DWORD *pid) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ TRACE("(%p)->(%p)\n", This, pid); ++ ++ if (!pid) ++ return E_POINTER; ++ ++ *pid = GetCurrentProcessId(); ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession( ++ IAudioSessionControl2 *iface) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ TRACE("(%p)\n", This); ++ ++ return S_FALSE; ++} ++ ++static HRESULT WINAPI AudioSessionControl_SetDuckingPreference( ++ IAudioSessionControl2 *iface, BOOL optout) ++{ ++ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); ++ ++ TRACE("(%p)->(%d)\n", This, optout); ++ ++ return S_OK; ++} ++ ++static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl = ++{ ++ AudioSessionControl_QueryInterface, ++ AudioSessionControl_AddRef, ++ AudioSessionControl_Release, ++ AudioSessionControl_GetState, ++ AudioSessionControl_GetDisplayName, ++ AudioSessionControl_SetDisplayName, ++ AudioSessionControl_GetIconPath, ++ AudioSessionControl_SetIconPath, ++ AudioSessionControl_GetGroupingParam, ++ AudioSessionControl_SetGroupingParam, ++ AudioSessionControl_RegisterAudioSessionNotification, ++ AudioSessionControl_UnregisterAudioSessionNotification, ++ AudioSessionControl_GetSessionIdentifier, ++ AudioSessionControl_GetSessionInstanceIdentifier, ++ AudioSessionControl_GetProcessId, ++ AudioSessionControl_IsSystemSoundsSession, ++ AudioSessionControl_SetDuckingPreference ++}; ++ ++typedef struct _SessionMgr { ++ IAudioSessionManager2 IAudioSessionManager2_iface; ++ ++ LONG ref; ++ ++ IMMDevice *device; ++} SessionMgr; ++ ++static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface, ++ REFIID riid, void **ppv) ++{ ++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); ++ ++ if (!ppv) ++ return E_POINTER; ++ *ppv = NULL; ++ ++ if (IsEqualIID(riid, &IID_IUnknown) || ++ IsEqualIID(riid, &IID_IAudioSessionManager) || ++ IsEqualIID(riid, &IID_IAudioSessionManager2)) ++ *ppv = iface; ++ if (*ppv) { ++ IUnknown_AddRef((IUnknown*)*ppv); ++ return S_OK; ++ } ++ ++ WARN("Unknown interface %s\n", debugstr_guid(riid)); ++ return E_NOINTERFACE; ++} ++ ++static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface) ++{ ++ return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface); ++} ++ ++static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface) ++{ ++ SessionMgr *This = impl_from_IAudioSessionManager2(iface); ++ ULONG ref; ++ ref = InterlockedIncrement(&This->ref); ++ TRACE("(%p) Refcount now %u\n", This, ref); ++ return ref; ++} ++ ++static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface) ++{ ++ SessionMgr *This = impl_from_IAudioSessionManager2(iface); ++ ULONG ref; ++ ref = InterlockedDecrement(&This->ref); ++ TRACE("(%p) Refcount now %u\n", This, ref); ++ if (!ref) ++ HeapFree(GetProcessHeap(), 0, This); ++ return ref; ++} ++ ++static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl( ++ IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags, ++ IAudioSessionControl **out) ++{ ++ SessionMgr *This = impl_from_IAudioSessionManager2(iface); ++ AudioSession *session; ++ AudioSessionWrapper *wrapper; ++ HRESULT hr; ++ ++ TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid), ++ flags, out); ++ ++ hr = get_audio_session(session_guid, This->device, 0, &session); ++ if (FAILED(hr)) ++ return hr; ++ ++ wrapper = AudioSessionWrapper_Create(NULL); ++ if (!wrapper) ++ return E_OUTOFMEMORY; ++ ++ wrapper->session = session; ++ ++ *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface; ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume( ++ IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags, ++ ISimpleAudioVolume **out) ++{ ++ SessionMgr *This = impl_from_IAudioSessionManager2(iface); ++ AudioSession *session; ++ AudioSessionWrapper *wrapper; ++ HRESULT hr; ++ ++ TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid), ++ flags, out); ++ ++ hr = get_audio_session(session_guid, This->device, 0, &session); ++ if (FAILED(hr)) ++ return hr; ++ ++ wrapper = AudioSessionWrapper_Create(NULL); ++ if (!wrapper) ++ return E_OUTOFMEMORY; ++ ++ wrapper->session = session; ++ ++ *out = &wrapper->ISimpleAudioVolume_iface; ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator( ++ IAudioSessionManager2 *iface, IAudioSessionEnumerator **out) ++{ ++ SessionMgr *This = impl_from_IAudioSessionManager2(iface); ++ FIXME("(%p)->(%p) - stub\n", This, out); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification( ++ IAudioSessionManager2 *iface, IAudioSessionNotification *notification) ++{ ++ SessionMgr *This = impl_from_IAudioSessionManager2(iface); ++ FIXME("(%p)->(%p) - stub\n", This, notification); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification( ++ IAudioSessionManager2 *iface, IAudioSessionNotification *notification) ++{ ++ SessionMgr *This = impl_from_IAudioSessionManager2(iface); ++ FIXME("(%p)->(%p) - stub\n", This, notification); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification( ++ IAudioSessionManager2 *iface, const WCHAR *session_id, ++ IAudioVolumeDuckNotification *notification) ++{ ++ SessionMgr *This = impl_from_IAudioSessionManager2(iface); ++ FIXME("(%p)->(%p) - stub\n", This, notification); ++ return E_NOTIMPL; ++} ++ ++static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification( ++ IAudioSessionManager2 *iface, ++ IAudioVolumeDuckNotification *notification) ++{ ++ SessionMgr *This = impl_from_IAudioSessionManager2(iface); ++ FIXME("(%p)->(%p) - stub\n", This, notification); ++ return E_NOTIMPL; ++} ++ ++static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl = ++{ ++ AudioSessionManager_QueryInterface, ++ AudioSessionManager_AddRef, ++ AudioSessionManager_Release, ++ AudioSessionManager_GetAudioSessionControl, ++ AudioSessionManager_GetSimpleAudioVolume, ++ AudioSessionManager_GetSessionEnumerator, ++ AudioSessionManager_RegisterSessionNotification, ++ AudioSessionManager_UnregisterSessionNotification, ++ AudioSessionManager_RegisterDuckNotification, ++ AudioSessionManager_UnregisterDuckNotification ++}; ++ ++static HRESULT WINAPI SimpleAudioVolume_QueryInterface( ++ ISimpleAudioVolume *iface, REFIID riid, void **ppv) ++{ ++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); ++ ++ if (!ppv) ++ return E_POINTER; ++ *ppv = NULL; ++ ++ if (IsEqualIID(riid, &IID_IUnknown) || ++ IsEqualIID(riid, &IID_ISimpleAudioVolume)) ++ *ppv = iface; ++ if (*ppv) { ++ IUnknown_AddRef((IUnknown*)*ppv); ++ return S_OK; ++ } ++ ++ WARN("Unknown interface %s\n", debugstr_guid(riid)); ++ return E_NOINTERFACE; ++} ++ ++static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface) ++{ ++ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); ++ return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface); ++} ++ ++static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface) ++{ ++ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); ++ return AudioSessionControl_Release(&This->IAudioSessionControl2_iface); ++} ++ ++static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume( ++ ISimpleAudioVolume *iface, float level, const GUID *context) ++{ ++ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); ++ AudioSession *session = This->session; ++ ++ TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context)); ++ ++ if (level < 0.f || level > 1.f) ++ return E_INVALIDARG; ++ ++ if (context) ++ FIXME("Notifications not supported yet\n"); ++ ++ TRACE("Pulseaudio does not support session volume control\n"); ++ ++ pthread_mutex_lock(&pulse_lock); ++ session->master_vol = level; ++ pthread_mutex_unlock(&pulse_lock); ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume( ++ ISimpleAudioVolume *iface, float *level) ++{ ++ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); ++ AudioSession *session = This->session; ++ ++ TRACE("(%p)->(%p)\n", session, level); ++ ++ if (!level) ++ return NULL_PTR_ERR; ++ ++ *level = session->master_vol; ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface, ++ BOOL mute, const GUID *context) ++{ ++ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); ++ AudioSession *session = This->session; ++ ++ TRACE("(%p)->(%u, %p)\n", session, mute, context); ++ ++ if (context) ++ FIXME("Notifications not supported yet\n"); ++ ++ session->mute = mute; ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface, ++ BOOL *mute) ++{ ++ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); ++ AudioSession *session = This->session; ++ ++ TRACE("(%p)->(%p)\n", session, mute); ++ ++ if (!mute) ++ return NULL_PTR_ERR; ++ ++ *mute = session->mute; ++ ++ return S_OK; ++} ++ ++static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl = ++{ ++ SimpleAudioVolume_QueryInterface, ++ SimpleAudioVolume_AddRef, ++ SimpleAudioVolume_Release, ++ SimpleAudioVolume_SetMasterVolume, ++ SimpleAudioVolume_GetMasterVolume, ++ SimpleAudioVolume_SetMute, ++ SimpleAudioVolume_GetMute ++}; ++ ++static HRESULT WINAPI ChannelAudioVolume_QueryInterface( ++ IChannelAudioVolume *iface, REFIID riid, void **ppv) ++{ ++ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); ++ ++ if (!ppv) ++ return E_POINTER; ++ *ppv = NULL; ++ ++ if (IsEqualIID(riid, &IID_IUnknown) || ++ IsEqualIID(riid, &IID_IChannelAudioVolume)) ++ *ppv = iface; ++ if (*ppv) { ++ IUnknown_AddRef((IUnknown*)*ppv); ++ return S_OK; ++ } ++ ++ WARN("Unknown interface %s\n", debugstr_guid(riid)); ++ return E_NOINTERFACE; ++} ++ ++static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface) ++{ ++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); ++ return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface); ++} ++ ++static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface) ++{ ++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); ++ return AudioSessionControl_Release(&This->IAudioSessionControl2_iface); ++} ++ ++static HRESULT WINAPI ChannelAudioVolume_GetChannelCount( ++ IChannelAudioVolume *iface, UINT32 *out) ++{ ++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); ++ AudioSession *session = This->session; ++ ++ TRACE("(%p)->(%p)\n", session, out); ++ ++ if (!out) ++ return NULL_PTR_ERR; ++ ++ *out = session->channel_count; ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume( ++ IChannelAudioVolume *iface, UINT32 index, float level, ++ const GUID *context) ++{ ++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); ++ AudioSession *session = This->session; ++ ++ TRACE("(%p)->(%d, %f, %s)\n", session, index, level, ++ wine_dbgstr_guid(context)); ++ ++ if (level < 0.f || level > 1.f) ++ return E_INVALIDARG; ++ ++ if (index >= session->channel_count) ++ return E_INVALIDARG; ++ ++ if (context) ++ FIXME("Notifications not supported yet\n"); ++ ++ TRACE("Pulseaudio does not support session volume control\n"); ++ ++ pthread_mutex_lock(&pulse_lock); ++ session->channel_vols[index] = level; ++ pthread_mutex_unlock(&pulse_lock); ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume( ++ IChannelAudioVolume *iface, UINT32 index, float *level) ++{ ++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); ++ AudioSession *session = This->session; ++ ++ TRACE("(%p)->(%d, %p)\n", session, index, level); ++ ++ if (!level) ++ return NULL_PTR_ERR; ++ ++ if (index >= session->channel_count) ++ return E_INVALIDARG; ++ ++ *level = session->channel_vols[index]; ++ ++ return S_OK; ++} ++ ++static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes( ++ IChannelAudioVolume *iface, UINT32 count, const float *levels, ++ const GUID *context) ++{ ++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); ++ AudioSession *session = This->session; ++ int i; ++ ++ TRACE("(%p)->(%d, %p, %s)\n", session, count, levels, ++ wine_dbgstr_guid(context)); ++ ++ if (!levels) ++ return NULL_PTR_ERR; ++ ++ if (count != session->channel_count) ++ return E_INVALIDARG; ++ ++ if (context) ++ FIXME("Notifications not supported yet\n"); ++ ++ TRACE("Pulseaudio does not support session volume control\n"); ++ ++ pthread_mutex_lock(&pulse_lock); ++ for(i = 0; i < count; ++i) ++ session->channel_vols[i] = levels[i]; ++ pthread_mutex_unlock(&pulse_lock); ++ return S_OK; ++} ++ ++static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes( ++ IChannelAudioVolume *iface, UINT32 count, float *levels) ++{ ++ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); ++ AudioSession *session = This->session; ++ int i; ++ ++ TRACE("(%p)->(%d, %p)\n", session, count, levels); ++ ++ if (!levels) ++ return NULL_PTR_ERR; ++ ++ if (count != session->channel_count) ++ return E_INVALIDARG; ++ ++ for(i = 0; i < count; ++i) ++ levels[i] = session->channel_vols[i]; ++ ++ return S_OK; ++} ++ ++static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl = ++{ ++ ChannelAudioVolume_QueryInterface, ++ ChannelAudioVolume_AddRef, ++ ChannelAudioVolume_Release, ++ ChannelAudioVolume_GetChannelCount, ++ ChannelAudioVolume_SetChannelVolume, ++ ChannelAudioVolume_GetChannelVolume, ++ ChannelAudioVolume_SetAllVolumes, ++ ChannelAudioVolume_GetAllVolumes ++}; ++ ++HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device, ++ IAudioSessionManager2 **out) ++{ ++ SessionMgr *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr)); ++ *out = NULL; ++ if (!This) ++ return E_OUTOFMEMORY; ++ This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl; ++ This->device = device; ++ This->ref = 1; ++ *out = &This->IAudioSessionManager2_iface; ++ return S_OK; ++} +diff --git a/dlls/winepulse.drv/winepulse.drv.spec b/dlls/winepulse.drv/winepulse.drv.spec +new file mode 100644 +index 0000000..612bf46 +--- /dev/null ++++ b/dlls/winepulse.drv/winepulse.drv.spec +@@ -0,0 +1,5 @@ ++# MMDevAPI driver functions ++@ stdcall -private GetPriority() AUDDRV_GetPriority ++@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs ++@ stdcall -private GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint ++@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager +diff --git a/dlls/winmm/lolvldrv.c b/dlls/winmm/lolvldrv.c +index c3b3674..56cfe35 100644 +--- a/dlls/winmm/lolvldrv.c ++++ b/dlls/winmm/lolvldrv.c +@@ -544,7 +544,10 @@ static void MMDRV_Init(void) + drvA = HeapAlloc(GetProcessHeap(), 0, size); + WideCharToMultiByte(CP_ACP, 0, pv.u.pwszVal, -1, drvA, size, NULL, NULL); + +- MMDRV_Install(drvA, drvA, FALSE); ++ if (!strcasecmp(drvA, "winepulse.drv")) ++ MMDRV_Install("winealsa.drv", "winealsa.drv", 0); ++ else ++ MMDRV_Install(drvA, drvA, FALSE); + + HeapFree(GetProcessHeap(), 0, drvA); + PropVariantClear(&pv); +diff --git a/dlls/winmm/waveform.c b/dlls/winmm/waveform.c +index 6b259ff..541dc62 100644 +--- a/dlls/winmm/waveform.c ++++ b/dlls/winmm/waveform.c +@@ -68,8 +68,8 @@ static const WCHAR muteW[] = {'M','u','t','e',0}; + * - We must be able to identify bad devices without crashing. + */ + +-/* buffer size = 100 * 100000 (100 ns) = 1 second */ +-#define AC_BUFLEN (100 * 100000) ++/* buffer size = 10 * 100000 (100 ns) = 0.1 seconds */ ++#define AC_BUFLEN (10 * 100000) + #define MAX_DEVICES 256 + #define MAPPER_INDEX 0x3F + +diff --git a/libs/wine/loader.c b/libs/wine/loader.c +index 5c0192d..365144c 100644 +--- a/libs/wine/loader.c ++++ b/libs/wine/loader.c +@@ -63,12 +63,23 @@ extern char **environ; + #include "winbase.h" + #include "wine/library.h" + ++#ifdef HAVE_VALGRIND_MEMCHECK_H ++#include ++#endif ++ + /* argc/argv for the Windows application */ + int __wine_main_argc = 0; + char **__wine_main_argv = NULL; + WCHAR **__wine_main_wargv = NULL; + char **__wine_main_environ = NULL; + ++#ifdef __linux__ ++#include ++typeof(pthread_create) *call_pthread_create, *__glob_pthread_create; ++typeof(pthread_join) *call_pthread_join, *__glob_pthread_join; ++typeof(pthread_detach) *call_pthread_detach, *__glob_pthread_detach; ++#endif ++ + struct dll_path_context + { + unsigned int index; /* current index in the dll path list */ +@@ -650,7 +661,6 @@ int wine_dll_get_owner( const char *name, char *buffer, int size, int *exists ) + return ret; + } + +- + /*********************************************************************** + * set_max_limit + * +@@ -661,6 +671,11 @@ static void set_max_limit( int limit ) + #ifdef HAVE_SETRLIMIT + struct rlimit rlimit; + ++#if defined(RLIMIT_NOFILE) && defined(RUNNING_ON_VALGRIND) ++ if (limit == RLIMIT_NOFILE && RUNNING_ON_VALGRIND) ++ return; ++#endif ++ + if (!getrlimit( limit, &rlimit )) + { + rlimit.rlim_cur = rlimit.rlim_max; +@@ -907,6 +922,34 @@ jint JNI_OnLoad( JavaVM *vm, void *reserved ) + #endif /* __ANDROID__ */ + + /*********************************************************************** ++ * set_rttime_limit ++ * ++ * set a limit on the cpu time used ++ */ ++static void set_rttime_limit(void) ++{ ++#if defined(HAVE_SETRLIMIT) && defined(__linux__) ++#ifndef RLIMIT_RTTIME ++#define RLIMIT_RTTIME 15 ++#endif ++ struct rlimit rlimit; ++ ++ if (!getrlimit( RLIMIT_RTTIME, &rlimit )) ++ { ++ /* 1000 ms maximum realtime before the first SIGXCPU, this will drop ++ * all realtime threads to normal priority. ++ */ ++ if (rlimit.rlim_max > 5000000) ++ rlimit.rlim_max = 5000000; ++ rlimit.rlim_cur = 1000000; ++ ++ setrlimit( RLIMIT_RTTIME, &rlimit ); ++ } ++#endif ++} ++ ++ ++/*********************************************************************** + * wine_init + * + * Main Wine initialisation. +@@ -925,6 +968,7 @@ void wine_init( int argc, char *argv[], char *error, int error_size ) + #ifdef RLIMIT_AS + set_max_limit( RLIMIT_AS ); + #endif ++ set_rttime_limit(); + + wine_init_argv0_path( argv[0] ); + build_dll_path(); +diff --git a/libs/wine/wine.map b/libs/wine/wine.map +index 2159fac..fb3b951 100644 +--- a/libs/wine/wine.map ++++ b/libs/wine/wine.map +@@ -117,6 +117,12 @@ WINE_1.0 + wine_utf8_mbstowcs; + wine_utf8_wcstombs; + wine_wctype_table; ++ __glob_pthread_create; ++ call_pthread_create; ++ __glob_pthread_join; ++ call_pthread_join; ++ __glob_pthread_detach; ++ call_pthread_detach; + + local: *; + }; +diff --git a/loader/Makefile.in b/loader/Makefile.in +index 95e4798..a18dd02 100644 +--- a/loader/Makefile.in ++++ b/loader/Makefile.in +@@ -1,4 +1,4 @@ +-EXTRALIBS = $(PTHREAD_LIBS) ++EXTRALIBS = $(PTHREAD_LIBS) $(DL_LIBS) + + C_SRCS = \ + main.c \ +diff --git a/loader/main.c b/loader/main.c +index ac67290..76609e1 100644 +--- a/loader/main.c ++++ b/loader/main.c +@@ -202,6 +202,45 @@ static int pre_exec(void) + + #endif + ++#ifdef __linux__ ++ ++extern typeof(pthread_create) *call_pthread_create, *__glob_pthread_create; ++extern typeof(pthread_detach) *call_pthread_detach, *__glob_pthread_detach; ++extern typeof(pthread_join) *call_pthread_join, *__glob_pthread_join; ++ ++int pthread_create(pthread_t *thread, const pthread_attr_t *attr, ++ void *(*start_routine) (void *), void *arg) ++{ ++ return call_pthread_create(thread, attr, start_routine, arg); ++} ++ ++int pthread_detach(pthread_t thread) ++{ ++ return call_pthread_detach(thread); ++} ++ ++int pthread_join(pthread_t thread, void **retval) ++{ ++ return call_pthread_join(thread, retval); ++} ++ ++static void init_thread_hook(void) { ++ call_pthread_create = __glob_pthread_create = dlvsym(RTLD_NEXT, "pthread_create", "GLIBC_2.2.5"); ++ if (!__glob_pthread_create) ++ call_pthread_create = __glob_pthread_create = dlvsym(RTLD_NEXT, "pthread_create", "GLIBC_2.1"); ++ ++ call_pthread_detach = __glob_pthread_detach = dlsym(RTLD_NEXT, "pthread_detach"); ++ call_pthread_join = __glob_pthread_join = dlsym(RTLD_NEXT, "pthread_join"); ++ ++ /* Call a function from libpthread to ensure being linked against it */ ++ pthread_yield(); ++} ++ ++#else ++ ++#define init_thread_hook() ++ ++#endif + + /********************************************************************** + * main +@@ -211,6 +250,8 @@ int main( int argc, char *argv[] ) + char error[1024]; + int i; + ++ init_thread_hook(); ++ + if (!getenv( "WINELOADERNOEXEC" )) /* first time around */ + { + static char noexec[] = "WINELOADERNOEXEC=1"; +diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c +index 4675369..84e793d 100644 +--- a/programs/winemenubuilder/winemenubuilder.c ++++ b/programs/winemenubuilder/winemenubuilder.c +@@ -2535,7 +2535,7 @@ static BOOL write_freedesktop_association_entry(const char *desktopPath, const c + fclose(desktop); + } + else +- WINE_ERR("error writing association file %s\n", wine_dbgstr_a(desktopPath)); ++ WINE_WARN("error writing association file %s\n", wine_dbgstr_a(desktopPath)); + return ret; + } + +diff --git a/server/Makefile.in b/server/Makefile.in +index 19a4fac..76bdb96 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -1,4 +1,5 @@ + EXTRALIBS = $(POLL_LIBS) $(RT_LIBS) ++EXTRAINCL = $(DBUS_CFLAGS) + + C_SRCS = \ + async.c \ +@@ -30,6 +31,7 @@ C_SRCS = \ + region.c \ + registry.c \ + request.c \ ++ rtkit.c \ + semaphore.c \ + serial.c \ + signal.c \ +diff --git a/server/main.c b/server/main.c +index 7aed338..f78c0fb 100644 +--- a/server/main.c ++++ b/server/main.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #ifdef HAVE_GETOPT_H + # include + #endif +@@ -38,6 +39,8 @@ + #include "request.h" + #include "wine/library.h" + ++extern int rtkit_make_realtime(struct thread *thread, pid_t tid, int priority); ++ + /* command-line options */ + int debug_level = 0; + int foreground = 0; +@@ -145,6 +148,7 @@ int main( int argc, char *argv[] ) + init_signals(); + init_directories(); + init_registry(); ++ rtkit_make_realtime(NULL, syscall( SYS_gettid ), 3); + main_loop(); + return 0; + } +diff --git a/server/rtkit.c b/server/rtkit.c +new file mode 100644 +index 0000000..53e5ce4 +--- /dev/null ++++ b/server/rtkit.c +@@ -0,0 +1,292 @@ ++/* ++ * Rtkit dbus calls ++ * Copyright 2010 Maarten Lankhorst 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 ++ */ ++ ++#include "config.h" ++#include "wine/port.h" ++#include "wine/library.h" ++ ++#include ++#include ++#ifdef HAVE_SYS_SCHED_H ++#include ++#endif ++#include ++ ++#if defined(HAVE_SETRLIMIT) && defined(__linux__) && defined(SONAME_LIBDBUS_1) && defined(HAVE_SCHED_H) ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "object.h" ++#include "thread.h" ++ ++#ifndef RLIMIT_RTTIME ++#define RLIMIT_RTTIME 15 ++#endif ++ ++#define FUNCPTR(fn) static typeof(fn) *p ##fn ++ ++FUNCPTR(dbus_error_init); ++FUNCPTR(dbus_error_free); ++FUNCPTR(dbus_bus_get); ++FUNCPTR(dbus_message_new_method_call); ++FUNCPTR(dbus_message_append_args); ++FUNCPTR(dbus_connection_send_with_reply_and_block); ++FUNCPTR(dbus_message_unref); ++FUNCPTR(dbus_set_error_from_message); ++#undef FUNCPTR ++ ++static struct list rt_thread_list = LIST_INIT(rt_thread_list); ++ ++static int translate_error( unsigned tid, const char *name ) ++{ ++ if (!strcmp( name, DBUS_ERROR_NO_MEMORY )) ++ return -ENOMEM; ++ if (!strcmp( name, DBUS_ERROR_SERVICE_UNKNOWN ) || ++ !strcmp( name, DBUS_ERROR_NAME_HAS_NO_OWNER )) ++ return -ENOENT; ++ if (!strcmp( name, DBUS_ERROR_ACCESS_DENIED ) || ++ !strcmp( name, DBUS_ERROR_AUTH_FAILED )) ++ return -EACCES; ++ ++ if (debug_level) ++ fprintf( stderr, "%04x: Could not map error \"%s\"\n", tid, name ); ++ return -EIO; ++} ++ ++static void init_dbus(void) ++{ ++#define FUNCPTR(fn) p ##fn = wine_dlsym( libdbus, #fn, NULL, 0 ); ++ char error[512]; ++ void *libdbus = wine_dlopen( SONAME_LIBDBUS_1, RTLD_NOW, error, sizeof( error ) ); ++ FUNCPTR(dbus_error_init); ++ FUNCPTR(dbus_error_free); ++ FUNCPTR(dbus_bus_get); ++ FUNCPTR(dbus_message_new_method_call); ++ FUNCPTR(dbus_message_append_args); ++ FUNCPTR(dbus_connection_send_with_reply_and_block); ++ FUNCPTR(dbus_message_unref); ++ FUNCPTR(dbus_set_error_from_message); ++#undef FUNCPTR ++} ++ ++#define MSG_SIGXCPU "wineserver: SIGXCPU called on wineserver from kernel, realtime priority removed!\n" ++ ++static int sched_normal(struct thread *cur) ++{ ++ int ret = 0; ++ ++ if (cur->unix_tid != -1) { ++ struct sched_param parm; ++ memset( &parm, 0, sizeof( parm ) ); ++ ret = sched_setscheduler(cur->unix_tid, SCHED_OTHER | SCHED_RESET_ON_FORK, &parm); ++ if (ret < 0) ++ ret = -errno; ++ } ++ ++ list_remove(&cur->rt_entry); ++ list_init(&cur->rt_entry); ++ cur->rt_prio = 0; ++ cur->priority = 0; ++ return ret; ++} ++ ++static void sigxcpu_handler(int sig, siginfo_t *si, void *ucontext) ++{ ++ struct thread *cur, *tmp; ++ int found = 0; ++ int old_errno = errno; ++ ++ if (si->si_code & SI_KERNEL) { ++ struct sched_param parm; ++ memset( &parm, 0, sizeof( parm ) ); ++ ++ sched_setscheduler(syscall( SYS_gettid ), SCHED_OTHER | SCHED_RESET_ON_FORK, &parm); ++ ++ write(2, MSG_SIGXCPU, sizeof(MSG_SIGXCPU)-1); ++ goto restore_errno; ++ } ++ ++ LIST_FOR_EACH_ENTRY_SAFE(cur, tmp, &rt_thread_list, struct thread, rt_entry) ++ { ++ if (si->si_pid == cur->unix_pid && cur->rt_prio == 1) { ++ found = 1; ++ sched_normal(cur); ++ } ++ } ++ ++ if (!found) { ++ LIST_FOR_EACH_ENTRY_SAFE(cur, tmp, &rt_thread_list, struct thread, rt_entry) ++ { ++ if (si->si_pid == cur->unix_pid) ++ sched_normal(cur); ++ } ++ } ++ ++restore_errno: ++ errno = old_errno; ++} ++ ++static void setup_rt(void) ++{ ++ struct sigaction sa; ++ struct rlimit rlimit; ++ ++ sa.sa_sigaction = sigxcpu_handler; ++ sigemptyset(&sa.sa_mask); ++ sa.sa_flags = SA_SIGINFO; ++ sigaction(SIGXCPU, &sa, NULL); ++ ++ if (!getrlimit( RLIMIT_RTTIME, &rlimit )) ++ { ++ /* wineserver can run for 1.5 seconds continuously at realtime before ++ * it gets throttled down. At this point we probably hit a bug ++ * somewhere. ++ */ ++ if (rlimit.rlim_max > 2000000) ++ rlimit.rlim_max = 2000000; ++ if (rlimit.rlim_cur > 1500000) ++ rlimit.rlim_cur = 1500000; ++ ++ setrlimit( RLIMIT_RTTIME, &rlimit ); ++ } ++} ++ ++static DBusConnection *get_dbus(void) ++{ ++ static DBusConnection *bus; ++ DBusError error; ++ ++ if (bus) ++ return bus; ++ init_dbus(); ++ pdbus_error_init( &error ); ++ ++ bus = pdbus_bus_get( DBUS_BUS_SYSTEM, &error ); ++ setup_rt(); ++ return bus; ++} ++ ++int rtkit_make_realtime( struct thread *thread, pid_t unix_tid, int priority ) ++{ ++ DBusConnection *bus; ++ DBusMessage *m = NULL, *r = NULL; ++ dbus_uint64_t pid = thread ? thread->unix_pid : getpid(); ++ dbus_uint64_t tid = unix_tid; ++ dbus_uint32_t rtprio = priority; ++ sigset_t sigset; ++ DBusError error; ++ int ret; ++ ++ bus = get_dbus(); ++ if (!bus) ++ return -ENOTSUP; ++ ++ pdbus_error_init( &error ); ++ m = pdbus_message_new_method_call( "org.freedesktop.RealtimeKit1", ++ "/org/freedesktop/RealtimeKit1", ++ "org.freedesktop.RealtimeKit1", ++ "MakeThreadRealtimeWithPID" ); ++ if (!m) ++ { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ ret = pdbus_message_append_args( m, DBUS_TYPE_UINT64, &pid, ++ DBUS_TYPE_UINT64, &tid, ++ DBUS_TYPE_UINT32, &rtprio, ++ DBUS_TYPE_INVALID ); ++ if (!ret) ++ { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ sigemptyset( &sigset ); ++ sigaddset( &sigset, SIGXCPU ); ++ sigprocmask( SIG_BLOCK, &sigset, NULL ); ++ ++ r = pdbus_connection_send_with_reply_and_block( bus, m, -1, &error ); ++ if (!r) ++ { ++ ret = translate_error( tid, error.name ); ++ goto out_unblock; ++ } ++ if (pdbus_set_error_from_message( &error, r )) ++ ret = translate_error( tid, error.name ); ++ else { ++ ret = 0; ++ if (thread) { ++ if (list_empty(&thread->rt_entry)) ++ list_add_tail( &rt_thread_list, &thread->rt_entry ); ++ thread->rt_prio = rtprio; ++ } ++ } ++out_unblock: ++ sigprocmask( SIG_UNBLOCK, &sigset, NULL ); ++out: ++ if (m) ++ pdbus_message_unref( m ); ++ if (r) ++ pdbus_message_unref( r ); ++ pdbus_error_free( &error ); ++ if (debug_level) ++ fprintf( stderr, "%04x: Setting realtime priority of %u returns %i %m\n", (int)tid, rtprio, ret ); ++ return ret; ++} ++ ++int rtkit_undo_realtime( struct thread *thread ) ++{ ++ sigset_t sigset; ++ int ret = 0; ++ ++ sigemptyset( &sigset ); ++ sigaddset( &sigset, SIGXCPU ); ++ sigprocmask( SIG_BLOCK, &sigset, NULL ); ++ ++ if (!list_empty(&thread->rt_entry)) ++ ret = sched_normal(thread); ++ ++ if (debug_level) ++ fprintf( stderr, "%04x: Removing realtime priority of %u returns %i %m\n", ++ (int)thread->unix_tid, thread->rt_prio, ret ); ++ ++ sigprocmask( SIG_UNBLOCK, &sigset, NULL ); ++ return ret < 0 ? -errno : 0; ++} ++ ++#else ++ ++int rtkit_make_realtime( struct thread *thread, pid_t unix_tid, int priority ) ++{ ++ return -ENOTSUP; ++} ++ ++int rtkit_undo_realtime( struct thread *thread ) ++{ ++ return -ENOTSUP; ++} ++ ++#endif +diff --git a/server/thread.c b/server/thread.c +index 50d6940..778015e 100644 +--- a/server/thread.c ++++ b/server/thread.c +@@ -52,6 +52,8 @@ + #include "user.h" + #include "security.h" + ++extern int rtkit_make_realtime(struct thread *thread, pid_t tid, int priority); ++extern int rtkit_undo_realtime(struct thread *thread); + + #ifdef __i386__ + static const unsigned int supported_cpus = CPU_FLAG(CPU_x86); +@@ -201,6 +203,8 @@ static inline void init_thread_structure( struct thread *thread ) + list_init( &thread->mutex_list ); + list_init( &thread->system_apc ); + list_init( &thread->user_apc ); ++ list_init( &thread->rt_entry ); ++ thread->rt_prio = 0; + + for (i = 0; i < MAX_INFLIGHT_FDS; i++) + thread->inflight[i].server = thread->inflight[i].client = -1; +@@ -269,6 +273,9 @@ static void cleanup_thread( struct thread *thread ) + { + int i; + ++ thread->unix_tid = -1; ++ if (!list_empty(&thread->rt_entry)) ++ rtkit_undo_realtime(thread); + clear_apc_queue( &thread->system_apc ); + clear_apc_queue( &thread->user_apc ); + free( thread->req_data ); +@@ -460,6 +467,15 @@ affinity_t get_thread_affinity( struct thread *thread ) + #define THREAD_PRIORITY_REALTIME_HIGHEST 6 + #define THREAD_PRIORITY_REALTIME_LOWEST -7 + ++static int rtprio(int ntprio) ++{ ++ if (ntprio == THREAD_PRIORITY_TIME_CRITICAL - 1) ++ return 1; ++ else if (ntprio == THREAD_PRIORITY_TIME_CRITICAL) ++ return 2; ++ return 0; ++} ++ + /* set all information about a thread */ + static void set_thread_info( struct thread *thread, + const struct set_thread_info_request *req ) +@@ -475,8 +491,22 @@ static void set_thread_info( struct thread *thread, + } + if ((req->priority >= min && req->priority <= max) || + req->priority == THREAD_PRIORITY_IDLE || ++ req->priority == THREAD_PRIORITY_TIME_CRITICAL - 1 || + req->priority == THREAD_PRIORITY_TIME_CRITICAL) +- thread->priority = req->priority; ++ { ++ int newprio = rtprio(req->priority); ++ if (thread->unix_tid == -1) ++ thread->rt_prio = newprio; ++ else if (thread->priority == THREAD_PRIORITY_TIME_CRITICAL && !newprio) ++ rtkit_undo_realtime(thread); ++ else if (thread->rt_prio != newprio) ++ rtkit_make_realtime(thread, thread->unix_tid, newprio); ++ ++ if (newprio) ++ thread->priority = THREAD_PRIORITY_TIME_CRITICAL; ++ else ++ thread->priority = req->priority; ++ } + else + set_error( STATUS_INVALID_PARAMETER ); + } +@@ -1301,6 +1331,10 @@ DECL_HANDLER(init_thread) + } + debug_level = max( debug_level, req->debug_level ); + ++ /* Raced with SetThreadPriority */ ++ if (current->priority == THREAD_PRIORITY_TIME_CRITICAL) ++ rtkit_make_realtime(current, current->unix_tid, current->rt_prio); ++ + reply->pid = get_process_id( process ); + reply->tid = get_thread_id( current ); + reply->version = SERVER_PROTOCOL_VERSION; +diff --git a/server/thread.h b/server/thread.h +index 996d95b..36f09ce 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -87,6 +87,8 @@ struct thread + timeout_t creation_time; /* Thread creation time */ + timeout_t exit_time; /* Thread exit time */ + struct token *token; /* security token associated with this thread */ ++ struct list rt_entry; /* entry for member in linked realtime list */ ++ int rt_prio; /* current realtime thread priority */ + }; + + struct thread_snapshot +-- +2.0.1 +