Skip to content

Commit

Permalink
Initial PulseAudio code.
Browse files Browse the repository at this point in the history
  • Loading branch information
Themaister committed Jan 29, 2011
1 parent 334f11d commit e83f3ed
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 12 deletions.
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -35,6 +35,8 @@ SSNES needs one of these audio driver libraries:
- OpenAL
- JACK
- SDL
- XAudio2 (Win32)
- PulseAudio

# Building libsnes

Expand Down
191 changes: 191 additions & 0 deletions audio/pulse.c
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/


#include "driver.h"
#include "general.h"
#include <pulse/pulseaudio.h>
#include <stdbool.h>
#include <string.h>

#include <stdio.h>

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"
};






3 changes: 3 additions & 0 deletions config.def.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#define AUDIO_JACK 6
#define AUDIO_SDL 8
#define AUDIO_XAUDIO 9
#define AUDIO_PULSE 10
////////////////////////
#define INPUT_SDL 7
////////////////////////
Expand All @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down
1 change: 1 addition & 0 deletions driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
////////////////////////////////////////////////
Expand Down
3 changes: 2 additions & 1 deletion qb/config.libs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand All @@ -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

1 change: 1 addition & 0 deletions qb/config.params.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
26 changes: 16 additions & 10 deletions ssnes.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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

Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit e83f3ed

Please sign in to comment.