You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1189 lines
44 KiB
1189 lines
44 KiB
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(¤t_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(¤t_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
|
|
|