Blob Blame History Raw
diff --git a/configure.ac b/configure.ac
index 91850de..b21be92 100644
--- a/configure.ac
+++ b/configure.ac
@@ -55,6 +55,7 @@ AC_ARG_WITH(oss,       AS_HELP_STRING([--without-oss],[do not use the OSS sound
             [if test "x$withval" = "xno"; then ac_cv_header_soundcard_h=no; ac_cv_header_sys_soundcard_h=no; ac_cv_header_machine_soundcard_h=no; fi])
 AC_ARG_WITH(png,       AS_HELP_STRING([--without-png],[do not use PNG]),
             [if test "x$withval" = "xno"; then ac_cv_header_png_h=no; fi])
+AC_ARG_WITH(pulse,     AC_HELP_STRING([--without-pulse],[do not use PulseAudio sound support]))
 AC_ARG_WITH(sane,      AS_HELP_STRING([--without-sane],[do not use SANE (scanner support)]))
 AC_ARG_WITH(xcomposite,AS_HELP_STRING([--without-xcomposite],[do not use the Xcomposite extension]),
             [if test "x$withval" = "xno"; then ac_cv_header_X11_extensions_Xcomposite_h=no; fi])
@@ -1117,6 +1118,29 @@ then
     CFLAGS="$save_CFLAGS"
 fi
 
+dnl **** Check for PulseAudio ****
+if test "x$with_pulse" != "xno"; then
+    if test "$PKG_CONFIG" != "false"; then
+	AC_MSG_CHECKING([for pulseaudio >= 0.9.8])
+	if "$PKG_CONFIG" --atleast-version=0.9.8 libpulse; then
+	    have_pulseaudio="yes"
+        else
+	    have_pulseaudio="no"
+	fi
+	AC_MSG_RESULT([$have_pulseaudio])
+	if test x"$have_pulseaudio" = xyes; then
+	    if "$PKG_CONFIG" --atleast-version=0.9.11 libpulse; then
+	        AC_DEFINE([HAVE_PULSEAUDIO_0_9_11], 1, [define this if pulseaudio is at least version 0.9.11])
+	    else
+		AC_DEFINE([HAVE_PULSEAUDIO_0_9_11], 0, [define this if pulseaudio is at least version 0.9.11])
+	    fi
+	    ac_pulse_libs=`$PKG_CONFIG --libs libpulse`
+	    AC_DEFINE([HAVE_PULSEAUDIO], 1, [define this if you have pulseaudio])
+	    AC_SUBST(PULSELIBS, "$ac_pulse_libs")
+	fi
+    fi
+fi
+
 dnl **** Check for ALSA 1.x ****
 AC_SUBST(ALSALIBS,"")
 if test "$ac_cv_header_sys_asoundlib_h" = "yes" -o "$ac_cv_header_alsa_asoundlib_h" = "yes"
@@ -1222,7 +1246,7 @@ dnl **** Check for libodbc ****
 WINE_CHECK_SONAME(odbc,SQLConnect,,[AC_DEFINE_UNQUOTED(SONAME_LIBODBC,["libodbc.$LIBEXT"])])
 
 dnl **** Check for any sound system ****
-if test "x$ALSALIBS$AUDIOIOLIBS$COREAUDIO$NASLIBS$ESDLIBS$ac_cv_lib_soname_jack" = "x" -a \
+if test "x$ALSALIBS$AUDIOIOLIBS$COREAUDIO$NASLIBS$ESDLIBS$PULSELIBS$ac_cv_lib_soname_jack" = "x" -a \
         "$ac_cv_header_sys_soundcard_h" != "yes" -a \
         "$ac_cv_header_machine_soundcard_h" != "yes" -a \
         "$ac_cv_header_soundcard_h" != "yes" -a \
@@ -2064,6 +2088,7 @@ WINE_CONFIG_MAKEFILE([dlls/winemp3.acm/Makefile],[dlls/Makedll.rules],[dlls],[AL
 WINE_CONFIG_MAKEFILE([dlls/winenas.drv/Makefile],[dlls/Makedll.rules],[dlls],[ALL_DLL_DIRS])
 WINE_CONFIG_MAKEFILE([dlls/wineoss.drv/Makefile],[dlls/Makedll.rules],[dlls],[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],[dlls],[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],[dlls],[ALL_DLL_DIRS])
 WINE_CONFIG_MAKEFILE([dlls/wing32/Makefile],[dlls/Makedll.rules],[dlls],[ALL_DLL_DIRS])
diff --git a/dlls/winepulse.drv/Makefile.in b/dlls/winepulse.drv/Makefile.in
new file mode 100644
index 0000000..52a6671
--- /dev/null
+++ b/dlls/winepulse.drv/Makefile.in
@@ -0,0 +1,15 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = winepulse.drv
+IMPORTS   = dxguid uuid winmm user32 advapi32 kernel32
+EXTRALIBS = @PULSELIBS@
+
+C_SRCS = \
+	waveout.c \
+	pulse.c
+
+@MAKE_DLL_RULES@
+
+@DEPENDENCIES@  # everything below this line is overwritten by make depend
diff --git a/dlls/winepulse.drv/pulse.c b/dlls/winepulse.drv/pulse.c
new file mode 100644
index 0000000..3ef6a03
--- /dev/null
+++ b/dlls/winepulse.drv/pulse.c
@@ -0,0 +1,732 @@
+/*
+ * Wine Driver for PulseAudio
+ * http://pulseaudio.org/
+ *
+ * Copyright	2008 Arthur Taylor <art@ified.ca>
+ *
+ * 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, USA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "mmddk.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);
+
+/*
+ * - Need to subscribe to sink/source events and keep the WInDev and WOutDev
+ *   structures updated
+ */
+
+/* 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;
+}
+
+/*======================================================================*
+ *          Ring Buffer Functions - copied from winealsa.drv            *
+ *======================================================================*/
+
+/* 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] = omr->msg_pipe[1] = -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 = 0; write((omr)->msg_pipe[1], &x, sizeof(x)); } while (0)
+#define CLEAR_OMR(omr) do { int x = 0; 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 = (omr)->msg_pipe[0]; \
+       pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)
+#else
+#define INIT_OMR(omr) do { omr->msg_event = CreateEventW(NULL, FALSE, FALSE, 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
+
+/******************************************************************
+ *		PULSE_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 = 0;
+    omr->msg_tosave = 0;
+    INIT_OMR(omr);
+    omr->ring_buffer_size = PULSE_RING_BUFFER_INCREMENT;
+    omr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,omr->ring_buffer_size * sizeof(PULSE_MSG));
+
+    InitializeCriticalSection(&omr->msg_crst);
+    omr->msg_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": PULSE_MSG_RING.msg_crst");
+    return 0;
+}
+
+/******************************************************************
+ *		PULSE_DestroyRingMessage
+ *
+ */
+int PULSE_DestroyRingMessage(PULSE_MSG_RING* omr)
+{
+    CLOSE_OMR(omr);
+    HeapFree(GetProcessHeap(),0,omr->messages);
+    omr->messages = NULL;
+    omr->ring_buffer_size = PULSE_RING_BUFFER_INCREMENT;
+    omr->msg_crst.DebugInfo->Spare[0] = 0;
+    DeleteCriticalSection(&omr->msg_crst);
+    return 0;
+}
+/******************************************************************
+ *		PULSE_ResetRingMessage
+ *
+ */
+void PULSE_ResetRingMessage(PULSE_MSG_RING* omr)
+{
+    RESET_OMR(omr);
+}
+
+/******************************************************************
+ *		PULSE_WaitRingMessage
+ *
+ */
+void PULSE_WaitRingMessage(PULSE_MSG_RING* omr, DWORD sleep)
+{
+    WAIT_OMR(omr, sleep);
+}
+
+/******************************************************************
+ *		PULSE_AddRingMessage
+ *
+ * Inserts a new message into the ring (should be called from DriverProc derived routines)
+ */
+int PULSE_AddRingMessage(PULSE_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait)
+{
+    HANDLE	hEvent = INVALID_HANDLE_VALUE;
+
+    EnterCriticalSection(&omr->msg_crst);
+    if ((omr->msg_toget == ((omr->msg_tosave + 1) % omr->ring_buffer_size)))
+    {
+	int old_ring_buffer_size = omr->ring_buffer_size;
+	omr->ring_buffer_size += PULSE_RING_BUFFER_INCREMENT;
+	omr->messages = HeapReAlloc(GetProcessHeap(),0,omr->messages, omr->ring_buffer_size * sizeof(PULSE_MSG));
+	/* Now we need to rearrange the ring buffer so that the new
+	   buffers just allocated are in between omr->msg_tosave and
+	   omr->msg_toget.
+	*/
+	if (omr->msg_tosave < omr->msg_toget)
+	{
+	    memmove(&(omr->messages[omr->msg_toget + PULSE_RING_BUFFER_INCREMENT]),
+		    &(omr->messages[omr->msg_toget]),
+		    sizeof(PULSE_MSG)*(old_ring_buffer_size - omr->msg_toget)
+		    );
+	    omr->msg_toget += PULSE_RING_BUFFER_INCREMENT;
+	}
+    }
+    if (wait)
+    {
+        hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+        if (hEvent == INVALID_HANDLE_VALUE)
+        {
+            ERR("can't create event !?\n");
+            LeaveCriticalSection(&omr->msg_crst);
+            return 0;
+        }
+        if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER)
+            FIXME("two fast messages in the queue!!!!\n"); /* toget = %d(%s), tosave=%d(%s)\n",
+                  omr->msg_toget,ALSA_getCmdString(omr->messages[omr->msg_toget].msg),
+                  omr->msg_tosave,ALSA_getCmdString(omr->messages[omr->msg_tosave].msg)); */
+
+        /* fast messages have to be added at the start of the queue */
+        omr->msg_toget = (omr->msg_toget + omr->ring_buffer_size - 1) % omr->ring_buffer_size;
+
+        omr->messages[omr->msg_toget].msg = msg;
+        omr->messages[omr->msg_toget].param = param;
+        omr->messages[omr->msg_toget].hEvent = hEvent;
+    }
+    else
+    {
+        omr->messages[omr->msg_tosave].msg = msg;
+        omr->messages[omr->msg_tosave].param = param;
+        omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
+        omr->msg_tosave = (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;
+}
+
+/******************************************************************
+ *		PULSE_RetrieveRingMessage
+ *
+ * Get a message from the ring. Should be called by the playback/record thread.
+ */
+int PULSE_RetrieveRingMessage(PULSE_MSG_RING* omr,
+                                   enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
+{
+    EnterCriticalSection(&omr->msg_crst);
+
+    if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
+    {
+        LeaveCriticalSection(&omr->msg_crst);
+	return 0;
+    }
+
+    *msg = omr->messages[omr->msg_toget].msg;
+    omr->messages[omr->msg_toget].msg = 0;
+    *param = omr->messages[omr->msg_toget].param;
+    *hEvent = omr->messages[omr->msg_toget].hEvent;
+    omr->msg_toget = (omr->msg_toget + 1) % omr->ring_buffer_size;
+    CLEAR_OMR(omr);
+    LeaveCriticalSection(&omr->msg_crst);
+    return 1;
+}
+
+/**************************************************************************
+ * Utility Functions
+ */
+
+/******************************************************************
+ *		PULSE_free_wavedevs			[internal]
+ *
+ * Free and deallocated all the wavedevs in the array of size allocated
+ */
+static void PULSE_free_wavedevs(WINE_WAVEDEV *wd, DWORD *allocated) {
+    DWORD y, x = *allocated;
+    WINE_WAVEDEV *current_device;
+    WINE_WAVEINST *current_instance;
+
+    TRACE("%i\n", *allocated);
+
+    for (; x>0; ) {
+	current_device = &wd[--x];
+	
+	pa_xfree(current_device->device_name);
+
+	for (y = 0; y < PULSE_MAX_STREAM_INSTANCES; y++) {
+	    if ((current_instance = current_device->instance[y])) {
+		if (current_instance->hThread != INVALID_HANDLE_VALUE && current_instance->msgRing.messages) {
+		    PULSE_AddRingMessage(&current_instance->msgRing, WINE_WM_CLOSING, 1, TRUE);
+		    if (current_instance->hThread != INVALID_HANDLE_VALUE) {
+			/* Overkill? */
+			TerminateThread(current_instance->hThread, 1);
+			current_instance->hThread = INVALID_HANDLE_VALUE;
+		    }
+		    if (current_instance->stream)
+		        pa_stream_unref(current_instance->stream);
+		    PULSE_DestroyRingMessage(&current_instance->msgRing);
+		    current_device->instance[current_instance->instance_ref] = NULL;
+		    if (current_instance->buffer_attr)
+			pa_xfree(current_instance->buffer_attr);
+		    HeapFree(GetProcessHeap(), 0, current_instance);
+		}
+	    }
+	}
+    }
+
+    HeapFree(GetProcessHeap(), 0, wd);
+    *allocated = 0;
+}
+
+/******************************************************************
+ *		PULSE_wait_for_operation
+ *
+ * Waits for pa operations to complete, ensures success and dereferences the
+ * operations.
+ */
+void PULSE_wait_for_operation(pa_operation *o, WINE_WAVEINST *wd) {
+    assert(o && wd);
+
+    TRACE("Waiting...");
+
+    for (;;) {
+
+	if (!wd->stream ||
+	    !PULSE_context ||
+	    pa_context_get_state(PULSE_context) != PA_CONTEXT_READY ||
+	    pa_stream_get_state(wd->stream) != PA_STREAM_READY) {
+	    wd->state = WINE_WS_FAILED;
+	    if (wd->hThread != INVALID_HANDLE_VALUE && wd->msgRing.messages)
+		PULSE_AddRingMessage(&wd->msgRing, WINE_WM_CLOSING, 1, FALSE);
+	    break;
+	}
+
+	if (pa_operation_get_state(o) != PA_OPERATION_RUNNING)
+	    break;
+
+	pa_threaded_mainloop_wait(PULSE_ml);
+    }
+    TRACE(" done\n");
+    pa_operation_unref(o);
+}
+
+/**************************************************************************
+ * Common Callbacks
+ */
+
+/******************************************************************
+ *		PULSE_stream_state_callback
+ *
+ * Called by pulse whenever the state of the stream changes.
+ */
+void PULSE_stream_state_callback(pa_stream *s, void *userdata) {
+    WINE_WAVEINST *wd = (WINE_WAVEINST*)userdata;
+    assert(s && wd);
+
+    switch (pa_stream_get_state(s)) {
+
+    case PA_STREAM_READY:
+	TRACE("Stream ready\n");
+	break;
+    case PA_STREAM_TERMINATED:
+	TRACE("Stream terminated\n");
+	break;
+    case PA_STREAM_FAILED:
+	WARN("Stream failed!\n");
+	wd->state = WINE_WS_FAILED;
+	if (wd->hThread != INVALID_HANDLE_VALUE && wd->msgRing.messages)
+		PULSE_AddRingMessage(&wd->msgRing, WINE_WM_CLOSING, 1, FALSE);
+	break;
+    case PA_STREAM_UNCONNECTED:
+    case PA_STREAM_CREATING:
+	return;
+    }
+    pa_threaded_mainloop_signal(PULSE_ml, 0);
+}
+
+/**************************************************************************
+ * 			PULSE_stream_sucess_callback
+ */
+void PULSE_stream_success_callback(pa_stream *s, int success, void *userdata) {
+    if (!success)
+	WARN("Stream operation failed: %s\n", pa_strerror(pa_context_errno(PULSE_context)));
+
+    pa_threaded_mainloop_signal(PULSE_ml, 0);
+}
+
+/**************************************************************************
+ * 			PULSE_context_success_callback
+ */
+void PULSE_context_success_callback(pa_context *c, int success, void *userdata) {
+    if (!success)
+	WARN("Context operation failed: %s\n", pa_strerror(pa_context_errno(c)));
+
+    pa_threaded_mainloop_signal(PULSE_ml, 0);
+}
+
+/**************************************************************************
+ * Connection management and sink / source management.
+ */
+
+/**************************************************************************
+ * 				PULSE_context_state_callback    [internal]
+ */
+static void PULSE_context_state_callback(pa_context *c, void *userdata) {
+    assert(c);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+	case PA_CONTEXT_READY:
+	case PA_CONTEXT_TERMINATED:
+	    pa_threaded_mainloop_signal(PULSE_ml, 0);
+	    break;
+
+	case PA_CONTEXT_FAILED:
+	default:
+	    ERR("Conneciton failure: %s\n", pa_strerror(pa_context_errno(c)));
+	
+	    if (PULSE_context) {
+		pa_context_disconnect(PULSE_context);
+	    }
+
+	    PULSE_free_wavedevs(WOutDev, &PULSE_WodNumDevs);
+	    PULSE_free_wavedevs(WInDev, &PULSE_WidNumDevs);
+
+	    pa_threaded_mainloop_signal(PULSE_ml, 0);
+    }
+}
+
+/**************************************************************************
+ * 		    PULSE_add_input_device			[internal]
+ *
+ * Creates or adds a device to WInDev based on the pa_source_info, or if
+ * pa_source_info is null, a default.
+ */
+static void PULSE_add_input_device(pa_source_info *i, DWORD *allocated) {
+    WINE_WAVEDEV *wwi;
+    const char *description;
+    int x;
+
+    if (WInDev)
+	wwi = HeapReAlloc(GetProcessHeap(), 0, WInDev, sizeof(WINE_WAVEDEV) * ((*allocated)+1));
+    else
+	wwi = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_WAVEDEV));
+
+    if (!wwi)
+	return;
+    
+    WInDev = wwi;
+    wwi = &WInDev[(*allocated)++];
+
+    if (i) {
+	description = i->description;
+	wwi->device_name = pa_xstrdup(i->name);
+	strcpy(wwi->interface_name, "winepulse: ");
+	memcpy(wwi->interface_name + strlen(wwi->interface_name),
+	    i->name, min(strlen(i->name),
+	    sizeof(wwi->interface_name) - strlen("winepulse:   ")));
+    } else {
+	description = pa_xstrdup("PulseAudio Server Default");
+	wwi->device_name = NULL;
+	strcpy(wwi->interface_name, "winepulse: default");
+    }
+
+    memset(wwi, 0, sizeof(WINE_WAVEDEV));
+    memset(&(wwi->caps.in), 0, sizeof(wwi->caps.in));
+    MultiByteToWideChar(CP_ACP, 0, description, -1, wwi->caps.in.szPname, sizeof(wwi->caps.in.szPname)/sizeof(WCHAR));
+    wwi->caps.in.szPname[sizeof(wwi->caps.in.szPname)/sizeof(WCHAR) - 1] = '\0';
+    wwi->caps.in.wMid = MM_CREATIVE;
+    wwi->caps.in.wPid = MM_CREATIVE_SBP16_WAVEOUT;
+    wwi->caps.in.vDriverVersion = 0x0100;
+    wwi->caps.in.wChannels = 2;
+    wwi->caps.in.dwFormats |= 
+        WAVE_FORMAT_1M08 |	/* Mono	    11025Hz 8-bit  */
+        WAVE_FORMAT_1M16 |	/* Mono	    11025Hz 16-bit */
+        WAVE_FORMAT_1S08 |	/* Stereo   11025Hz 8-bit  */
+        WAVE_FORMAT_1S16 |	/* Stereo   11025Hz 16-bit */
+        WAVE_FORMAT_2M08 |	/* Mono	    22050Hz 8-bit  */
+        WAVE_FORMAT_2M16 |	/* Mono	    22050Hz 16-bit */
+        WAVE_FORMAT_2S08 |	/* Stereo   22050Hz 8-bit  */
+	WAVE_FORMAT_2S16 |	/* Stereo   22050Hz 16-bit */
+        WAVE_FORMAT_4M08 |	/* Mono	    44100Hz 8-bit  */
+	WAVE_FORMAT_4M16 |	/* Mono	    44100Hz 16-bit */
+        WAVE_FORMAT_4S08 |	/* Stereo   44100Hz 8-bit  */
+	WAVE_FORMAT_4S16 |	/* Stereo   44100Hz 16-bit */
+        WAVE_FORMAT_48M08 |	/* Mono	    48000Hz 8-bit  */
+        WAVE_FORMAT_48S08 |	/* Stereo   48000Hz 8-bit  */
+        WAVE_FORMAT_48M16 |	/* Mono	    48000Hz 16-bit */
+        WAVE_FORMAT_48S16 |	/* Stereo   48000Hz 16-bit */
+	WAVE_FORMAT_96M08 |	/* Mono	    96000Hz 8-bit  */
+	WAVE_FORMAT_96S08 |	/* Stereo   96000Hz 8-bit  */
+        WAVE_FORMAT_96M16 |	/* Mono	    96000Hz 16-bit */
+	WAVE_FORMAT_96S16 ;	/* Stereo   96000Hz 16-bit */
+
+    /* NULL out the instance pointers */
+    for (x = 0; x < PULSE_MAX_STREAM_INSTANCES; x++) wwi->instance[x] = NULL;
+}
+
+/**************************************************************************
+ * 		    PULSE_add_output_device			[internal]
+ *
+ * Creates or adds a device to WOutDev based on the pa_sinl_info, or if
+ * pa_sink_info is null, a default.
+ */
+static void PULSE_add_output_device(pa_sink_info *i, DWORD *allocated) {
+    WINE_WAVEDEV *wwo;
+    const char *description;
+    int x;
+
+    if (WOutDev)
+        wwo = HeapReAlloc(GetProcessHeap(), 0, WOutDev, sizeof(WINE_WAVEDEV) * ((*allocated)+1));
+    else
+        wwo = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_WAVEDEV));
+    
+    if (!wwo)
+	return;
+
+    WOutDev = wwo;
+    wwo = &WOutDev[(*allocated)++];
+
+    if (i) {
+	description = i->description;
+	wwo->device_name = pa_xstrdup(i->name);
+	strcpy(wwo->interface_name, "winepulse: ");
+	memcpy(wwo->interface_name + strlen(wwo->interface_name),
+	    wwo->device_name, min(strlen(wwo->device_name),
+	    sizeof(wwo->interface_name) - strlen("winepulse:   ")));
+    } else {
+	description = pa_xstrdup("PulseAudio Server Default");
+	wwo->device_name = NULL;
+	strcpy(wwo->interface_name, "winepulse: default");
+    }
+
+    memset(wwo, 0, sizeof(WINE_WAVEDEV));
+    memset(&(wwo->caps.out), 0, sizeof(wwo->caps.out));
+    MultiByteToWideChar(CP_ACP, 0, description, -1, wwo->caps.out.szPname, sizeof(wwo->caps.out.szPname)/sizeof(WCHAR));
+    wwo->caps.out.szPname[sizeof(wwo->caps.out.szPname)/sizeof(WCHAR) - 1] = '\0';
+    wwo->caps.out.wMid = MM_CREATIVE;
+    wwo->caps.out.wPid = MM_CREATIVE_SBP16_WAVEOUT;
+    wwo->caps.out.vDriverVersion = 0x0100;
+    wwo->caps.out.dwSupport |= WAVECAPS_VOLUME | WAVECAPS_LRVOLUME;
+    wwo->caps.out.wChannels = 2;
+    wwo->caps.out.dwFormats |= 
+        WAVE_FORMAT_1M08 |	/* Mono	    11025Hz 8-bit  */
+        WAVE_FORMAT_1M16 |	/* Mono	    11025Hz 16-bit */
+        WAVE_FORMAT_1S08 |	/* Stereo   11025Hz 8-bit  */
+        WAVE_FORMAT_1S16 |	/* Stereo   11025Hz 16-bit */
+        WAVE_FORMAT_2M08 |	/* Mono	    22050Hz 8-bit  */
+        WAVE_FORMAT_2M16 |	/* Mono	    22050Hz 16-bit */
+        WAVE_FORMAT_2S08 |	/* Stereo   22050Hz 8-bit  */
+        WAVE_FORMAT_2S16 |	/* Stereo   22050Hz 16-bit */
+        WAVE_FORMAT_4M08 |	/* Mono	    44100Hz 8-bit  */
+        WAVE_FORMAT_4M16 |	/* Mono	    44100Hz 16-bit */
+        WAVE_FORMAT_4S08 |	/* Stereo   44100Hz 8-bit  */
+        WAVE_FORMAT_4S16 |	/* Stereo   44100Hz 16-bit */
+        WAVE_FORMAT_48M08 |	/* Mono	    48000Hz 8-bit  */
+        WAVE_FORMAT_48S08 |	/* Stereo   48000Hz 8-bit  */
+        WAVE_FORMAT_48M16 |	/* Mono	    48000Hz 16-bit */
+        WAVE_FORMAT_48S16 |	/* Stereo   48000Hz 16-bit */
+        WAVE_FORMAT_96M08 |	/* Mono	    96000Hz 8-bit  */
+        WAVE_FORMAT_96S08 |	/* Stereo   96000Hz 8-bit  */
+	WAVE_FORMAT_96M16 |	/* Mono	    96000HZ 16-bit */
+        WAVE_FORMAT_96S16 ;	/* Stereo   96000Hz 16-bit */
+
+    wwo->left_vol = PA_VOLUME_NORM;
+    wwo->right_vol = PA_VOLUME_NORM;
+
+    /* NULL out the instance pointers */
+    for (x = 0; x < PULSE_MAX_STREAM_INSTANCES; x++) wwo->instance[x] = NULL;
+}
+
+/**************************************************************************
+ * 		    PULSE_source_info_callback			[internal]
+ *
+ * Calls PULSE_add_input_device to add the source to the list of devices
+ */
+static void PULSE_source_info_callback(pa_context *c, pa_source_info *i, int eol, void *userdata) {
+    assert(c);
+    if (!eol && i)
+	if (i->monitor_of_sink == PA_INVALID_INDEX) /* For now only add non-montior sources */
+	PULSE_add_input_device(i, (DWORD*)userdata);
+
+    pa_threaded_mainloop_signal(PULSE_ml, 0);
+}
+
+/**************************************************************************
+ * 		    PULSE_sink_info_callback			[internal]
+ *
+ * Calls PULSE_add_output_device to add the sink to the list of devices
+ */
+static void PULSE_sink_info_callback(pa_context *c, pa_sink_info *i, int eol, void *userdata) {
+    assert(c);
+    if (!eol && i)
+	PULSE_add_output_device(i, (DWORD*)userdata);
+
+    pa_threaded_mainloop_signal(PULSE_ml, 0);
+}
+
+/**************************************************************************
+ * 				PULSE_WaveInit                  [internal]
+ *
+ * Connects to the pulseaudio server, tries to discover sinks and sources and
+ * allocates the WaveIn/WaveOut devices.
+ */
+LONG PULSE_WaveInit(void) {
+    pa_operation *o = NULL;
+    DWORD allocated;
+
+    WOutDev = NULL;
+    WInDev = NULL;
+    PULSE_WodNumDevs = 0;
+    PULSE_WidNumDevs = 0;
+    PULSE_context = NULL;
+    PULSE_ml = NULL;
+
+    if (!(PULSE_ml = pa_threaded_mainloop_new())) {
+	WARN("Failed to create mainloop object.");
+	return -1;
+    }
+    
+    pa_threaded_mainloop_start(PULSE_ml);
+    
+    /* FIXME: better name? */
+    PULSE_context = pa_context_new(pa_threaded_mainloop_get_api(PULSE_ml), "Wine Application");
+    assert(PULSE_context);
+    
+    pa_context_set_state_callback(PULSE_context, PULSE_context_state_callback, NULL);
+    
+    if (pa_context_get_state(PULSE_context) != PA_CONTEXT_UNCONNECTED)
+	return 0;
+    
+    pa_threaded_mainloop_lock(PULSE_ml);
+
+    TRACE("Attempting to connect to pulseaudio server.\n");
+    if (pa_context_connect(PULSE_context, NULL, 0, NULL) < 0) {
+        WARN("failed to connect context object %s\n", pa_strerror(pa_context_errno(PULSE_context)));
+        return -1;
+    }
+
+    /* Wait for connection */
+    for (;;) {
+	pa_context_state_t state = pa_context_get_state(PULSE_context);
+
+	if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) {
+	    ERR("Failed to connect to pulseaudio server.\n");
+	    pa_context_unref(PULSE_context);
+	    pa_threaded_mainloop_unlock(PULSE_ml);
+	    pa_threaded_mainloop_stop(PULSE_ml);
+	    pa_threaded_mainloop_free(PULSE_ml);
+	    return -1;
+	}
+
+	if (state == PA_CONTEXT_READY) {
+	    TRACE("Connection succeeded!\n");
+	    break;
+	}
+    
+	pa_threaded_mainloop_wait(PULSE_ml);
+    }
+
+    /* Ask for all the sinks and create objects in the WOutDev array */
+    allocated=0;
+    /* add a fake output (server default sink) */
+    PULSE_add_output_device(NULL, &allocated);
+    o = pa_context_get_sink_info_list(PULSE_context, (pa_sink_info_cb_t)PULSE_sink_info_callback, &allocated);
+    assert(o);
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE)
+	pa_threaded_mainloop_wait(PULSE_ml);
+    pa_operation_unref(o);
+    pa_threaded_mainloop_unlock(PULSE_ml);
+    TRACE("Allocated %i output device(s).\n",allocated);
+    PULSE_WodNumDevs=allocated;
+    if (PULSE_WodNumDevs == 1) /* Only the fake sink exists */
+	PULSE_free_wavedevs(WOutDev, &PULSE_WodNumDevs);
+
+    /* Repeate for all the sources */
+    allocated=0;
+    /* add a fake input (server default source) */
+    PULSE_add_input_device(NULL, &allocated);
+    pa_threaded_mainloop_lock(PULSE_ml);
+    o = pa_context_get_source_info_list(PULSE_context, (pa_source_info_cb_t)PULSE_source_info_callback, &allocated);
+    assert(o);
+    while (pa_operation_get_state(o) != PA_OPERATION_DONE)
+	pa_threaded_mainloop_wait(PULSE_ml);
+    pa_operation_unref(o);
+    pa_threaded_mainloop_unlock(PULSE_ml);
+    TRACE("Allocated %i input device(s).\n", allocated);
+    PULSE_WidNumDevs=allocated;
+    if (PULSE_WidNumDevs == 1) /* Only the fake source exists */
+	PULSE_free_wavedevs(WInDev, &PULSE_WidNumDevs);
+
+    return 1;
+}
+
+/**************************************************************************
+ * 				PULSE_WaveClose                 [internal]
+ *
+ * Disconnect from the server, deallocated the WaveIn/WaveOut devices, stop and
+ * free the mainloop.
+ */
+
+LONG PULSE_WaveClose(void) {
+    pa_threaded_mainloop_lock(PULSE_ml);
+    if (PULSE_context) {
+	pa_context_disconnect(PULSE_context);
+	pa_context_unref(PULSE_context);
+	PULSE_context = NULL;
+    }
+
+    PULSE_free_wavedevs(WOutDev, &PULSE_WodNumDevs);
+    PULSE_free_wavedevs(WInDev, &PULSE_WidNumDevs);
+    
+    pa_threaded_mainloop_unlock(PULSE_ml);
+    pa_threaded_mainloop_stop(PULSE_ml);
+    pa_threaded_mainloop_free(PULSE_ml);
+    
+    return 1;
+}
+
+#endif /* HAVE_PULSEAUDIO */
+
+/**************************************************************************
+ * 				DriverProc (WINEPULSE.@)
+ */
+LRESULT CALLBACK PULSE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
+                                 LPARAM dwParam1, LPARAM dwParam2) {
+
+    switch(wMsg) {
+#ifdef HAVE_PULSEAUDIO
+    case DRV_LOAD:		PULSE_WaveInit();
+				return 1;
+    case DRV_FREE:		return PULSE_WaveClose();
+    case DRV_OPEN:		return 1;
+    case DRV_CLOSE:		return 1;
+    case DRV_ENABLE:		return 1;
+    case DRV_DISABLE:		return 1;
+    case DRV_QUERYCONFIGURE:	return 1;
+    case DRV_CONFIGURE:		MessageBoxA(0, "PulseAudio MultiMedia Driver !", "PulseAudio Driver", MB_OK);	return 1;
+    case DRV_INSTALL:		return DRVCNF_RESTART;
+    case DRV_REMOVE:		return DRVCNF_RESTART;
+#endif
+    default:
+	return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+    }
+}
diff --git a/dlls/winepulse.drv/waveout.c b/dlls/winepulse.drv/waveout.c
new file mode 100644
index 0000000..fe7543f
--- /dev/null
+++ b/dlls/winepulse.drv/waveout.c
@@ -0,0 +1,180 @@
+/*
+ * Wine Driver for PulseAudio - WaveOut Functionality
+ * http://pulseaudio.org/
+ *
+ * Copyright 2002	Eric Pouech
+ *	     2002	Marco Pietrobono
+ *	     2003	Christian Costa
+ *	     2006-2007	Maarten Lankhorst
+ *	     2008	Arthur Taylor (PulseAudio version)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.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 "ks.h"
+#include "ksguid.h"
+#include "ksmedia.h"
+#include "dsound.h"
+#include "dsdriver.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 => STOPPED) |
+ * | PAUSED  | reset()     | RESETTING     | PAUSED                          |
+ * | (other) | reset()	   | RESETTING     | STOPPED		      	     |
+ * | (any)   | close()	   | CLOSING	   | CLOSED		      	     |
+ * +---------+-------------+---------------+---------------------------------+
+ */
+
+/**************************************************************************
+ *                              wodGetDevCaps                   [internal]
+ */
+static DWORD wodGetDevCaps(DWORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize) {
+    TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
+
+    if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
+
+    if (wDevID >= PULSE_WodNumDevs) {
+	TRACE("Asked for device %d, but only %d known!\n", wDevID, PULSE_WodNumDevs);
+	return MMSYSERR_INVALHANDLE;
+    }
+
+    memcpy(lpCaps, &(WOutDev[wDevID].caps.out), min(dwSize, sizeof(*lpCaps)));
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                              wodDevInterfaceSize             [internal]
+ */
+static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1) {
+
+    *dwParam1 = 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 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
+                                        NULL, 0 ) * sizeof(WCHAR))
+    {
+        MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
+                            dwParam1, dwParam2 / sizeof(WCHAR));
+	return MMSYSERR_NOERROR;
+    }
+    return MMSYSERR_INVALPARAM;
+}
+
+/*======================================================================*
+ *                  Low level DSOUND implementation			*
+ *======================================================================*/
+static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv) {
+    /* Is this possible ?*/
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc) {
+    memset(desc, 0, sizeof(*desc));
+    strcpy(desc->szDesc, "Wine PulseAudio DirectSound Driver");
+    strcpy(desc->szDrvname, "winepulse.drv");
+    return MMSYSERR_NOERROR;
+}
+/**************************************************************************
+ * 				wodMessage (WINEPULSE.@)
+ */
+DWORD WINAPI PULSE_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
+			    DWORD dwParam1, DWORD dwParam2) {
+
+    switch (wMsg) {
+    case DRVM_INIT:
+    case DRVM_EXIT:
+    case DRVM_ENABLE:
+    case DRVM_DISABLE:
+	/* FIXME: Pretend this is supported */
+	return 0;
+    case WODM_OPEN:	 	return MMSYSERR_NOTSUPPORTED; 
+    case WODM_CLOSE:		return MMSYSERR_NOTSUPPORTED;
+    case WODM_WRITE:		return MMSYSERR_NOTSUPPORTED;
+    case WODM_PAUSE:		return MMSYSERR_NOTSUPPORTED;
+    case WODM_GETPOS:		return MMSYSERR_NOTSUPPORTED;
+    case WODM_BREAKLOOP: 	return MMSYSERR_NOTSUPPORTED;
+    case WODM_PREPARE:	 	return MMSYSERR_NOTSUPPORTED;
+    case WODM_UNPREPARE: 	return MMSYSERR_NOTSUPPORTED;
+    case WODM_GETDEVCAPS:	return wodGetDevCaps	(wDevID, (LPWAVEOUTCAPSW)dwParam1,	dwParam2);
+    case WODM_GETNUMDEVS:	return PULSE_WodNumDevs;
+    case WODM_GETPITCH:	 	return MMSYSERR_NOTSUPPORTED;
+    case WODM_SETPITCH:	 	return MMSYSERR_NOTSUPPORTED;
+    case WODM_GETPLAYBACKRATE:	return MMSYSERR_NOTSUPPORTED; /* support if theoretically possible */
+    case WODM_SETPLAYBACKRATE:	return MMSYSERR_NOTSUPPORTED; /* since pulseaudio 0.9.8 */
+    case WODM_GETVOLUME:	return MMSYSERR_NOTSUPPORTED;
+    case WODM_SETVOLUME:	return MMSYSERR_NOTSUPPORTED;
+    case WODM_RESTART:		return MMSYSERR_NOTSUPPORTED;
+    case WODM_RESET:		return MMSYSERR_NOTSUPPORTED;
+    case DRV_QUERYDEVICEINTERFACESIZE: return wodDevInterfaceSize      (wDevID, (LPDWORD)dwParam1);
+    case DRV_QUERYDEVICEINTERFACE:     return wodDevInterface          (wDevID, (PWCHAR)dwParam1, dwParam2);
+        case DRV_QUERYDSOUNDIFACE:	return wodDsCreate	(wDevID, (PIDSDRIVER*)dwParam1);
+    case DRV_QUERYDSOUNDDESC:	return wodDsDesc	(wDevID, (PDSDRIVERDESC)dwParam1);
+    default:
+	FIXME("unknown message %d!\n", wMsg);
+    }
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+#else /* !HAVE_PULSEAUDIO */
+
+/**************************************************************************
+ * 				wodMessage (WINEPULSE.@)
+ */
+DWORD WINAPI PULSE_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
+			      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/winepulse.drv.spec
new file mode 100644
index 0000000..1707969
--- /dev/null
+++ b/dlls/winepulse.drv/winepulse.drv.spec
@@ -0,0 +1,2 @@
+@ stdcall -private DriverProc(long long long long long long) PULSE_DriverProc
+@ stdcall -private wodMessage(long long long long long long) PULSE_wodMessage
diff --git a/dlls/winepulse.drv/winepulse.h b/dlls/winepulse.drv/winepulse.h
new file mode 100644
index 0000000..277221a
--- /dev/null
+++ b/dlls/winepulse.drv/winepulse.h
@@ -0,0 +1,169 @@
+/* Definitions for PulseAudio Wine Driver
+ *
+ * Copyright	2008 Arthur Taylor
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#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 => STOPPED) |
+ * | (any)   | reset()     | RESETTING     | STOPPED                         |
+ * | (any)   | close()     | CLOSING       | CLOSED                          |
+ * +---------+-------------+---------------+---------------------------------+
+ */
+
+#undef PULSE_VERBOSE
+
+/* 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_MAX_STREAM_INSTANCES 16 /* Sixteen streams per device should be good enough? */
+
+/* events to be send to device */
+enum win_wm_message {
+    WINE_WM_PAUSING = 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 	msg;	/* message identifier */
+    DWORD	                param;  /* parameter for this message */
+    HANDLE	                hEvent;	/* if message is synchronous, handle 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			* messages;
+    int                         ring_buffer_size;
+    int				msg_tosave;
+    int				msg_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		msg_crst;
+} PULSE_MSG_RING;
+
+/* Per-playback/record instance */
+typedef struct {
+    int			instance_ref;	    /* The array index of WINE_WAVEDEV->instance[] that this is */
+    volatile int        state;		    /* one of the WINE_WS_ manifest constants */
+    WAVEOPENDESC        waveDesc;
+    WORD		wFlags;
+    pa_stream		*stream;	    /* The PulseAudio stream */
+    const pa_timing_info	*timing_info;
+    pa_buffer_attr	*buffer_attr;
+    pa_sample_spec	sample_spec;	    /* Sample spec of this stream / device */
+    pa_cvolume		volume;
+
+    /* WavaHdr information */
+    LPWAVEHDR		lpQueuePtr;	    /* start of queued WAVEHDRs (waiting to be notified) */
+    LPWAVEHDR		lpPlayPtr;	    /* start of not yet fully played buffers */
+    DWORD		dwPartialOffset;    /* Offset of not yet written bytes in lpPlayPtr */
+    LPWAVEHDR		lpLoopPtr;          /* pointer of first buffer in loop, if any */
+    DWORD		dwLoops;	    /* private copy of loop counter */
+
+    /* Virtual stream positioning information */
+    DWORD		last_reset;	    /* When the last reset occured, as pa stream time doesn't reset */
+    BOOL		is_buffering;	    /* !is_playing */
+    struct timeval	last_header;	    /* When the last wavehdr was received, only updated when is_buffering is true */
+    BOOL		is_releasing;	    /* Whether we are releasing wavehdrs, may not always be the inverse of is_buffering */
+    struct timeval	started_releasing;  /* When wavehdr releasing started, for comparison to queued written wavehdrs */
+    DWORD		releasing_offset;   /* How much audio has been released prior when releasing started in this instance */
+
+    /* Thread communication and synchronization stuff */
+    HANDLE		hStartUpEvent;
+    HANDLE		hThread;
+    DWORD		dwThreadID;
+    PULSE_MSG_RING	msgRing;
+} WINE_WAVEINST;
+
+/* Per-playback/record device */
+typedef struct {
+    char		*device_name;	    /* Name of the device used as an identifer on the server */
+    char                interface_name[MAXPNAMELEN * 2];
+
+    pa_volume_t		left_vol;	    /* Volume is per device and always stereo */
+    pa_volume_t		right_vol;
+
+    union {
+        WAVEOUTCAPSW	out;
+	WAVEINCAPSW	in;
+    } caps;
+
+    WINE_WAVEINST	*instance[PULSE_MAX_STREAM_INSTANCES];
+} WINE_WAVEDEV;
+
+/* We establish one context per instance, so make it global to the lib */
+pa_context		*PULSE_context;   /* Connection Context */
+pa_threaded_mainloop	*PULSE_ml;        /* PA Runtime information */
+
+/* WaveIn / WaveOut devices */
+WINE_WAVEDEV *WOutDev;
+WINE_WAVEDEV *WInDev;
+DWORD PULSE_WodNumDevs;
+DWORD PULSE_WidNumDevs;
+
+/* pulse.c */
+void	PULSE_set_buffer_attr_callback(pa_stream *stream, int success, void *userdata);
+void	PULSE_wait_for_operation(pa_operation *o, WINE_WAVEINST *ww);
+void	PULSE_stream_success_callback(pa_stream *s, int success, void *userdata);
+void	PULSE_stream_state_callback(pa_stream *s, void *userdata);
+void	PULSE_context_success_callback(pa_context *c, int success, void *userdata);
+void	PULSE_stream_request_callback(pa_stream *s, size_t n, void *userdata);
+DWORD	PULSE_bytes_to_mmtime(LPMMTIME lpTime, DWORD position, pa_sample_spec *ss);
+BOOL	PULSE_setupFormat(LPWAVEFORMATEX wf, pa_sample_spec *ss);
+int	PULSE_InitRingMessage(PULSE_MSG_RING* omr);
+int	PULSE_DestroyRingMessage(PULSE_MSG_RING* omr);
+void	PULSE_ResetRingMessage(PULSE_MSG_RING* omr);
+void	PULSE_WaitRingMessage(PULSE_MSG_RING* omr, DWORD sleep);
+int	PULSE_AddRingMessage(PULSE_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait);
+int	PULSE_RetrieveRingMessage(PULSE_MSG_RING* omr, enum win_wm_message *msg, DWORD *param, HANDLE *hEvent);
+const char * PULSE_getCmdString(enum win_wm_message msg);
+#endif