added PulseAudio sound driver Thanks to Arthur Talyor

=3D?utf-8?q?R=3DC3=3DA9mi=3D20Assailly?=3D remi.assailly at free.fr
Wed Apr 8 14:38:23 CDT 2009


---
 configure                             |   50 ++-
 configure.ac                          |   22 +-
 dlls/winepulse.drv/Makefile.in        |   16 +
 dlls/winepulse.drv/dsoutput.c         |  576 +++++++++++++++++
 dlls/winepulse.drv/pulse.c            |  861 ++++++++++++++++++++++++++
 dlls/winepulse.drv/wavein.c           |  572 +++++++++++++++++
 dlls/winepulse.drv/waveout.c          | 1092 +++++++++++++++++++++++++++++=
++++
 dlls/winepulse.drv/winepulse.drv.spec |    3 +
 dlls/winepulse.drv/winepulse.h        |  228 +++++++
 include/config.h.in                   |    3 +
 programs/winecfg/Bg.rc                |    1 +
 programs/winecfg/Cs.rc                |    1 +
 programs/winecfg/Da.rc                |    1 +
 programs/winecfg/De.rc                |    1 +
 programs/winecfg/En.rc                |    1 +
 programs/winecfg/Es.rc                |    1 +
 programs/winecfg/Fi.rc                |    1 +
 programs/winecfg/Fr.rc                |    1 +
 programs/winecfg/Hu.rc                |    1 +
 programs/winecfg/Ja.rc                |    1 +
 programs/winecfg/Ko.rc                |    1 +
 programs/winecfg/Nl.rc                |    1 +
 programs/winecfg/No.rc                |    1 +
 programs/winecfg/Pl.rc                |    1 +
 programs/winecfg/Pt.rc                |    1 +
 programs/winecfg/Ro.rc                |    1 +
 programs/winecfg/Ru.rc                |    1 +
 programs/winecfg/Si.rc                |    1 +
 programs/winecfg/Sv.rc                |    1 +
 programs/winecfg/Tr.rc                |    1 +
 programs/winecfg/Zh.rc                |    1 +
 programs/winecfg/audio.c              |    1 +
 programs/winecfg/libraries.c          |    1 +
 programs/winecfg/resource.h           |    2 +-
 34 files changed, 3442 insertions(+), 6 deletions(-)
 create mode 100644 dlls/winepulse.drv/Makefile.in
 create mode 100644 dlls/winepulse.drv/dsoutput.c
 create mode 100644 dlls/winepulse.drv/pulse.c
 create mode 100644 dlls/winepulse.drv/wavein.c
 create mode 100644 dlls/winepulse.drv/waveout.c
 create mode 100644 dlls/winepulse.drv/winepulse.drv.spec
 create mode 100644 dlls/winepulse.drv/winepulse.h

diff --git a/configure b/configure
index 3e4d8d6..1296630 100755
--- a/configure
+++ b/configure
@@ -665,6 +665,7 @@ FONTCONFIGINCL
 CUPSINCL
 AUDIOIOLIBS
 ALSALIBS
+PULSELIBS
 ESDLIBS
 ESDINCL
 ESDCONFIG
@@ -841,6 +842,7 @@ with_openssl
 with_oss
 with_png
 with_pthread
+with_pulse
 with_sane
 with_xcomposite
 with_xcursor
@@ -1531,6 +1533,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-xcomposite    do not use the Xcomposite extension
   --without-xcursor       do not use the Xcursor extension
@@ -2206,6 +2209,12 @@ if test "${with_pthread+set}" =3D set; then
 fi
=20
=20
+# Check whether --with-pulse was given.
+if test "${with_pulse+set}" =3D set; then
+  withval=3D$with_pulse;
+fi
+
+
 # Check whether --with-sane was given.
 if test "${with_sane+set}" =3D set; then
   withval=3D$with_sane;
@@ -15610,6 +15619,30 @@ fi
     CFLAGS=3D"$save_CFLAGS"
 fi
=20
+if test "x$with_pulse" !=3D "xno"; then
+    if test "$PKG_CONFIG" !=3D "false"; then
+=09{ $as_echo "$as_me:$LINENO: checking for pulseaudio >=3D 0.9.8" >&5
+$as_echo_n "checking for pulseaudio >=3D 0.9.8... " >&6; }
+=09if "$PKG_CONFIG" --atleast-version=3D0.9.8 libpulse; then
+=09    have_pulseaudio=3D"yes"
+        else
+=09    have_pulseaudio=3D"no"
+=09fi
+=09{ $as_echo "$as_me:$LINENO: result: $have_pulseaudio" >&5
+$as_echo "$have_pulseaudio" >&6; }
+=09if test x"$have_pulseaudio" =3D xyes; then
+=09    ac_pulse_libs=3D`$PKG_CONFIG --libs libpulse`
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PULSEAUDIO 1
+_ACEOF
+
+=09    PULSELIBS=3D"$ac_pulse_libs"
+
+=09fi
+    fi
+fi
+
 ALSALIBS=3D""
=20
 if test "$ac_cv_header_sys_asoundlib_h" =3D "yes" -o "$ac_cv_header_alsa_a=
soundlib_h" =3D "yes"
@@ -17138,7 +17171,7 @@ _ACEOF
 fi
=20
=20
-if test "x$ALSALIBS$AUDIOIOLIBS$COREAUDIO$NASLIBS$ESDLIBS$ac_cv_lib_soname=
_jack" =3D "x" -a \
+if test "x$ALSALIBS$AUDIOIOLIBS$COREAUDIO$NASLIBS$ESDLIBS$PULSELIBS$ac_cv_=
lib_soname_jack" =3D "x" -a \
         "$ac_cv_header_sys_soundcard_h" !=3D "yes" -a \
         "$ac_cv_header_machine_soundcard_h" !=3D "yes" -a \
         "$ac_cv_header_soundcard_h" !=3D "yes" -a \
@@ -27411,6 +27444,14 @@ dlls/wineps.drv/Makefile: dlls/wineps.drv/Makefile=
.in dlls/Makedll.rules"
 ac_config_files=3D"$ac_config_files dlls/wineps.drv/Makefile"
=20
 ALL_MAKEFILES=3D"$ALL_MAKEFILES \\
+=09dlls/winepulse.drv/Makefile"
+test "x$enable_winepulse_drv" !=3D xno && ALL_DLL_DIRS=3D"$ALL_DLL_DIRS \\
+=09winepulse.drv"
+ALL_MAKEFILE_DEPENDS=3D"$ALL_MAKEFILE_DEPENDS
+dlls/winepulse.drv/Makefile: dlls/winepulse.drv/Makefile.in dlls/Makedll.r=
ules"
+ac_config_files=3D"$ac_config_files dlls/winepulse.drv/Makefile"
+
+ALL_MAKEFILES=3D"$ALL_MAKEFILES \\
 =09dlls/winequartz.drv/Makefile"
 test "x$enable_winequartz_drv" !=3D xno && ALL_DLL_DIRS=3D"$ALL_DLL_DIRS \=
\
 =09winequartz.drv"
@@ -28720,9 +28761,9 @@ esac
=20
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=3D1
 # Files that config.status was made for.
-config_files=3D"$ac_config_files"
-config_headers=3D"$ac_config_headers"
-config_commands=3D"$ac_config_commands"
+config_files=3D"`echo $ac_config_files`"
+config_headers=3D"`echo $ac_config_headers`"
+config_commands=3D"`echo $ac_config_commands`"
=20
 _ACEOF
=20
@@ -29280,6 +29321,7 @@ do
     "dlls/winenas.drv/Makefile") CONFIG_FILES=3D"$CONFIG_FILES dlls/winena=
s.drv/Makefile" ;;
     "dlls/wineoss.drv/Makefile") CONFIG_FILES=3D"$CONFIG_FILES dlls/wineos=
s.drv/Makefile" ;;
     "dlls/wineps.drv/Makefile") CONFIG_FILES=3D"$CONFIG_FILES dlls/wineps.=
drv/Makefile" ;;
+    "dlls/winepulse.drv/Makefile") CONFIG_FILES=3D"$CONFIG_FILES dlls/wine=
pulse.drv/Makefile" ;;
     "dlls/winequartz.drv/Makefile") CONFIG_FILES=3D"$CONFIG_FILES dlls/win=
equartz.drv/Makefile" ;;
     "dlls/winex11.drv/Makefile") CONFIG_FILES=3D"$CONFIG_FILES dlls/winex1=
1.drv/Makefile" ;;
     "dlls/wing32/Makefile") CONFIG_FILES=3D"$CONFIG_FILES dlls/wing32/Make=
file" ;;
diff --git a/configure.ac b/configure.ac
index e6d9688..0a9ff96 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,6 +58,7 @@ AC_ARG_WITH(png,       AS_HELP_STRING([--without-png],[do=
 not use PNG]),
             [if test "x$withval" =3D "xno"; then ac_cv_header_png_h=3Dno; =
fi])
 AC_ARG_WITH(pthread,   AS_HELP_STRING([--without-pthread],[do not use the =
pthread library]),
             [if test "x$withval" =3D "xno"; then ac_cv_header_pthread_h=3D=
no; fi])
+AC_ARG_WITH(pulse,     AC_HELP_STRING([--without-pulse],[do not use PulseA=
udio sound support]))
 AC_ARG_WITH(sane,      AS_HELP_STRING([--without-sane],[do not use SANE (s=
canner support)]))
 AC_ARG_WITH(xcomposite,AS_HELP_STRING([--without-xcomposite],[do not use t=
he Xcomposite extension]),
             [if test "x$withval" =3D "xno"; then ac_cv_header_X11_extensio=
ns_Xcomposite_h=3Dno; fi])
@@ -1183,6 +1184,24 @@ then
     CFLAGS=3D"$save_CFLAGS"
 fi
=20
+dnl **** Check for PulseAudio ****
+if test "x$with_pulse" !=3D "xno"; then
+    if test "$PKG_CONFIG" !=3D "false"; then
+=09AC_MSG_CHECKING([for pulseaudio >=3D 0.9.8])
+=09if "$PKG_CONFIG" --atleast-version=3D0.9.8 libpulse; then
+=09    have_pulseaudio=3D"yes"
+        else
+=09    have_pulseaudio=3D"no"
+=09fi
+=09AC_MSG_RESULT([$have_pulseaudio])
+=09if test x"$have_pulseaudio" =3D xyes; then
+=09    ac_pulse_libs=3D`$PKG_CONFIG --libs libpulse`
+=09    AC_DEFINE([HAVE_PULSEAUDIO], 1, [define this if you have pulseaudio=
])
+=09    AC_SUBST(PULSELIBS, "$ac_pulse_libs")
+=09fi
+    fi
+fi
+
 dnl **** Check for ALSA 1.x ****
 AC_SUBST(ALSALIBS,"")
 if test "$ac_cv_header_sys_asoundlib_h" =3D "yes" -o "$ac_cv_header_alsa_a=
