diff --git a/Makefile b/Makefile index c8926d613d..0eaffc23d2 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/Makefile.win b/Makefile.win index 7a5838c5b6..ff02d01eea 100644 --- a/Makefile.win +++ b/Makefile.win @@ -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) diff --git a/audio/hermite.c b/audio/hermite.c index e919dd6d8b..1dc80e3a6f 100644 --- a/audio/hermite.c +++ b/audio/hermite.c @@ -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", +}; + diff --git a/audio/resampler.h b/audio/resampler.h index 498e66a245..a865f2ab5a 100644 --- a/audio/resampler.h +++ b/audio/resampler.h @@ -24,13 +24,13 @@ #include #include #include +#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 diff --git a/audio/sinc.c b/audio/sinc.c index 38ac38e067..145a69ddc3 100644 --- a/audio/sinc.c +++ b/audio/sinc.c @@ -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", +}; + diff --git a/console/griffin/griffin.c b/console/griffin/griffin.c index e5997a1e52..6fc2a6efbf 100644 --- a/console/griffin/griffin.c +++ b/console/griffin/griffin.c @@ -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 diff --git a/driver.c b/driver.c index 17369e1968..0cfb87f968 100644 --- a/driver.c +++ b/driver.c @@ -22,6 +22,7 @@ #include #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; diff --git a/general.h b/general.h index 6b5272dc9c..cbc2314360 100644 --- a/general.h +++ b/general.h @@ -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; diff --git a/record/ffemu.c b/record/ffemu.c index 4c4f574c43..4728c7ac44 100644 --- a/record/ffemu.c +++ b/record/ffemu.c @@ -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; diff --git a/retroarch.c b/retroarch.c index 3c1dd6486c..34fc0aa6c8 100644 --- a/retroarch.c +++ b/retroarch.c @@ -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; diff --git a/retroarch.cfg b/retroarch.cfg index 46d01d83be..2237d20e35 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -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 diff --git a/settings.c b/settings.c index b57f2b84ed..b620eebcfb 100644 --- a/settings.c +++ b/settings.c @@ -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");