mirror of
https://github.com/CTCaer/RetroArch.git
synced 2024-12-12 12:58:34 +00:00
Add external API for audio driver.
This commit is contained in:
parent
4a78f2fef1
commit
5d5865cfd8
2
Makefile
2
Makefile
@ -98,7 +98,7 @@ ifeq ($(HAVE_XML), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
OBJ += gfx/ext.o
|
||||
OBJ += gfx/ext.o audio/ext.o
|
||||
LIBS += -ldl
|
||||
endif
|
||||
|
||||
|
@ -82,7 +82,7 @@ ifeq ($(HAVE_FBO), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
OBJ += gfx/ext.o
|
||||
OBJ += gfx/ext.o audio/ext.o
|
||||
endif
|
||||
|
||||
ifneq ($(V),1)
|
||||
|
@ -82,7 +82,7 @@ ifeq ($(HAVE_FBO), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
OBJ += gfx/ext.o
|
||||
OBJ += gfx/ext.o audio/ext.o
|
||||
endif
|
||||
|
||||
|
||||
|
149
audio/ext.c
Normal file
149
audio/ext.c
Normal file
@ -0,0 +1,149 @@
|
||||
/* 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 "ext/ssnes_audio.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "driver.h"
|
||||
#include "dynamic.h"
|
||||
#include "general.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef struct audio_ext
|
||||
{
|
||||
dylib_t lib;
|
||||
const ssnes_audio_driver_t *driver;
|
||||
void *handle;
|
||||
bool is_float;
|
||||
} audio_ext_t;
|
||||
|
||||
static void audio_ext_free(void *data)
|
||||
{
|
||||
audio_ext_t *ext = data;
|
||||
if (ext)
|
||||
{
|
||||
if (ext->driver && ext->handle)
|
||||
ext->driver->free(ext->handle);
|
||||
if (ext->lib)
|
||||
dylib_close(ext->lib);
|
||||
free(ext);
|
||||
}
|
||||
}
|
||||
|
||||
static void* audio_ext_init(const char *device, int rate, int latency)
|
||||
{
|
||||
if (!(*g_settings.audio.external_driver))
|
||||
{
|
||||
SSNES_ERR("Please define an external audio driver.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
audio_ext_t *ext = calloc(1, sizeof(*ext));
|
||||
if (!ext)
|
||||
return NULL;
|
||||
|
||||
ext->lib = dylib_load(g_settings.audio.external_driver);
|
||||
if (!ext->lib)
|
||||
{
|
||||
SSNES_ERR("Failed to load external library \"%s\"\n", g_settings.audio.external_driver);
|
||||
goto error;
|
||||
}
|
||||
|
||||
const ssnes_audio_driver_t* (*plugin_load)(void) = dylib_proc(ext->lib, "ssnes_audio_driver_init");
|
||||
|
||||
if (!plugin_load)
|
||||
{
|
||||
SSNES_ERR("Failed to find symbol \"ssnes_audio_driver_init\" in plugin.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ext->driver = plugin_load();
|
||||
if (!ext->driver)
|
||||
{
|
||||
SSNES_ERR("Received invalid driver from plugin.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ssnes_audio_driver_info_t info = {
|
||||
.device = device,
|
||||
.sample_rate = rate,
|
||||
.latency = latency
|
||||
};
|
||||
|
||||
ext->handle = ext->driver->init(&info);
|
||||
if (!ext->handle)
|
||||
{
|
||||
SSNES_ERR("Failed to init audio driver.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
return ext;
|
||||
|
||||
error:
|
||||
audio_ext_free(ext);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t audio_ext_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
audio_ext_t *ext = data;
|
||||
unsigned frame_size = ext->is_float ? (2 * sizeof(float)) : (2 * sizeof(int16_t));
|
||||
size /= frame_size;
|
||||
|
||||
int ret = ext->driver->write(ext->handle, buf, size);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
return ret * frame_size;
|
||||
}
|
||||
|
||||
static bool audio_ext_start(void *data)
|
||||
{
|
||||
audio_ext_t *ext = data;
|
||||
return ext->driver->start(ext->handle);
|
||||
}
|
||||
|
||||
static bool audio_ext_stop(void *data)
|
||||
{
|
||||
audio_ext_t *ext = data;
|
||||
return ext->driver->stop(ext->handle);
|
||||
}
|
||||
|
||||
static void audio_ext_set_nonblock_state(void *data, bool toggle)
|
||||
{
|
||||
audio_ext_t *ext = data;
|
||||
ext->driver->set_nonblock_state(ext->handle, toggle);
|
||||
}
|
||||
|
||||
static bool audio_ext_use_float(void *data)
|
||||
{
|
||||
audio_ext_t *ext = data;
|
||||
ext->is_float = ext->driver->use_float(ext->handle);
|
||||
return ext->is_float;
|
||||
}
|
||||
|
||||
|
||||
const audio_driver_t audio_ext = {
|
||||
.init = audio_ext_init,
|
||||
.write = audio_ext_write,
|
||||
.stop = audio_ext_stop,
|
||||
.start = audio_ext_start,
|
||||
.set_nonblock_state = audio_ext_set_nonblock_state,
|
||||
.use_float = audio_ext_use_float,
|
||||
.free = audio_ext_free,
|
||||
.ident = "ext"
|
||||
};
|
110
audio/ext/ssnes_audio.h
Normal file
110
audio/ext/ssnes_audio.h
Normal file
@ -0,0 +1,110 @@
|
||||
/////
|
||||
// API header for external SSNES audio driver plugins.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __SSNES_AUDIO_DRIVER_PLUGIN_H
|
||||
#define __SSNES_AUDIO_DRIVER_PLUGIN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef SSNES_DLL_IMPORT
|
||||
#define SSNES_API_EXPORT __declspec(dllimport)
|
||||
#else
|
||||
#define SSNES_API_EXPORT __declspec(dllexport)
|
||||
#endif
|
||||
#define SSNES_API_CALLTYPE __cdecl
|
||||
#else
|
||||
#define SSNES_API_EXPORT
|
||||
#define SSNES_API_CALLTYPE
|
||||
#endif
|
||||
|
||||
#define SSNES_TRUE 1
|
||||
#define SSNES_FALSE 0
|
||||
#define SSNES_OK 1
|
||||
#define SSNES_ERROR 0
|
||||
|
||||
#define SSNES_API_VERSION 1
|
||||
|
||||
typedef struct ssnes_audio_driver_info
|
||||
{
|
||||
// A hint for a subdevice of the audio driver.
|
||||
// This is driver independent, and not relevant for all
|
||||
// audio drivers. I.e. ALSA driver might use "hw:0",
|
||||
// OSS "/dev/audio", etc.
|
||||
const char *device;
|
||||
|
||||
// Audio sample rate.
|
||||
unsigned sample_rate;
|
||||
|
||||
// Maximum audio latency requested for output,
|
||||
// measured in milliseconds.
|
||||
// If driver is not able to provide this latency, it can
|
||||
// be disregarded.
|
||||
unsigned latency;
|
||||
} ssnes_audio_driver_info_t;
|
||||
|
||||
typedef struct ssnes_audio_driver
|
||||
{
|
||||
// Initializes the device.
|
||||
void* (*init)(const ssnes_audio_driver_info_t *info);
|
||||
|
||||
// Write data in buffer to audio driver.
|
||||
// A frame here is defined as one combined sample of left and right
|
||||
// channels. (I.e. 44.1kHz, 16-bit stereo has 88.2k samples/s, and
|
||||
// 44.1k frames/s.)
|
||||
//
|
||||
// Samples are interleaved in format LRLRLRLRLR ...
|
||||
// If the driver returns true in use_float(), a floating point
|
||||
// format will be used, with range [-1.0, 1.0].
|
||||
// If not, signed 16-bit samples in native byte ordering will be used.
|
||||
//
|
||||
// This function returns the number of frames successfully written.
|
||||
// If an error occurs, -1 should be returned.
|
||||
// Note that non-blocking behavior that cannot write at this time
|
||||
// should return 0 as returning -1 will terminate the driver.
|
||||
//
|
||||
// Unless said otherwise with set_nonblock_state(), all writes
|
||||
// are blocking, and it should block till it has written all frames.
|
||||
int (*write)(void *data, const void *buffer, unsigned frames);
|
||||
|
||||
// Temporarily pauses the audio driver.
|
||||
int (*stop)(void *data);
|
||||
|
||||
// Resumes audio driver from the paused state.
|
||||
int (*start)(void *data);
|
||||
|
||||
// If state is true, nonblocking operation is assumed.
|
||||
// This is typically used for fast-forwarding. If driver cannot
|
||||
// implement nonblocking writes, this can be disregarded, but should
|
||||
// log a message to stderr.
|
||||
void (*set_nonblock_state)(void *data, int state);
|
||||
|
||||
// Stops and frees the audio driver.
|
||||
void (*free)(void *data);
|
||||
|
||||
// If true is returned, the audio driver is capable of using
|
||||
// floating point data. This will likely increase performance as the
|
||||
// resampler unit uses floating point. The sample range is
|
||||
// [-1.0, 1.0].
|
||||
int (*use_float)(void *data);
|
||||
|
||||
// Human readable identification string for the driver.
|
||||
const char *ident;
|
||||
|
||||
// Must be set to SSNES_API_VERSION.
|
||||
// Used for detecting API mismatch.
|
||||
int api_version;
|
||||
} ssnes_audio_driver_t;
|
||||
|
||||
SSNES_API_EXPORT const ssnes_audio_driver_t* SSNES_API_CALLTYPE
|
||||
ssnes_audio_driver_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -56,6 +56,7 @@
|
||||
#define AUDIO_SDL 8
|
||||
#define AUDIO_XAUDIO 9
|
||||
#define AUDIO_PULSE 10
|
||||
#define AUDIO_EXT 15
|
||||
////////////////////////
|
||||
#define INPUT_SDL 7
|
||||
#define INPUT_X 12
|
||||
@ -91,6 +92,8 @@
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_RSOUND
|
||||
#elif defined(HAVE_ROAR)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_ROAR
|
||||
#elif defined(HAVE_DYLIB)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_EXT
|
||||
#else
|
||||
#error "Need at least one audio driver!"
|
||||
#endif
|
||||
|
3
driver.c
3
driver.c
@ -56,6 +56,9 @@ static const audio_driver_t *audio_drivers[] = {
|
||||
#ifdef HAVE_PULSE
|
||||
&audio_pulse,
|
||||
#endif
|
||||
#ifdef HAVE_DYLIB
|
||||
&audio_ext,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const video_driver_t *video_drivers[] = {
|
||||
|
1
driver.h
1
driver.h
@ -154,6 +154,7 @@ 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 audio_driver_t audio_ext;
|
||||
extern const video_driver_t video_gl;
|
||||
extern const video_driver_t video_xvideo;
|
||||
extern const video_driver_t video_sdl;
|
||||
|
@ -106,6 +106,7 @@ struct settings
|
||||
int src_quality;
|
||||
|
||||
char dsp_plugin[256];
|
||||
char external_driver[256];
|
||||
} audio;
|
||||
|
||||
struct
|
||||
|
@ -83,6 +83,9 @@ static void set_defaults(void)
|
||||
case AUDIO_PULSE:
|
||||
def_audio = "pulse";
|
||||
break;
|
||||
case AUDIO_EXT:
|
||||
def_audio = "ext";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -315,6 +318,7 @@ static void parse_config_file(void)
|
||||
#ifdef HAVE_DYLIB
|
||||
CONFIG_GET_STRING(video.filter_path, "video_filter");
|
||||
CONFIG_GET_STRING(video.external_driver, "video_external_driver");
|
||||
CONFIG_GET_STRING(audio.external_driver, "audio_external_driver");
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CG) || defined(HAVE_XML)
|
||||
|
@ -99,9 +99,12 @@
|
||||
# When altering audio_in_rate on-the-fly, define by how much each time.
|
||||
# audio_rate_step = 0.25
|
||||
|
||||
# Audio driver backend. Depending on configuration possible candidates are: alsa, pulse, 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, xaudio and ext (external driver).
|
||||
# audio_driver =
|
||||
|
||||
# Path to external audio driver using the SSNES audio driver API.
|
||||
# audio_external_driver =
|
||||
|
||||
# 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 =
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user