soundlib_h" =3D "yes"
@@ -1296,7 +1315,7 @@ dnl **** Check for libodbc ****
 WINE_CHECK_SONAME(odbc,SQLConnect,,[AC_DEFINE_UNQUOTED(SONAME_LIBODBC,["li=
bodbc.$LIBEXT"])])
=20
 dnl **** Check for any sound system ****
-if test "x$ALSALIBS$AUDIOIOLIBS$COREAUDIO$NASLIBS$ESDLIBS$ac_cv_lib_soname=
_jack" =3D "x" -a \
+if test "x$ALSALIBS$AUDIOIOLIBS$COREAUDIO$NASLIBS$ESDLIBS$PULSELIBS$ac_cv_=
lib_soname_jack" =3D "x" -a \
         "$ac_cv_header_sys_soundcard_h" !=3D "yes" -a \
         "$ac_cv_header_machine_soundcard_h" !=3D "yes" -a \
         "$ac_cv_header_soundcard_h" !=3D "yes" -a \
@@ -2255,6 +2274,7 @@ WINE_CONFIG_MAKEFILE([dlls/winemp3.acm/Makefile],[dll=
s/Makedll.rules],[dlls],[AL
 WINE_CONFIG_MAKEFILE([dlls/winenas.drv/Makefile],[dlls/Makedll.rules],[dll=
s],[ALL_DLL_DIRS])
 WINE_CONFIG_MAKEFILE([dlls/wineoss.drv/Makefile],[dlls/Makedll.rules],[dll=
s],[ALL_DLL_DIRS])
 WINE_CONFIG_MAKEFILE([dlls/wineps.drv/Makefile],[dlls/Makedll.rules],[dlls=
],[ALL_DLL_DIRS])
+WINE_CONFIG_MAKEFILE([dlls/winepulse.drv/Makefile],[dlls/Makedll.rules],[d=
lls],[ALL_DLL_DIRS])
 WINE_CONFIG_MAKEFILE([dlls/winequartz.drv/Makefile],[dlls/Makedll.rules],[=
dlls],[ALL_DLL_DIRS])
 WINE_CONFIG_MAKEFILE([dlls/winex11.drv/Makefile],[dlls/Makedll.rules],[dll=
s],[ALL_DLL_DIRS])
 WINE_CONFIG_MAKEFILE([dlls/wing32/Makefile],[dlls/Makedll.rules],[dlls],[A=
LL_DLL_DIRS])
diff --git a/dlls/winepulse.drv/Makefile.in b/dlls/winepulse.drv/Makefile.i=
n
new file mode 100644
index 0000000..327f225
--- /dev/null
+++ b/dlls/winepulse.drv/Makefile.in
@@ -0,0 +1,16 @@
+TOPSRCDIR =3D @top_srcdir@
+TOPOBJDIR =3D ../..
+SRCDIR    =3D @srcdir@
+VPATH     =3D @srcdir@
+MODULE    =3D winepulse.drv
+IMPORTS   =3D dxguid uuid winmm user32 advapi32 kernel32
+EXTRALIBS =3D @PULSELIBS@
+
+C_SRCS =3D dsoutput.c \
+=09waveout.c \
+=09wavein.c \
+=09pulse.c
+
+ at MAKE_DLL_RULES@
+
+ at DEPENDENCIES@  # everything below this line is overwritten by make depend
diff --git a/dlls/winepulse.drv/dsoutput.c b/dlls/winepulse.drv/dsoutput.c
new file mode 100644
index 0000000..b37313a
--- /dev/null
+++ b/dlls/winepulse.drv/dsoutput.c
@@ -0,0 +1,576 @@
+/*
+ * Wine Driver for PulseAudio - DSound Output Functionality
+ * http://pulseaudio.org/
+ *
+ * Copyright=092009 Arthur Talyor <theycallhimart at gmail.com>
+ *
+ * Conatins code from other wine multimedia drivers.
+ *
+ * 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, U=
SA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "mmddk.h"
+#include "dsound.h"
+#include "dsdriver.h"
+#include "winreg.h"
+
+#include <winepulse.h>
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(dspulse);
+
+#if HAVE_PULSEAUDIO
+
+/*=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*
+ *                  Low level DSOUND implementation=09=09=09*
+ *=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*/
+
+/* A buffer is allocated with a pointer indicating the read position in th=
e
+ * buffer. The pulse write callback reads data from the buffer, updating t=
he
+ * read pointer to the location at the end of the read. Upon reaching the =
end
+ * or attempting to read past the end of buffer the read pointer wraps aro=
und
+ * to the beginning again. DirectSound applications can write anywhere in =
the
+ * buffer at anytime without locking and can know the location of the read
+ * pointer. The position of the read pointer cannot be changed by the
+ * application and access to it uses a locking scheme. A fake pointer
+ * indicating estimated playback position is also available to the applica=
tion.
+ * Applications can potentially write to the same area of memory which is =
also
+ * being read by the pulse thread. However, this is uncommon as directsoun=
d
+ * applications know where pulse should be reading from via the pointer
+ * locations and MSDN says that such an operation should be avoided with t=
he
+ * results being undefined.
+ */
+
+/* Fragment lengths try to be a power of two close to 10ms worth of data. =
See
+ * dlls/dsound/mixer.c
+ */
+static int fragment_length(pa_sample_spec *s) {
+    if (s->rate <=3D 12800)
+        return 128 * pa_frame_size(s);
+
+    if (s->rate <=3D 25600)
+        return 256 * pa_frame_size(s);
+
+    if (s->rate <=3D 51200)
+        return 512 * pa_frame_size(s);
+
+    return 1024 * pa_frame_size(s);
+}
+
+/* Callback from the pulse thread for stream data */
+static void DSPULSE_BufferReadCallback(pa_stream *s, size_t nbytes, void *=
userdata) {
+    IDsDriverBufferImpl *This =3D (IDsDriverBufferImpl *)userdata;
+
+    if (!This->buffer)
+        return;
+
+    /* Fraglens are always powers of 2 */
+    nbytes+=3D This->fraglen - 1;
+    nbytes&=3D ~(This->fraglen - 1);
+
+    /* If we advance more than 10 fragments at a time it appears that the =
buffer
+     * pointer is never advancing because of wrap-around. Evil magic numbe=
rs. */
+    if (nbytes > This->fraglen * 5)
+        nbytes =3D This->fraglen * 5;
+
+    TRACE("Reading %u bytes.\n", nbytes);
+
+    if (This->buffer_read_offset + nbytes <=3D This->buffer_length) {
+        pa_stream_write(s, This->buffer + This->buffer_read_offset, nbytes=
, NULL, 0, PA_SEEK_RELATIVE);
+        This->buffer_play_offset =3D This->buffer_read_offset;
+        This->buffer_read_offset +=3D nbytes;
+    } else {
+        size_t write_length =3D This->buffer_length - This->buffer_read_of=
fset;
+        nbytes -=3D write_length;
+        pa_stream_write(s, This->buffer + This->buffer_read_offset, write_=
length, NULL, 0, PA_SEEK_RELATIVE);
+        pa_stream_write(s, This->buffer, nbytes, NULL, 0, PA_SEEK_RELATIVE=
);
+        This->buffer_play_offset =3D This->buffer_read_offset;
+        This->buffer_read_offset =3D nbytes;
+    }
+
+    This->buffer_read_offset %=3D This->buffer_length;
+}
+
+/* Called when the stream underruns. Just for information */
+static void DSPULSE_BufferUnderflowCallback(pa_stream *s, void *userdata) =
{
+    WARN("(%p) underrun.\n", userdata);
+}
+
+/* Connects a stream to the server. Does not update
+ * IDsDriverBufferImpl->fraglen. Does not lock the pulse mainloop or free
+ * objects in case of failure. This should be handled by the calling funct=
ion.
+ */
+static HRESULT DSPULSE_ConnectStream(IDsDriverBufferImpl* This) {
+    pa_buffer_attr ba_request;
+    const pa_buffer_attr *ba_obtained;
+    char c[PA_SAMPLE_SPEC_SNPRINT_MAX];
+    pa_stream_flags_t stream_flags =3D PA_STREAM_START_CORKED;
+
+#if PA_PROTOCOL_VERSION >=3D 14
+    /* We are a "fragment wait based" application, so this flag should be
+     * acceptable. */
+    stream_flags |=3D PA_STREAM_EARLY_REQUESTS;
+#elif PA_PROTOCOL_VERSION >=3D 13 && PA_API_VERSION >=3D 12
+    stream_flags |=3D PA_STREAM_ADJUST_LATENCY;
+#endif
+
+    pa_sample_spec_snprint(c, PA_SAMPLE_SPEC_SNPRINT_MAX, &This->sample_sp=
ec);
+    TRACE("Sample spec %s fragment size %u.\n", c, This->fraglen);
+
+    ba_request.tlength =3D This->fraglen * 4;     //  ~40ms
+    ba_request.minreq =3D This->fraglen;          //  ~10ms
+    ba_request.prebuf =3D (uint32_t)-1;           //  same as tlength
+    ba_request.maxlength =3D This->buffer_length; //  2^x =3D ~3s
+
+    TRACE("Asking for buffer tlength:%u (%llums) minreq:%u (%llums)\n",
+        ba_request.tlength, pa_bytes_to_usec(ba_request.tlength, &This->sa=
mple_spec)/1000,
+        ba_request.minreq, pa_bytes_to_usec(ba_request.minreq, &This->samp=
le_spec)/1000);
+
+    This->stream =3D pa_stream_new(PULSE_context, "DirectSound Buffer", &T=
his->sample_spec, NULL);
+    if (!This->stream) return DSERR_BADFORMAT;
+
+    pa_stream_set_state_callback(This->stream, PULSE_StreamStateCallback, =
This);
+    pa_stream_set_write_callback(This->stream, DSPULSE_BufferReadCallback,=
 This);
+    pa_stream_set_underflow_callback(This->stream, DSPULSE_BufferUnderflow=
Callback, This);
+
+    TRACE("Attempting to connect (%p)->stream for playback on %s\n", This,=
 WOutDev[This->drv->wDevID].device_name);
+    pa_stream_connect_playback(This->stream, WOutDev[This->drv->wDevID].de=
vice_name, &ba_request, stream_flags, NULL, NULL);
+    for (;;) {
+        pa_context_state_t cstate =3D pa_context_get_state(PULSE_context);
+        pa_stream_state_t sstate =3D pa_stream_get_state(This->stream);
+
+        if (cstate =3D=3D PA_CONTEXT_FAILED || cstate =3D=3D PA_CONTEXT_TE=
RMINATED ||
+            sstate =3D=3D PA_STREAM_FAILED || sstate =3D=3D PA_STREAM_TERM=
INATED) {
+            ERR("Failed to connect stream context object: %s\n", pa_strerr=
or(pa_context_errno(PULSE_context)));
+            return DSERR_BUFFERLOST;
+        }
+
+        if (sstate =3D=3D PA_STREAM_READY)
+            break;
+
+        pa_threaded_mainloop_wait(PULSE_ml);
+    }
+    TRACE("(%p)->stream connected for playback.\n", This);
+    ba_obtained =3D pa_stream_get_buffer_attr(This->stream);
+
+    TRACE("Obtained buffer tlength:%u (%llums) minreq:%u (%llums)\n",
+        ba_obtained->tlength, pa_bytes_to_usec(ba_obtained->tlength, &This=
->sample_spec)/1000,
+        ba_obtained->minreq, pa_bytes_to_usec(ba_obtained->minreq, &This->=
sample_spec)/1000);
+
+    return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER =
iface, REFIID riid, LPVOID *ppobj) {
+    /* IDsDriverBufferImpl *This =3D (IDsDriverBufferImpl *)iface; */
+    FIXME("(): stub!\n");
+    return DSERR_UNSUPPORTED;
+}
+
+static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface) {
+    IDsDriverBufferImpl *This =3D (IDsDriverBufferImpl *)iface;
+    ULONG refCount =3D InterlockedIncrement(&This->ref);
+
+    TRACE("(%p)->(ref before=3D%u)\n",This, refCount - 1);
+
+    return refCount;
+}
+
+static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface) {
+    IDsDriverBufferImpl *This =3D (IDsDriverBufferImpl *)iface;
+    ULONG refCount =3D InterlockedDecrement(&This->ref);
+
+    TRACE("(%p)->(ref before=3D%u)\n",This, refCount + 1);
+
+    if (refCount)
+        return refCount;
+
+    TRACE("mmap buffer %p destroyed\n", This->buffer);
+    pa_threaded_mainloop_lock(PULSE_ml);
+    PULSE_WaitForOperation(pa_stream_cork(This->stream, 1, PULSE_StreamSuc=
cessCallback, This));
+    pa_stream_disconnect(This->stream);
+    if (This =3D=3D This->drv->primary)
+        This->drv->primary =3D NULL;
+    HeapFree(GetProcessHeap(), 0, This->buffer);
+    This->buffer =3D NULL;
+    pa_stream_unref(This->stream);
+    This->stream =3D NULL;
+    HeapFree(GetProcessHeap(), 0, This);
+    pa_threaded_mainloop_unlock(PULSE_ml);
+
+    return 0;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
+=09=09=09=09=09       LPVOID*ppvAudio1,LPDWORD pdwLen1,
+=09=09=09=09=09       LPVOID*ppvAudio2,LPDWORD pdwLen2,
+=09=09=09=09=09       DWORD dwWritePosition,DWORD dwWriteLen,
+=09=09=09=09=09       DWORD dwFlags)
+{
+    /* We set DSDDESC_DONTNEEDPRIMARYLOCK so this should never be called *=
/
+    TRACE("(%p): stub", iface);
+    return DSERR_UNSUPPORTED;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
+=09=09=09=09=09=09 LPVOID pvAudio1,DWORD dwLen1,
+=09=09=09=09=09=09 LPVOID pvAudio2,DWORD dwLen2)
+{
+    /* We set DSDDESC_DONTNEEDPRIMARYLOCK so this should never be called *=
/
+    TRACE("(%p): stub", iface);
+    return DSERR_UNSUPPORTED;
+}
+
+/* You cannot change the sample format of a connected stream, so we need t=
o
+ * destroy and re-create the stream if the sample spec is different */
+static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface=
, LPWAVEFORMATEX pwfx) {
+    IDsDriverBufferImpl *This =3D (IDsDriverBufferImpl *)iface;
+    pa_sample_spec old_spec;
+
+    TRACE("(%p, %p)\n", iface, pwfx);
+
+    old_spec.rate =3D This->sample_spec.rate;
+    old_spec.format =3D This->sample_spec.format;
+    old_spec.channels =3D This->sample_spec.channels;
+
+    if (!PULSE_SetupFormat(pwfx, &This->sample_spec))
+        return DSERR_BADFORMAT;
+
+    if (old_spec.rate =3D=3D    This->sample_spec.rate &&
+        old_spec.format =3D=3D  This->sample_spec.format &&
+        old_spec.channels =3D=3D This->sample_spec.channels) {
+        TRACE("same as original sample spec, exiting.\n");
+        return DS_OK;
+    }
+
+    /* If the format doesn't match, return an error and the buffer will be=
 remade */
+    TRACE("Formats don't match, failing causing re-creation.\n");
+    return DSERR_BUFFERLOST;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER if=
ace, DWORD dwFreq)
+{
+    /* IDsDriverBufferImpl *This =3D (IDsDriverBufferImpl *)iface; */
+    /* You can't do for primary buffers anyways */
+    return S_OK;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER if=
ace, PDSVOLUMEPAN pVolPan) {
+    IDsDriverBufferImpl *This =3D (IDsDriverBufferImpl *)iface;
+
+    if (This->sample_spec.channels =3D=3D 2) {
+        This->volume.channels =3D 2;
+        if (pVolPan->lPan < 0) {
+            This->volume.values[0] =3D pa_sw_volume_from_dB(pVolPan->lVolu=
me/-10.0 + pVolPan->lPan/10.0);
+            This->volume.values[1] =3D pa_sw_volume_from_dB(pVolPan->lVolu=
me/-10.0);
+        } else {
+            This->volume.values[0] =3D pa_sw_volume_from_dB(pVolPan->lVolu=
me/-10.0);
+            This->volume.values[1] =3D pa_sw_volume_from_dB(pVolPan->lVolu=
me/-10.0 - pVolPan->lPan/10.0);
+        }
+    } else {
+        WARN("Panning non-stereo streams not supported yet!\n");
+        pa_cvolume_set(&This->volume, This->sample_spec.channels, pa_sw_vo=
lume_from_dB(pVolPan->lVolume/-10.0));
+        /* Would be nice to return DSERR_CONTROLUNAVAIL, but that isn't up=
 to us
+         * here */
+    }
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    if (This->stream && PULSE_context && pa_context_get_state(PULSE_contex=
t) =3D=3D PA_CONTEXT_READY &&
+=09pa_stream_get_state(This->stream) =3D=3D PA_STREAM_READY && pa_cvolume_=
valid(&This->volume))
+        PULSE_WaitForOperation(
+=09    pa_context_set_sink_input_volume(
+=09=09PULSE_context, pa_stream_get_index(This->stream),
+=09=09&This->volume, PULSE_ContextSuccessCallback, This));
+
+    pa_threaded_mainloop_unlock(PULSE_ml);
+
+    return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER ifa=
ce, DWORD dwNewPos)
+{
+    /* IDsDriverBufferImpl *This =3D (IDsDriverBufferImpl *)iface; */
+    /* Not allowed on primary buffers */
+    return DSERR_UNSUPPORTED;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER ifa=
ce,
+=09=09=09=09=09=09      LPDWORD lpdwPlay, LPDWORD lpdwWrite)
+{
+    IDsDriverBufferImpl *This =3D (IDsDriverBufferImpl *)iface;
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    if (!This->buffer || pa_stream_get_state(This->stream) !=3D PA_STREAM_=
READY) {
+        pa_threaded_mainloop_unlock(PULSE_ml);
+        return DSERR_UNINITIALIZED;
+    }
+
+    if (lpdwPlay)
+        *lpdwPlay =3D This->buffer_play_offset;
+    if (lpdwWrite)
+        *lpdwWrite =3D This->buffer_read_offset;
+    pa_threaded_mainloop_unlock(PULSE_ml);
+
+    return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWO=
RD dwRes1, DWORD dwRes2, DWORD dwFlags) {
+    IDsDriverBufferImpl *This =3D (IDsDriverBufferImpl *)iface;
+    TRACE("(%p,%x,%x,%x)\n",iface,dwRes1,dwRes2,dwFlags);
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    PULSE_WaitForOperation(pa_stream_cork(This->stream, 0, PULSE_StreamSuc=
cessCallback, This));
+    pa_threaded_mainloop_unlock(PULSE_ml);
+    return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface) {
+    IDsDriverBufferImpl *This =3D (IDsDriverBufferImpl *)iface;
+    TRACE("(%p)\n",iface);
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    PULSE_WaitForOperation(pa_stream_cork(This->stream, 1, PULSE_StreamSuc=
cessCallback, This));
+    pa_threaded_mainloop_unlock(PULSE_ml);
+    return DS_OK;
+}
+
+/*************************************************************************=
****/
+
+static const IDsDriverBufferVtbl dsdbvt =3D
+{
+    IDsDriverBufferImpl_QueryInterface,
+    IDsDriverBufferImpl_AddRef,
+    IDsDriverBufferImpl_Release,
+    IDsDriverBufferImpl_Lock,
+    IDsDriverBufferImpl_Unlock,
+    IDsDriverBufferImpl_SetFormat,
+    IDsDriverBufferImpl_SetFrequency,
+    IDsDriverBufferImpl_SetVolumePan,
+    IDsDriverBufferImpl_SetPosition,
+    IDsDriverBufferImpl_GetPosition,
+    IDsDriverBufferImpl_Play,
+    IDsDriverBufferImpl_Stop
+};
+
+static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
+=09=09=09=09=09=09      LPWAVEFORMATEX pwfx,
+=09=09=09=09=09=09      DWORD dwFlags, DWORD dwCardAddress,
+=09=09=09=09=09=09      LPDWORD pdwcbBufferSize,
+=09=09=09=09=09=09      LPBYTE *ppbBuffer,
+=09=09=09=09=09=09      LPVOID *ppvObj) {
+    IDsDriverImpl *This =3D (IDsDriverImpl *)iface;
+    IDsDriverBufferImpl** ippdsdb =3D (IDsDriverBufferImpl**)ppvObj;
+    IDsDriverBufferImpl *That;
+    HRESULT ret;
+
+    TRACE("(%p,%p,%x,%x)\n",iface,pwfx,dwFlags,dwCardAddress);
+    /* we only support primary buffers */
+   =20
+    if (!(dwFlags & DSBCAPS_PRIMARYBUFFER))
+        return DSERR_UNSUPPORTED;
+    if (This->primary)
+        return DSERR_ALLOCATED;
+    This->primary =3D *ippdsdb;
+
+    *ippdsdb =3D HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDsD=
riverBufferImpl));
+
+    if (!*ippdsdb) {
+        ERR("Out of memory\n");
+        return DSERR_OUTOFMEMORY;
+    }
+
+    That =3D *ippdsdb;
+    That->lpVtbl  =3D &dsdbvt;
+    That->ref =3D 1;
+
+    That->drv =3D This;
+    TRACE("IdsDriverBufferImpl %p created.\n", That);
+       =20
+    if (!PULSE_SetupFormat(pwfx, &That->sample_spec)) {
+        WARN("Bad audio format.\n");
+        ret =3D DSERR_BADFORMAT;
+        goto err;
+    }
+
+    /* The buffer length has to be greater than fraglen * 20 or else logic=
 in
+     * dlls/dsound/mixer.c fails to correctly understand buffer wrap aroun=
d. */
+    That->fraglen =3D fragment_length(&That->sample_spec);
+    That->buffer_length =3D That->fraglen * 32;
+    That->buffer =3D HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, That->b=
uffer_length);
+    if (!That->buffer) {
+        ret =3D DSERR_OUTOFMEMORY;
+        goto err;
+    }
+    That->buffer_read_offset =3D 0;
+    That->buffer_play_offset =3D 0;
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    ret =3D DSPULSE_ConnectStream(That);
+    pa_threaded_mainloop_unlock(PULSE_ml);
+
+    if (ret !=3D DS_OK)
+        goto err;
+
+    *pdwcbBufferSize =3D That->buffer_length;
+    *ppbBuffer =3D That->buffer;
+   =20
+    TRACE("Exiting with success. Have buffer %p of length %u\n", That->buf=
fer, That->buffer_length);
+    return DS_OK;
+   =20
+err:
+    pa_threaded_mainloop_lock(PULSE_ml);
+    if (That->stream) {
+=09    if (pa_stream_get_state(That->stream) =3D=3D PA_STREAM_READY)
+=09        pa_stream_disconnect(That->stream);
+=09    pa_stream_unref(That->stream);
+=09    That->stream =3D NULL;
+    }
+    pa_threaded_mainloop_unlock(PULSE_ml);
+    HeapFree(GetProcessHeap(), 0, That->buffer);
+    HeapFree(GetProcessHeap(), 0, *ippdsdb);
+    WARN("exiting with failure.\n");
+    return ret;
+}
+
+static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFII=
D riid, LPVOID *ppobj) {
+    /* IDsDriverImpl *This =3D (IDsDriverImpl *)iface; */
+    FIXME("(%p): stub!\n",iface);
+    return DSERR_UNSUPPORTED;
+}
+
+static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface) {
+    IDsDriverImpl *This =3D (IDsDriverImpl *)iface;
+    ULONG refCount =3D InterlockedIncrement(&This->ref);
+
+    TRACE("(%p)->(ref before=3D%u)\n",This, refCount - 1);
+
+    return refCount;
+}
+
+static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface) {
+    IDsDriverImpl *This =3D (IDsDriverImpl *)iface;
+    ULONG refCount =3D InterlockedDecrement(&This->ref);
+
+    TRACE("(%p)->(ref before=3D%u)\n",This, refCount + 1);
+
+    if (refCount)
+        return refCount;
+
+    HeapFree(GetProcessHeap(), 0, This);
+    return 0;
+}
+
+static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRI=
VERDESC pDesc) {
+    IDsDriverImpl *This =3D (IDsDriverImpl *)iface;
+    TRACE("(%p,%p)\n",iface,pDesc);
+    *pDesc=09=09=09=3D WOutDev[This->wDevID].ds_desc;
+    pDesc->dwFlags=09=09=3D DSDDESC_DONTNEEDSECONDARYLOCK | DSDDESC_DONTNE=
EDPRIMARYLOCK;
+    pDesc->dnDevNode=09=09=3D 0; /*TODO: Bwah? */
+    pDesc->wVxdId           =3D 0;
+    pDesc->wReserved=09=09=3D 0;
+    pDesc->ulDeviceNum=09=09=3D This->wDevID;
+    pDesc->dwHeapType=09=09=3D DSDHEAP_NOHEAP;
+    pDesc->pvDirectDrawHeap=09=3D NULL;
+    pDesc->dwMemStartAddress=09=3D 0xDEAD0000;
+    pDesc->dwMemEndAddress=09=3D 0xDEAF0000;
+    pDesc->dwMemAllocExtra=09=3D 0;
+    pDesc->pvReserved1=09=09=3D NULL;
+    pDesc->pvReserved2=09=09=3D NULL;
+    return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface) {
+    IDsDriverImpl *This =3D (IDsDriverImpl *)iface;
+    TRACE("(%p) stub, harmless\n",This);
+    return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface) {
+    IDsDriverImpl *This =3D (IDsDriverImpl *)iface;
+    TRACE("(%p) stub, harmless\n",This);
+    return S_OK;
+}
+
+static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAP=
S pCaps) {
+    IDsDriverImpl *This =3D (IDsDriverImpl *)iface;
+
+    if (pa_context_get_state(PULSE_context) !=3D PA_CONTEXT_READY) {
+        ERR("Context failure.\n");
+        return DSERR_GENERIC;
+    }
+
+    TRACE("(%p,%p)\n",iface,pCaps);
+    *pCaps =3D WOutDev[This->wDevID].ds_caps;
+    return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
+=09=09=09=09=09=09=09 PIDSDRIVERBUFFER pBuffer,
+=09=09=09=09=09=09=09 LPVOID *ppvObj)
+{
+    IDsDriverImpl *This =3D (IDsDriverImpl *)iface;
+    FIXME("(%p,%p): stub\n",This,pBuffer);
+    return DSERR_INVALIDCALL;
+}
+
+static const IDsDriverVtbl dsdvt =3D
+{
+    IDsDriverImpl_QueryInterface,
+    IDsDriverImpl_AddRef,
+    IDsDriverImpl_Release,
+    IDsDriverImpl_GetDriverDesc,
+    IDsDriverImpl_Open,
+    IDsDriverImpl_Close,
+    IDsDriverImpl_GetCaps,
+    IDsDriverImpl_CreateSoundBuffer,
+    IDsDriverImpl_DuplicateSoundBuffer
+};
+
+DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv) {
+    IDsDriverImpl** idrv =3D (IDsDriverImpl**)drv;
+
+    if (pa_context_get_state(PULSE_context) !=3D PA_CONTEXT_READY || pa_co=
ntext_is_local(PULSE_context) !=3D 1) {
+        WARN("Connection failure or server is not local, falling back to W=
aveOut HEL.\n");
+        return MMSYSERR_NOTSUPPORTED;
+    }
+
+    *idrv =3D HeapAlloc(GetProcessHeap(), 0, sizeof(IDsDriverImpl));
+    if (!*idrv)
+        return MMSYSERR_NOMEM;
+   =20
+    TRACE("IDsDriverImpl %p created.\n", *idrv);
+
+    (*idrv)->lpVtbl =3D &dsdvt;
+    (*idrv)->ref =3D 1;
+    (*idrv)->wDevID =3D wDevID;
+    (*idrv)->primary =3D NULL;
+
+    return MMSYSERR_NOERROR;
+}
+
+DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc) {
+    TRACE("(%u, %p)\n", wDevID, desc);
+    *desc =3D WOutDev[wDevID].ds_desc;
+    return MMSYSERR_NOERROR;
+}
+#endif /* HAVE_PULSEAUDIO */
diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c
new file mode 100644
index 0000000..aa84c9b
--- /dev/null
+++ b/dlls/winepulse.drv/pulse.c
@@ -0,0 +1,861 @@
+/*
+ * Wine Driver for PulseAudio
+ * http://pulseaudio.org/
+ *
+ * Copyright=092009 Arthur Taylor <theycallhimart at gmail.com>
+ *
+ * Contains code from other wine sound drivers.
+ *
+ * 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, U=
SA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "mmddk.h"
+#include "ks.h"
+#include "ksguid.h"
+#include "ksmedia.h"
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <poll.h>
+
+#ifdef HAVE_PULSEAUDIO
+
+#include "wine/unicode.h"
+#include "wine/debug.h"
+#include "wine/library.h"
+
+#include <winepulse.h>
+#include <pulse/pulseaudio.h>
+WINE_DEFAULT_DEBUG_CHANNEL(wave);
+
+/* These strings used only for tracing */
+const char * PULSE_getCmdString(enum win_wm_message msg) {
+    static char unknown[32];
+#define MSG_TO_STR(x) case x: return #x
+    switch(msg) {
+    MSG_TO_STR(WINE_WM_PAUSING);
+    MSG_TO_STR(WINE_WM_RESTARTING);
+    MSG_TO_STR(WINE_WM_RESETTING);
+    MSG_TO_STR(WINE_WM_HEADER);
+    MSG_TO_STR(WINE_WM_BREAKLOOP);
+    MSG_TO_STR(WINE_WM_CLOSING);
+    MSG_TO_STR(WINE_WM_STARTING);
+    MSG_TO_STR(WINE_WM_STOPPING);
+    MSG_TO_STR(WINE_WM_XRUN);
+    MSG_TO_STR(WINE_WM_FEED);
+    }
+#undef MSG_TO_STR
+    sprintf(unknown, "UNKNOWN(0x%08x)", msg);
+    return unknown;
+}
+
+/*=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*
+ *          Ring Buffer Functions - copied from winealsa.drv            *
+ *=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*/
+
+/* unless someone makes a wineserver kernel module, Unix pipes are faster =
than win32 events */
+#define USE_PIPE_SYNC
+
+#ifdef USE_PIPE_SYNC
+#define INIT_OMR(omr) do { if (pipe(omr->msg_pipe) < 0) { omr->msg_pipe[0]=
 =3D omr->msg_pipe[1] =3D -1; } } while (0)
