Make resamplers more modular.

This commit is contained in:
Themaister 2013-02-08 11:49:51 +01:00
parent 5b57e99b89
commit d33d2e9f0c
12 changed files with 101 additions and 40 deletions

View File

@ -27,6 +27,8 @@ OBJ = retroarch.o \
gfx/image.o \
gfx/fonts/fonts.o \
gfx/fonts/bitmapfont.o \
audio/hermite.o \
audio/resampler.o \
performance.o
JOYCONFIG_OBJ = tools/retroarch-joyconfig.o \
@ -298,8 +300,6 @@ ifeq ($(HAVE_SINC), 1)
ifeq ($(HAVE_NEON),1)
OBJ += audio/sinc_neon.o
endif
else
OBJ += audio/hermite.o
endif
OBJ += audio/utils.o
ifeq ($(HAVE_NEON),1)

View File

@ -30,6 +30,8 @@ OBJ = retroarch.o \
gfx/fonts/fonts.o \
gfx/fonts/bitmapfont.o \
gfx/image.o \
audio/hermite.o \
audio/resampler.o \
performance.o
JOBJ := conf/config_file.o \
@ -208,8 +210,7 @@ endif
ifeq ($(HAVE_SINC), 1)
OBJ += audio/sinc.o
else
OBJ += audio/hermite.o
DEFINES += -DHAVE_SINC
endif
ifneq ($(V), 1)

View File

@ -27,11 +27,11 @@
#define CHANNELS 2
struct rarch_resampler
typedef struct rarch_hermite_resampler
{
float chan_data[CHANNELS][4];
double r_frac;
};
} rarch_hermite_resampler_t;
static inline float hermite_kernel(float mu1, float a, float b, float c, float d)
{
@ -51,16 +51,17 @@ static inline float hermite_kernel(float mu1, float a, float b, float c, float d
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
rarch_resampler_t *resampler_new(void)
void *resampler_hermite_new(void)
{
#ifndef RESAMPLER_TEST
RARCH_LOG("Hermite resampler [C]\n");
#endif
return (rarch_resampler_t*)calloc(1, sizeof(rarch_resampler_t));
return calloc(1, sizeof(rarch_hermite_resampler_t));
}
void resampler_process(rarch_resampler_t *re, struct resampler_data *data)
static void resampler_hermite_process(void *re_, struct resampler_data *data)
{
rarch_hermite_resampler_t *re = (rarch_hermite_resampler_t*)re_;
double r_step = 1.0 / data->ratio;
size_t processed_out = 0;
@ -101,8 +102,15 @@ void resampler_process(rarch_resampler_t *re, struct resampler_data *data)
data->output_frames = processed_out;
}
void resampler_free(rarch_resampler_t *re)
static void resampler_hermite_free(void *re)
{
free(re);
}
const rarch_resampler_t hermite_resampler = {
resampler_hermite_new,
resampler_hermite_process,
resampler_hermite_free,
"hermite",
};

View File

@ -24,13 +24,13 @@
#include <stddef.h>
#include <stdint.h>
#include <math.h>
#include "../boolean.h"
// M_PI is left out of ISO C99 :(
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327
#endif
typedef struct rarch_resampler rarch_resampler_t;
typedef float sample_t;
struct resampler_data
@ -44,9 +44,33 @@ struct resampler_data
double ratio;
};
rarch_resampler_t *resampler_new(void);
void resampler_process(rarch_resampler_t *re, struct resampler_data *data);
void resampler_free(rarch_resampler_t *re);
typedef struct rarch_resampler
{
void *(*init)(void);
void (*process)(void *re, struct resampler_data *data);
void (*free)(void *re);
const char *ident;
} rarch_resampler_t;
extern const rarch_resampler_t hermite_resampler;
extern const rarch_resampler_t sinc_resampler;
// Reallocs resampler. Will free previous handle before allocating a new one.
// If ident is NULL, first resampler will be used.
bool rarch_resampler_realloc(void **re, const rarch_resampler_t **backend, const char *ident);
// Convenience macros.
// freep makes sure to set handles to NULL to avoid double-free in rarch_resampler_realloc.
#define rarch_resampler_freep(backend, handle) do { \
if (*(backend) && *(handle)) \
(*backend)->free(*handle); \
*backend = NULL; \
*handle = NULL; \
} while(0)
#define rarch_resampler_process(backend, handle, data) do { \
(backend)->process(handle, data); \
} while(0)
#endif

View File

