Index: src/Makefile.am
===================================================================
--- src/Makefile.am (revision 28129)
+++ src/Makefile.am (working copy)
@@ -198,6 +198,12 @@
Sound += arch/Sound/RageSoundDriver_OSS.cpp arch/Sound/RageSoundDriver_OSS.h
endif

+if HAVE_PULSE
+Sound += arch/Sound/RageSoundDriver_Pulse.cpp arch/Sound/RageSoundDriver_Pulse.h
+AM_CXXFLAGS += $(PULSE_CFLAGS)
+AM_LDFLAGS += $(PULSE_LIBS)
+endif
+
if HAVE_ALSA
Sound += arch/Sound/ALSA9Dynamic.cpp arch/Sound/ALSA9Dynamic.h arch/Sound/ALSA9Functions.h \
arch/Sound/ALSA9Helpers.cpp arch/Sound/ALSA9Helpers.h \
Index: src/arch/arch_default.h
===================================================================
--- src/arch/arch_default.h (revision 28129)
+++ src/arch/arch_default.h (working copy)
@@ -49,7 +49,7 @@
#define DEFAULT_INPUT_DRIVER_LIST "X11"
#endif
#define DEFAULT_MOVIE_DRIVER_LIST "Theora,FFMpeg,Null"
-#define DEFAULT_SOUND_DRIVER_LIST "ALSA-sw,OSS,Null"
+#define DEFAULT_SOUND_DRIVER_LIST "pulse,ALSA-sw,OSS,Null"
#else
#error Which arch?
#endif
Index: src/arch/Sound/RageSoundDriver_Pulse.cpp
===================================================================
--- src/arch/Sound/RageSoundDriver_Pulse.cpp (revision 0)
+++ src/arch/Sound/RageSoundDriver_Pulse.cpp (revision 0)
@@ -0,0 +1,166 @@
+#include "global.h"
+#include "RageSoundDriver_Pulse.h"
+
+#include "RageLog.h"
+#include "RageSound.h"
+#include "RageSoundManager.h"
+#include "RageUtil.h"
+#include "PrefsManager.h"
+
+#include <errno.h>
+#include <string.h>
+#include <pulse/error.h>
+
+REGISTER_SOUND_DRIVER_CLASS( Pulse );
+
+/* samples */
+const int channels = 2;
+const int bytes_per_frame = channels*2; /* 16-bit */
+const int chunk_order = 12;
+const int num_chunks = 2; //4;
+const int buffersize = num_chunks * (1 << (chunk_order-1)); /* in bytes */
+const int buffersize_frames = buffersize/bytes_per_frame; /* in frames */
+
+int RageSoundDriver_Pulse::MixerThread_start(void *p)
+{
+ ((RageSoundDriver_Pulse *) p)->MixerThread();
+ return 0;
+}
+
+void RageSoundDriver_Pulse::MixerThread()
+{
+ /* We want to set a higher priority, but Unix only lets root renice
+ * < 0, which is silly. Give it a try, anyway. */
+ int status = nice( -10 );
+ if( status != -1 )
+ LOG->Trace( "Set MixerThread nice value to %d", status );
+
+ while( !shutdown )
+ {
+ GetData();
+
+ usleep( 10000 );
+ }
+}
+
+void RageSoundDriver_Pulse::SetupDecodingThread()
+{
+ int status = nice( -5 );
+ if( status != -1 )
+ LOG->Trace( "Set DecodingThread nice value to %d", status );
+}
+
+bool RageSoundDriver_Pulse::GetData()
+{
+ const int chunksize = buffersize;
+
+ static int16_t *buf = NULL;
+ if(!buf)
+ buf = new int16_t[chunksize / sizeof(int16_t)];
+
+ this->Mix( buf, chunksize/bytes_per_frame, last_cursor_pos, GetPosition() );
+
+ if (pa_simple_write( s, buf, chunksize, &err ) < 0)
+ return true; // need more data
+
+ /* Increment last_cursor_pos. */
+ last_cursor_pos += chunksize / bytes_per_frame;
+
+ return false; // have enough data
+}
+
+int64_t RageSoundDriver_Pulse::GetPosition() const
+{
+ return last_cursor_pos - (buffersize / bytes_per_frame);
+ //return last_cursor_pos;
+}
+
+RageSoundDriver_Pulse::RageSoundDriver_Pulse()
+{
+ s = NULL;
+ shutdown = false;
+ last_cursor_pos = 0;
+}
+
+RString RageSoundDriver_Pulse::Init()
+{
+ samplerate = PREFSMAN->m_iSoundPreferredSampleRate;
+ if( samplerate == 0 )
+ samplerate = 44100;
+
+ spec.format = PA_SAMPLE_S16LE;
+ spec.channels = channels;
+ spec.rate = samplerate;
+
+ //bufattr.maxlength = 4096;
+ bufattr.maxlength = buffersize * 2;
+
+ s = pa_simple_new(
+ NULL, // default server
+ "StepMania", // app name
+ PA_STREAM_PLAYBACK, // playback
+ NULL, // default device
+ "Audio", // stream description
+ &spec, // sample spec
+ NULL, // default channel map
+ &bufattr, // h4xed buffering
+ &err // error code
+ );
+ if (s == NULL)
+ return ssprintf( "RageSoundDriver_Pulse: Couldn't connect to Pulse server: %s", pa_strerror(err) );
+
+ StartDecodeThread();
+
+ MixingThread.SetName( "RageSoundDriver_Pulse" );
+ MixingThread.Create( MixerThread_start, this );
+
+ return "";
+}
+
+RageSoundDriver_Pulse::~RageSoundDriver_Pulse()
+{
+ if( MixingThread.IsCreated() )
+ {
+ /* Signal the mixing thread to quit. */
+ shutdown = true;
+ LOG->Trace("Shutting down mixer thread ...");
+ MixingThread.Wait();
+ LOG->Trace("Mixer thread shut down.");
+ }
+
+ if (s != NULL)
+ pa_simple_free(s);
+}
+
+float RageSoundDriver_Pulse::GetPlayLatency() const
+{
+ //return (1.0f / samplerate) * (buffersize_frames - chunksize_frames);
+ return pa_simple_get_latency(s, NULL) / 1000000.0f;
+ //return 0;
+}
+
+/*
+ * (c) 2002-2004 Glenn Maynard
+ * (c) 2009 Ondrej Hosek
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, provided that the above
+ * copyright notice(s) and this permission notice appear in all copies of
+ * the Software and that both the above copyright notice(s) and this
+ * permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
Index: src/arch/Sound/RageSoundDriver_Pulse.h
===================================================================
--- src/arch/Sound/RageSoundDriver_Pulse.h (revision 0)
+++ src/arch/Sound/RageSoundDriver_Pulse.h (revision 0)
@@ -0,0 +1,66 @@
+#ifndef RAGE_SOUND_PULSE
+#define RAGE_SOUND_PULSE
+
+#include "RageSoundDriver.h"
+#include "RageThreads.h"
+#include "RageTimer.h"
+
+#include <pulse/simple.h>
+
+class RageSoundDriver_Pulse: public RageSoundDriver
+{
+ pa_simple *s;
+ pa_sample_spec spec;
+ pa_buffer_attr bufattr;
+
+ bool shutdown;
+ int last_cursor_pos;
+ int samplerate;
+
+ static int MixerThread_start(void *p);
+ void MixerThread();
+ RageThread MixingThread;
+
+ int err;
+
+public:
+ bool GetData();
+ int GetSampleRate() const { return samplerate; }
+
+ /* virtuals: */
+ int64_t GetPosition() const;
+ float GetPlayLatency() const;
+ void SetupDecodingThread();
+
+ RageSoundDriver_Pulse();
+ RString Init();
+ ~RageSoundDriver_Pulse();
+};
+
+#endif
+
+/*
+ * (c) 2002-2004 Glenn Maynard
+ * (c) 2009 Ondrej Hosek
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, and/or sell copies of the Software, and to permit persons to
+ * whom the Software is furnished to do so, provided that the above
+ * copyright notice(s) and this permission notice appear in all copies of
+ * the Software and that both the above copyright notice(s) and this
+ * permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+ * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+ * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+ * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+ * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
Index: configure.ac
===================================================================
--- configure.ac (revision 28129)
+++ configure.ac (working copy)
@@ -208,6 +208,14 @@
AC_CHECK_HEADER(stdint.h, , [AC_DEFINE(MISSING_STDINT_H, 1, [stdint.h is missing])])
AC_CHECK_HEADERS([inttypes.h endian.h machine/endian.h alloca.h])

+have_pulse=no
+AC_CHECK_LIB(pulse, pa_stream_new, have_pulse=yes)
+if test x$have_pulse = xyes; then
+ AC_DEFINE(HAVE_PULSE, 1, [pulseaudio support available])
+ AC_SUBST(PULSE_LIBS)
+ AC_SUBST(PULSE_CFLAGS)
+fi
+
AC_MSG_CHECKING(if cstdlib breaks llabs)
AC_LANG_PUSH(C++)
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <stdlib.h>
@@ -227,6 +234,7 @@
AM_CONDITIONAL(HAVE_ALSA, test x$alsa != xfalse )
AM_CONDITIONAL(HAVE_GTK, test "$enable_gtk2" != "no" )
AM_CONDITIONAL(HAVE_OSS, test x$ac_cv_header_sys_soundcard_h = xyes )
+AM_CONDITIONAL(HAVE_PULSE, test x$have_pulse = xyes)
AM_CONDITIONAL(USE_CRASH_HANDLER, test "$use_crash_handler" = "yes" )

if test x$force_oss = xyes && test x$ac_cv_header_sys_soundcard_h = xyes; then