+#define CLOSE_OMR(omr) do { close(omr->msg_pipe[0]); close(omr->msg_pipe[1=
]); } while (0)
+#define SIGNAL_OMR(omr) do { int x =3D 0; int foo; foo =3D write((omr)->ms=
g_pipe[1], &x, sizeof(x)); } while (0)
+#define CLEAR_OMR(omr) do { int x =3D 0; int foo; foo =3D read((omr)->msg_=
pipe[0], &x, sizeof(x)); } while (0)
+#define RESET_OMR(omr) do { } while (0)
+#define WAIT_OMR(omr, sleep) \
+  do { struct pollfd pfd; pfd.fd =3D (omr)->msg_pipe[0]; \
+       pfd.events =3D POLLIN; poll(&pfd, 1, sleep); } while (0)
+#else
+#define INIT_OMR(omr) do { omr->msg_event =3D CreateEventW(NULL, FALSE, FA=
LSE, NULL); } while (0)
+#define CLOSE_OMR(omr) do { CloseHandle(omr->msg_event); } while (0)
+#define SIGNAL_OMR(omr) do { SetEvent((omr)->msg_event); } while (0)
+#define CLEAR_OMR(omr) do { } while (0)
+#define RESET_OMR(omr) do { ResetEvent((omr)->msg_event); } while (0)
+#define WAIT_OMR(omr, sleep) \
+  do { WaitForSingleObject((omr)->msg_event, sleep); } while (0)
+#endif
+
+#define PULSE_RING_BUFFER_INCREMENT      64
+
+/******************************************************************
+ *=09=09PULSE_InitRingMessage
+ *
+ * Initialize the ring of messages for passing between driver's caller
+ * and playback/record thread
+ */
+int PULSE_InitRingMessage(PULSE_MSG_RING* omr)
+{
+    omr->msg_toget =3D 0;
+    omr->msg_tosave =3D 0;
+    INIT_OMR(omr);
+    omr->ring_buffer_size =3D PULSE_RING_BUFFER_INCREMENT;
+    omr->messages =3D HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,omr->rin=
g_buffer_size * sizeof(PULSE_MSG));
+
+    InitializeCriticalSection(&omr->msg_crst);
+    omr->msg_crst.DebugInfo->Spare[0] =3D (DWORD_PTR)(__FILE__ ": PULSE_MS=
G_RING.msg_crst");
+    return 0;
+}
+
+/******************************************************************
+ *=09=09PULSE_DestroyRingMessage
+ *
+ */
+int PULSE_DestroyRingMessage(PULSE_MSG_RING* omr)
+{
+    CLOSE_OMR(omr);
+    HeapFree(GetProcessHeap(),0,omr->messages);
+    omr->messages =3D NULL;
+    omr->ring_buffer_size =3D PULSE_RING_BUFFER_INCREMENT;
+    omr->msg_crst.DebugInfo->Spare[0] =3D 0;
+    DeleteCriticalSection(&omr->msg_crst);
+    return 0;
+}
+/******************************************************************
+ *=09=09PULSE_ResetRingMessage
+ *
+ */
+void PULSE_ResetRingMessage(PULSE_MSG_RING* omr)
+{
+    RESET_OMR(omr);
+}
+
+/******************************************************************
+ *=09=09PULSE_WaitRingMessage
+ *
+ */
+void PULSE_WaitRingMessage(PULSE_MSG_RING* omr, DWORD sleep)
+{
+    WAIT_OMR(omr, sleep);
+}
+
+/******************************************************************
+ *=09=09PULSE_AddRingMessage
+ *
+ * Inserts a new message into the ring (should be called from DriverProc d=
erived routines)
+ */
+int PULSE_AddRingMessage(PULSE_MSG_RING* omr, enum win_wm_message msg, DWO=
RD param, BOOL wait)
+{
+    HANDLE=09hEvent =3D INVALID_HANDLE_VALUE;
+
+    EnterCriticalSection(&omr->msg_crst);
+    if ((omr->msg_toget =3D=3D ((omr->msg_tosave + 1) % omr->ring_buffer_s=
ize)))
+    {
+=09int old_ring_buffer_size =3D omr->ring_buffer_size;
+=09omr->ring_buffer_size +=3D PULSE_RING_BUFFER_INCREMENT;
+=09omr->messages =3D HeapReAlloc(GetProcessHeap(),0,omr->messages, omr->ri=
ng_buffer_size * sizeof(PULSE_MSG));
+=09/* Now we need to rearrange the ring buffer so that the new
+=09   buffers just allocated are in between omr->msg_tosave and
+=09   omr->msg_toget.
+=09*/
+=09if (omr->msg_tosave < omr->msg_toget)
+=09{
+=09    memmove(&(omr->messages[omr->msg_toget + PULSE_RING_BUFFER_INCREMEN=
T]),
+=09=09    &(omr->messages[omr->msg_toget]),
+=09=09    sizeof(PULSE_MSG)*(old_ring_buffer_size - omr->msg_toget)
+=09=09    );
+=09    omr->msg_toget +=3D PULSE_RING_BUFFER_INCREMENT;
+=09}
+    }
+    if (wait)
+    {
+        hEvent =3D CreateEventW(NULL, FALSE, FALSE, NULL);
+        if (hEvent =3D=3D INVALID_HANDLE_VALUE)
+        {
+            ERR("can't create event !?\n");
+            LeaveCriticalSection(&omr->msg_crst);
+            return 0;
+        }
+        /* fast messages have to be added at the start of the queue */
+        omr->msg_toget =3D (omr->msg_toget + omr->ring_buffer_size - 1) % =
omr->ring_buffer_size;
+
+        omr->messages[omr->msg_toget].msg =3D msg;
+        omr->messages[omr->msg_toget].param =3D param;
+        omr->messages[omr->msg_toget].hEvent =3D hEvent;
+    }
+    else
+    {
+        omr->messages[omr->msg_tosave].msg =3D msg;
+        omr->messages[omr->msg_tosave].param =3D param;
+        omr->messages[omr->msg_tosave].hEvent =3D INVALID_HANDLE_VALUE;
+        omr->msg_tosave =3D (omr->msg_tosave + 1) % omr->ring_buffer_size;
+    }
+    LeaveCriticalSection(&omr->msg_crst);
+    /* signal a new message */
+    SIGNAL_OMR(omr);
+    if (wait)
+    {
+        /* wait for playback/record thread to have processed the message *=
/
+        WaitForSingleObject(hEvent, INFINITE);
+        CloseHandle(hEvent);
+    }
+    return 1;
+}
+
+/******************************************************************
+ *=09=09PULSE_RetrieveRingMessage
+ *
+ * Get a message from the ring. Should be called by the playback/record th=
read.
+ */
+int PULSE_RetrieveRingMessage(PULSE_MSG_RING* omr,
+                                   enum win_wm_message *msg, DWORD *param,=
 HANDLE *hEvent)
+{
+    EnterCriticalSection(&omr->msg_crst);
+
+    if (omr->msg_toget =3D=3D omr->msg_tosave) /* buffer empty ? */
+    {
+        LeaveCriticalSection(&omr->msg_crst);
+=09return 0;
+    }
+
+    *msg =3D omr->messages[omr->msg_toget].msg;
+    omr->messages[omr->msg_toget].msg =3D 0;
+    *param =3D omr->messages[omr->msg_toget].param;
+    *hEvent =3D omr->messages[omr->msg_toget].hEvent;
+    omr->msg_toget =3D (omr->msg_toget + 1) % omr->ring_buffer_size;
+    CLEAR_OMR(omr);
+    LeaveCriticalSection(&omr->msg_crst);
+    return 1;
+}
+
+/*************************************************************************=
*
+ * Utility Functions
+ *************************************************************************=
/
+
+/******************************************************************
+ *=09=09PULSE_SetupFormat
+ *
+ * Checks to see if the audio format in wf is supported, and if so set up =
the
+ * pa_sample_spec at ss to that format.
+ */
+BOOL PULSE_SetupFormat(LPWAVEFORMATEX wf, pa_sample_spec *ss) {
+    WAVEFORMATEXTENSIBLE *wfex;
+
+    ss->channels =3D wf->nChannels;
+    ss->rate =3D wf->nSamplesPerSec;
+    ss->format =3D PA_SAMPLE_INVALID;
+   =20
+    if (ss->rate < DSBFREQUENCY_MIN || ss->rate > DSBFREQUENCY_MAX) return=
 FALSE;
+
+    switch (wf->wFormatTag) {
+=09case WAVE_FORMAT_PCM:
+=09/* MSDN says that for WAVE_FORMAT_PCM, nChannels must be 1 or 2 and
+=09 * wBitsPerSample must be 8 or 16, yet other values are used by some
+=09 * applications in the wild for surround. */
+=09if (ss->channels > 6 || ss->channels < 1) return FALSE;
+=09ss->format =3D wf->wBitsPerSample =3D=3D 8  ? PA_SAMPLE_U8
+=09=09   : wf->wBitsPerSample =3D=3D 16 ? PA_SAMPLE_S16NE
+#if PA_PROTOCOL_VERSION >=3D 15
+=09=09   : wf->wBitsPerSample =3D=3D 24 ? PA_SAMPLE_S24NE
+#endif
+=09=09   : wf->wBitsPerSample =3D=3D 32 ? PA_SAMPLE_S32NE
+=09=09   : PA_SAMPLE_INVALID;
+=09break;
+
+=09case WAVE_FORMAT_MULAW:
+=09if (wf->wBitsPerSample =3D=3D 8) ss->format =3D PA_SAMPLE_ULAW;
+=09break;
+
+=09case WAVE_FORMAT_ALAW:
+=09if (wf->wBitsPerSample =3D=3D 8) ss->format =3D PA_SAMPLE_ALAW;
+=09break;
+
+=09case WAVE_FORMAT_EXTENSIBLE:
+=09if (wf->cbSize > 22) return FALSE;
+        if (ss->channels < 1 || ss->channels > 6) return FALSE;
+=09wfex =3D (WAVEFORMATEXTENSIBLE *)wf;
+        if (IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
+=09    if (wf->wBitsPerSample =3D=3D wfex->Samples.wValidBitsPerSample) {
+=09        ss->format =3D wf->wBitsPerSample =3D=3D 8  ? PA_SAMPLE_U8
+=09=09           : wf->wBitsPerSample =3D=3D 16 ? PA_SAMPLE_S16NE
+#if PA_PROTOCOL_VERSION >=3D 15=20
+=09=09           : wf->wBitsPerSample =3D=3D 24 ? PA_SAMPLE_S24NE
+#endif
+=09=09           : wf->wBitsPerSample =3D=3D 32 ? PA_SAMPLE_S32NE
+=09=09           : PA_SAMPLE_INVALID;
+=09    } else {
+#if PA_PROTOCOL_VERSION >=3D 15
+=09=09if (wf->wBitsPerSample =3D=3D 32 && wfex->Samples.wValidBitsPerSampl=
e =3D=3D 24)
+=09=09    ss->format =3D PA_SAMPLE_S24_32NE;
+=09        else
+#endif
+=09=09return FALSE;
+=09    }
+=09} else if (wf->wBitsPerSample !=3D wfex->Samples.wValidBitsPerSample) {
+=09    return FALSE;
+=09} else if ((IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FL=
OAT))) {
+=09    ss->format =3D PA_SAMPLE_FLOAT32NE;
+=09} else {
+=09    WARN("only KSDATAFORMAT_SUBTYPE_PCM and KSDATAFORMAT_SUBTYPE_IEEE_F=
LOAT of WAVE_FORMAT_EXTENSIBLE supported\n");
+=09    return FALSE;
+=09}
+=09break;
+
+=09default:
+=09WARN("only WAVE_FORMAT_PCM, WAVE_FORMAT_MULAW, WAVE_FORMAT_ALAW and WAV=
E_FORMAT_EXTENSIBLE supported\n");
+=09return FALSE;
+    }
+
+    if (!pa_sample_spec_valid(ss)) return FALSE;
+    if (wf->nBlockAlign !=3D pa_frame_size(ss)) {
+=09ERR("wf->nBlockAlign !=3D the format frame size!\n");
+=09return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*************************************************************************=
*
+ *                              PULSE_GetMMTime                 [internal]
+ */
+void PULSE_GetMMTime(const pa_timing_info *t, pa_sample_spec *s, size_t la=
st_reset, LPMMTIME lpTime) {
+    pa_usec_t=09time, time_temp;
+    size_t=09bytes, bytes_temp;=09
+
+    /* If this is a recording stream we want the write_index and not the r=
ead index */
+    if (last_reset =3D=3D (size_t) -1) {
+        bytes =3D t->write_index;
+=09last_reset =3D 0;
+    } else {
+        bytes =3D t->read_index;
+=09if (last_reset > bytes)
+=09    last_reset =3D 0;
+    }
+    time =3D pa_bytes_to_usec(bytes, s);
+    time +=3D pa_timeval_age(&t->timestamp);
+
+    if (t->playing) {
+=09bytes +=3D ((pa_timeval_age(&t->timestamp) / 1000) * pa_bytes_per_secon=
d(s)) / 1000;
+=09bytes_temp =3D (time_temp =3D t->sink_usec + t->transport_usec)/1000 * =
pa_bytes_per_second(s)/1000;
+    } else {
+=09time =3D 0;
+=09time_temp =3D 0;
+=09bytes_temp =3D 0;
+    }
+
+    time -=3D pa_bytes_to_usec(last_reset, s);
+    bytes -=3D last_reset;
+    if (bytes > bytes_temp)
+=09bytes -=3D bytes_temp;
+    else
+=09bytes =3D 0;
+
+    if (time > time_temp)
+=09time -=3D time_temp;
+    else
+=09time =3D 0;
+
+    bytes -=3D bytes % pa_frame_size(s);
+    time /=3D 1000; /* In milliseconds now */
+
+    switch (lpTime->wType) {
+    case TIME_SAMPLES:
+        lpTime->u.sample =3D bytes / pa_frame_size(s);
+=09TRACE("TIME_SAMPLES=3D%u\n", lpTime->u.sample);
+        break;
+    case TIME_MS:
+        lpTime->u.ms =3D time;
+=09TRACE("TIME_MS=3D%u\n", lpTime->u.ms);
+=09break;
+    case TIME_SMPTE:
+        lpTime->u.smpte.fps =3D 30;
+        lpTime->u.smpte.sec =3D time/1000;
+        lpTime->u.smpte.min =3D lpTime->u.smpte.sec / 60;
+        lpTime->u.smpte.sec -=3D 60 * lpTime->u.smpte.min;
+        lpTime->u.smpte.hour =3D lpTime->u.smpte.min / 60;
+        lpTime->u.smpte.min -=3D 60 * lpTime->u.smpte.hour;
+        lpTime->u.smpte.frame =3D time / lpTime->u.smpte.fps * 1000;
+        TRACE("TIME_SMPTE=3D%02u:%02u:%02u:%02u\n",
+              lpTime->u.smpte.hour, lpTime->u.smpte.min,
+              lpTime->u.smpte.sec, lpTime->u.smpte.frame);
+        break;
+    default:
+        WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wTyp=
e);
+        lpTime->wType =3D TIME_BYTES;
+        /* fall through */
+    case TIME_BYTES:
+        lpTime->u.cb =3D bytes;
+        TRACE("TIME_BYTES=3D%u\n", lpTime->u.cb);
+        break;
+    }
+}
+
+/*************************************************************************=
*
+ *=09=09PULSE_WaitForOperation
+ *
+ * Waits for pa operations to complete, and dereferences the operation.
+ */
+void PULSE_WaitForOperation(pa_operation *o) {
+    if (!o) return;
+
+    for (;;) {
+=09if (pa_operation_get_state(o) !=3D PA_OPERATION_RUNNING)
+=09    break;
+=09pa_threaded_mainloop_wait(PULSE_ml);
+    }
+    pa_operation_unref(o);
+}
+
+/*************************************************************************=
*
+ * Common Callbacks
+ */
+
+/*************************************************************************=
*
+ * =09=09=09PULSE_StreamRequestCallback
+ *
+ * Called by the pulse mainloop whenever it wants or has audio data.
+ */
+void PULSE_StreamRequestCallback(pa_stream *s, size_t nbytes, void *userda=
ta) {
+    WINE_WAVEINST *ww =3D (WINE_WAVEINST*)userdata;
+    assert(s && ww);
+
+    TRACE("Asking to feed/be fed %u bytes\n", nbytes);
+
+    /* Make sure that the player/recorder is running */
+    if (ww->hThread !=3D INVALID_HANDLE_VALUE && ww->msgRing.messages) {
+    =09PULSE_AddRingMessage(&ww->msgRing, WINE_WM_FEED, (DWORD)nbytes, FAL=
SE);
+    }
+}
+/*************************************************************************=
*
+ * =09=09=09PULSE_StreamSuspendedCallback=09=09[internal]
+ *
+ * Called by the pulse mainloop any time stream playback is intentionally
+ * suspended or resumed from being suspended.
+ */
+void PULSE_StreamSuspendedCallback(pa_stream *s, void *userdata) {
+    WINE_WAVEINST *wwo =3D (WINE_WAVEINST*)userdata;
+    assert(s && wwo);
+
+    /* Currently we handle this kinda like an underrun. Perhaps we should
+     * tell the client somehow so it doesn't just hang? */
+
+    if (!pa_stream_is_suspended(s) && wwo->hThread !=3D INVALID_HANDLE_VAL=
UE && wwo->msgRing.ring_buffer_size > 0)
+=09PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_XRUN, 0, FALSE);
+}
+
+/*************************************************************************=
*
+ * =09=09=09PULSE_StreamUnderflowCallback=09=09[internal]
+ *
+ * Called by the pulse mainloop when the prebuf runs out of data.
+ */
+void PULSE_StreamUnderflowCallback(pa_stream *s, void *userdata) {
+    WINE_WAVEINST *wwo =3D (WINE_WAVEINST*)userdata;
+    assert(s && wwo);
+
+    /* If we aren't playing, don't care ^_^ */
+    if (wwo->state !=3D WINE_WS_PLAYING) return;
+
+    TRACE("Underrun occurred.\n");
+
+    if (wwo->hThread !=3D INVALID_HANDLE_VALUE && wwo->msgRing.ring_buffer=
_size > 0);
+        PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_XRUN, 0, FALSE);
+}
+
+/*************************************************************************=
*
+ * =09=09=09PULSE_StreamOverflowCallback=09    =09[internal]
+ *
+ * Called by the pulse mainloop when the write buffer was overflowed and d=
ata
+ * was lost.
+ */
+void PULSE_StreamOverflowCallback(pa_stream *s, void *userdata) {
+    WINE_WAVEINST *wwo =3D (WINE_WAVEINST*)userdata;
+    assert(s && wwo);
+
+    ERR("Overflow occurred!?!\n");
+
+    if (wwo->hThread !=3D INVALID_HANDLE_VALUE && wwo->msgRing.ring_buffer=
_size > 0);
+        PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_XRUN, 1, FALSE);
+}
+
+/*************************************************************************=
*
+ * =09=09=09PULSE_StreamMovedCallback=09=09[internal]
+ *
+ * Called by the pulse mainloop when the stream gets moved, resulting in
+ * possibly different metrics.
+ */
+void PULSE_StreamMovedCallback(pa_stream *s, void *userdata) {
+    FIXME("stub");
+}
+
+
+/******************************************************************
+ *=09=09PULSE_StreamStateCallback
+ *
+ * Called by pulse whenever the state of the stream changes.
+ *
+ * void *userdata is either the dsound or wave instance
+ */
+void PULSE_StreamStateCallback(pa_stream *s, void *userdata) {
+    assert(s);
+
+    switch (pa_stream_get_state(s)) {
+    =09case PA_STREAM_READY:
+=09TRACE("Stream %p ready\n", userdata);
+=09break;
+
+    =09case PA_STREAM_TERMINATED:
+=09TRACE("Stream %p terminated\n", userdata);
+=09break;
+
+    =09case PA_STREAM_FAILED:
+=09ERR("Stream %p failed!\n", userdata);
+=09break;
+
+    =09case PA_STREAM_UNCONNECTED:
+    =09case PA_STREAM_CREATING:
+=09return;
+    }
+    pa_threaded_mainloop_signal(PULSE_ml, 0);
+}
+
+/*************************************************************************=
*
+ * =09=09=09PULSE_StreamSucessCallback
+ */
+void PULSE_StreamSuccessCallback(pa_stream *s, int success, void *userdata=
) {
+    if (!success)
+=09WARN("Stream %p operation failed: %s\n", userdata, pa_strerror(pa_conte=
xt_errno(PULSE_context)));
+
+    pa_threaded_mainloop_signal(PULSE_ml, 0);
+}
+
+/*************************************************************************=
*
+ * =09=09=09PULSE_ContextSuccessCallback
+ */
+void PULSE_ContextSuccessCallback(pa_context *c, int success, void *userda=
ta) {
+    if (!success) ERR("Context operation failed: %s\n", pa_strerror(pa_con=
text_errno(c)));
+    pa_threaded_mainloop_signal(PULSE_ml, 0);
+}
+
+/*************************************************************************=
*
+ * Connection management and sink / source management.
+ */
+
+/*************************************************************************=
*
+ * =09=09=09=09PULSE_ContextStateCallback    [internal]
+ */
+static void PULSE_ContextStateCallback(pa_context *c, void *userdata) {
+    assert(c);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+=09case PA_CONTEXT_UNCONNECTED:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+=09case PA_CONTEXT_READY:
+=09case PA_CONTEXT_TERMINATED:
+=09    pa_threaded_mainloop_signal(PULSE_ml, 0);
+=09    break;
+
+=09case PA_CONTEXT_FAILED:
+=09    ERR("Context failure: %s\n", pa_strerror(pa_context_errno(c)));
+=09    pa_threaded_mainloop_signal(PULSE_ml, 0);
+=09    break;
+    }
+}
+
+/*************************************************************************=
*
+ * =09=09    PULSE_AllocateWaveinDevice   =09=09[internal]
+ *
+ * Creates or adds a device to WInDev based on the pa_source_info.
+ */
+static void PULSE_AllocateWaveinDevice(const char *name, const char *devic=
e, const char *description, const pa_cvolume *v) {
+    WINE_WAVEDEV *wdi;
+
+    if (WInDev)
+=09wdi =3D HeapReAlloc(GetProcessHeap(), 0, WInDev, sizeof(WINE_WAVEDEV) *=
 (PULSE_WidNumDevs + 1));
+    else
+=09wdi =3D HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_WAVEDEV));
+
+    if (!wdi) return;
+   =20
+    WInDev =3D wdi;
+    wdi =3D &WInDev[PULSE_WidNumDevs++];
+    memset(wdi, 0, sizeof(WINE_WAVEDEV));
+    memset(&(wdi->caps.in), 0, sizeof(wdi->caps.in));
+    snprintf(wdi->interface_name, MAXPNAMELEN * 2, "winepulse: %s", name);
+    wdi->device_name =3D pa_xstrdup(device);
+    strcpy(wdi->interface_name, "winepulse: ");
+    MultiByteToWideChar(CP_ACP, 0, description, -1, wdi->caps.in.szPname, =
sizeof(wdi->caps.in.szPname)/sizeof(WCHAR));
+    wdi->caps.in.szPname[sizeof(wdi->caps.in.szPname)/sizeof(WCHAR) - 1] =
=3D '\0';
+    wdi->caps.in.wMid =3D MM_CREATIVE;
+    wdi->caps.in.wPid =3D MM_CREATIVE_SBP16_WAVEOUT;
+    wdi->caps.in.vDriverVersion =3D 0x0100;
+    wdi->caps.in.wChannels =3D v->channels =3D=3D 1 ? 1 : 2;
+    wdi->caps.in.dwFormats =3D PULSE_ALL_FORMATS;
+    memset(&wdi->ds_desc, 0, sizeof(DSDRIVERDESC));
+    memcpy(wdi->ds_desc.szDesc, description, min(sizeof(wdi->ds_desc.szDes=
c) - 1, strlen(description)));
+    memcpy(wdi->ds_desc.szDrvname, "winepulse.drv", 14);
+    wdi->ds_caps.dwMinSecondarySampleRate =3D DSBFREQUENCY_MIN;
+    wdi->ds_caps.dwMaxSecondarySampleRate =3D DSBFREQUENCY_MAX;
+    wdi->ds_caps.dwPrimaryBuffers =3D 1;
+    wdi->ds_caps.dwFlags =3D \
+=09DSCAPS_PRIMARYMONO |
+=09DSCAPS_PRIMARYSTEREO |
+=09DSCAPS_PRIMARY8BIT |
+=09DSCAPS_PRIMARY16BIT |
+=09DSCAPS_SECONDARYMONO |
+=09DSCAPS_SECONDARYSTEREO |
+=09DSCAPS_SECONDARY8BIT |
+=09DSCAPS_SECONDARY16BIT |
+=09DSCCAPS_MULTIPLECAPTURE |
+=09DSCAPS_CERTIFIED | /* Useful? */
+=09DSCAPS_EMULDRIVER; /* Useful? */
+
+}
+
+/*************************************************************************=
*
+ * =09=09    PULSE_AllocateWaveoutDevice=09=09=09[internal]
+ *
+ *  Creates or adds a sink to the WOutDev array.
+ */
+static void PULSE_AllocateWaveoutDevice(const char *name, const char *devi=
ce, const char *description, const pa_cvolume *v) {
+    WINE_WAVEDEV *wdo;
+    int x;
+
+    if (WOutDev)
+        wdo =3D HeapReAlloc(GetProcessHeap(), 0, WOutDev, sizeof(WINE_WAVE=
DEV) * (PULSE_WodNumDevs + 1));
+    else
+        wdo =3D HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_WAVEDEV));
+
+    if (!wdo) return;
+
+    WOutDev =3D wdo;
+    wdo =3D &WOutDev[PULSE_WodNumDevs++];
+    memset(wdo, 0, sizeof(WINE_WAVEDEV));
+
+    wdo->device_name =3D pa_xstrdup(device);
+    wdo->volume.channels =3D v->channels;
+    for (x =3D 0; x < v->channels; x++) wdo->volume.values[x] =3D v->value=
s[x];
+    snprintf(wdo->interface_name, MAXPNAMELEN * 2, "winepulse: %s", name);
+    MultiByteToWideChar(CP_ACP, 0, description, -1, wdo->caps.out.szPname,=
 sizeof(wdo->caps.out.szPname)/sizeof(WCHAR));
