mirror of
https://github.com/libretro/RetroArch
synced 2025-03-02 19:13:34 +00:00
Add SDL audio driver.
This commit is contained in:
parent
ff78f74393
commit
a5bbc68a84
2
Makefile
2
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
|
||||
|
121
audio/buffer.c
Normal file
121
audio/buffer.c
Normal 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
36
audio/buffer.h
Normal 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
191
audio/sdl.c
Normal 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"
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
3
driver.c
3
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[] = {
|
||||
|
1
driver.h
1
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;
|
||||
////////////////////////////////////////////////
|
||||
|
Loading…
x
Reference in New Issue
Block a user