@ -62,7 +62,7 @@
#define TAPS (SIDELOBES * 2)
#define CUTOFF 0.98
struct rarch_resampler
typedef struct rarch_sinc_resampler
{
sample_t phase_table[1 << PHASE_BITS][TAPS];
sample_t buffer_l[2 * TAPS];
@ -70,7 +70,7 @@ struct rarch_resampler
unsigned ptr;
uint32_t time;
};
} rarch_sinc_resampler_t;
static inline double sinc(double val)
{
@ -85,7 +85,7 @@ static inline double lanzcos(double index)
return sinc(index);
}
static void init_sinc_table(rarch_resampler_t *resamp)
static void init_sinc_table(rarch_sinc_resampler_t *resamp)
{
// Sinc phases: [..., p + 3, p + 2, p + 1, p + 0, p - 1, p - 2, p - 3, p - 4, ...]
for (int i = 0; i < (1 << PHASE_BITS); i++)
@ -121,7 +121,7 @@ static void aligned_free__(void *ptr)
free(p[-1]);
}
static inline void process_sinc_C(rarch_resampler_t *resamp, float *out_buffer)
static inline void process_sinc_C(rarch_sinc_resampler_t *resamp, float *out_buffer)
{
float sum_l = 0.0f;
float sum_r = 0.0f;
@ -144,7 +144,7 @@ static inline void process_sinc_C(rarch_resampler_t *resamp, float *out_buffer)
#if defined(__AVX__) && ENABLE_AVX
#define process_sinc_func process_sinc
static void process_sinc(rarch_resampler_t *resamp, float *out_buffer)
static void process_sinc(rarch_sinc_resampler_t *resamp, float *out_buffer)
{
__m256 sum_l = _mm256_setzero_ps();
__m256 sum_r = _mm256_setzero_ps();
@ -180,7 +180,7 @@ static void process_sinc(rarch_resampler_t *resamp, float *out_buffer)
}
#elif defined(__SSE__)
#define process_sinc_func process_sinc
static void process_sinc(rarch_resampler_t *resamp, float *out_buffer)
static void process_sinc(rarch_sinc_resampler_t *resamp, float *out_buffer)
{
__m128 sum_l = _mm_setzero_ps();
__m128 sum_r = _mm_setzero_ps();
@ -230,10 +230,10 @@ static void process_sinc(rarch_resampler_t *resamp, float *out_buffer)
// Need to make this function pointer as Android doesn't have built-in targets
// for NEON and plain ARMv7a.
static void (*process_sinc_func)(rarch_resampler_t *resamp, float *out_buffer);
static void (*process_sinc_func)(rarch_sinc_resampler_t *resamp, float *out_buffer);
void process_sinc_neon_asm(float *out, const float *left, const float *right, const float *coeff);
static void process_sinc_neon(rarch_resampler_t *resamp, float *out_buffer)
static void process_sinc_neon(rarch_sinc_resampler_t *resamp, float *out_buffer)
{
const float *buffer_l = resamp->buffer_l + resamp->ptr;
const float *buffer_r = resamp->buffer_r + resamp->ptr;
@ -247,8 +247,10 @@ static void process_sinc_neon(rarch_resampler_t *resamp, float *out_buffer)
#define process_sinc_func process_sinc_C
#endif
void resampler_process(rarch_resampler_t *re, struct resampler_data *data)
static void resampler_sinc_process(void *re_, struct resampler_data *data)
{
rarch_sinc_resampler_t *re = (rarch_sinc_resampler_t*)re_;
// If data->ratio is < 1, we are downsampling.
// The sinc table is not set up for this, as it always assumes upsampling.
// Downsampling will work, but with some added noise due to aliasing might be present.
@ -283,14 +285,14 @@ void resampler_process(rarch_resampler_t *re, struct resampler_data *data)
data->output_frames = out_frames;
}
void resampler_free(rarch_resampler_t *re)
static void resampler_sinc_free(void *re)
{
aligned_free__(re);
}
rarch_resampler_t *resampler_new(void)
static void *resampler_sinc_new(void)
{
rarch_resampler_t *re = (rarch_resampler_t*)aligned_alloc__(1024, sizeof(*re));
rarch_sinc_resampler_t *re = (rarch_sinc_resampler_t*)aligned_alloc__(128, sizeof(*re));
if (!re)
return NULL;
@ -311,6 +313,15 @@ rarch_resampler_t *resampler_new(void)
RARCH_LOG("Sinc resampler [C]\n");
#endif
RARCH_LOG("SINC params (%u phase bits, %u taps).\n", PHASE_BITS, TAPS);
return re;
}
const rarch_resampler_t sinc_resampler = {
resampler_sinc_new,
resampler_sinc_process,
resampler_sinc_free,
"sinc",
};

View File

@ -275,13 +275,13 @@ FIFO BUFFER
#include "../../fifo_buffer.c"
/*============================================================
AUDIO HERMITE
AUDIO RESAMPLER
============================================================ */
#include "../../audio/resampler.c"
#ifdef HAVE_SINC
#include "../../audio/sinc.c"
#else
#include "../../audio/hermite.c"
#endif
#include "../../audio/hermite.c"
/*============================================================
RSOUND

View File

@ -22,6 +22,7 @@
#include <math.h>
#include "compat/posix_string.h"
#include "audio/utils.h"
#include "audio/resampler.h"
#ifdef HAVE_X11
#include "gfx/context/x11_common.h"
@ -405,9 +406,13 @@ void init_audio(void)
g_extern.audio_data.chunk_size = g_extern.audio_data.nonblock_chunk_size;
}
g_extern.audio_data.source = resampler_new();
if (!g_extern.audio_data.source)
const char *resampler = *g_settings.audio.resampler ? g_settings.audio.resampler : NULL;
if (!rarch_resampler_realloc(&g_extern.audio_data.resampler_data, &g_extern.audio_data.resampler,
resampler))
{
RARCH_ERR("Failed to initialize resampler \"%s\".\n", resampler ? resampler : "(default)");
g_extern.audio_active = false;
}
rarch_assert(g_extern.audio_data.data = (float*)malloc(max_bufsamples * sizeof(float)));
@ -536,8 +541,7 @@ void uninit_audio(void)
if (driver.audio_data && driver.audio)
driver.audio->free(driver.audio_data);
if (g_extern.audio_data.source)
resampler_free(g_extern.audio_data.source);
rarch_resampler_freep(&g_extern.audio_data.resampler, &g_extern.audio_data.resampler_data);
free(g_extern.audio_data.data);
g_extern.audio_data.data = NULL;

View File

@ -217,6 +217,8 @@ struct settings
bool rate_control;
float rate_control_delta;
float volume; // dB scale
char resampler[32];
} audio;
struct
@ -373,7 +375,8 @@ struct global
struct
{
rarch_resampler_t *source;
void *resampler_data;
const rarch_resampler_t *resampler;
float *data;

View File

@ -99,7 +99,9 @@ struct ff_audio_info
// Most lossy audio codecs only support certain sampling rates.
// Could use libswresample, but it doesn't support floating point ratios. :(
// Use either S16 or (planar) float for simplicity.
rarch_resampler_t *resampler;
const rarch_resampler_t *resampler;
void *resampler_data;
bool use_float;
bool is_planar;
unsigned sample_size;
@ -277,7 +279,9 @@ static bool ffemu_init_audio(ffemu_t *handle)
audio->codec->sample_rate = params->sample_rate;
audio->codec->time_base = av_d2q(1.0 / params->sample_rate, 1000000);
audio->resampler = resampler_new();
rarch_resampler_realloc(&audio->resampler_data,
&audio->resampler,
*g_settings.audio.resampler ? g_settings.audio.resampler : NULL);
}
else
{
@ -683,8 +687,8 @@ void ffemu_free(ffemu_t *handle)
if (handle->config.audio_opts)
av_dict_free(&handle->config.audio_opts);
if (handle->audio.resampler)
resampler_free(handle->audio.resampler);
rarch_resampler_freep(&handle->audio.resampler,
&handle->audio.resampler_data);
av_free(handle->audio.float_conv);
av_free(handle->audio.resample_out);
@ -1023,7 +1027,7 @@ static void ffemu_audio_resample(ffemu_t *handle, struct ffemu_audio_data *data)
info.input_frames = data->frames;
info.ratio = handle->audio.ratio;
resampler_process(handle->audio.resampler, &info);
rarch_resampler_process(handle->audio.resampler, handle->audio.resampler_data, &info);
data->data = handle->audio.resample_out;
data->frames = info.output_frames;

View File

@ -402,7 +402,8 @@ static bool audio_flush(const int16_t *data, size_t samples)
RARCH_PERFORMANCE_INIT(resampler_proc);
RARCH_PERFORMANCE_START(resampler_proc);
resampler_process(g_extern.audio_data.source, &src_data);
rarch_resampler_process(g_extern.audio_data.resampler,
g_extern.audio_data.resampler_data, &src_data);
RARCH_PERFORMANCE_STOP(resampler_proc);
output_data = g_extern.audio_data.outsamples;

View File

@ -159,6 +159,10 @@
# Audio output samplerate.
# audio_out_rate = 48000
# Which resampler to use. "sinc" and "hermite" are currently implemented.
# Default will use "sinc" if compiled in.
# audio_resampler =
# When altering audio_in_rate on-the-fly, define by how much each time.
# audio_rate_step = 0.25

View File

@ -641,6 +641,7 @@ bool config_load_file(const char *path)
CONFIG_GET_BOOL(audio.rate_control, "audio_rate_control");
CONFIG_GET_FLOAT(audio.rate_control_delta, "audio_rate_control_delta");
CONFIG_GET_FLOAT(audio.volume, "audio_volume");
CONFIG_GET_STRING(audio.resampler, "audio_resampler");
CONFIG_GET_STRING(video.driver, "video_driver");
CONFIG_GET_STRING(audio.driver, "audio_driver");