+    wdo->caps.out.szPname[sizeof(wdo->caps.out.szPname)/sizeof(WCHAR) - 1]=
 =3D '\0';
+    wdo->caps.out.wMid =3D MM_CREATIVE;
+    wdo->caps.out.wPid =3D MM_CREATIVE_SBP16_WAVEOUT;
+    wdo->caps.out.vDriverVersion =3D 0x0100;
+    wdo->caps.out.dwSupport =3D WAVECAPS_VOLUME | WAVECAPS_SAMPLEACCURATE =
| WAVECAPS_DIRECTSOUND;
+    if (v->channels >=3D 2) {
+=09wdo->caps.out.wChannels =3D 2;
+=09wdo->caps.out.dwSupport |=3D WAVECAPS_LRVOLUME;
+    } else
+=09wdo->caps.out.wChannels =3D 1;
+    wdo->caps.out.dwFormats =3D PULSE_ALL_FORMATS;
+    memset(&wdo->ds_desc, 0, sizeof(DSDRIVERDESC));
+    memcpy(wdo->ds_desc.szDesc, description, min(sizeof(wdo->ds_desc.szDes=
c) - 1, strlen(description)));
+    memcpy(wdo->ds_desc.szDrvname, "winepulse.drv", 14);
+    wdo->ds_caps.dwMinSecondarySampleRate =3D DSBFREQUENCY_MIN;
+    wdo->ds_caps.dwMaxSecondarySampleRate =3D DSBFREQUENCY_MAX;
+    wdo->ds_caps.dwPrimaryBuffers =3D 1;
+    wdo->ds_caps.dwFlags =3D \
+=09DSCAPS_PRIMARYMONO |
+=09DSCAPS_PRIMARYSTEREO |
+=09DSCAPS_PRIMARY8BIT |
+=09DSCAPS_PRIMARY16BIT |
+=09DSCAPS_SECONDARYMONO |
+=09DSCAPS_SECONDARYSTEREO |
+=09DSCAPS_SECONDARY8BIT |
+=09DSCAPS_SECONDARY16BIT |
+=09DSCAPS_CERTIFIED;
+}
+
+/*************************************************************************=
*
+ *                              PULSE_SourceInfoCallback        [internal]
+ */
+static void PULSE_SourceInfoCallback(pa_context *c, const pa_source_info *=
i, int eol, void *userdata) {
+
+    if (!eol && i)
+        PULSE_AllocateWaveinDevice(i->name, i->name, i->description, &i->v=
olume);
+
+    pa_threaded_mainloop_signal(PULSE_ml, 0);
+}
+
+/*************************************************************************=
*
+ *                              PULSE_SinkInfoCallback          [internal]
+ */
+static void PULSE_SinkInfoCallback(pa_context *c, const pa_sink_info *i, i=
nt eol, void *userdata) {
+
+    if (!eol && i)
+=09PULSE_AllocateWaveoutDevice(i->name, i->name, i->description, &i->volum=
e);
+
+    pa_threaded_mainloop_signal(PULSE_ml, 0);
+}
+
+/*************************************************************************=
*
+ *                              PULSE_ContextNotifyCallback     [internal]
+ */
+static void PULSE_ContextNotifyCallback(pa_context *c, void *userdata) {
+    pa_threaded_mainloop_signal(PULSE_ml, 0);
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09PULSE_WaveClose                 [internal]
+ *
+ * Disconnect from the server, deallocated the WaveIn/WaveOut devices, sto=
p and
+ * free the mainloop.
+ */
+
+static LONG PULSE_WaveClose(void) {
+    int x;
+    TRACE("()\n");
+    if (!PULSE_ml) return DRV_FAILURE;
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    /* device_name is allocated with pa_xstrdup, free with pa_xfree */
+    for (x =3D 0; x < PULSE_WodNumDevs; x++) pa_xfree(WOutDev[x].device_na=
me);
+    for (x =3D 0; x < PULSE_WidNumDevs; x++) pa_xfree(WInDev[x].device_nam=
e);
+    HeapFree(GetProcessHeap(), 0, WOutDev);
+    HeapFree(GetProcessHeap(), 0, WInDev);
+    if (PULSE_context) {
+=09PULSE_WaitForOperation(pa_context_drain(PULSE_context, PULSE_ContextNot=
ifyCallback, NULL));
+=09pa_context_disconnect(PULSE_context);
+=09pa_context_unref(PULSE_context);
+=09PULSE_context =3D NULL;
+    }
+
+    pa_threaded_mainloop_unlock(PULSE_ml);
+    pa_threaded_mainloop_stop(PULSE_ml);
+    pa_threaded_mainloop_free(PULSE_ml);
+    PULSE_ml =3D NULL;
+   =20
+    return DRV_SUCCESS;
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09PULSE_WaveInit                  [internal]
+ *
+ * Connects to the pulseaudio server, tries to discover sinks and sources =
and
+ * allocates the WaveIn/WaveOut devices.
+ */
+static LONG PULSE_WaveInit(void) {
+    char *app_name;
+    char path[PATH_MAX];
+    char *offset =3D NULL;
+    int x =3D 0;
+    pa_cvolume fake_cvolume;
+   =20
+    WOutDev =3D NULL;
+    WInDev =3D NULL;
+    PULSE_WodNumDevs =3D 0;
+    PULSE_WidNumDevs =3D 0;
+    PULSE_context =3D NULL;
+    PULSE_ml =3D NULL;
+
+    if (!(PULSE_ml =3D pa_threaded_mainloop_new())) {
+=09WARN("Failed to create mainloop object.");
+=09return -1;
+    }
+   =20
+    /* Application name giving to pulse should be unique to the binary so =
that
+     * pulse-*-restore can be useful */
+
+    /* Get binary path, and remove path a-la strrchr */
+    if (GetModuleFileNameA(NULL, path, PATH_MAX))
+=09offset =3D strrchr(path, '\\');
+
+    if (offset && ++offset && offset < path + PATH_MAX) {
+        app_name =3D pa_xmalloc(strlen(offset) + 8);
+=09snprintf(app_name, strlen(offset) + 8, "WINE [%s]", offset);
+    } else
+=09app_name =3D pa_xstrdup("WINE Application");
+
+    TRACE("App name is \"%s\"\n", app_name);
+
+    pa_threaded_mainloop_start(PULSE_ml);
+    PULSE_context =3D pa_context_new(pa_threaded_mainloop_get_api(PULSE_ml=
), app_name);
+    assert(PULSE_context);
+    pa_xfree(app_name);
+   =20
+    pa_context_set_state_callback(PULSE_context, PULSE_ContextStateCallbac=
k, NULL);
+      =20
+    pa_threaded_mainloop_lock(PULSE_ml);
+
+    TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_ge=
t_protocol_version(PULSE_context), PA_API_VERSION);
+    TRACE("Attempting to connect to pulseaudio server.\n");
+    if (pa_context_connect(PULSE_context, NULL, 0, NULL) < 0)
+=09goto fail;
+
+    /* Wait for connection */
+    for (;;) {
+=09pa_context_state_t state =3D pa_context_get_state(PULSE_context);
+
+=09if (state =3D=3D PA_CONTEXT_FAILED || state =3D=3D PA_CONTEXT_TERMINATE=
D)
+=09    goto fail;
+
+=09if (state =3D=3D PA_CONTEXT_READY)
+=09    break;
+   =20
+=09pa_threaded_mainloop_wait(PULSE_ml);
+    }
+
+    x =3D pa_context_get_server_protocol_version(PULSE_context);
+    TRACE("Connected to server %s with protocol version: %i.\n", pa_contex=
t_get_server(PULSE_context), x);
+    if (x < 14)
+=09WARN("Server is old, expect poor latency or buggy-ness!\n");
+
+    fake_cvolume.channels =3D 2;
+    pa_cvolume_reset(&fake_cvolume, 2);
+    /* FIXME Translations? */
+    PULSE_AllocateWaveoutDevice("default", NULL, "Default", &fake_cvolume)=
;
+    PULSE_AllocateWaveinDevice("default", NULL, "Default", &fake_cvolume);
+    PULSE_WaitForOperation(pa_context_get_sink_info_list(PULSE_context, PU=
LSE_SinkInfoCallback, &PULSE_WodNumDevs));
+    PULSE_WaitForOperation(pa_context_get_source_info_list(PULSE_context, =
PULSE_SourceInfoCallback, &PULSE_WidNumDevs));
+    TRACE("Found %u output and %u input device(s).\n", PULSE_WodNumDevs - =
1, PULSE_WidNumDevs - 1);
+
+    pa_threaded_mainloop_unlock(PULSE_ml);
+
+    return DRV_SUCCESS;
+
+fail:
+    pa_threaded_mainloop_unlock(PULSE_ml);   =20
+    ERR("Failed to connect to server\n");
+    return DRV_FAILURE;
+}
+
+#endif /* HAVE_PULSEAUDIO */
+
+/*************************************************************************=
*
+ * =09=09=09=09DriverProc (WINEPULSE.@)
+ */
+LRESULT CALLBACK PULSE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMs=
g,
+                                 LPARAM dwParam1, LPARAM dwParam2) {
+
+    switch(wMsg) {
+#ifdef HAVE_PULSEAUDIO
+    case DRV_LOAD:=09=09return PULSE_WaveInit();
+    case DRV_FREE:=09=09return PULSE_WaveClose();
+    case DRV_OPEN:=09=09return 1;
+    case DRV_CLOSE:=09=09return 1;
+    case DRV_ENABLE:=09=09return 1;
+    case DRV_DISABLE:=09=09return 1;
+    case DRV_QUERYCONFIGURE:=09return 1;
+    case DRV_CONFIGURE:=09=09MessageBoxA(0, "PulseAudio MultiMedia Driver =
!", "PulseAudio Driver", MB_OK);=09return 1;
+    case DRV_INSTALL:=09=09return DRVCNF_RESTART;
+    case DRV_REMOVE:=09=09return DRVCNF_RESTART;
+#endif
+    default:
+=09return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+    }
+}
diff --git a/dlls/winepulse.drv/wavein.c b/dlls/winepulse.drv/wavein.c
new file mode 100644
index 0000000..1534d6e
--- /dev/null
+++ b/dlls/winepulse.drv/wavein.c
@@ -0,0 +1,572 @@
+/*
+ * Wine Driver for PulseAudio - WaveIn Functionality
+ * http://pulseaudio.org/
+ *
+ * Copyright=092009 Arthur Taylor <theycallhimart at gmail.com>
+ *
+ * Contains code from other wine multimedia drivers.
+ *
+ * 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, U=
SA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "mmddk.h"
+
+#include <winepulse.h>
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wave);
+
+#if HAVE_PULSEAUDIO
+
+/*=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*
+ *                  WAVE IN specific PulseAudio Callbacks=09=09*
+ *=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*/
+
+/*************************************************************************=
*
+* =09=09=09widNotifyClient=09=09=09[internal]
+*/
+static DWORD widNotifyClient(WINE_WAVEINST* wwi, WORD wMsg, DWORD dwParam1=
, DWORD dwParam2) {=20
+   TRACE("wMsg =3D 0x%04x dwParm1 =3D %04X dwParam2 =3D %04X\n", wMsg, dwP=
aram1, dwParam2);
+
+   switch (wMsg) {
+   case WIM_OPEN:
+   case WIM_CLOSE:
+   case WIM_DATA:
+       if (wwi->wFlags !=3D DCB_NULL &&
+=09   !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags, (HDRVR)wwi->w=
aveDesc.hWave,
+=09=09=09   wMsg, wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
+=09   WARN("can't notify client !\n");
+=09   return MMSYSERR_ERROR;
+       }
+       break;
+   default:
+       FIXME("Unknown callback message %u\n", wMsg);
+       return MMSYSERR_INVALPARAM;
+   }
+   return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                         widRecorder_CopyData                 [internal]
+ *
+ * Copys data from the fragments pulse returns to queued buffers.
+ */
+static void widRecorder_CopyData(WINE_WAVEINST *wwi) {
+    LPWAVEHDR lpWaveHdr =3D wwi->lpQueuePtr;
+    size_t size;
+    while (lpWaveHdr && wwi->state =3D=3D WINE_WS_PLAYING && wwi->buffer) =
{
+        size =3D min(wwi->buffer_length - wwi->buffer_read_offset, lpWaveH=
dr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
+        if (size =3D=3D 0) ERR("Size is 0! buffer is full but not freed?\n=
");
+        memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, (PBYTE)wwi-=
>buffer + wwi->buffer_read_offset, size);
+        wwi->buffer_read_offset +=3D size;
+=09if (wwi->buffer_read_offset =3D=3D wwi->buffer_length) {
+=09    pa_threaded_mainloop_lock(PULSE_ml);
+=09    pa_stream_drop(wwi->stream);
+=09    pa_threaded_mainloop_unlock(PULSE_ml);
+=09    wwi->buffer =3D NULL;
+=09    wwi->buffer_length =3D 0;
+=09    wwi->buffer_read_offset =3D 0;
+=09}
+        lpWaveHdr->dwBytesRecorded +=3D size;
+=09if (lpWaveHdr->dwBytesRecorded =3D=3D lpWaveHdr->dwBufferLength) {
+=09    wwi->lpQueuePtr =3D lpWaveHdr->lpNext;
+=09    lpWaveHdr->dwFlags &=3D ~WHDR_INQUEUE;
+            lpWaveHdr->dwFlags |=3D  WHDR_DONE;
+=09    widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
+=09    lpWaveHdr =3D wwi->lpQueuePtr;
+=09}
+    }
+}
+
+/*************************************************************************=
*
+* =09=09=09widRecorder_NextFragment=09=09[internal]
+*
+* Switches the current fragment to the next based upon a message from the
+* server.
+*/
+static void widRecorder_NextFragment(WINE_WAVEINST *wwi, size_t sizer) {
+    LPWAVEHDR lpWaveHdr =3D wwi->lpQueuePtr;
+    size_t request =3D 0;
+
+    for (;lpWaveHdr; lpWaveHdr =3D lpWaveHdr->lpNext)
+=09request +=3D lpWaveHdr->dwBufferLength;
+
+    lpWaveHdr =3D wwi->lpQueuePtr;
+    pa_threaded_mainloop_lock(PULSE_ml);
+    pa_stream_peek(wwi->stream, &wwi->buffer, &request);
+    wwi->buffer_length =3D request;
+    pa_threaded_mainloop_unlock(PULSE_ml);
+
+    widRecorder_CopyData(wwi);
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09widRecorder=09=09=09[internal]
+ */
+static DWORD CALLBACK widRecorder(LPVOID lpParam) {
+    WINE_WAVEINST *wwi =3D (WINE_WAVEINST*)lpParam;
+    LPWAVEHDR           lpWaveHdr;
+    enum win_wm_message=09msg;
+    DWORD=09=09param;
+    HANDLE=09=09ev;
+
+    wwi->state =3D WINE_WS_STOPPED;
+    SetEvent(wwi->hStartUpEvent);
+
+    for (;;) {
+=09PULSE_WaitRingMessage(&wwi->msgRing, INFINITE);
+=09while (PULSE_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev)) {
+=09    TRACE("Received %s %x\n", PULSE_getCmdString(msg), param);
+=09
+=09    switch (msg) {
+=09    case WINE_WM_FEED:
+=09=09SetEvent(ev);
+=09=09if (wwi->state =3D=3D WINE_WS_PLAYING)
+=09=09    widRecorder_NextFragment(wwi, param);
+=09=09break;
+=09    case WINE_WM_STARTING:
+=09=09wwi->state =3D WINE_WS_PLAYING;
+=09=09pa_threaded_mainloop_lock(PULSE_ml);
+=09=09PULSE_WaitForOperation(pa_stream_cork(wwi->stream, 0, PULSE_StreamSu=
ccessCallback, NULL));
+=09=09pa_threaded_mainloop_unlock(PULSE_ml);
+=09=09SetEvent(ev);
+=09=09break;
+=09    case WINE_WM_HEADER:
+=09=09lpWaveHdr =3D (LPWAVEHDR)param;
+=09=09lpWaveHdr->lpNext =3D 0;
+
+=09=09/* insert buffer at the end of queue */
+=09=09{
+=09=09    LPWAVEHDR*=09wh;
+=09=09    for (wh =3D &(wwi->lpQueuePtr); *wh; wh =3D &((*wh)->lpNext));
+=09=09    *wh =3D lpWaveHdr;
+=09=09}
+=09=09break;
+=09    case WINE_WM_STOPPING:
+=09=09if (wwi->state !=3D WINE_WS_STOPPED) {
+=09=09    pa_threaded_mainloop_lock(PULSE_ml);
+=09=09    PULSE_WaitForOperation(pa_stream_cork(wwi->stream, 1, PULSE_Stre=
amSuccessCallback, NULL));
+=09=09    pa_stream_drop(wwi->stream);
+=09=09    pa_threaded_mainloop_unlock(PULSE_ml);
+
+=09=09    /* return current buffer to app */
+=09=09    lpWaveHdr =3D wwi->lpQueuePtr;
+=09=09    if (lpWaveHdr) {
+=09=09=09LPWAVEHDR=09lpNext =3D lpWaveHdr->lpNext;
+=09=09=09TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
+=09=09=09lpWaveHdr->dwFlags &=3D ~WHDR_INQUEUE;
+=09=09=09lpWaveHdr->dwFlags |=3D WHDR_DONE;
+=09=09=09wwi->lpQueuePtr =3D lpNext;
+=09=09=09widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
+=09=09    }
+=09=09}
+=09=09wwi->state =3D WINE_WS_STOPPED;
+=09=09SetEvent(ev);
+=09=09break;
+=09    case WINE_WM_RESETTING:
+=09=09if (wwi->state !=3D WINE_WS_STOPPED) {
+=09=09    pa_threaded_mainloop_lock(PULSE_ml);
+=09=09    pa_stream_drop(wwi->stream);
+=09=09    pa_threaded_mainloop_unlock(PULSE_ml);
+=09=09}
+=09=09wwi->state =3D WINE_WS_STOPPED;
+
+=09=09/* return all buffers to the app */
+=09=09for (lpWaveHdr =3D wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr =3D wwi->lp=
QueuePtr) {
+=09=09    TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
+=09=09    lpWaveHdr->dwFlags &=3D ~WHDR_INQUEUE;
+=09=09    lpWaveHdr->dwFlags |=3D WHDR_DONE;
+=09=09    wwi->lpQueuePtr =3D lpWaveHdr->lpNext;
+=09=09    widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
+=09=09}
+=09=09wwi->lpQueuePtr =3D NULL;
+=09=09SetEvent(ev);
+=09=09break;
+=09    case WINE_WM_XRUN:
+=09=09pa_threaded_mainloop_lock(PULSE_ml);
+=09=09pa_stream_drop(wwi->stream);
+=09=09pa_threaded_mainloop_unlock(PULSE_ml);
+=09=09wwi->buffer_read_offset =3D 0;
+=09=09break;
+=09    case WINE_WM_CLOSING:
+=09=09wwi->hThread =3D 0;
+=09=09if ((DWORD)param =3D=3D 1) {
+=09=09    wwi->state =3D WINE_WS_FAILED;
+=09=09    SetEvent(ev);
+=09=09    PULSE_DestroyRingMessage(&wwi->msgRing);
+=09=09    widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
+=09=09    wwi->lpPlayPtr =3D wwi->lpQueuePtr =3D NULL;
+=09=09    pa_threaded_mainloop_lock(PULSE_ml);
+=09=09    pa_stream_disconnect(wwi->stream);
+=09=09    pa_threaded_mainloop_unlock(PULSE_ml);
+=09=09    TRACE("Thread exiting because of failure.\n");
+=09=09    ExitThread(1);
+=09=09}
+=09=09wwi->state =3D WINE_WS_CLOSED;
+=09=09SetEvent(ev);
+=09=09ExitThread(0);
+=09=09/* shouldn't go here */
+=09    default:
+=09=09FIXME("unknown message %d\n", msg);
+=09=09break;
+=09    }
+=09}
+    } /* for (;;) */
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09widOpen=09=09=09=09[internal]
+ */
+static DWORD widOpen(WORD wDevID, LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc,=
 DWORD dwFlags) {
+    WINE_WAVEDEV *wdi;
+    WINE_WAVEINST *wwi =3D NULL;
+    DWORD ret =3D MMSYSERR_NOERROR;
+
+    TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
+    if (lpDesc =3D=3D NULL) {
+=09WARN("Invalid Parameter !\n");
+=09return MMSYSERR_INVALPARAM;
+    }
+
+    if (wDevID >=3D PULSE_WidNumDevs) {
+=09TRACE("Asked for device %d, but only %d known!\n", wDevID, PULSE_WidNum=
Devs);
+=09return MMSYSERR_BADDEVICEID;
+    }
+    wdi =3D &WInDev[wDevID];
+
+    wwi =3D HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_WAVE=
INST));
+    if (!wwi) return MMSYSERR_NOMEM;
+    *lpdwUser =3D (DWORD)wwi;
+   =20
+    /* check to see if format is supported and make pa_sample_spec struct =
*/
+    if (!PULSE_SetupFormat(lpDesc->lpFormat, &wwi->sample_spec)) {
+=09WARN("Bad format: tag=3D%04X nChannels=3D%d nSamplesPerSec=3D%d !\n",
+=09     lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
+=09     lpDesc->lpFormat->nSamplesPerSec);
+=09ret =3D WAVERR_BADFORMAT;
+=09goto exit;
+    }
+
+    if (TRACE_ON(wave)) {
+    =09char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
+    =09pa_sample_spec_snprint(t, sizeof(t), &wwi->sample_spec);
+    =09TRACE("Sample spec '%s'\n", t);
+    }
+
+    if (dwFlags & WAVE_FORMAT_QUERY) {
+=09TRACE("Query format: tag=3D%04X nChannels=3D%d nSamplesPerSec=3D%d !\n"=
,
+=09     lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
+=09     lpDesc->lpFormat->nSamplesPerSec);
+=09ret =3D MMSYSERR_NOERROR;
+=09goto exit;
+    }
+
+    wwi->wFlags =3D HIWORD(dwFlags & CALLBACK_TYPEMASK);
+    wwi->waveDesc =3D *lpDesc;
+    PULSE_InitRingMessage(&wwi->msgRing);
+
+    wwi->stream =3D pa_stream_new(PULSE_context, "WaveIn", &wwi->sample_sp=
ec, NULL);
+    if (!wwi->stream) {
+=09ret =3D WAVERR_BADFORMAT;
+=09goto exit;
+    }
+
+    pa_stream_set_state_callback(wwi->stream, PULSE_StreamStateCallback, w=
wi);
+    pa_stream_set_read_callback(wwi->stream, PULSE_StreamRequestCallback, =
wwi);
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    TRACE("Asking to open %s for recording.\n", wdi->device_name);
+    pa_stream_connect_record(wwi->stream, wdi->device_name, NULL,
+=09=09=09     PA_STREAM_START_CORKED |
+=09=09=09     PA_STREAM_AUTO_TIMING_UPDATE |
+=09=09=09     PA_STREAM_INTERPOLATE_TIMING);
+
+    for (;;) {
+=09pa_context_state_t cstate =3D pa_context_get_state(PULSE_context);
+=09pa_stream_state_t sstate =3D pa_stream_get_state(wwi->stream);
+
+=09if (cstate =3D=3D PA_CONTEXT_FAILED || cstate =3D=3D PA_CONTEXT_TERMINA=
TED ||
+=09    sstate =3D=3D PA_STREAM_FAILED || sstate =3D=3D PA_STREAM_TERMINATE=
D) {
+=09    ERR("Failed to connect context object: %s\n", pa_strerror(pa_contex=
t_errno(PULSE_context)));
+=09=09ret =3D MMSYSERR_NODRIVER;
+=09=09pa_threaded_mainloop_unlock(PULSE_ml);
+=09=09goto exit;
+=09    }
+
+=09    if (sstate =3D=3D PA_STREAM_READY)
+=09=09break;
+
+=09    pa_threaded_mainloop_wait(PULSE_ml);
+=09}
+    TRACE("(%p)->stream connected for recording.\n", wwi);
+
+    PULSE_WaitForOperation(pa_stream_update_timing_info(wwi->stream, PULSE=
_StreamSuccessCallback, wwi));
+
+    wwi->timing_info =3D pa_stream_get_timing_info(wwi->stream);
+    assert(wwi->timing_info);
+    pa_threaded_mainloop_unlock(PULSE_ml);
+
+    wwi->hStartUpEvent =3D CreateEventW(NULL, FALSE, FALSE, NULL);
+    wwi->hThread =3D CreateThread(NULL, 0, widRecorder, (LPVOID)wwi, 0, &(=
wwi->dwThreadID));
+    if (wwi->hThread)
+        SetThreadPriority(wwi->hThread, THREAD_PRIORITY_TIME_CRITICAL);
+    else {
+        ERR("Thread creation for the widRecorder failed!\n");
+        ret =3D MMSYSERR_NOMEM;
+=09goto exit;
+    }
+    WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
+    CloseHandle(wwi->hStartUpEvent);
+    wwi->hStartUpEvent =3D INVALID_HANDLE_VALUE;
+
+    return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
+
+exit:
+    if (!wwi)
+=09return ret;
+
+    if (wwi->hStartUpEvent !=3D INVALID_HANDLE_VALUE)
+=09CloseHandle(wwi->hStartUpEvent);
+
+    if (wwi->msgRing.ring_buffer_size > 0)
+=09PULSE_DestroyRingMessage(&wwi->msgRing);
+
+    if (wwi->stream) {
+=09if (pa_stream_get_state(wwi->stream) =3D=3D PA_STREAM_READY)
+=09    pa_stream_disconnect(wwi->stream);
+        pa_stream_unref(wwi->stream);
+    }
+    HeapFree(GetProcessHeap(), 0, wwi);
+
+    return ret;
+}
+/*************************************************************************=
*
+ *                              widClose                        [internal]
+ */
+static DWORD widClose(WORD wDevID, WINE_WAVEINST *wwi) {
+    DWORD ret;
+
+    TRACE("(%u, %p);\n", wDevID, wwi);
+    if (wDevID >=3D PULSE_WidNumDevs) {
+=09WARN("Asked for device %d, but only %d known!\n", wDevID, PULSE_WodNumD=
evs);
+=09return MMSYSERR_INVALHANDLE;
+    } else if (!wwi) {
+=09WARN("Stream instance invalid.\n");
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    if (wwi->state !=3D WINE_WS_FAILED) {
+=09if (wwi->lpQueuePtr) {
+=09    WARN("buffers recording recording !\n");
+=09    return WAVERR_STILLPLAYING;
+=09}
+
+=09pa_threaded_mainloop_lock(PULSE_ml);
+=09if (pa_stream_get_state(wwi->stream) =3D=3D PA_STREAM_READY)
+=09    pa_stream_drop(wwi->stream);
+=09    pa_stream_disconnect(wwi->stream);
+=09pa_threaded_mainloop_unlock(PULSE_ml);
+
+=09if (wwi->hThread !=3D INVALID_HANDLE_VALUE)
+=09    PULSE_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
+
+=09PULSE_DestroyRingMessage(&wwi->msgRing);
+    }
+    ret =3D widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
+
+    pa_stream_unref(wwi->stream);
+    TRACE("Deallocating record instance.\n");
+    HeapFree(GetProcessHeap(), 0, wwi);
+    return ret;
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09widAddBuffer=09=09=09[internal]
+ *
+ */
+static DWORD widAddBuffer(WINE_WAVEINST* wwi, LPWAVEHDR lpWaveHdr, DWORD d=
wSize) {
+    TRACE("(%p, %p, %08X);\n", wwi, lpWaveHdr, dwSize);
+
+    if (!wwi ||=09wwi->state =3D=3D WINE_WS_FAILED) {
+=09WARN("Stream instance invalid.\n");
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    if (lpWaveHdr->lpData =3D=3D NULL || !(lpWaveHdr->dwFlags & WHDR_PREPA=
RED))
+=09return WAVERR_UNPREPARED;
+
+    if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
+=09return WAVERR_STILLPLAYING;
+
+    lpWaveHdr->dwFlags &=3D ~WHDR_DONE;
+    lpWaveHdr->dwFlags |=3D WHDR_INQUEUE;
+    lpWaveHdr->dwBytesRecorded =3D 0;
+    lpWaveHdr->lpNext =3D 0;
+
+    PULSE_AddRingMessage(&wwi->msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, =
FALSE);
+
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              widRecorderMessage                [interna=
l]
+ */
+static DWORD widRecorderMessage(WINE_WAVEINST *wwi, enum win_wm_message me=
ssage) {
+    if (!wwi ||=09wwi->state =3D=3D WINE_WS_FAILED) {
+=09WARN("Stream instance invalid.\n");
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    PULSE_AddRingMessage(&wwi->msgRing, message, 0, TRUE);
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              widGetPosition                  [internal]
+ */
+static DWORD widGetPosition(WINE_WAVEINST *wwi, LPMMTIME lpTime, DWORD uSi=
ze) {
+    if (!wwi ||=09wwi->state =3D=3D WINE_WS_FAILED) {
+=09WARN("Stream instance invalid.\n");
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    if (lpTime =3D=3D NULL)=09return MMSYSERR_INVALPARAM;
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    PULSE_GetMMTime(wwi->timing_info, &wwi->sample_spec, (size_t)-1, lpTim=
e);
+    pa_threaded_mainloop_unlock(PULSE_ml);
+
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              widGetDevCaps                   [internal]
+ */
+static DWORD widGetDevCaps(DWORD wDevID, LPWAVEINCAPSW lpCaps, DWORD dwSiz=
e) {
+    TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
+
+    if (lpCaps =3D=3D NULL) return MMSYSERR_NOTENABLED;
+
+    if (wDevID >=3D PULSE_WidNumDevs) {
+=09TRACE("Asked for device %d, but only %d known!\n", wDevID, PULSE_WidNum=
Devs);
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    memcpy(lpCaps, &(WInDev[wDevID].caps.in), min(dwSize, sizeof(*lpCaps))=
);
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              widGetNumDevs                   [internal]
+ * Context-sanity check here, as if we respond with 0, WINE will move on
+ * to the next wavein driver.
+ */
+static DWORD widGetNumDevs() {
+    if (pa_context_get_state(PULSE_context) !=3D PA_CONTEXT_READY)
+=09return 0;
+   =20
+    return PULSE_WidNumDevs;
+}
+
+/*************************************************************************=
*
+ *                              widDevInterfaceSize             [internal]
+ */
+static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1) {
+    TRACE("(%u, %p)\n", wDevID, dwParam1);
+
+    *dwParam1 =3D MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interfa=
ce_name, -1,
+                                    NULL, 0 ) * sizeof(WCHAR);
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              widDevInterface                 [internal]
+ */
+static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)=
 {
+    if (dwParam2 >=3D MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].int=
erface_name, -1,
+                                        NULL, 0 ) * sizeof(WCHAR))
+    {
+        MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -=
1,
+                            dwParam1, dwParam2 / sizeof(WCHAR));
+        return MMSYSERR_NOERROR;
+    }
+    return MMSYSERR_INVALPARAM;
+}
+
+/*************************************************************************=
*
+ *                              widDsDesc                       [internal]
+ */
+DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc)
+{
+    *desc =3D WInDev[wDevID].ds_desc;
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09widMessage (WINEPULSE.@)
+ */
+DWORD WINAPI PULSE_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
+                             DWORD dwParam1, DWORD dwParam2) {
+
+    switch (wMsg) {
+    case DRVM_INIT:
+    case DRVM_EXIT:
+    case DRVM_ENABLE:
+    case DRVM_DISABLE:
+=09/* FIXME: Pretend this is supported */
+=09return 0;
+    case WIDM_OPEN:=09 =09return widOpen=09=09(wDevID, (LPDWORD)dwUser, (L=
PWAVEOPENDESC)dwParam1,=09dwParam2);
+    case WIDM_CLOSE:=09 =09return widClose=09=09(wDevID, (WINE_WAVEINST*)d=
wUser);
+    case WIDM_ADDBUFFER:=09return widAddBuffer=09((WINE_WAVEINST*)dwUser, =
(LPWAVEHDR)dwParam1,=09=09dwParam2);
+    case WIDM_PREPARE:=09 =09return MMSYSERR_NOTSUPPORTED;
+    case WIDM_UNPREPARE: =09return MMSYSERR_NOTSUPPORTED;
+    case WIDM_GETDEVCAPS:=09return widGetDevCaps=09(wDevID, (LPWAVEINCAPSW=
)dwParam1,=09dwParam2);
+    case WIDM_GETNUMDEVS:=09return widGetNumDevs=09();
+    case WIDM_GETPOS:=09 =09return widGetPosition=09((WINE_WAVEINST*)dwUse=
r,    (LPMMTIME)dwParam1, =09=09dwParam2);
+    case WIDM_RESET:=09=09return widRecorderMessage((WINE_WAVEINST*)dwUser=
, WINE_WM_RESETTING);=20
+    case WIDM_START: =09=09return widRecorderMessage((WINE_WAVEINST*)dwUse=
r, WINE_WM_STARTING);
+    case WIDM_STOP: =09=09return widRecorderMessage((WINE_WAVEINST*)dwUser=
, WINE_WM_STOPPING);
+    case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize   (wDevI=
D, (LPDWORD)dwParam1);
+    case DRV_QUERYDEVICEINTERFACE:     return widDevInterface=09    (wDevI=
D, (PWCHAR)dwParam1, dwParam2);
+    case DRV_QUERYDSOUNDIFACE:=09return MMSYSERR_NOTSUPPORTED; /* Use emul=
ation, as there is no advantage */
+    case DRV_QUERYDSOUNDDESC:=09return widDsDesc=09(wDevID,    (PDSDRIVERD=
ESC)dwParam1);
+    default:
+=09FIXME("unknown message %d!\n", wMsg);
+    }
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+#else /* HAVE_PULSEAUDIO */
+
+/*************************************************************************=
*
+ * =09=09=09=09widMessage (WINEPULSE.@)
+ */
+DWORD WINAPI PULSE_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
+                             DWORD dwParam1, DWORD dwParam2) {
+//    FIXME("(%u, %04X, %08X, %08X, %08X):stub\n", wDevID, wMsg, dwUser, d=
wParam1, dwParam2);
+    return MMSYSERR_NOTENABLED;
+}
+
+#endif /* HAVE_PULSEAUDIO */
diff --git a/dlls/winepulse.drv/waveout.c b/dlls/winepulse.drv/waveout.c
new file mode 100644
index 0000000..4539103
--- /dev/null
+++ b/dlls/winepulse.drv/waveout.c
@@ -0,0 +1,1092 @@
+/*
+ * Wine Driver for PulseAudio - WaveOut Functionality
+ * http://pulseaudio.org/
+ *
+ * Copyright=092009 Arthur Taylor <theycallhimart at gmail.com>
+ *
+ * Contains code from other wine multimedia drivers.
+ *
+ * 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, U=
SA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winerror.h"
+#include "mmddk.h"
+#include "mmreg.h"
+
+#include <winepulse.h>
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wave);
+
+#if HAVE_PULSEAUDIO
+
+/* state diagram for waveOut writing:
+ *
+ * +---------+-------------+---------------+------------------------------=
---+
+ * |  state  |  function   |     event     |            new state         =
   |
+ * +---------+-------------+---------------+------------------------------=
---+
+ * |         | open()      |               | STOPPED                      =
   |
+ * | PAUSED  | write()     |               | PAUSED                       =
   |
+ * | STOPPED | write()     | <thrd create> | PLAYING                      =
   |
+ * | PLAYING | write()     | HEADER        | PLAYING                      =
   |
+ * | (other) | write()     | <error>       |                              =
   |
+ * | (any)   | pause()     | PAUSING       | PAUSED                       =
   |
+ * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd =3D> STOP=
PED) |
+ * | PAUSED  | reset()     | RESETTING     | PAUSED                       =
   |
+ * | (other) | reset()     | RESETTING     | STOPPED                      =
   |
+ * | (any)   | close()     | CLOSING       | CLOSED                       =
   |
+ * +---------+-------------+---------------+------------------------------=
---+
+ */
+
+/*
+ * - It is currently unknown if pausing in a loop works the same as expect=
ed.
+ */
+
+/*=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*
+ *                  WAVE OUT specific PulseAudio Callbacks=09=09*
+ *=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*/
+
+/*************************************************************************=
*
+ * =09=09=09WAVEOUT_StreamStartedCallback=09=09[internal]
+ *
+ * Called by the pulse mainloop whenever stream playback resumes after an
+ * underflow or an initial start. Requires libpulse >=3D 0.9.11
+ */
+#if PA_API_VERSION >=3D 12
+static void WAVEOUT_StreamStartedCallback(pa_stream *s, void *userdata) {
+    WINE_WAVEINST *wwo =3D (WINE_WAVEINST*)userdata;
+    assert(s && wwo);
+
+    TRACE("Audio flowing.\n");
+
+    if (wwo->buffer_attr.tlength =3D=3D 0)
+=09wwo->buffer_attr.tlength =3D (uint32_t) -1;
+
+    if (wwo->hThread !=3D INVALID_HANDLE_VALUE && wwo->msgRing.ring_buffer=
_size) {
+=09PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_STARTING, 0, FALSE);
+    }
+}
+#endif
+/*************************************************************************=
*
+ * =09=09    WAVEOUT_StreamTimingInfoUpdateCallback      [internal]
+ *
+ * Called by the pulse mainloop whenever the timing info gets updated, we
+ * use this to send the started signal */
+static void WAVEOUT_StreamTimingInfoUpdateCallback(pa_stream *s, void *use=
rdata) {
+    WINE_WAVEINST *wwo =3D (WINE_WAVEINST*)userdata;
+    assert(s && wwo);
+
+    if (!wwo->is_releasing && wwo->timing_info && wwo->timing_info->playin=
g) {
+        TRACE("Audio flowing.\n");
+
+=09if (wwo->buffer_attr.tlength =3D=3D 0)
+=09    wwo->buffer_attr.tlength =3D (uint32_t) -1;
+
+        if (wwo->hThread !=3D INVALID_HANDLE_VALUE && wwo->msgRing.ring_bu=
ffer_size)
+=09    PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_STARTING, 0, FALSE);
+    }
+}
+
+
+static void WAVEOUT_SinkInputInfoCallback(pa_context *c, const pa_sink_inp=
ut_info *i, int eol, void *userdata) {
+    WINE_WAVEINST* wwo =3D (WINE_WAVEINST*)userdata;
+    if (!eol && i) {
+=09for (wwo->volume.channels =3D 0; wwo->volume.channels !=3D i->volume.ch=
annels; wwo->volume.channels++)
+=09    wwo->volume.values[wwo->volume.channels] =3D i->volume.values[wwo->=
volume.channels];
+=09pa_threaded_mainloop_signal(PULSE_ml, 0);
+    }
+}
+
+/*=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*
+ *                  "Low level" WAVE OUT implementation=09=09=09*
+ *=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*/
+
+/*************************************************************************=
*
+ * =09=09=09wodPlayer_NotifyClient=09=09=09[internal]
+ */
+static DWORD wodPlayer_NotifyClient(WINE_WAVEINST* wwi, WORD wMsg, DWORD d=
wParam1, DWORD dwParam2) {
+    TRACE("wMsg =3D 0x%04x dwParm1 =3D %04X dwParam2 =3D %04X\n", wMsg, dw=
Param1, dwParam2);
+
+    switch (wMsg) {
+    case WOM_OPEN:
+    case WOM_CLOSE:
+    case WOM_DONE:
+=09if (wwi->wFlags !=3D DCB_NULL &&
+=09    !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags, (HDRVR)wwi->=
waveDesc.hWave,
+=09=09=09    wMsg, wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
+=09    WARN("can't notify client !\n");
+=09    return MMSYSERR_ERROR;
+=09}
+=09break;
+    default:
+=09FIXME("Unknown callback message %u\n", wMsg);
+        return MMSYSERR_INVALPARAM;
+    }
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09wodPlayer_BeginWaveHdr          [internal]
+ *
+ * Makes the specified lpWaveHdr the currently playing wave header.
+ * If the specified wave header is a begin loop and we're not already in
+ * a loop, setup the loop.
+ */
+static void wodPlayer_BeginWaveHdr(WINE_WAVEINST* wwo, LPWAVEHDR lpWaveHdr=
) {
+    wwo->lpPlayPtr =3D lpWaveHdr;
+
+    if (!lpWaveHdr) return;
+
+    if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
+=09if (wwo->lpLoopPtr) {
+=09    WARN("Already in a loop. Discarding loop on this header (%p)\n", lp=
WaveHdr);
+=09} else {
+            TRACE("Starting loop (%dx) with %p\n", lpWaveHdr->dwLoops, lpW=
aveHdr);
+=09    wwo->lpLoopPtr =3D lpWaveHdr;
+=09    /* Windows does not touch WAVEHDR.dwLoops,
+=09     * so we need to make an internal copy */
+=09    wwo->dwLoops =3D lpWaveHdr->dwLoops;
+=09}
+    }
+    wwo->dwPartialOffset =3D 0;
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09wodPlayer_PlayPtrNext=09        [internal]
+ *
+ * Advance the play pointer to the next waveheader, looping if required.
+ */
+static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEINST* wwo) {
+    LPWAVEHDR lpWaveHdr =3D wwo->lpPlayPtr;
+
+    wwo->dwPartialOffset =3D 0;
+    if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
+=09/* We're at the end of a loop, loop if required */
+=09if (--wwo->dwLoops > 0) {
+=09    wwo->lpPlayPtr =3D wwo->lpLoopPtr;
+=09} else {
+=09    /* Handle overlapping loops correctly */
+=09    if (wwo->lpLoopPtr !=3D lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEG=
INLOOP)) {
+=09=09FIXME("Correctly handled case ? (ending loop buffer also starts a ne=
w loop)\n");
+=09=09/* shall we consider the END flag for the closing loop or for
+=09=09 * the opening one or for both ???
+=09=09 * code assumes for closing loop only
+=09=09 */
+=09    } else {
+                lpWaveHdr =3D lpWaveHdr->lpNext;
+            }
+            wwo->lpLoopPtr =3D NULL;
+            wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
+=09}
+    } else {
+=09/* We're not in a loop.  Advance to the next wave header */
+=09wodPlayer_BeginWaveHdr(wwo, lpWaveHdr =3D lpWaveHdr->lpNext);
+    }
+
+    return lpWaveHdr;
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09wodPlayer_CheckReleasing=09[internal]
+ *
+ * Check to make sure that playback has not stalled
+ */
+static void wodPlayer_CheckReleasing(WINE_WAVEINST *wwo) {
+    LPWAVEHDR lpWaveHdr =3D wwo->lpQueuePtr;
+
+    /* If we aren't playing, and we have queued data and we aren't relasin=
g,
+     * start releasing if either:
+     * - We have stopped being given wavehdrs, or
+     * - We have 2s worth of audio built up.*/
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    if (!wwo->timing_info->playing &&
+=09(pa_bytes_to_usec(lpWaveHdr->dwBufferLength, &wwo->sample_spec)/2 < pa_=
timeval_age(&wwo->last_header)||
+=09wwo->timing_info->write_index - wwo->releasing_offset > pa_bytes_per_se=
cond(&wwo->sample_spec)*2)) {
+
+=09/* Try and adjust the buffer attributes so there is less latency.=20
+=09 * Because of bugs this call does not work on older servers. Once
+=09 * new version of pulseaudio become ubiquitous we will drop support for
+=09 * versions before 0.9.15 because they have too many bugs.*/
+=09if (wwo->buffer_attr.tlength =3D=3D 0 &&
+=09    pa_context_get_server_protocol_version(PULSE_context) >=3D 15) {
+=09    wwo->buffer_attr.tlength =3D wwo->timing_info->write_index;
+=09    wwo->buffer_attr.prebuf =3D (uint32_t) -1;
+=09    wwo->buffer_attr.minreq =3D (uint32_t) -1;
+=09    wwo->buffer_attr.maxlength =3D (uint32_t) -1;
+=09    WARN("Asking for new buffer tlength of %ums (%u bytes)\n", (unsigne=
d int)(pa_bytes_to_usec(wwo->buffer_attr.tlength, &wwo->sample_spec)/1000),=
 wwo->buffer_attr.tlength);
+=09    pa_stream_set_buffer_attr(wwo->stream, &wwo->buffer_attr, PULSE_Str=
eamSuccessCallback, wwo);
+=09} else {
+=09    /* Fake playback start earlier, introducing latency */
+=09    pa_gettimeofday(&wwo->started_releasing);
+=09    wwo->is_releasing =3D TRUE;
+=09    wwo->releasing_offset =3D wwo->lpQueuePtr->reserved;
+=09    TRACE("Starting to release early: %u\n", wwo->releasing_offset);
+=09}
+    }
+    pa_threaded_mainloop_unlock(PULSE_ml);
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09wodPlayer_NotifyCompletions=09[internal]
+ *
+ * Notifies and remove from queue all wavehdrs which have been played to
+ * the speaker based on a reference time of (theoretical) playback start. =
If
+ * force is true, we notify all wavehdrs and remove them all from the queu=
e
+ * even if they are unplayed or part of a loop. We return the time to wait
+ * until the next wavehdr needs to be freed, or INFINITE if there are no m=
ore
+ * wavehdrs. We use timevals rather than the stream position data that pul=
se
+ * gives us as realeasing needs to be constant and smooth for good applica=
tion
+ * behaviour.
+ */
+static DWORD wodPlayer_NotifyCompletions(WINE_WAVEINST* wwo, BOOL force) {
+    LPWAVEHDR=09lpWaveHdr;
+    pa_usec_t=09time;
+    pa_usec_t=09wait;
+
+    time =3D pa_bytes_to_usec(wwo->releasing_offset, &wwo->sample_spec);
+    if (wwo->is_releasing)
+=09time +=3D pa_timeval_age(&wwo->started_releasing);
+
+    for (lpWaveHdr =3D wwo->lpQueuePtr; lpWaveHdr;) {
+=09if (!force) {
+=09    /* Start from lpQueuePtr and keep notifying until:
+=09     * - we hit an unwritten wavehdr
+=09     * - we hit the beginning of a running loop
+=09     * - we hit a wavehdr which hasn't finished playing
+=09     */
+            if (lpWaveHdr =3D=3D wwo->lpPlayPtr) { TRACE("play %p\n", lpWa=
veHdr); return INFINITE; }
+=09    if (lpWaveHdr =3D=3D wwo->lpLoopPtr) { TRACE("loop %p\n", lpWaveHdr=
); return INFINITE; }
+
+=09    /* See if this data has been played, and if not, return when it wil=
l have been */
+=09    wait =3D pa_bytes_to_usec(lpWaveHdr->reserved + lpWaveHdr->dwBuffer=
Length, &wwo->sample_spec);
+=09    if (wait >=3D time) {
+=09=09wait =3D ((wait - time) + 999) / 1000;
+=09        return wait ?: 1;
+=09    }
+=09}
+
+=09/* return the wavehdr */
+=09wwo->lpQueuePtr =3D lpWaveHdr->lpNext;
+=09lpWaveHdr->dwFlags &=3D ~WHDR_INQUEUE;
+=09lpWaveHdr->dwFlags |=3D WHDR_DONE;
+
+=09wodPlayer_NotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
+=09lpWaveHdr =3D wwo->lpQueuePtr;
+    }
+    /* No more wavehdrs */
+    TRACE("Empty queue\n");
+    return INFINITE;
+}
+
+/*************************************************************************=
*
+ * =09=09=09     wodPlayer_WriteMax=09=09=09[internal]
+ *
+ * Write either how much free space or how much data we have, depending on
+ * which is less
+ */
+static int wodPlayer_WriteMax(WINE_WAVEINST *wwo, size_t *space) {
+    LPWAVEHDR lpWaveHdr =3D wwo->lpPlayPtr;
+    size_t toWrite =3D min(lpWaveHdr->dwBufferLength - wwo->dwPartialOffse=
t, *space);
+
+    if (toWrite > 0 && pa_stream_write(wwo->stream, lpWaveHdr->lpData + ww=
o->dwPartialOffset, toWrite, NULL, 0, PA_SEEK_RELATIVE) >=3D 0)
+=09TRACE("Writing wavehdr %p.%u[%u]\n", lpWaveHdr, wwo->dwPartialOffset, l=
pWaveHdr->dwBufferLength);
+    else
+        return 0;
+
+    /* Check to see if we wrote all of the wavehdr */
+    if ((wwo->dwPartialOffset +=3D toWrite) >=3D lpWaveHdr->dwBufferLength=
)
+=09wodPlayer_PlayPtrNext(wwo);
+=09
+    *space -=3D toWrite;
+
+    return toWrite;
+}
+
+/*************************************************************************=
*
+ * =09=09=09     wodPlayer_Feed=09=09=09[internal]
+ *
+ * Feed as much sound data as we can into pulse using wodPlayer_WriteMax.
+ * size_t space _must_ have come from either pa_stream_writable_size() or
+ * the value from a stream write callback, as if it isn't you run the risk
+ * of a buffer overflow in which audio data will be lost.
+ */
+static void wodPlayer_Feed(WINE_WAVEINST* wwo, size_t space) {
+
+    /* no more room... no need to try to feed */
+    if (space =3D=3D 0) return;
+
+    if (!wwo->stream || !PULSE_context ||=20
+        pa_context_get_state(PULSE_context) !=3D PA_CONTEXT_READY ||
+        pa_stream_get_state(wwo->stream) !=3D PA_STREAM_READY)
+        return;
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    /* Feed from a partial wavehdr */
+    if (wwo->lpPlayPtr && wwo->dwPartialOffset !=3D 0)
+        wodPlayer_WriteMax(wwo, &space);
+
+    /* Feed wavehdrs until we run out of wavehdrs or buffer space */
+    if (wwo->dwPartialOffset =3D=3D 0 && wwo->lpPlayPtr) {
+        do {
+            wwo->lpPlayPtr->reserved =3D wwo->timing_info->write_index;
+        } while (wodPlayer_WriteMax(wwo, &space) > 0 && wwo->lpPlayPtr && =
space > 0);
+    }
+    pa_threaded_mainloop_unlock(PULSE_ml);
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09wodPlayer_Reset=09=09=09[internal]
+ *
+ * wodPlayer helper. Resets current output stream.
+ */
+static void wodPlayer_Reset(WINE_WAVEINST* wwo) {
+    enum win_wm_message=09    msg;
+    DWORD=09=09    param;
+    HANDLE=09=09    ev;
+
+    TRACE("(%p)\n", wwo);
+
+    /* remove any buffer */
+    wodPlayer_NotifyCompletions(wwo, TRUE);
+
+    wwo->lpPlayPtr =3D wwo->lpQueuePtr =3D wwo->lpLoopPtr =3D NULL;
+    if (wwo->state !=3D WINE_WS_PAUSED)
+=09wwo->state =3D WINE_WS_STOPPED;
+    wwo->dwPartialOffset =3D 0;
+
+    if (!wwo->stream ||
+        !PULSE_context ||
+        pa_context_get_state(PULSE_context) !=3D PA_CONTEXT_READY ||
+        pa_stream_get_state(wwo->stream) !=3D PA_STREAM_READY) {
+=09return;
+    }
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+
+    /* flush the output buffer of written data*/
+    PULSE_WaitForOperation(pa_stream_flush(wwo->stream, PULSE_StreamSucces=
sCallback, NULL));
+
+    /* Reset the written byte count as some data may have been flushed */
+    if (wwo->timing_info->write_index_corrupt)
+=09PULSE_WaitForOperation(pa_stream_update_timing_info(wwo->stream, PULSE_=
StreamSuccessCallback, wwo));
+    wwo->releasing_offset =3D wwo->last_reset =3D wwo->timing_info->write_=
index;
+    if (wwo->is_releasing)
+        pa_gettimeofday(&wwo->started_releasing);
+
+    /* return all pending headers in queue */=20
+    EnterCriticalSection(&wwo->msgRing.msg_crst);
+    while (PULSE_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
+=09if (msg !=3D WINE_WM_HEADER) {
+=09    SetEvent(ev);
+=09    continue;
+=09}
+=09((LPWAVEHDR)param)->dwFlags &=3D ~WHDR_INQUEUE;
+=09((LPWAVEHDR)param)->dwFlags |=3D WHDR_DONE;
+=09wodPlayer_NotifyClient(wwo, WOM_DONE, param, 0);
+    }
+    PULSE_ResetRingMessage(&wwo->msgRing);
+    LeaveCriticalSection(&wwo->msgRing.msg_crst);=20
+
+    pa_threaded_mainloop_unlock(PULSE_ml);
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09wodPlayer_Underrun=09=09[internal]
+ *
+ * wodPlayer helper. Deal with a stream underrun.
+ */
+static void wodPlayer_Underrun(WINE_WAVEINST* wwo) {
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+   =20
+    /* Ask for a timing update */
+    PULSE_WaitForOperation(pa_stream_update_timing_info(wwo->stream, PULSE=
_StreamSuccessCallback, wwo));
+
+    /* See if we recovered while the message was waiting in the queue */
+    if (wwo->timing_info->playing) {
+=09TRACE("False alarm\n");
+        pa_threaded_mainloop_unlock(PULSE_ml);
+=09return;
+    }
+
+    if (wwo->lpPlayPtr) {
+    =09size_t space;
+
+=09TRACE("There is queued data. Trying to recover.\n");=09
+
+        /* Ask for a timing update */
+        if (wwo->timing_info->write_index_corrupt)
+=09    PULSE_WaitForOperation(pa_stream_update_timing_info(wwo->stream, PU=
LSE_StreamSuccessCallback, wwo));
+=09space =3D pa_stream_writable_size(wwo->stream);
+=09wodPlayer_Feed(wwo, space);
+    }
+    WARN("Stream underrun!\n");
+    wwo->is_releasing =3D FALSE;
+    wwo->releasing_offset =3D wwo->timing_info->write_index;
+    pa_threaded_mainloop_unlock(PULSE_ml);
+}
+
+
+/*************************************************************************=
*
+ * =09=09      wodPlayer_ProcessMessages=09=09=09[internal]
+ */
+static void wodPlayer_ProcessMessages(WINE_WAVEINST* wwo) {
+    LPWAVEHDR           lpWaveHdr;
+    enum win_wm_message=09msg;
+    DWORD=09=09param;
+    HANDLE=09=09ev;
+
+    while (PULSE_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
+=09TRACE("Received %s %x\n", PULSE_getCmdString(msg), param);
+
+=09switch (msg) {
+=09case WINE_WM_PAUSING:
+=09    wwo->state =3D WINE_WS_PAUSED;
+
+=09    pa_threaded_mainloop_lock(PULSE_ml);
+=09    PULSE_WaitForOperation(pa_stream_cork(wwo->stream, 1, PULSE_StreamS=
uccessCallback, wwo));
+=09    /* save how far we are, as releasing will restart from here */
+=09    if (wwo->is_releasing)
+=09=09wwo->releasing_offset =3D wwo->timing_info->write_index;
+=09    wwo->is_releasing =3D FALSE;
+=09    pa_threaded_mainloop_unlock(PULSE_ml);
+=09    SetEvent(ev);
+=09    break;
+
+=09case WINE_WM_RESTARTING:
+            if (wwo->state =3D=3D WINE_WS_PAUSED) {
+=09=09wwo->state =3D WINE_WS_PLAYING;
+=09=09pa_threaded_mainloop_lock(PULSE_ml);
+=09=09PULSE_WaitForOperation(pa_stream_cork(wwo->stream, 0, PULSE_StreamSu=
ccessCallback, wwo));
+=09=09/* If the serverside buffer was near full before pause, we need to
+=09=09 * have space to write soon, so force playback start */
+=09=09PULSE_WaitForOperation(pa_stream_trigger(wwo->stream, PULSE_StreamSu=
ccessCallback, wwo));
+=09=09pa_threaded_mainloop_unlock(PULSE_ml);
+=09    }
+=09    SetEvent(ev);
+=09    break;
+
+=09case WINE_WM_HEADER:
+=09    lpWaveHdr =3D (LPWAVEHDR)param;
+=09    /* insert buffer at the end of queue */
+=09    {
+=09=09LPWAVEHDR*=09wh;
+=09=09for (wh =3D &(wwo->lpQueuePtr); *wh; wh =3D &((*wh)->lpNext));
+=09=09*wh =3D lpWaveHdr;
+=09    }
+
+            if (!wwo->lpPlayPtr)
+=09        wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
+=09    if (wwo->state =3D=3D WINE_WS_STOPPED)
+=09=09wwo->state =3D WINE_WS_PLAYING;
+
+=09    wodPlayer_Feed(wwo, pa_stream_writable_size(wwo->stream));
+=09    if (!wwo->timing_info->playing && !wwo->is_releasing)
+=09=09pa_gettimeofday(&wwo->last_header);
+=09    SetEvent(ev);
+=09    break;
+
+=09case WINE_WM_RESETTING:
+=09    wodPlayer_Reset(wwo);
+=09    SetEvent(ev);
+=09    break;
+
+        case WINE_WM_BREAKLOOP:
+            if (wwo->state =3D=3D WINE_WS_PLAYING && wwo->lpLoopPtr !=3D N=
ULL)
+                /* ensure exit at end of current loop */
+                wwo->dwLoops =3D 1;
+=09    SetEvent(ev);
+            break;
+
+=09case WINE_WM_FEED: /* Sent by the pulse thread */
+=09    wodPlayer_Feed(wwo, pa_stream_writable_size(wwo->stream));
+=09    SetEvent(ev);
+=09    break;
+
+=09case WINE_WM_XRUN: /* Sent by the pulse thread */
+=09    if ((DWORD)param =3D=3D 1) {
+=09=09ERR("Buffer overflow!\n");
+=09=09pa_threaded_mainloop_lock(PULSE_ml);
+=09=09PULSE_WaitForOperation(pa_stream_drain(wwo->stream, PULSE_StreamSucc=
essCallback, wwo));
+=09=09wwo->is_releasing =3D FALSE;
+=09=09pa_threaded_mainloop_unlock(PULSE_ml);
+=09    } else
+=09=09wodPlayer_Underrun(wwo);
+=09    SetEvent(ev);
+=09    break;
+
+=09case WINE_WM_STARTING: /* Sent by the pulse thread */
+=09    /* Start releasing wavehdrs if we haven't already */
+=09    if (!wwo->is_releasing) {
+=09=09wwo->is_releasing =3D TRUE;
+=09=09pa_gettimeofday(&wwo->started_releasing);
+=09    }
+=09    SetEvent(ev);
+=09    break;
+
+=09case WINE_WM_CLOSING: /* If param =3D 1, close because of a failure */
+=09    wwo->hThread =3D NULL;
+=09    if ((DWORD)param =3D=3D 1) {
+=09=09/* If we are here, the stream has failed */
+=09=09wwo->state =3D WINE_WS_FAILED;
+=09=09SetEvent(ev);
+=09=09PULSE_DestroyRingMessage(&wwo->msgRing);
+=09=09wodPlayer_NotifyCompletions(wwo, TRUE);
+=09=09wodPlayer_NotifyClient(wwo, WOM_CLOSE, 0L, 0L);
+=09=09wwo->lpPlayPtr =3D wwo->lpQueuePtr =3D wwo->lpLoopPtr =3D NULL;
+=09=09pa_threaded_mainloop_lock(PULSE_ml);
+=09=09pa_stream_disconnect(wwo->stream);
+=09=09pa_threaded_mainloop_unlock(PULSE_ml);
+=09=09TRACE("Thread exiting because of failure.\n");
+=09=09ExitThread(1);
+=09=09/* Stream instance will get de-refferenced upon close */
+=09    }
+=09    wwo->state =3D WINE_WS_CLOSED;
+=09    /* sanity check: this should not happen since the device must have =
been reset before */
+=09    if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
+=09    SetEvent(ev);
+=09    TRACE("Thread exiting.\n");
+=09    ExitThread(0);
+=09    /* shouldn't go here */
+
+=09default:
+=09    FIXME("unknown message %d\n", msg);
+=09    break;
+=09}
+    }
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09wodPlayer=09=09=09[internal]
+ */
+static DWORD CALLBACK wodPlayer(LPVOID lpParam) {
+    WINE_WAVEINST *wwo =3D (WINE_WAVEINST*)lpParam;
+    DWORD         dwSleepTime =3D INFINITE;
+
+    wwo->state =3D WINE_WS_STOPPED;
+    SetEvent(wwo->hStartUpEvent);
+
+    /* Wait for the shortest time before an action is required.  If there =
are
+     * no pending actions, wait forever for a command. */
+    for (;;) {
+=09TRACE("Waiting %u ms\n", dwSleepTime);
+        PULSE_WaitRingMessage(&wwo->msgRing, dwSleepTime);
+=09wodPlayer_ProcessMessages(wwo);
+=09if (wwo->state =3D=3D WINE_WS_PLAYING) {
+=09    if (!wwo->is_releasing && wwo->lpQueuePtr)
+=09=09wodPlayer_CheckReleasing(wwo);
+=09    dwSleepTime =3D wodPlayer_NotifyCompletions(wwo, FALSE);
+=09} else
+=09    dwSleepTime =3D INFINITE;
+    }
+}
+
+/*************************************************************************=
*
+ *                              wodOpen                         [internal]
+ */
+static DWORD wodOpen(WORD wDevID, LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc,=
 DWORD dwFlags) {
+    WINE_WAVEDEV *wdo;
+    WINE_WAVEINST *wwo =3D NULL;
+    DWORD ret =3D MMSYSERR_NOERROR;
+    pa_stream_flags_t stream_flags;
+
+    TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
+    if (lpDesc =3D=3D NULL) {
+=09WARN("Invalid Parameter !\n");
+=09return MMSYSERR_INVALPARAM;
+    }
+
+    if (wDevID >=3D PULSE_WodNumDevs) {
+=09WARN("Asked for device %d, but only %d known!\n", wDevID, PULSE_WodNumD=
evs);
+=09return MMSYSERR_BADDEVICEID;
+    }
+    wdo =3D &WOutDev[wDevID];
+
+    wwo =3D HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_WAVE=
INST));
+    if (!wwo) return MMSYSERR_NOMEM;
+    *lpdwUser =3D (DWORD)wwo;
+
+    /* check to see if format is supported and make pa_sample_spec struct =
*/
+    if (!PULSE_SetupFormat(lpDesc->lpFormat, &wwo->sample_spec)) {
+=09WARN("Bad format: tag=3D%04X nChannels=3D%d nSamplesPerSec=3D%d !\n",
+=09     lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
+=09     lpDesc->lpFormat->nSamplesPerSec);
+=09ret =3D WAVERR_BADFORMAT;
+=09goto exit;
+    }
+
+    if (TRACE_ON(wave)) {
+    =09char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
+    =09pa_sample_spec_snprint(t, sizeof(t), &wwo->sample_spec);
+    =09TRACE("Sample spec '%s'\n", t);
+    }
+
+    if (dwFlags & WAVE_FORMAT_QUERY) {
+=09TRACE("Query format: tag=3D%04X nChannels=3D%d nSamplesPerSec=3D%d !\n"=
,
+=09     lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
+=09     lpDesc->lpFormat->nSamplesPerSec);
+=09ret =3D MMSYSERR_NOERROR;
+=09goto exit;
+    }
+
+    wwo->wFlags =3D HIWORD(dwFlags & CALLBACK_TYPEMASK);
+    wwo->waveDesc =3D *lpDesc;
+    PULSE_InitRingMessage(&wwo->msgRing);
+
+    wwo->stream =3D pa_stream_new(PULSE_context, "WaveOut", &wwo->sample_s=
pec, NULL);
+    /* If server doesn't support sample_spec, we will error out here */
+    if (!wwo->stream) {
+        ret =3D WAVERR_BADFORMAT;
+=09goto exit;
+    }
+
+    pa_stream_set_state_callback=09(wwo->stream, PULSE_StreamStateCallback=
,=09wwo);
+    pa_stream_set_write_callback=09(wwo->stream, PULSE_StreamRequestCallba=
ck,=09wwo);
+    pa_stream_set_underflow_callback=09(wwo->stream, PULSE_StreamUnderflow=
Callback,=09wwo);
+    pa_stream_set_overflow_callback=09(wwo->stream, PULSE_StreamOverflowCa=
llback,=09wwo);
+    pa_stream_set_moved_callback=09(wwo->stream, PULSE_StreamMovedCallback=
,=09wwo);
+    pa_stream_set_suspended_callback=09(wwo->stream, PULSE_StreamSuspended=
Callback,=09wwo);
+
+    stream_flags =3D PA_STREAM_AUTO_TIMING_UPDATE;
+
+#if PA_API_VERSION >=3D 12
+    if (pa_context_get_server_protocol_version(PULSE_context) >=3D 15) {
+=09pa_stream_set_started_callback(wwo->stream, WAVEOUT_StreamStartedCallba=
ck, wwo);
+=09stream_flags |=3D PA_STREAM_ADJUST_LATENCY;
+    } else
+#endif
+    {
+=09pa_stream_set_latency_update_callback(wwo->stream, WAVEOUT_StreamTiming=
InfoUpdateCallback, wwo);
+    }
+
+    TRACE("Connecting stream for playback on %s.\n", wdo->device_name);
+    pa_threaded_mainloop_lock(PULSE_ml);
+    pa_stream_connect_playback(wwo->stream, wdo->device_name, NULL, stream=
_flags, NULL, NULL);
+
+    for (;;) {
+        pa_context_state_t cstate =3D pa_context_get_state(PULSE_context);
+        pa_stream_state_t sstate =3D pa_stream_get_state(wwo->stream);
+
+=09if (cstate =3D=3D PA_CONTEXT_FAILED || cstate =3D=3D PA_CONTEXT_TERMINA=
TED ||
+=09    sstate =3D=3D PA_STREAM_FAILED || sstate =3D=3D PA_STREAM_TERMINATE=
D) {
+=09    ERR("Failed to connect stream context object: %s\n", pa_strerror(pa=
_context_errno(PULSE_context)));
+=09    pa_threaded_mainloop_unlock(PULSE_ml);
+=09    ret =3D MMSYSERR_NODRIVER;
+=09    goto exit;
+=09}
+
+=09if (sstate =3D=3D PA_STREAM_READY)
+=09    break;
+
+=09pa_threaded_mainloop_wait(PULSE_ml);
+    }
+    TRACE("(%p)->stream connected for playback.\n", wwo);
+
+    PULSE_WaitForOperation(pa_stream_update_timing_info(wwo->stream, PULSE=
_StreamSuccessCallback, wwo));
+
+    wwo->timing_info =3D pa_stream_get_timing_info(wwo->stream);
+    assert(wwo->timing_info);
+    pa_threaded_mainloop_unlock(PULSE_ml);
+
+    wwo->hStartUpEvent =3D CreateEventW(NULL, FALSE, FALSE, NULL);
+    wwo->hThread =3D CreateThread(NULL, 0, wodPlayer, (LPVOID)wwo, 0, &(ww=
o->dwThreadID));
+    if (wwo->hThread)
+        SetThreadPriority(wwo->hThread, THREAD_PRIORITY_TIME_CRITICAL);
+    else {
+        ERR("Thread creation for the wodPlayer failed!\n");
+        ret =3D MMSYSERR_NOMEM;
+=09goto exit;
+    }
+    WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
+    CloseHandle(wwo->hStartUpEvent);
+    wwo->hStartUpEvent =3D INVALID_HANDLE_VALUE;
+
+
+    return wodPlayer_NotifyClient (wwo, WOM_OPEN, 0L, 0L);
+
+exit:
+    if (!wwo)
+=09return ret;
+
+    if (wwo->hStartUpEvent !=3D INVALID_HANDLE_VALUE)
+=09CloseHandle(wwo->hStartUpEvent);
+
+    if (wwo->msgRing.ring_buffer_size > 0)
+=09    PULSE_DestroyRingMessage(&wwo->msgRing);
+
+    if (wwo->stream) {
+        if (pa_stream_get_state(wwo->stream) =3D=3D PA_STREAM_READY)
+=09    pa_stream_disconnect(wwo->stream);
+=09pa_stream_unref(wwo->stream);
+=09wwo->stream =3D NULL;
+    }
+    HeapFree(GetProcessHeap(), 0, wwo);
+
+    return ret;
+}
+
+/*************************************************************************=
*
+ *                              wodClose                        [internal]
+ */
+static DWORD wodClose(WINE_WAVEINST *wwo) {
+    DWORD ret;
+
+    TRACE("(%p);\n", wwo);
+    if (!wwo) {
+=09WARN("Stream instance invalid.\n");
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    if (wwo->state !=3D WINE_WS_FAILED) {
+=09if (wwo->lpQueuePtr && wwo->lpPlayPtr) {
+=09    WARN("buffers still playing !\n");
+=09    return WAVERR_STILLPLAYING;
+=09}
+
+=09pa_threaded_mainloop_lock(PULSE_ml);
+=09PULSE_WaitForOperation(pa_stream_drain(wwo->stream, PULSE_StreamSuccess=
Callback, NULL));
+=09pa_stream_disconnect(wwo->stream);
+=09pa_threaded_mainloop_unlock(PULSE_ml);
+
+=09if (wwo->hThread !=3D INVALID_HANDLE_VALUE)
+=09    PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
+
+=09PULSE_DestroyRingMessage(&wwo->msgRing);
+    }
+
+    if (wwo->stream)
+=09pa_stream_unref(wwo->stream);
+    ret =3D wodPlayer_NotifyClient(wwo, WOM_CLOSE, 0L, 0L);
+
+    HeapFree(GetProcessHeap(), 0, wwo);
+
+    return ret;
+}
+
+/*************************************************************************=
*
+ *                              wodWrite                        [internal]
+ */
+static DWORD wodWrite(WINE_WAVEINST *wwo, LPWAVEHDR lpWaveHdr, DWORD dwSiz=
e) {
+    if (!wwo || wwo->state =3D=3D WINE_WS_FAILED) {
+=09WARN("Stream instance invalid.\n");
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    if (lpWaveHdr->lpData =3D=3D NULL || !(lpWaveHdr->dwFlags & WHDR_PREPA=
RED))
+=09return WAVERR_UNPREPARED;
+
+    if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
+=09return WAVERR_STILLPLAYING;
+
+    lpWaveHdr->dwFlags &=3D ~WHDR_DONE;
+    lpWaveHdr->dwFlags |=3D WHDR_INQUEUE;
+    lpWaveHdr->lpNext =3D 0;
+    lpWaveHdr->reserved =3D 0;
+
+    PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, =
FALSE);
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              wodPause                        [internal]
+ */
+static DWORD wodPause(WINE_WAVEINST *wwo) {
+    if (!wwo ||=09wwo->state =3D=3D WINE_WS_FAILED) {
+=09WARN("Stream instance invalid.\n");
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_PAUSING, 0, TRUE);
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              wodGetPosition                  [internal]
+ */
+static DWORD wodGetPosition(WINE_WAVEINST *wwo, LPMMTIME lpTime, DWORD uSi=
ze) {
+    if (!wwo ||=09wwo->state =3D=3D WINE_WS_FAILED) {
+=09WARN("Stream instance invalid.\n");
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    if (lpTime =3D=3D NULL)=09return MMSYSERR_INVALPARAM;
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    if (wwo->timing_info->read_index_corrupt || wwo->timing_info->write_in=
dex_corrupt)
+=09PULSE_WaitForOperation(pa_stream_update_timing_info(wwo->stream, PULSE_=
StreamSuccessCallback, wwo));
+
+    PULSE_GetMMTime(wwo->timing_info, &wwo->sample_spec, wwo->last_reset, =
lpTime);
+    pa_threaded_mainloop_unlock(PULSE_ml);
+
+    return MMSYSERR_NOERROR;
+}
+/*************************************************************************=
*
+ *                              wodBreakLoop                    [internal]
+ */
+static DWORD wodBreakLoop(WINE_WAVEINST *wwo) {
+    if (!wwo ||=09wwo->state =3D=3D WINE_WS_FAILED) {
+=09WARN("Stream instance invalid.\n");
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              wodGetDevCaps                   [internal]
+ */
+static DWORD wodGetDevCaps(DWORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSi=
ze) {
+    TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
+
+    if (lpCaps =3D=3D NULL) return MMSYSERR_NOTENABLED;
+
+    if (wDevID >=3D PULSE_WodNumDevs) {
+=09TRACE("Asked for device %d, but only %d known!\n", wDevID, PULSE_WodNum=
Devs);
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    memcpy(lpCaps, &(WOutDev[wDevID].caps.out), min(dwSize, sizeof(*lpCaps=
)));
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              wodGetNumDevs                   [internal]
+ * Context-sanity check here, as if we respond with 0, WINE will move on
+ * to the next waveout driver.
+ */
+static DWORD wodGetNumDevs() {
+    if (!PULSE_ml || !PULSE_context || pa_context_get_state(PULSE_context)=
 !=3D PA_CONTEXT_READY)
+=09return 0;
+
+    return PULSE_WodNumDevs;
+}
+
+/*************************************************************************=
*
+ *                              wodGetVolume                    [internal]
+ */
+static DWORD wodGetVolume(WINE_WAVEINST *wwo, LPDWORD lpdwVol) {
+    float   value1, value2;
+    DWORD   wleft, wright;
+
+    if (!wwo ||=09wwo->state =3D=3D WINE_WS_FAILED) {
+=09WARN("Stream instance invalid.\n");
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    TRACE("(%p, %p);\n", wwo, lpdwVol);
+
+    if (lpdwVol =3D=3D NULL)
+=09return MMSYSERR_NOTENABLED;
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    if (wwo->stream && PULSE_context && pa_context_get_state(PULSE_context=
) =3D=3D PA_CONTEXT_READY &&
+=09pa_stream_get_state(wwo->stream) =3D=3D PA_STREAM_READY) {
+=09PULSE_WaitForOperation(pa_context_get_sink_input_info(PULSE_context, pa=
_stream_get_index(wwo->stream), WAVEOUT_SinkInputInfoCallback, wwo));
+    }
+    pa_threaded_mainloop_unlock(PULSE_ml);
+
+
+    if (wwo->volume.channels =3D=3D 2) {
+=09value1 =3D pa_sw_volume_to_dB(wwo->volume.values[0]);
+=09value2 =3D pa_sw_volume_to_dB(wwo->volume.values[1]);
+    } else {
+=09value1 =3D pa_sw_volume_to_dB(pa_cvolume_avg(&wwo->volume));
+=09value2 =3D 0;
+    }
+
+    if (value1 < -60)
+=09wleft =3D 0;
+    else
+
+    if (value2 < -60)
+=09wright =3D 0;
+    else
+=09wright =3D 0xFFFFl - ((value2 / -60)*(float)0xFFFFl);
+
+    if (wleft > 0xFFFFl)
+=09wleft =3D 0xFFFFl;
+    if (wright > 0xFFFFl)
+=09wright =3D 0xFFFFl;
+
+    *lpdwVol =3D (WORD)wleft + (WORD)(wright << 16);
+
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              wodSetVolume                    [internal]
+ */
+static DWORD wodSetVolume(WINE_WAVEINST *wwo, DWORD dwParam1) {
+    double value1, value2;
+
+    TRACE("(%p, %08X);\n", wwo, dwParam1);
+    if (!wwo ||=09wwo->state =3D=3D WINE_WS_FAILED) {
+=09WARN("Stream instance invalid.\n");
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    /* waveOut volumes are /supposed/ to be logarithmic */
+    value1 =3D LOWORD(dwParam1) =3D=3D 0 ? PA_DECIBEL_MININFTY : ((float)(=
0xFFFFl - LOWORD(dwParam1))/0xFFFFl) * -60.0;
+    value2 =3D HIWORD(dwParam1) =3D=3D 0 ? PA_DECIBEL_MININFTY : ((float)(=
0xFFFFl - HIWORD(dwParam1))/0xFFFFl) * -60.0;
+
+    if (wwo->sample_spec.channels =3D=3D 2) {
+=09wwo->volume.channels =3D 2;
+=09wwo->volume.values[0] =3D pa_sw_volume_from_dB(value1);
+=09wwo->volume.values[1] =3D pa_sw_volume_from_dB(value2);
+    } else {
+=09if (value1 !=3D value2) FIXME("Non-stereo streams can't pan!\n");
+        wwo->volume.channels =3D wwo->sample_spec.channels;
+=09pa_cvolume_set(&wwo->volume, wwo->volume.channels, pa_sw_volume_from_dB=
(max(value1, value2)));
+    }
+
+    if (TRACE_ON(wave)) {
+=09char s[PA_CVOLUME_SNPRINT_MAX];
+=09pa_cvolume_snprint(s, PA_CVOLUME_SNPRINT_MAX, &wwo->volume);
+=09TRACE("%s\n", s);
+    }
+
+    pa_threaded_mainloop_lock(PULSE_ml);
+    if (!wwo->stream || !PULSE_context || pa_context_get_state(PULSE_conte=
xt) !=3D PA_CONTEXT_READY ||
+=09pa_stream_get_state(wwo->stream) !=3D PA_STREAM_READY || !pa_cvolume_va=
lid(&wwo->volume)) {
+=09pa_threaded_mainloop_unlock(PULSE_ml);
+=09return MMSYSERR_NOERROR;
+    }
+
+    PULSE_WaitForOperation(pa_context_set_sink_input_volume(PULSE_context,
+=09    pa_stream_get_index(wwo->stream), &wwo->volume,
+=09    PULSE_ContextSuccessCallback, wwo));
+    pa_threaded_mainloop_unlock(PULSE_ml);
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              wodRestart                      [internal]
+ */
+static DWORD wodRestart(WINE_WAVEINST *wwo) {
+    if (!wwo ||=09wwo->state =3D=3D WINE_WS_FAILED) {
+        WARN("Stream instance invalid.\n");
+        return MMSYSERR_INVALHANDLE;
+    }
+
+    if (wwo->state =3D=3D WINE_WS_PAUSED)
+=09PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_RESTARTING, 0, TRUE);
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              wodReset                        [internal]
+ */
+static DWORD wodReset(WINE_WAVEINST *wwo) {
+    if (!wwo ||=09wwo->state =3D=3D WINE_WS_FAILED) {
+=09WARN("Stream instance invalid.\n");
+=09return MMSYSERR_INVALHANDLE;
+    }
+
+    PULSE_AddRingMessage(&wwo->msgRing, WINE_WM_RESETTING, 0, TRUE);
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              wodDevInterfaceSize             [internal]
+ */
+static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1) {
+
+    *dwParam1 =3D MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface=
_name, -1, NULL, 0) * sizeof(WCHAR);
+    return MMSYSERR_NOERROR;
+}
+
+/*************************************************************************=
*
+ *                              wodDevInterface                 [internal]
+ */
+static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)=
 {
+    if (dwParam2 >=3D MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].inter=
face_name, -1,
+                                        NULL, 0 ) * sizeof(WCHAR))
+    {
+        MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
+                            dwParam1, dwParam2 / sizeof(WCHAR));
+=09return MMSYSERR_NOERROR;
+    }
+    return MMSYSERR_INVALPARAM;
+}
+
+/*************************************************************************=
*
+ * =09=09=09=09wodMessage (WINEPULSE.@)
+ */
+DWORD WINAPI PULSE_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
+=09=09=09    DWORD dwParam1, DWORD dwParam2) {
+/*
+    TRACE("(%u, %s, %08X, %08X, %08X);\n",
+=09  wDevID, PULSE_getMessage(wMsg), dwUser, dwParam1, dwParam2);
+*/
+    switch (wMsg) {
+    case DRVM_INIT:
+    case DRVM_EXIT:
+    case DRVM_ENABLE:
+    case DRVM_DISABLE:
+=09/* FIXME: Pretend this is supported */
+=09return 0;
+    case WODM_OPEN:=09 =09return wodOpen=09=09(wDevID, (LPDWORD)dwUser, (L=
PWAVEOPENDESC)dwParam1,=09dwParam2);
+    case WODM_CLOSE:=09 =09return wodClose=09=09((WINE_WAVEINST*)dwUser);
+    case WODM_WRITE:=09 =09return wodWrite=09=09((WINE_WAVEINST*)dwUser,  =
  (LPWAVEHDR)dwParam1,=09=09dwParam2);
+    case WODM_PAUSE:=09 =09return wodPause=09=09((WINE_WAVEINST*)dwUser);
+    case WODM_GETPOS:=09 =09return wodGetPosition=09((WINE_WAVEINST*)dwUse=
r,    (LPMMTIME)dwParam1, =09=09dwParam2);
+    case WODM_BREAKLOOP: =09return wodBreakLoop     ((WINE_WAVEINST*)dwUse=
r);
+    case WODM_PREPARE:=09 =09return MMSYSERR_NOTSUPPORTED;
+    case WODM_UNPREPARE: =09return MMSYSERR_NOTSUPPORTED;
+    case WODM_GETDEVCAPS:=09return wodGetDevCaps=09(wDevID, (LPWAVEOUTCAPS=
W)dwParam1,=09dwParam2);
+    case WODM_GETNUMDEVS:=09return wodGetNumDevs=09();
+    case WODM_GETPITCH:=09 =09return MMSYSERR_NOTSUPPORTED;
+    case WODM_SETPITCH:=09 =09return MMSYSERR_NOTSUPPORTED;
+    case WODM_GETPLAYBACKRATE:=09return MMSYSERR_NOTSUPPORTED; /* support =
if theoretically possible */
+    case WODM_SETPLAYBACKRATE:=09return MMSYSERR_NOTSUPPORTED; /* since pu=
lseaudio 0.9.8 */
+    case WODM_GETVOLUME:=09return wodGetVolume=09((WINE_WAVEINST*)dwUser, =
(LPDWORD)dwParam1);
+    case WODM_SETVOLUME:=09return wodSetVolume=09((WINE_WAVEINST*)dwUser, =
dwParam1);
+    case WODM_RESTART:=09=09return wodRestart=09((WINE_WAVEINST*)dwUser);
+    case WODM_RESET:=09=09return wodReset=09=09((WINE_WAVEINST*)dwUser);
+    case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize      (wD=
evID, (LPDWORD)dwParam1);
+    case DRV_QUERYDEVICEINTERFACE:     return wodDevInterface          (wD=
evID, (PWCHAR)dwParam1, dwParam2);
+    case DRV_QUERYDSOUNDIFACE:=09return wodDsCreate=09(wDevID, (PIDSDRIVER=
*)dwParam1);
+    case DRV_QUERYDSOUNDDESC:=09return wodDsDesc=09(wDevID, (PDSDRIVERDESC=
)dwParam1);
+    default:
+=09FIXME("unknown message %d!\n", wMsg);
+    }
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+#else /* !HAVE_PULSEAUDIO */
+
+/*************************************************************************=
*
+ * =09=09=09=09wodMessage (WINEPULSE.@)
+ */
+DWORD WINAPI PULSE_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
+=09=09=09      DWORD dwParam1, DWORD dwParam2) {
+    FIXME("(%u, %04X, %08X, %08X, %08X):stub\n", wDevID, wMsg, dwUser,
+          dwParam1, dwParam2);
+    return MMSYSERR_NOTENABLED;
+}
+
+#endif /* HAVE_PULSEAUDIO */
diff --git a/dlls/winepulse.drv/winepulse.drv.spec b/dlls/winepulse.drv/win=
epulse.drv.spec
new file mode 100644
index 0000000..1b49460
--- /dev/null
+++ b/dlls/winepulse.drv/winepulse.drv.spec
@@ -0,0 +1,3 @@
+@ stdcall -private DriverProc(long long long long long long) PULSE_DriverP=
roc
+@ stdcall -private wodMessage(long long long long long long) PULSE_wodMess=
age
+@ stdcall -private widMessage(long long long long long long) PULSE_widMess=
age
diff --git a/dlls/winepulse.drv/winepulse.h b/dlls/winepulse.drv/winepulse.=
h
new file mode 100644
index 0000000..59fbb38
--- /dev/null
+++ b/dlls/winepulse.drv/winepulse.h
@@ -0,0 +1,228 @@
+/* Definitions for PulseAudio Wine Driver
+ *
+ * Copyright=092009 Arthur Taylor <theycallhimart at gmail.com>
+ *
+ * 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, U=
SA
+ */
+
+#ifndef __WINE_CONFIG_H
+# error You must include config.h to use this header
+#endif
+
+#if defined(HAVE_PULSEAUDIO) && !defined(__WINEPULSE_H)
+#define __WINEPULSE_H
+
+#include "mmreg.h"
+#include "dsound.h"
+#include "dsdriver.h"
+
+#include "ks.h"
+#include "ksmedia.h"
+#include "ksguid.h"
+
+#include <pulse/pulseaudio.h>
+
+/* state diagram for waveOut writing:
+ *
+ * +---------+-------------+---------------+------------------------------=
---+
+ * |  state  |  function   |     event     |            new state         =
   |
+ * +---------+-------------+---------------+------------------------------=
---+
+ * |         | open()      |               | STOPPED                      =
   |
+ * | PAUSED  | write()     |               | PAUSED                       =
   |
+ * | STOPPED | write()     | <thrd create> | PLAYING                      =
   |
+ * | PLAYING | write()     | HEADER        | PLAYING                      =
   |
+ * | (other) | write()     | <error>       |                              =
   |
+ * | (any)   | pause()     | PAUSING       | PAUSED                       =
   |
+ * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd =3D> STOP=
PED) |
+ * | (any)   | reset()     | RESETTING     | STOPPED                      =
   |
+ * | (any)   | close()     | CLOSING       | CLOSED                       =
   |
+ * +---------+-------------+---------------+------------------------------=
---+
+ */
+
+/* states of the playing device */
+#define WINE_WS_PLAYING         1
+#define WINE_WS_PAUSED          2
+#define WINE_WS_STOPPED         3
+#define WINE_WS_CLOSED          4
+#define WINE_WS_FAILED          5
+
+#define PULSE_ALL_FORMATS \
+        WAVE_FORMAT_1M08 |=09/* Mono=09    11025Hz 8-bit  */\
+        WAVE_FORMAT_1M16 |=09/* Mono=09    11025Hz 16-bit */\
+        WAVE_FORMAT_1S08 |=09/* Stereo   11025Hz 8-bit  */\
+        WAVE_FORMAT_1S16 |=09/* Stereo   11025Hz 16-bit */\
+        WAVE_FORMAT_2M08 |=09/* Mono=09    22050Hz 8-bit  */\
+        WAVE_FORMAT_2M16 |=09/* Mono=09    22050Hz 16-bit */\
+        WAVE_FORMAT_2S08 |=09/* Stereo   22050Hz 8-bit  */\
+=09WAVE_FORMAT_2S16 |=09/* Stereo   22050Hz 16-bit */\
+        WAVE_FORMAT_4M08 |=09/* Mono=09    44100Hz 8-bit  */\
+=09WAVE_FORMAT_4M16 |=09/* Mono=09    44100Hz 16-bit */\
+        WAVE_FORMAT_4S08 |=09/* Stereo   44100Hz 8-bit  */\
+=09WAVE_FORMAT_4S16 |=09/* Stereo   44100Hz 16-bit */\
+        WAVE_FORMAT_48M08 |=09/* Mono=09    48000Hz 8-bit  */\
+        WAVE_FORMAT_48S08 |=09/* Stereo   48000Hz 8-bit  */\
+        WAVE_FORMAT_48M16 |=09/* Mono=09    48000Hz 16-bit */\
+        WAVE_FORMAT_48S16 |=09/* Stereo   48000Hz 16-bit */\
+=09WAVE_FORMAT_96M08 |=09/* Mono=09    96000Hz 8-bit  */\
+=09WAVE_FORMAT_96S08 |=09/* Stereo   96000Hz 8-bit  */\
+        WAVE_FORMAT_96M16 |=09/* Mono=09    96000Hz 16-bit */\
+=09WAVE_FORMAT_96S16=09/* Stereo   96000Hz 16-bit */
+
+/* events to be sent to device */
+enum win_wm_message {
+    WINE_WM_PAUSING =3D WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING=
, WINE_WM_HEADER,
+    WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING=
, WINE_WM_XRUN, WINE_WM_FEED
+};
+
+typedef struct {
+    enum win_wm_message =09msg;=09/* message identifier */
+    DWORD=09                param;  /* parameter for this message */
+    HANDLE=09                hEvent;=09/* if message is synchronous, handl=
e of event for synchro */
+} PULSE_MSG;
+
+/* implement an in-process message ring for better performance
+ * (compared to passing thru the server)
+ * this ring will be used by the input (resp output) record (resp playback=
) routine
+ */
+typedef struct {
+    PULSE_MSG=09=09=09* messages;
+    int                         ring_buffer_size;
+    int=09=09=09=09msg_tosave;
+    int=09=09=09=09msg_toget;
+/* Either pipe or event is used, but that is defined in pulse.c,
+ * since this is a global header we define both here */
+    int                         msg_pipe[2];
+    HANDLE                      msg_event;
+    CRITICAL_SECTION=09=09msg_crst;
+} PULSE_MSG_RING;
+
+typedef struct WINE_WAVEDEV WINE_WAVEDEV;
+typedef struct WINE_WAVEINST WINE_WAVEINST;
+typedef struct IDsDriverImpl IDsDriverImpl;
+typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
+
+struct IDsDriverImpl {
+    /* IUnknown fields */
+    const IDsDriverVtbl *lpVtbl;
+    LONG    ref;
+
+    IDsDriverBufferImpl *primary;
+    UINT    wDevID;
+};
+
+struct IDsDriverBufferImpl {
+    const IDsDriverBufferVtbl=09*lpVtbl;
+    IDsDriverImpl*=09=09drv;
+    LONG=09=09=09ref;
+    pa_stream=09=09=09*stream;
+    pa_sample_spec=09=09sample_spec;
+    pa_cvolume=09=09=09volume;
+
+    PBYTE=09=09buffer;
+    DWORD=09=09buffer_length;
+    DWORD=09=09buffer_read_offset;
+    DWORD=09=09buffer_play_offset;
+    DWORD=09=09fraglen;
+};
+
+/* Per-playback/record device */
+struct WINE_WAVEDEV {
+    char                interface_name[MAXPNAMELEN * 2];
+    char=09=09*device_name;
+    pa_cvolume=09=09volume;
+
+    union {
+        WAVEOUTCAPSW=09out;
+=09WAVEINCAPSW=09in;
+    } caps;
+   =20
+    /* DirectSound stuff */
+    DSDRIVERDESC                ds_desc;
+    DSDRIVERCAPS                ds_caps;
+};
+
+/* Per-playback/record instance */
+struct WINE_WAVEINST {
+    volatile INT        state;=09=09    /* one of the WINE_WS_ manifest co=
nstants */
+    WAVEOPENDESC        waveDesc;
+    WORD=09=09wFlags;
+
+    pa_stream=09=09*stream;=09    /* The PulseAudio stream */
+    const pa_timing_info=09*timing_info;
+    pa_sample_spec=09sample_spec;=09    /* Sample spec of this stream / de=
vice */
+    pa_cvolume=09=09volume;
+    pa_buffer_attr=09buffer_attr;
+
+    /* waveIn / waveOut wavaHdr information */
+    LPWAVEHDR=09=09lpQueuePtr;=09    /* start of queued WAVEHDRs (waiting =
to be notified) */
+    LPWAVEHDR=09=09lpPlayPtr;=09    /* start of not yet fully written buff=
ers */
+    DWORD=09=09dwPartialOffset;    /* Offset of not yet written bytes in l=
pPlayPtr */
+    LPWAVEHDR=09=09lpLoopPtr;          /* pointer of first buffer in loop,=
 if any */
+    DWORD=09=09dwLoops;=09    /* private copy of loop counter */
+
+    /* Virtual stream positioning information */
+    DWORD=09=09last_reset;=09    /* When the last reset occured, as pa str=
eam time doesn't reset */
+    struct timeval=09last_header;=09    /* When the last wavehdr was recei=
ved, only updated when audio is not playing yet */
+    BOOL=09=09is_releasing;=09    /* Whether we are releasing wavehdrs */
+    struct timeval=09started_releasing;  /* When wavehdr releasing started=
, for comparison to queued written wavehdrs */
+    DWORD=09=09releasing_offset;   /* How much audio has been released pri=
or when releasing started in this instance */
+
+    /* waveIn */
+    const void=09=09*buffer;=09    /* Pointer to the latest data fragment =
for recording streams */
+    DWORD=09=09buffer_length;=09    /* How large the latest data fragment =
is */
+    DWORD=09=09buffer_read_offset; /* How far into latest data fragment we=
 last read */
+    DWORD=09=09fraglen;   =20
+
+    /* Thread communication and synchronization stuff */
+    HANDLE=09=09hStartUpEvent;
+    HANDLE=09=09hThread;
+    DWORD=09=09dwThreadID;
+    PULSE_MSG_RING=09msgRing;
+};
+
+/* We establish one context per instance, so make it global to the lib */
+pa_context=09=09*PULSE_context;   /* Connection Context */
+pa_threaded_mainloop=09*PULSE_ml;        /* PA Runtime information */
+
+/* WaveIn / WaveOut devices */
+WINE_WAVEDEV *WOutDev;
+WINE_WAVEDEV *WInDev;
+DWORD PULSE_WodNumDevs;
+DWORD PULSE_WidNumDevs;
+
+/* pulse.c */
+void=09PULSE_WaitForOperation(pa_operation *o);
+void=09PULSE_StreamSuccessCallback(pa_stream *s, int success, void *userda=
ta);
+void=09PULSE_StreamStateCallback(pa_stream *s, void *userdata);
+void=09PULSE_StreamRequestCallback(pa_stream *s, size_t n, void *userdata)=
;
+void=09PULSE_StreamUnderflowCallback(pa_stream *s, void *userdata);
+void=09PULSE_StreamOverflowCallback(pa_stream *s, void *userdata);
+void=09PULSE_StreamSuspendedCallback(pa_stream *s, void *userdata);
+void=09PULSE_StreamMovedCallback(pa_stream *s, void *userdata);
+void=09PULSE_ContextSuccessCallback(pa_context *c, int success, void *user=
data);
+BOOL=09PULSE_SetupFormat(LPWAVEFORMATEX wf, pa_sample_spec *ss);
+void=09PULSE_GetMMTime(const pa_timing_info *t, pa_sample_spec *s, size_t =
last_reset, LPMMTIME lpTime);
+int=09PULSE_InitRingMessage(PULSE_MSG_RING* omr);
+int=09PULSE_DestroyRingMessage(PULSE_MSG_RING* omr);
+void=09PULSE_ResetRingMessage(PULSE_MSG_RING* omr);
+void=09PULSE_WaitRingMessage(PULSE_MSG_RING* omr, DWORD sleep);
+int=09PULSE_AddRingMessage(PULSE_MSG_RING* omr, enum win_wm_message msg, D=
WORD param, BOOL wait);
+int=09PULSE_RetrieveRingMessage(PULSE_MSG_RING* omr, enum win_wm_message *=
msg, DWORD *param, HANDLE *hEvent);
+const char * PULSE_getCmdString(enum win_wm_message msg);
+
+/* dsoutput.c */
+DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
+DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
+#endif
diff --git a/include/config.h.in b/include/config.h.in
index 9e93513..908e2f6 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -594,6 +594,9 @@
 /* Define to 1 if you have the <pthread_np.h> header file. */
 #undef HAVE_PTHREAD_NP_H
=20
+/* define this if you have pulseaudio */
+#undef HAVE_PULSEAUDIO
+
 /* Define to 1 if you have the <pwd.h> header file. */
 #undef HAVE_PWD_H
=20
diff --git a/programs/winecfg/Bg.rc b/programs/winecfg/Bg.rc
index 8861657..cf91012 100644
--- a/programs/winecfg/Bg.rc
+++ b/programs/winecfg/Bg.rc
@@ -274,6 +274,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standard"
     IDS_ACCEL_BASIC             "Basic"
     IDS_ACCEL_EMULATION         "Emulation"
+    IDS_DRIVER_PULSE             "PulseAudio Driver"
     IDS_DRIVER_ALSA             "ALSA Driver"
     IDS_DRIVER_ESOUND           "EsounD Driver"
     IDS_DRIVER_OSS              "OSS Driver"
diff --git a/programs/winecfg/Cs.rc b/programs/winecfg/Cs.rc
index 07f035f..8897237 100644
--- a/programs/winecfg/Cs.rc
+++ b/programs/winecfg/Cs.rc
@@ -273,6 +273,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standardn=ED"
     IDS_ACCEL_BASIC             "Z=E1kladn=ED"
     IDS_ACCEL_EMULATION         "Emulace"
+    IDS_DRIVER_PULSE             "Ovlada=E8 PulseAudio"
     IDS_DRIVER_ALSA             "Ovlada=E8 ALSA"
     IDS_DRIVER_ESOUND           "Ovlada=E8 EsounD"
     IDS_DRIVER_OSS              "Ovlada=E8 OSS"
diff --git a/programs/winecfg/Da.rc b/programs/winecfg/Da.rc
index 1d655ad..a1c9653 100644
--- a/programs/winecfg/Da.rc
+++ b/programs/winecfg/Da.rc
@@ -268,6 +268,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standard"
     IDS_ACCEL_BASIC             "Grundl=E6ggende"
     IDS_ACCEL_EMULATION         "Emul=E9ring"
+    IDS_DRIVER_PULSE             "PulseAudio-driver"
     IDS_DRIVER_ALSA             "ALSA-driver"
     IDS_DRIVER_ESOUND           "EsounD-driver"
     IDS_DRIVER_OSS              "OSS-driver"
diff --git a/programs/winecfg/De.rc b/programs/winecfg/De.rc
index 9ed3d63..614247b 100644
--- a/programs/winecfg/De.rc
+++ b/programs/winecfg/De.rc
@@ -268,6 +268,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standard"
     IDS_ACCEL_BASIC             "Einfach"
     IDS_ACCEL_EMULATION         "Emulation"
+    IDS_DRIVER_PULSE             "PulseAudio-Treiber"
     IDS_DRIVER_ALSA             "ALSA-Treiber"
     IDS_DRIVER_ESOUND           "EsounD-Treiber"
     IDS_DRIVER_OSS              "OSS-Treiber"
diff --git a/programs/winecfg/En.rc b/programs/winecfg/En.rc
index 5743ba3..b165a81 100644
--- a/programs/winecfg/En.rc
+++ b/programs/winecfg/En.rc
@@ -270,6 +270,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standard"
     IDS_ACCEL_BASIC             "Basic"
     IDS_ACCEL_EMULATION         "Emulation"
+    IDS_DRIVER_PULSE             "PulseAudio Driver"
     IDS_DRIVER_ALSA             "ALSA Driver"
     IDS_DRIVER_ESOUND           "EsounD Driver"
     IDS_DRIVER_OSS              "OSS Driver"
diff --git a/programs/winecfg/Es.rc b/programs/winecfg/Es.rc
index 440ea71..dd1b764 100644
--- a/programs/winecfg/Es.rc
+++ b/programs/winecfg/Es.rc
@@ -268,6 +268,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Est=E1ndar"
     IDS_ACCEL_BASIC             "B=E1sica"
     IDS_ACCEL_EMULATION         "Emulaci=F3n"
+    IDS_DRIVER_PULSE             "Manejador PulseAudio"
     IDS_DRIVER_ALSA             "Manejador ALSA"
     IDS_DRIVER_ESOUND           "Manejador EsounD"
     IDS_DRIVER_OSS              "Manejador OSS"
diff --git a/programs/winecfg/Fi.rc b/programs/winecfg/Fi.rc
index 1d761f8..e1b1583 100644
--- a/programs/winecfg/Fi.rc
+++ b/programs/winecfg/Fi.rc
@@ -268,6 +268,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standard"
     IDS_ACCEL_BASIC             "Basic"
     IDS_ACCEL_EMULATION         "Emulation"
+    IDS_DRIVER_PULSE             "PulseAudio Driver"
     IDS_DRIVER_ALSA             "ALSA Driver"
     IDS_DRIVER_ESOUND           "EsounD Driver"
     IDS_DRIVER_OSS              "OSS Driver"
diff --git a/programs/winecfg/Fr.rc b/programs/winecfg/Fr.rc
index d72bafc..e0495c2 100644
--- a/programs/winecfg/Fr.rc
+++ b/programs/winecfg/Fr.rc
@@ -268,6 +268,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standard"
     IDS_ACCEL_BASIC             "Basique"
     IDS_ACCEL_EMULATION         "=C9mulation"
+    IDS_DRIVER_PULSE             "Pilote PulseAudio"
     IDS_DRIVER_ALSA             "Pilote ALSA"
     IDS_DRIVER_ESOUND           "Pilote EsounD"
     IDS_DRIVER_OSS              "Pilote OSS"
diff --git a/programs/winecfg/Hu.rc b/programs/winecfg/Hu.rc
index 5afbf91..4131f5f 100644
--- a/programs/winecfg/Hu.rc
+++ b/programs/winecfg/Hu.rc
@@ -268,6 +268,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standard"
     IDS_ACCEL_BASIC             "Basic"
     IDS_ACCEL_EMULATION         "Emulation"
+    IDS_DRIVER_PULSE             "PulseAudio Driver"
     IDS_DRIVER_ALSA             "ALSA Driver"
     IDS_DRIVER_ESOUND           "EsounD Driver"
     IDS_DRIVER_OSS              "OSS Driver"
diff --git a/programs/winecfg/Ja.rc b/programs/winecfg/Ja.rc
index 8eaa603..543ed2c 100644
--- a/programs/winecfg/Ja.rc
+++ b/programs/winecfg/Ja.rc
@@ -273,6 +273,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "=E6=A8=99=E6=BA=96"
     IDS_ACCEL_BASIC             "=E5=9F=BA=E6=9C=AC"
     IDS_ACCEL_EMULATION         "=E3=82=A8=E3=83=9F=E3=83=A5=E3=83=AC=E3=
=83=BC=E3=82=B7=E3=83=A7=E3=83=B3"
+    IDS_DRIVER_PULSE             "PulseAudio Driver"
     IDS_DRIVER_ALSA             "ALSA Driver"
     IDS_DRIVER_ESOUND           "EsounD Driver"
     IDS_DRIVER_OSS              "OSS Driver"
diff --git a/programs/winecfg/Ko.rc b/programs/winecfg/Ko.rc
index ca94b5b..a79cc03 100644
--- a/programs/winecfg/Ko.rc
+++ b/programs/winecfg/Ko.rc
@@ -272,6 +272,7 @@ BEGIN
    IDS_ACCEL_STANDARD          "=C7=A5=C1=D8"
    IDS_ACCEL_BASIC             "=B1=E2=BA=BB"
    IDS_ACCEL_EMULATION         "=BE=D6=B9=C4=B7=B9=C0=CC=BC=C7"
+    IDS_DRIVER_PULSE             "PulseAudio =B5=E5=B6=F3=C0=CC=B9=F6"
    IDS_DRIVER_ALSA             "ALSA =B5=E5=B6=F3=C0=CC=B9=F6"
    IDS_DRIVER_ESOUND           "EsounD =B5=E5=B6=F3=C0=CC=B9=F6"
    IDS_DRIVER_OSS              "OSS =B5=E5=B6=F3=C0=CC=B9=F6"
diff --git a/programs/winecfg/Nl.rc b/programs/winecfg/Nl.rc
index fb91290..b23e919 100644
--- a/programs/winecfg/Nl.rc
+++ b/programs/winecfg/Nl.rc
@@ -270,6 +270,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standaard"
     IDS_ACCEL_BASIC             "Eenvoudig"
     IDS_ACCEL_EMULATION         "Emulatie"
+    IDS_DRIVER_PULSE             "PulseAudio Stuurprogramma"
     IDS_DRIVER_ALSA             "ALSA Stuurprogramma"
     IDS_DRIVER_ESOUND           "EsounD Stuurprogramma"
     IDS_DRIVER_OSS              "OSS Stuurprogramma"
diff --git a/programs/winecfg/No.rc b/programs/winecfg/No.rc
index aaa64c3..9dd3572 100644
--- a/programs/winecfg/No.rc
+++ b/programs/winecfg/No.rc
@@ -268,6 +268,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standard"
     IDS_ACCEL_BASIC             "Grunnleggende"
     IDS_ACCEL_EMULATION         "Emulering"
+    IDS_DRIVER_PULSE             "PulseAudio-driver"
     IDS_DRIVER_ALSA             "ALSA-driver"
     IDS_DRIVER_ESOUND           "EsounD-driver"
     IDS_DRIVER_OSS              "OSS-driver"
diff --git a/programs/winecfg/Pl.rc b/programs/winecfg/Pl.rc
index c426443..e3bf093 100644
--- a/programs/winecfg/Pl.rc
+++ b/programs/winecfg/Pl.rc
@@ -271,6 +271,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standardowe"
     IDS_ACCEL_BASIC             "Podstawowe"
     IDS_ACCEL_EMULATION         "Emulacja"
+    IDS_DRIVER_PULSE             "Sterownik PulseAudio"
     IDS_DRIVER_ALSA             "Sterownik ALSA"
     IDS_DRIVER_ESOUND           "Sterownik EsounD"
     IDS_DRIVER_OSS              "Sterownik OSS"
diff --git a/programs/winecfg/Pt.rc b/programs/winecfg/Pt.rc
index 830cabf..0ed1dbd 100644
--- a/programs/winecfg/Pt.rc
+++ b/programs/winecfg/Pt.rc
@@ -465,6 +465,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Padr=E3o"
     IDS_ACCEL_BASIC             "B=E1sico"
     IDS_ACCEL_EMULATION         "Emula=E7=E3o"
+    IDS_DRIVER_PULSE             "Controlador PulseAudio"
     IDS_DRIVER_ALSA             "Controlador ALSA"
     IDS_DRIVER_ESOUND           "Controlador EsounD"
     IDS_DRIVER_OSS              "Controlador OSS"
diff --git a/programs/winecfg/Ro.rc b/programs/winecfg/Ro.rc
index a4b0cad..c065d36 100644
--- a/programs/winecfg/Ro.rc
+++ b/programs/winecfg/Ro.rc
@@ -270,6 +270,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standard"
     IDS_ACCEL_BASIC             "De bas=C4=83"
     IDS_ACCEL_EMULATION         "Emulare"
+    IDS_DRIVER_PULSE             "Driver PulseAudio"
     IDS_DRIVER_ALSA             "Driver ALSA"
     IDS_DRIVER_ESOUND           "Driver Esound"
     IDS_DRIVER_OSS              "Driver OSS"
diff --git a/programs/winecfg/Ru.rc b/programs/winecfg/Ru.rc
index 782e1fd..f516399 100644
--- a/programs/winecfg/Ru.rc
+++ b/programs/winecfg/Ru.rc
@@ -272,6 +272,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "=D1=F2=E0=ED=E4=E0=F0=F2=ED=EE=E5"
     IDS_ACCEL_BASIC             "=CC=E8=ED=E8=EC=E0=EB=FC=ED=EE=E5"
     IDS_ACCEL_EMULATION         "=DD=EC=F3=EB=FF=F6=E8=FF"
+    IDS_DRIVER_PULSE             "PulseAudio =E4=F0=E0=E9=E2=E5=F0"
     IDS_DRIVER_ALSA             "ALSA =E4=F0=E0=E9=E2=E5=F0"
     IDS_DRIVER_ESOUND           "EsounD =E4=F0=E0=E9=E2=E5=F0"
     IDS_DRIVER_OSS              "OSS =E4=F0=E0=E9=E2=E5=F0"
diff --git a/programs/winecfg/Si.rc b/programs/winecfg/Si.rc
index 0bd04ef..c8bd35e 100644
--- a/programs/winecfg/Si.rc
+++ b/programs/winecfg/Si.rc
@@ -270,6 +270,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standardno"
     IDS_ACCEL_BASIC             "Osnovno"
     IDS_ACCEL_EMULATION         "Emulacija"
+    IDS_DRIVER_PULSE             "PulseAudio gonilnik"
     IDS_DRIVER_ALSA             "ALSA gonilnik"
     IDS_DRIVER_ESOUND           "EsounD gonilnik"
     IDS_DRIVER_OSS              "OSS gonilnik"
diff --git a/programs/winecfg/Sv.rc b/programs/winecfg/Sv.rc
index 49bb236..7efe2c0 100644
--- a/programs/winecfg/Sv.rc
+++ b/programs/winecfg/Sv.rc
@@ -268,6 +268,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standard"
     IDS_ACCEL_BASIC             "Grundl=E4ggande"
     IDS_ACCEL_EMULATION         "Emulering"
+    IDS_DRIVER_PULSE             "PulseAudio-drivrutin"
     IDS_DRIVER_ALSA             "ALSA-drivrutin"
     IDS_DRIVER_ESOUND           "EsounD-drivrutin"
     IDS_DRIVER_OSS              "OSS-drivrutin"
diff --git a/programs/winecfg/Tr.rc b/programs/winecfg/Tr.rc
index 4157f86..1be23c5 100644
--- a/programs/winecfg/Tr.rc
+++ b/programs/winecfg/Tr.rc
@@ -268,6 +268,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "Standart"
     IDS_ACCEL_BASIC             "Temel"
     IDS_ACCEL_EMULATION         "Taklit"
+    IDS_DRIVER_PULSE             "PulseAudio S=FCr=FCc=FCs=FC"
     IDS_DRIVER_ALSA             "ALSA S=FCr=FCc=FCs=FC"
     IDS_DRIVER_ESOUND           "EsounD S=FCr=FCc=FCs=FC"
     IDS_DRIVER_OSS              "OSS S=FCr=FCc=FCs=FC"
diff --git a/programs/winecfg/Zh.rc b/programs/winecfg/Zh.rc
index 4c18e99..029a26d 100644
--- a/programs/winecfg/Zh.rc
+++ b/programs/winecfg/Zh.rc
@@ -271,6 +271,7 @@ BEGIN
     IDS_ACCEL_STANDARD          "=E6=A0=87=E5=87=86"
     IDS_ACCEL_BASIC             "=E5=9F=BA=E6=9C=AC"
     IDS_ACCEL_EMULATION         "=E8=BD=AF=E4=BB=B6=E6=A8=A1=E6=8B=9F"
+    IDS_DRIVER_PULSE             "PulseAudio =E9=A9=B1=E5=8A=A8"
     IDS_DRIVER_ALSA             "ALSA =E9=A9=B1=E5=8A=A8"
     IDS_DRIVER_ESOUND           "EsounD =E9=A9=B1=E5=8A=A8"
     IDS_DRIVER_OSS              "OSS =E9=A9=B1=E5=8A=A8"
diff --git a/programs/winecfg/audio.c b/programs/winecfg/audio.c
index 8e966a5..9c2cde3 100644
--- a/programs/winecfg/audio.c
+++ b/programs/winecfg/audio.c
@@ -88,6 +88,7 @@ typedef struct
 } AUDIO_DRIVER;
=20
 static const AUDIO_DRIVER sAudioDrivers[] =3D {
+  {IDS_DRIVER_PULSE,      "pulse"},
   {IDS_DRIVER_ALSA,      "alsa"},
   {IDS_DRIVER_OSS,       "oss"},
   {IDS_DRIVER_COREAUDIO, "coreaudio"},
diff --git a/programs/winecfg/libraries.c b/programs/winecfg/libraries.c
index 37cc12b..a53c4cf 100644
--- a/programs/winecfg/libraries.c
+++ b/programs/winecfg/libraries.c
@@ -69,6 +69,7 @@ static const char * const builtin_only[] =3D
     "user32",
     "vdmdbg",
     "w32skrnl",
+    "winepulse.drv",
     "winealsa.drv",
     "wineaudioio.drv",
     "wined3d",
diff --git a/programs/winecfg/resource.h b/programs/winecfg/resource.h
index a18fe76..1c4a1e7 100644
--- a/programs/winecfg/resource.h
+++ b/programs/winecfg/resource.h
@@ -182,7 +182,7 @@
 #define IDS_ACCEL_BASIC                 8302
 #define IDS_ACCEL_EMULATION             8303
 #define IDS_DRIVER_ALSA                 8304
-
+#define IDS_DRIVER_PULSE                 8305
 #define IDS_DRIVER_ESOUND               8306
 #define IDS_DRIVER_OSS                  8307
 #define IDS_DRIVER_JACK                 8308
--=20
1.6.1


------=_Part_65631_13774827.1239220781610--



More information about the wine-patches mailing list