Add SDL audio driver.

This commit is contained in:
Themaister 2011-01-07 15:50:16 +01:00
parent ff78f74393
commit a5bbc68a84
7 changed files with 356 additions and 1 deletions

View File

@ -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

121
audio/buffer.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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;
}

36
audio/buffer.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef __BUFFER_H
#define __BUFFER_H
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#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

191
audio/sdl.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "driver.h"
#include <stdlib.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <SDL/SDL.h>
#include <SDL/SDL_audio.h>
#include <SDL/SDL_thread.h>
#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"
};

View File

@ -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

View File

@ -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[] = {

View File

@ -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;
////////////////////////////////////////////////