diff --git a/Makefile b/Makefile
index 36b582f8f0f..a44c6fbc7d7 100644
--- a/Makefile
+++ b/Makefile
@@ -42,6 +42,11 @@ ifeq ($(HAVE_JACK),1)
OBJ += audio/jack.o
LIBS += -ljack
endif
+ifeq ($(HAVE_PULSE), 1)
+ OBJ += audio/pulse.o
+ LIBS += $(PULSE_LIBS)
+ DEFINES += $(PULSE_CFLAGS)
+endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
diff --git a/README.md b/README.md
index 9d250865848..817a89767f2 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ SSNES can utilize these libraries if enabled:
- libxml2 (bSNES XML shaders)
- libfreetype2 (TTF font rendering on screen)
-SSNES needs one of these audio driver libraries:
+SSNES needs at least one of these audio driver libraries:
- ALSA
- OSS
@@ -35,6 +35,8 @@ SSNES needs one of these audio driver libraries:
- OpenAL
- JACK
- SDL
+ - XAudio2 (Win32)
+ - PulseAudio
# Building libsnes
diff --git a/audio/pulse.c b/audio/pulse.c
new file mode 100644
index 00000000000..79e0fd88224
--- /dev/null
+++ b/audio/pulse.c
@@ -0,0 +1,191 @@
+/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
+ * Copyright (C) 2010-2011 - Hans-Kristian Arntzen
+ *
+ * Some code herein may be based on code found in BSNES.
+ *
+ * SSNES is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * SSNES 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with SSNES.
+ * If not, see .
+ */
+
+
+#include "driver.h"
+#include "general.h"
+#include
+#include
+#include
+
+#include
+
+typedef struct
+{
+ pa_mainloop *mainloop;
+ pa_context *context;
+ pa_stream *stream;
+ bool nonblock;
+} pa_t;
+
+static void __pulse_free(void *data)
+{
+ pa_t *pa = data;
+ if (pa)
+ {
+ if (pa->stream)
+ {
+ pa_stream_disconnect(pa->stream);
+ pa_stream_unref(pa->stream);
+ }
+
+ if (pa->context)
+ {
+ pa_context_disconnect(pa->context);
+ pa_context_unref(pa->context);
+ }
+
+ if (pa->mainloop)
+ pa_mainloop_free(pa->mainloop);
+
+ free(pa);
+ }
+}
+
+static inline uint8_t is_little_endian(void)
+{
+ union
+ {
+ uint16_t x;
+ uint8_t y[2];
+ } u;
+
+ u.x = 1;
+ return u.y[0];
+}
+
+static void* __pulse_init(const char* device, int rate, int latency)
+{
+ pa_t *pa = calloc(1, sizeof(*pa));
+ if (!pa)
+ goto error;
+
+ pa->mainloop = pa_mainloop_new();
+ if (!pa->mainloop)
+ goto error;
+
+ pa->context = pa_context_new(pa_mainloop_get_api(pa->mainloop), "SSNES");
+ if (!pa->context)
+ goto error;
+ if (pa_context_connect(pa->context, device, PA_CONTEXT_NOFLAGS, NULL) < 0)
+ goto error;
+
+ pa_context_state_t cstate;
+ do
+ {
+ pa_mainloop_iterate(pa->mainloop, 1, NULL);
+ cstate = pa_context_get_state(pa->context);
+ if (!PA_CONTEXT_IS_GOOD(cstate)) goto error;
+ } while (cstate != PA_CONTEXT_READY);
+
+ pa_sample_spec spec = {
+ .format = is_little_endian() ? PA_SAMPLE_FLOAT32LE : PA_SAMPLE_FLOAT32BE,
+ .channels = 2,
+ .rate = rate
+ };
+
+ pa->stream = pa_stream_new(pa->context, "audio", &spec, NULL);
+ if (!pa->stream)
+ goto error;
+
+ pa_buffer_attr buffer_attr = {
+ .maxlength = -1,
+ .tlength = pa_usec_to_bytes(latency * PA_USEC_PER_MSEC, &spec),
+ .prebuf = -1,
+ .minreq = -1,
+ .fragsize = -1
+ };
+
+ if (pa_stream_connect_playback(pa->stream, NULL, &buffer_attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0)
+ goto error;
+
+ pa_stream_state_t sstate;
+ do
+ {
+ pa_mainloop_iterate(pa->mainloop, 1, NULL);
+ sstate = pa_stream_get_state(pa->stream);
+ if(!PA_STREAM_IS_GOOD(sstate)) goto error;
+ } while(sstate != PA_STREAM_READY);
+
+ return pa;
+
+error:
+ __pulse_free(pa);
+ return NULL;
+}
+
+static ssize_t __pulse_write(void* data, const void* buf, size_t size)
+{
+ pa_t *pa = data;
+
+ unsigned length = pa_stream_writable_size(pa->stream);
+ while (length < size)
+ {
+ pa_mainloop_iterate(pa->mainloop, 1, NULL);
+
+ length = pa_stream_writable_size(pa->stream);
+
+ if (pa->nonblock)
+ break;
+ }
+
+ size_t write_size = length < size ? length : size;
+
+ pa_stream_write(pa->stream, buf, write_size, NULL, 0LL, PA_SEEK_RELATIVE);
+ return write_size;
+}
+
+static bool __pulse_stop(void *data)
+{
+ (void)data;
+ return true;
+}
+
+static bool __pulse_start(void *data)
+{
+ (void)data;
+ return true;
+}
+
+static void __pulse_set_nonblock_state(void *data, bool state)
+{
+ pa_t *pa = data;
+ pa->nonblock = state;
+}
+
+static bool __pulse_use_float(void *data)
+{
+ (void)data;
+ return true;
+}
+
+const audio_driver_t audio_pulse = {
+ .init = __pulse_init,
+ .write = __pulse_write,
+ .stop = __pulse_stop,
+ .start = __pulse_start,
+ .set_nonblock_state = __pulse_set_nonblock_state,
+ .use_float = __pulse_use_float,
+ .free = __pulse_free,
+ .ident = "pulse"
+};
+
+
+
+
+
+
diff --git a/config.def.h b/config.def.h
index 62d477085ca..5d2378c8430 100644
--- a/config.def.h
+++ b/config.def.h
@@ -54,6 +54,7 @@
#define AUDIO_JACK 6
#define AUDIO_SDL 8
#define AUDIO_XAUDIO 9
+#define AUDIO_PULSE 10
////////////////////////
#define INPUT_SDL 7
////////////////////////
@@ -62,6 +63,8 @@
#if defined(HAVE_ALSA)
#define AUDIO_DEFAULT_DRIVER AUDIO_ALSA
+#elif defined(HAVE_PULSE)
+#define AUDIO_DEFAULT_DRIVER AUDIO_PULSE
#elif defined(HAVE_OSS)
#define AUDIO_DEFAULT_DRIVER AUDIO_OSS
#elif defined(HAVE_JACK)
diff --git a/driver.c b/driver.c
index 4c621e1942e..1b20000d0da 100644
--- a/driver.c
+++ b/driver.c
@@ -52,6 +52,9 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_XAUDIO
&audio_xa,
#endif
+#ifdef HAVE_PULSE
+ &audio_pulse,
+#endif
};
static const video_driver_t *video_drivers[] = {
diff --git a/driver.h b/driver.h
index c5792dd26ed..0a0458a5d41 100644
--- a/driver.h
+++ b/driver.h
@@ -139,6 +139,7 @@ extern const audio_driver_t audio_openal;
extern const audio_driver_t audio_jack;
extern const audio_driver_t audio_sdl;
extern const audio_driver_t audio_xa;
+extern const audio_driver_t audio_pulse;
extern const video_driver_t video_gl;
extern const input_driver_t input_sdl;
////////////////////////////////////////////////
diff --git a/qb/config.libs.sh b/qb/config.libs.sh
index 2aad11cbb15..19e397bf127 100644
--- a/qb/config.libs.sh
+++ b/qb/config.libs.sh
@@ -21,6 +21,7 @@ check_lib AL -lopenal alcOpenDevice
check_lib RSOUND -lrsound rsd_init
check_lib ROAR -lroar roar_vs_new
check_lib JACK -ljack jack_client_open
+check_pkgconf PULSE libpulse
check_pkgconf SDL sdl 1.2.10
check_critical SDL "Cannot find SDL library."
@@ -46,7 +47,7 @@ check_lib DYNAMIC -ldl dlopen
check_pkgconf FREETYPE freetype2
# Creates config.mk and config.h.
-VARS="ALSA OSS AL RSOUND ROAR JACK SDL FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE FREETYPE"
+VARS="ALSA OSS AL RSOUND ROAR JACK PULSE SDL FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE FREETYPE"
create_config_make config.mk $VARS
create_config_header config.h $VARS
diff --git a/qb/config.params.sh b/qb/config.params.sh
index 783e67ed5ee..cbf4ecec768 100644
--- a/qb/config.params.sh
+++ b/qb/config.params.sh
@@ -20,4 +20,5 @@ add_command_line_enable RSOUND "Enable RSound support" auto
add_command_line_enable ROAR "Enable RoarAudio support" auto
add_command_line_enable AL "Enable OpenAL support" auto
add_command_line_enable JACK "Enable JACK support" auto
+add_command_line_enable PULSE "Enable PulseAudio support" auto
add_command_line_enable FREETYPE "Enable FreeType support" auto
diff --git a/settings.c b/settings.c
index dc0397b9c1d..8592993a23a 100644
--- a/settings.c
+++ b/settings.c
@@ -72,6 +72,9 @@ static void set_defaults(void)
case AUDIO_XAUDIO:
def_audio = "xaudio";
break;
+ case AUDIO_PULSE:
+ def_audio = "pulse";
+ break;
default:
break;
}
diff --git a/ssnes.cfg b/ssnes.cfg
index cce93f740a9..9a497a8f7f5 100644
--- a/ssnes.cfg
+++ b/ssnes.cfg
@@ -19,7 +19,7 @@
# Video vsync.
# video_vsync = true
-# Smoothens picture with bilinear filtering. Should be disabled if using Cg shaders.
+# Smoothens picture with bilinear filtering. Should be disabled if using pixel shaders.
# video_smooth = true
# Forces rendering area to stay equal to SNES aspect ratio 4:3 or as defined in video_aspect_ratio.
@@ -28,10 +28,10 @@
# A floating point value for video aspect ratio (width / height)
# video_aspect_ratio = 1.333
-# Path to Cg shader. If enabled
+# Path to Cg shader.
# video_cg_shader = "/path/to/cg/shader.cg"
-# Path to bSNES-style XML shader. If both Cg shader path and XML shader path are defined, Cg shader will take priority.
+# Path to bSNES-style XML shader (GLSL only). If both Cg shader path and XML shader path are defined, Cg shader will take priority.
# video_bsnes_shader = "/path/to/bsnes/xml/shader.shader"
# CPU-based filter. Valid ones are: hq2x, hq4x, grayscale, bleed, ntsc.
@@ -43,7 +43,8 @@
# Size of the TTF font rendered.
# video_font_size = 48
-# Offset for where messages will be placed on screen. Values are in range 0.0 to 1.0 for both x and y values.
+# Offset for where messages will be placed on screen. Values are in range 0.0 to 1.0 for both x and y values.
+# [0.0, 0.0] maps to the lower left corner of the screen.
# video_message_pos_x = 0.05
# video_message_pox_y = 0.05
@@ -61,10 +62,10 @@
# but lots of dropped frames. Reasonable values for this is 32000 +/- 100 Hz.
# audio_in_rate = 31980
-# Audio driver backend. Depending on configuration possible candidates are: alsa, oss, jack, rsound, roar, openal, sdl and xaudio
+# Audio driver backend. Depending on configuration possible candidates are: alsa, pulse, oss, jack, rsound, roar, openal, sdl and xaudio
# audio_driver =
-# Override the default audio device the audio_driver uses.
+# Override the default audio device the audio_driver uses. This is driver dependant. E.g. ALSA wants a PCM device, OSS wants a path (e.g. /dev/dsp), Jack wants portnames (e.g. system:playback1,system:playback_2), and so on ...
# audio_device =
# Will sync (block) on audio. Recommended.
@@ -73,7 +74,7 @@
# Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency.
# audio_latency = 64
-# libsamplerate quality. Valid values are from 1 to 5. These values map to zero_order_hold, linear, sinc_fastest, sinc_medium and sinc_best.
+# libsamplerate quality. Valid values are from 1 to 5. These values map to zero_order_hold, linear, sinc_fastest, sinc_medium and sinc_best respectively.
# audio_src_quality =
### Input
@@ -98,11 +99,14 @@
# input_player1_up = up
# input_player1_down = down
-# If desired, it is possible to override which joypads are being used for player 1 and 2. First joypad available is 0.
+# If desired, it is possible to override which joypads are being used for player 1 through 5. First joypad available is 0.
# input_player1_joypad_index = 0
# input_player2_joypad_index = 1
+# input_player3_joypad_index = 2
+# input_player4_joypad_index = 3
+# input_player5_joypad_index = 4
-# Joypad buttons. Figure these out by looking at jstest /dev/input/js0 output.
+# Joypad buttons. Figure these out by looking at jstest /dev/input/js0 output, or use ssnes-joyconfig.
# You can use joypad hats with hnxx, where n is the hat, and xx is a string representing direction.
# E.g. "h0up"
# input_player1_a_btn = 1
@@ -118,7 +122,7 @@
# input_player1_up_btn = 13
# input_player1_down_btn = 14
-# Axis for DPAD.
+# Axis for SNES DPAD.
# Needs to be either '+' or '-' in the first character signaling either positive or negative direction of the axis, then the axis number.
# Do note that every other input option has the corresponding _btn and _axis binds as well; they are omitted here for clarity.
# input_player1_left_axis = -0
@@ -159,6 +163,8 @@
# input_player2_up_axis = -1
# input_player2_down_axis = +1
+# This goes all the way to player 5, but again omitted for clarity.
+
# Toggles fullscreen.
# input_toggle_fullscreen = f
# Saves state.