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, ¶m, &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, ¶m, &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, ¶m, &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