Report abuse

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