diff --git a/Makefile b/Makefile
index 511e40341d..31cfd447da 100644
--- a/Makefile
+++ b/Makefile
@@ -38,7 +38,7 @@ ifeq ($(HAVE_JACK),1)
endif
ifeq ($(HAVE_SDL), 1)
- OBJ += gfx/gl.o input/sdl.o
+ OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
LIBS += $(SDL_LIBS) -lGL
DEFINES += $(SDL_CFLAGS)
endif
diff --git a/audio/buffer.c b/audio/buffer.c
new file mode 100644
index 0000000000..f3df435acb
--- /dev/null
+++ b/audio/buffer.c
@@ -0,0 +1,121 @@
+/* RSound - A PCM audio client/server
+ * Copyright (C) 2010 - Hans-Kristian Arntzen
+ *
+ * RSound 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.
+ *
+ * RSound 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 RSound.
+ * If not, see .
+ */
+
+#include "buffer.h"
+
+struct rsound_fifo_buffer
+{
+ char *buffer;
+ size_t bufsize;
+ size_t first;
+ size_t end;
+};
+
+rsound_fifo_buffer_t* rsnd_fifo_new(size_t size)
+{
+ rsound_fifo_buffer_t *buf = calloc(1, sizeof(*buf));
+ if (buf == NULL)
+ return NULL;
+
+ buf->buffer = calloc(1, size + 1);
+ if (buf->buffer == NULL)
+ {
+ free(buf);
+ return NULL;
+ }
+ buf->bufsize = size + 1;
+
+ return buf;
+}
+
+void rsnd_fifo_free(rsound_fifo_buffer_t* buffer)
+{
+ assert(buffer);
+ assert(buffer->buffer);
+
+ free(buffer->buffer);
+ free(buffer);
+}
+
+size_t rsnd_fifo_read_avail(rsound_fifo_buffer_t* buffer)
+{
+ assert(buffer);
+ assert(buffer->buffer);
+
+ size_t first = buffer->first;
+ size_t end = buffer->end;
+ if (end < first)
+ end += buffer->bufsize;
+ return end - first;
+}
+
+size_t rsnd_fifo_write_avail(rsound_fifo_buffer_t* buffer)
+{
+ assert(buffer);
+ assert(buffer->buffer);
+
+ size_t first = buffer->first;
+ size_t end = buffer->end;
+ if (end < first)
+ end += buffer->bufsize;
+
+ return (buffer->bufsize - 1) - (end - first);
+}
+
+void rsnd_fifo_write(rsound_fifo_buffer_t* buffer, const void* in_buf, size_t size)
+{
+ assert(buffer);
+ assert(buffer->buffer);
+ assert(in_buf);
+ assert(rsnd_fifo_write_avail(buffer) >= size);
+
+ size_t first_write = size;
+ size_t rest_write = 0;
+ if (buffer->end + size > buffer->bufsize)
+ {
+ first_write = buffer->bufsize - buffer->end;
+ rest_write = size - first_write;
+ }
+
+ memcpy(buffer->buffer + buffer->end, in_buf, first_write);
+ if (rest_write > 0)
+ memcpy(buffer->buffer, (const char*)in_buf + first_write, rest_write);
+
+ buffer->end = (buffer->end + size) % buffer->bufsize;
+}
+
+
+void rsnd_fifo_read(rsound_fifo_buffer_t* buffer, void* in_buf, size_t size)
+{
+ assert(buffer);
+ assert(buffer->buffer);
+ assert(in_buf);
+ assert(rsnd_fifo_read_avail(buffer) >= size);
+
+ size_t first_read = size;
+ size_t rest_read = 0;
+ if (buffer->first + size > buffer->bufsize)
+ {
+ first_read = buffer->bufsize - buffer->first;
+ rest_read = size - first_read;
+ }
+
+ memcpy(in_buf, (const char*)buffer->buffer + buffer->first, first_read);
+ if (rest_read > 0)
+ memcpy((char*)in_buf + first_read, buffer->buffer, rest_read);
+
+ buffer->first = (buffer->first + size) % buffer->bufsize;
+}
+
diff --git a/audio/buffer.h b/audio/buffer.h
new file mode 100644
index 0000000000..63678d2223
--- /dev/null
+++ b/audio/buffer.h
@@ -0,0 +1,36 @@
+/* RSound - A PCM audio client/server
+ * Copyright (C) 2010 - Hans-Kristian Arntzen
+ *
+ * RSound 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.
+ *
+ * RSound 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 RSound.
+ * If not, see .
+ */
+
+#ifndef __BUFFER_H
+#define __BUFFER_H
+
+#include
+#include
+#include
+#include
+
+#ifndef RSD_FIFO_BUF_TYPEDEF
+#define RSD_FIFO_BUF_TYPEDEF
+typedef struct rsound_fifo_buffer rsound_fifo_buffer_t;
+#endif
+
+rsound_fifo_buffer_t* rsnd_fifo_new(size_t size);
+void rsnd_fifo_write(rsound_fifo_buffer_t* buffer, const void* in_buf, size_t size);
+void rsnd_fifo_read(rsound_fifo_buffer_t* buffer, void* in_buf, size_t size);
+void rsnd_fifo_free(rsound_fifo_buffer_t* buffer);
+size_t rsnd_fifo_read_avail(rsound_fifo_buffer_t* buffer);
+size_t rsnd_fifo_write_avail(rsound_fifo_buffer_t* buffer);
+
+#endif
diff --git a/audio/sdl.c b/audio/sdl.c
new file mode 100644
index 0000000000..f24836f570
--- /dev/null
+++ b/audio/sdl.c
@@ -0,0 +1,191 @@
+/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes.
+ * Copyright (C) 2010 - 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
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include "general.h"
+#include "buffer.h"
+
+typedef struct sdl_audio
+{
+ bool nonblock;
+
+ SDL_mutex *lock;
+ SDL_cond *cond;
+ rsound_fifo_buffer_t *buffer;
+} sdl_audio_t;
+
+static void sdl_audio_cb(void *data, Uint8 *stream, int len)
+{
+ sdl_audio_t *sdl = data;
+
+ size_t avail = rsnd_fifo_read_avail(sdl->buffer);
+ size_t write_size = len > avail ? avail : len;
+ rsnd_fifo_read(sdl->buffer, stream, write_size);
+ SDL_CondSignal(sdl->cond);
+
+ // If underrun, fill rest with silence.
+ memset(stream + write_size, 0, len - write_size);
+}
+
+static void* sdl_audio_init(const char* device, int rate, int latency)
+{
+ if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
+ return NULL;
+
+ sdl_audio_t *sdl = calloc(1, sizeof(*sdl));
+ if (!sdl)
+ return NULL;
+
+ SDL_AudioSpec spec = {
+ .freq = rate,
+ .format = AUDIO_S16SYS,
+ .channels = 2,
+ .samples = 512,
+ .callback = sdl_audio_cb,
+ .userdata = sdl
+ };
+
+ SDL_AudioSpec out;
+
+ if (SDL_OpenAudio(&spec, &out) < 0)
+ {
+ SSNES_ERR("Failed to open SDL audio: %s\n", SDL_GetError());
+ free(sdl);
+ return 0;
+ }
+ g_settings.audio.out_rate = out.freq;
+
+ sdl->lock = SDL_CreateMutex();
+ sdl->cond = SDL_CreateCond();
+
+ // Create a buffer twice as big as needed and prefill the buffer.
+ size_t bufsize = out.samples * 4 * sizeof(int16_t);
+ void *tmp = calloc(1, bufsize);
+ sdl->buffer = rsnd_fifo_new(bufsize);
+ if (tmp)
+ {
+ rsnd_fifo_write(sdl->buffer, tmp, bufsize);
+ free(tmp);
+ }
+
+ SDL_PauseAudio(0);
+ return sdl;
+}
+
+static ssize_t sdl_audio_write(void* data, const void* buf, size_t size)
+{
+ sdl_audio_t *sdl = data;
+
+ ssize_t ret = 0;
+ if (sdl->nonblock)
+ {
+ SDL_LockAudio();
+ size_t avail = rsnd_fifo_write_avail(sdl->buffer);
+ size_t write_amt = avail > size ? size : avail;
+ rsnd_fifo_write(sdl->buffer, buf, write_amt);
+ SDL_UnlockAudio();
+ ret = write_amt;
+ }
+ else
+ {
+ size_t written = 0;
+ while (written < size)
+ {
+ SDL_LockAudio();
+ size_t avail = rsnd_fifo_write_avail(sdl->buffer);
+
+ if (avail == 0)
+ {
+ SDL_UnlockAudio();
+ SDL_mutexP(sdl->lock);
+ SDL_CondWait(sdl->cond, sdl->lock);
+ SDL_mutexV(sdl->lock);
+ }
+ else
+ {
+ size_t write_amt = size - written > avail ? avail : size - written;
+ rsnd_fifo_write(sdl->buffer, (const char*)buf + written, write_amt);
+ SDL_UnlockAudio();
+ written += write_amt;
+ }
+ }
+ ret = written;
+ }
+
+ return ret;
+}
+
+static bool sdl_audio_stop(void *data)
+{
+ (void)data;
+ SDL_PauseAudio(1);
+ return true;
+}
+
+static bool sdl_audio_start(void *data)
+{
+ (void)data;
+ SDL_PauseAudio(0);
+ return true;
+}
+
+static void sdl_audio_set_nonblock_state(void *data, bool state)
+{
+ sdl_audio_t *sdl = data;
+ sdl->nonblock = state;
+}
+
+static void sdl_audio_free(void *data)
+{
+ SDL_CloseAudio();
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+
+ sdl_audio_t *sdl = data;
+ if (sdl)
+ {
+ rsnd_fifo_free(sdl->buffer);
+ SDL_DestroyMutex(sdl->lock);
+ SDL_DestroyCond(sdl->cond);
+ }
+ free(sdl);
+}
+
+const audio_driver_t audio_sdl = {
+ .init = sdl_audio_init,
+ .write = sdl_audio_write,
+ .stop = sdl_audio_stop,
+ .start = sdl_audio_start,
+ .set_nonblock_state = sdl_audio_set_nonblock_state,
+ .free = sdl_audio_free,
+ .ident = "sdl"
+};
+
+
+
+
+
+
diff --git a/config.def.h b/config.def.h
index 1453430a5b..fea34b99a7 100644
--- a/config.def.h
+++ b/config.def.h
@@ -42,6 +42,7 @@
#define AUDIO_ROAR 4
#define AUDIO_AL 5
#define AUDIO_JACK 6
+#define AUDIO_SDL 8
////////////////////////
#define INPUT_SDL 7
////////////////////////
@@ -60,6 +61,8 @@
#define AUDIO_DEFAULT_DRIVER AUDIO_ROAR
#elif HAVE_AL
#define AUDIO_DEFAULT_DRIVER AUDIO_AL
+#elif HAVE_SDL
+#define AUDIO_DEFAULT_DRIVER AUDIO_SDL
#endif
#define INPUT_DEFAULT_DRIVER INPUT_SDL
diff --git a/driver.c b/driver.c
index 8c73f53eef..fafb0139d1 100644
--- a/driver.c
+++ b/driver.c
@@ -42,6 +42,9 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_JACK
&audio_jack,
#endif
+#ifdef HAVE_SDL
+ &audio_sdl,
+#endif
};
static const video_driver_t *video_drivers[] = {
diff --git a/driver.h b/driver.h
index ed80623f4f..ec5f3dcca8 100644
--- a/driver.h
+++ b/driver.h
@@ -111,6 +111,7 @@ extern const audio_driver_t audio_alsa;
extern const audio_driver_t audio_roar;
extern const audio_driver_t audio_openal;
extern const audio_driver_t audio_jack;
+extern const audio_driver_t audio_sdl;
extern const video_driver_t video_gl;
extern const input_driver_t input_sdl;
////////////////////////////////////////////////