diff --git a/audio/audio_mix.c b/audio/audio_mix.c index 55a4c4d..776a9f2 100644 --- a/audio/audio_mix.c +++ b/audio/audio_mix.c @@ -192,6 +192,7 @@ audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate) retro_resampler_realloc(&chunk->resampler_data, &chunk->resampler, NULL, + RESAMPLER_QUALITY_DONTCARE, chunk->ratio); if (chunk->resampler && chunk->resampler_data) diff --git a/audio/audio_mixer.c b/audio/audio_mixer.c index 3e912cb..c781297 100644 --- a/audio/audio_mixer.c +++ b/audio/audio_mixer.c @@ -219,7 +219,8 @@ static bool one_shot_resample(const float* in, size_t samples_in, const retro_resampler_t* resampler = NULL; float ratio = (double)s_rate / (double)rate; - if (!retro_resampler_realloc(&data, &resampler, NULL, ratio)) + if (!retro_resampler_realloc(&data, &resampler, NULL, + RESAMPLER_QUALITY_DONTCARE, ratio)) return false; /* @@ -430,7 +431,8 @@ static bool audio_mixer_play_ogg( ratio = (double)s_rate / (double)info.sample_rate; if (!retro_resampler_realloc(&resampler_data, - &resamp, NULL, ratio)) + &resamp, NULL, RESAMPLER_QUALITY_DONTCARE, + ratio)) goto error; } diff --git a/audio/resampler/audio_resampler.c b/audio/resampler/audio_resampler.c index 0a4b8b3..3cf4660 100644 --- a/audio/resampler/audio_resampler.c +++ b/audio/resampler/audio_resampler.c @@ -127,12 +127,13 @@ static const retro_resampler_t *find_resampler_driver(const char *ident) **/ static bool resampler_append_plugs(void **re, const retro_resampler_t **backend, + enum resampler_quality quality, double bw_ratio) { resampler_simd_mask_t mask = (resampler_simd_mask_t)cpu_features_get(); if (*backend) - *re = (*backend)->init(&resampler_config, bw_ratio, mask); + *re = (*backend)->init(&resampler_config, bw_ratio, quality, mask); if (!*re) return false; @@ -152,7 +153,7 @@ static bool resampler_append_plugs(void **re, * Returns: true (1) if successful, otherwise false (0). **/ bool retro_resampler_realloc(void **re, const retro_resampler_t **backend, - const char *ident, double bw_ratio) + const char *ident, enum resampler_quality quality, double bw_ratio) { if (*re && *backend) (*backend)->free(*re); @@ -160,7 +161,7 @@ bool retro_resampler_realloc(void **re, const retro_resampler_t **backend, *re = NULL; *backend = find_resampler_driver(ident); - if (!resampler_append_plugs(re, backend, bw_ratio)) + if (!resampler_append_plugs(re, backend, quality, bw_ratio)) { if (!*re) *backend = NULL; diff --git a/audio/resampler/drivers/nearest_resampler.c b/audio/resampler/drivers/nearest_resampler.c index 65ced85..1083c85 100644 --- a/audio/resampler/drivers/nearest_resampler.c +++ b/audio/resampler/drivers/nearest_resampler.c @@ -62,7 +62,9 @@ static void resampler_nearest_free(void *re_) } static void *resampler_nearest_init(const struct resampler_config *config, - double bandwidth_mod, resampler_simd_mask_t mask) + double bandwidth_mod, + enum resampler_quality quality, + resampler_simd_mask_t mask) { rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*) calloc(1, sizeof(rarch_nearest_resampler_t)); diff --git a/audio/resampler/drivers/null_resampler.c b/audio/resampler/drivers/null_resampler.c index 0348702..e750269 100644 --- a/audio/resampler/drivers/null_resampler.c +++ b/audio/resampler/drivers/null_resampler.c @@ -41,7 +41,9 @@ static void resampler_null_free(void *re_) } static void *resampler_null_init(const struct resampler_config *config, - double bandwidth_mod, resampler_simd_mask_t mask) + double bandwidth_mod, + enum resampler_quality quality, + resampler_simd_mask_t mask) { return (void*)0; } diff --git a/audio/resampler/drivers/sinc_resampler.c b/audio/resampler/drivers/sinc_resampler.c index c1a240d..486f96a 100644 --- a/audio/resampler/drivers/sinc_resampler.c +++ b/audio/resampler/drivers/sinc_resampler.c @@ -38,7 +38,7 @@ #include #endif -#if defined(__AVX__) && ENABLE_AVX +#if defined(__AVX__) #include #endif @@ -51,64 +51,13 @@ */ /* TODO, make all this more configurable. */ -#if defined(SINC_LOWEST_QUALITY) -#define SINC_WINDOW_LANCZOS -#define CUTOFF 0.98 -#define PHASE_BITS 12 -#define SINC_COEFF_LERP 0 -#define SUBPHASE_BITS 10 -#define SIDELOBES 2 -#define ENABLE_AVX 0 -#elif defined(SINC_LOWER_QUALITY) -#define SINC_WINDOW_LANCZOS -#define CUTOFF 0.98 -#define PHASE_BITS 12 -#define SUBPHASE_BITS 10 -#define SINC_COEFF_LERP 0 -#define SIDELOBES 4 -#define ENABLE_AVX 0 -#elif defined(SINC_HIGHER_QUALITY) -#define SINC_WINDOW_KAISER -#define SINC_WINDOW_KAISER_BETA 10.5 -#define CUTOFF 0.90 -#define PHASE_BITS 10 -#define SUBPHASE_BITS 14 -#define SINC_COEFF_LERP 1 -#define SIDELOBES 32 -#define ENABLE_AVX 1 -#elif defined(SINC_HIGHEST_QUALITY) -#define SINC_WINDOW_KAISER -#define SINC_WINDOW_KAISER_BETA 14.5 -#define CUTOFF 0.962 -#define PHASE_BITS 10 -#define SUBPHASE_BITS 14 -#define SINC_COEFF_LERP 1 -#define SIDELOBES 128 -#define ENABLE_AVX 1 -#else -#define SINC_WINDOW_KAISER -#define SINC_WINDOW_KAISER_BETA 5.5 -#define CUTOFF 0.825 -#define PHASE_BITS 8 -#define SUBPHASE_BITS 16 -#define SINC_COEFF_LERP 1 -#define SIDELOBES 8 -#define ENABLE_AVX 0 -#endif -#if SINC_COEFF_LERP -#define TAPS_MULT 2 -#else -#define TAPS_MULT 1 -#endif - -#if defined(SINC_WINDOW_LANCZOS) -#define window_function(idx) (lanzcos_window_function(idx)) -#elif defined(SINC_WINDOW_KAISER) -#define window_function(idx) (kaiser_window_function(idx, SINC_WINDOW_KAISER_BETA)) -#else -#error "No SINC window function defined." -#endif +enum sinc_window +{ + SINC_WINDOW_NONE = 0, + SINC_WINDOW_KAISER, + SINC_WINDOW_LANCZOS +}; /* For the little amount of taps we're using, * SSE1 is faster than AVX for some reason. @@ -116,30 +65,29 @@ * of sinc taps, the AVX code is clearly faster than SSE1. */ -#define PHASES (1 << (PHASE_BITS + SUBPHASE_BITS)) - -#define TAPS (SIDELOBES * 2) -#define SUBPHASE_MASK ((1 << SUBPHASE_BITS) - 1) -#define SUBPHASE_MOD (1.0f / (1 << SUBPHASE_BITS)) - typedef struct rarch_sinc_resampler { - float *phase_table; - float *buffer_l; - float *buffer_r; - + unsigned enable_avx; + unsigned phase_bits; + unsigned subphase_bits; + unsigned subphase_mask; unsigned taps; - unsigned ptr; uint32_t time; + float subphase_mod; + float kaiser_beta; + enum sinc_window window_type; /* A buffer for phase_table, buffer_l and buffer_r * are created in a single calloc(). * Ensure that we get as good cache locality as we can hope for. */ float *main_buffer; + float *phase_table; + float *buffer_l; + float *buffer_r; } rarch_sinc_resampler_t; -#if defined(__ARM_NEON__) && !defined(SINC_COEFF_LERP) +#if defined(__ARM_NEON__) /* Assumes that taps >= 8, and that taps is a multiple of 8. */ void process_sinc_neon_asm(float *out, const float *left, const float *right, const float *coeff, unsigned taps); @@ -147,8 +95,9 @@ void process_sinc_neon_asm(float *out, const float *left, static void resampler_sinc_process_neon(void *re_, struct resampler_data *data) { rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_; + unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits); - uint32_t ratio = PHASES / data->ratio; + uint32_t ratio = phases / data->ratio; const float *input = data->data_in; float *output = data->data_out; size_t frames = data->input_frames; @@ -156,7 +105,7 @@ static void resampler_sinc_process_neon(void *re_, struct resampler_data *data) while (frames) { - while (frames && resamp->time >= PHASES) + while (frames && resamp->time >= phases) { /* Push in reverse to make filter more obvious. */ if (!resamp->ptr) @@ -169,17 +118,17 @@ static void resampler_sinc_process_neon(void *re_, struct resampler_data *data) resamp->buffer_r[resamp->ptr + resamp->taps] = resamp->buffer_r[resamp->ptr] = *input++; - resamp->time -= PHASES; + resamp->time -= phases; frames--; } - while (resamp->time < PHASES) + while (resamp->time < phases) { unsigned i; const float *buffer_l = resamp->buffer_l + resamp->ptr; const float *buffer_r = resamp->buffer_r + resamp->ptr; unsigned taps = resamp->taps; - unsigned phase = resamp->time >> SUBPHASE_BITS; + unsigned phase = resamp->time >> resamp->subphase_bits; const float *phase_table = resamp->phase_table + phase * taps; process_sinc_neon_asm(output, buffer_l, buffer_r, phase_table, taps); @@ -194,12 +143,13 @@ static void resampler_sinc_process_neon(void *re_, struct resampler_data *data) } #endif -#if defined(__AVX__) && ENABLE_AVX +#if defined(__AVX__) static void resampler_sinc_process_avx(void *re_, struct resampler_data *data) { rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_; + unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits); - uint32_t ratio = PHASES / data->ratio; + uint32_t ratio = phases / data->ratio; const float *input = data->data_in; float *output = data->data_out; size_t frames = data->input_frames; @@ -207,7 +157,7 @@ static void resampler_sinc_process_avx(void *re_, struct resampler_data *data) while (frames) { - while (frames && resamp->time >= PHASES) + while (frames && resamp->time >= phases) { /* Push in reverse to make filter more obvious. */ if (!resamp->ptr) @@ -220,39 +170,51 @@ static void resampler_sinc_process_avx(void *re_, struct resampler_data *data) resamp->buffer_r[resamp->ptr + resamp->taps] = resamp->buffer_r[resamp->ptr] = *input++; - resamp->time -= PHASES; + resamp->time -= phases; frames--; } - while (resamp->time < PHASES) + while (resamp->time < phases) { unsigned i; + __m256 delta, sum_l, sum_r; + float *delta_table = NULL; + float *phase_table = NULL; const float *buffer_l = resamp->buffer_l + resamp->ptr; const float *buffer_r = resamp->buffer_r + resamp->ptr; unsigned taps = resamp->taps; - unsigned phase = resamp->time >> SUBPHASE_BITS; - const float *phase_table = resamp->phase_table + phase * taps * TAPS_MULT; -#if SINC_COEFF_LERP - const float *delta_table = phase_table + taps; - __m256 delta = _mm256_set1_ps((float) - (resamp->time & SUBPHASE_MASK) * SUBPHASE_MOD); -#endif + unsigned phase = resamp->time >> resamp->subphase_bits; - __m256 sum_l = _mm256_setzero_ps(); - __m256 sum_r = _mm256_setzero_ps(); + phase_table = resamp->phase_table + phase * taps; + + if (resamp->window_type == SINC_WINDOW_KAISER) + { + phase_table = resamp->phase_table + phase * taps * 2; + delta_table = phase_table + taps; + delta = _mm256_set1_ps((float) + (resamp->time & resamp->subphase_mask) * resamp->subphase_mod); + } + + sum_l = _mm256_setzero_ps(); + sum_r = _mm256_setzero_ps(); for (i = 0; i < taps; i += 8) { + __m256 sinc; __m256 buf_l = _mm256_loadu_ps(buffer_l + i); __m256 buf_r = _mm256_loadu_ps(buffer_r + i); -#if SINC_COEFF_LERP - __m256 deltas = _mm256_load_ps(delta_table + i); - __m256 sinc = _mm256_add_ps(_mm256_load_ps(phase_table + i), - _mm256_mul_ps(deltas, delta)); -#else - __m256 sinc = _mm256_load_ps(phase_table + i); -#endif + if (resamp->window_type == SINC_WINDOW_KAISER) + { + __m256 deltas = _mm256_load_ps(delta_table + i); + sinc = _mm256_add_ps(_mm256_load_ps((const float*)phase_table + i), + _mm256_mul_ps(deltas, delta)); + } + else + { + sinc = _mm256_load_ps((const float*)phase_table + i); + } + sum_l = _mm256_add_ps(sum_l, _mm256_mul_ps(buf_l, sinc)); sum_r = _mm256_add_ps(sum_r, _mm256_mul_ps(buf_r, sinc)); } @@ -285,8 +247,9 @@ static void resampler_sinc_process_avx(void *re_, struct resampler_data *data) static void resampler_sinc_process_sse(void *re_, struct resampler_data *data) { rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_; + unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits); - uint32_t ratio = PHASES / data->ratio; + uint32_t ratio = phases / data->ratio; const float *input = data->data_in; float *output = data->data_out; size_t frames = data->input_frames; @@ -294,7 +257,7 @@ static void resampler_sinc_process_sse(void *re_, struct resampler_data *data) while (frames) { - while (frames && resamp->time >= PHASES) + while (frames && resamp->time >= phases) { /* Push in reverse to make filter more obvious. */ if (!resamp->ptr) @@ -307,40 +270,52 @@ static void resampler_sinc_process_sse(void *re_, struct resampler_data *data) resamp->buffer_r[resamp->ptr + resamp->taps] = resamp->buffer_r[resamp->ptr] = *input++; - resamp->time -= PHASES; + resamp->time -= phases; frames--; } - while (resamp->time < PHASES) + while (resamp->time < phases) { unsigned i; - __m128 sum; + __m128 sum, sum_l, sum_r, delta; + float *phase_table = NULL; + float *delta_table = NULL; const float *buffer_l = resamp->buffer_l + resamp->ptr; const float *buffer_r = resamp->buffer_r + resamp->ptr; unsigned taps = resamp->taps; - unsigned phase = resamp->time >> SUBPHASE_BITS; - const float *phase_table = resamp->phase_table + phase * taps * TAPS_MULT; -#if SINC_COEFF_LERP - const float *delta_table = phase_table + taps; - __m128 delta = _mm_set1_ps((float) - (resamp->time & SUBPHASE_MASK) * SUBPHASE_MOD); -#endif + unsigned phase = resamp->time >> resamp->subphase_bits; - __m128 sum_l = _mm_setzero_ps(); - __m128 sum_r = _mm_setzero_ps(); + if (resamp->window_type == SINC_WINDOW_KAISER) + { + phase_table = resamp->phase_table + phase * taps * 2; + delta_table = phase_table + taps; + delta = _mm_set1_ps((float) + (resamp->time & resamp->subphase_mask) * resamp->subphase_mod); + } + else + { + phase_table = resamp->phase_table + phase * taps; + } + + sum_l = _mm_setzero_ps(); + sum_r = _mm_setzero_ps(); for (i = 0; i < taps; i += 4) { + __m128 deltas, _sinc; __m128 buf_l = _mm_loadu_ps(buffer_l + i); __m128 buf_r = _mm_loadu_ps(buffer_r + i); -#if SINC_COEFF_LERP - __m128 deltas = _mm_load_ps(delta_table + i); - __m128 _sinc = _mm_add_ps(_mm_load_ps(phase_table + i), - _mm_mul_ps(deltas, delta)); -#else - __m128 _sinc = _mm_load_ps(phase_table + i); -#endif + if (resamp->window_type == SINC_WINDOW_KAISER) + { + deltas = _mm_load_ps(delta_table + i); + _sinc = _mm_add_ps(_mm_load_ps((const float*)phase_table + i), + _mm_mul_ps(deltas, delta)); + } + else + { + _sinc = _mm_load_ps((const float*)phase_table + i); + } sum_l = _mm_add_ps(sum_l, _mm_mul_ps(buf_l, _sinc)); sum_r = _mm_add_ps(sum_r, _mm_mul_ps(buf_r, _sinc)); } @@ -383,8 +358,9 @@ static void resampler_sinc_process_sse(void *re_, struct resampler_data *data) static void resampler_sinc_process_c(void *re_, struct resampler_data *data) { rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_; + unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits); - uint32_t ratio = PHASES / data->ratio; + uint32_t ratio = phases / data->ratio; const float *input = data->data_in; float *output = data->data_out; size_t frames = data->input_frames; @@ -392,46 +368,55 @@ static void resampler_sinc_process_c(void *re_, struct resampler_data *data) while (frames) { - while (frames && resamp->time >= PHASES) + while (frames && resamp->time >= phases) { /* Push in reverse to make filter more obvious. */ if (!resamp->ptr) resamp->ptr = resamp->taps; resamp->ptr--; - resamp->buffer_l[resamp->ptr + resamp->taps] = + resamp->buffer_l[resamp->ptr + resamp->taps] = resamp->buffer_l[resamp->ptr] = *input++; - resamp->buffer_r[resamp->ptr + resamp->taps] = + resamp->buffer_r[resamp->ptr + resamp->taps] = resamp->buffer_r[resamp->ptr] = *input++; - resamp->time -= PHASES; + resamp->time -= phases; frames--; } - while (resamp->time < PHASES) + while (resamp->time < phases) { unsigned i; + float delta = 0.0f; + float sum_l = 0.0f; + float sum_r = 0.0f; + float *phase_table = NULL; + float *delta_table = NULL; const float *buffer_l = resamp->buffer_l + resamp->ptr; const float *buffer_r = resamp->buffer_r + resamp->ptr; unsigned taps = resamp->taps; - unsigned phase = resamp->time >> SUBPHASE_BITS; - const float *phase_table = resamp->phase_table + phase * taps * TAPS_MULT; -#if SINC_COEFF_LERP - const float *delta_table = phase_table + taps; - float delta = (float) - (resamp->time & SUBPHASE_MASK) * SUBPHASE_MOD; -#endif - float sum_l = 0.0f; - float sum_r = 0.0f; + unsigned phase = resamp->time >> resamp->subphase_bits; + + if (resamp->window_type == SINC_WINDOW_KAISER) + { + phase_table = resamp->phase_table + phase * taps * 2; + delta_table = phase_table + taps; + delta = (float) + (resamp->time & resamp->subphase_mask) * resamp->subphase_mod; + } + else + { + phase_table = resamp->phase_table + phase * taps; + } for (i = 0; i < taps; i++) { -#if SINC_COEFF_LERP - float sinc_val = phase_table[i] + delta_table[i] * delta; -#else float sinc_val = phase_table[i]; -#endif + + if (resamp->window_type == SINC_WINDOW_KAISER) + sinc_val = sinc_val + delta_table[i] * delta; + sum_l += buffer_l[i] * sinc_val; sum_r += buffer_r[i] * sinc_val; } @@ -449,11 +434,20 @@ static void resampler_sinc_process_c(void *re_, struct resampler_data *data) data->output_frames = out_frames; } -static void sinc_init_table(rarch_sinc_resampler_t *resamp, double cutoff, +static void resampler_sinc_free(void *data) +{ + rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)data; + if (resamp) + memalign_free(resamp->main_buffer); + free(resamp); +} + +static void sinc_init_table_kaiser(rarch_sinc_resampler_t *resamp, + double cutoff, float *phase_table, int phases, int taps, bool calculate_delta) { int i, j; - double window_mod = window_function(0.0); /* Need to normalize w(0) to 1.0. */ + double window_mod = kaiser_window_function(0.0, resamp->kaiser_beta); /* Need to normalize w(0) to 1.0. */ int stride = calculate_delta ? 2 : 1; double sidelobes = taps / 2.0; @@ -468,7 +462,7 @@ static void sinc_init_table(rarch_sinc_resampler_t *resamp, double cutoff, window_phase = 2.0 * window_phase - 1.0; /* [-1, 1) */ sinc_phase = sidelobes * window_phase; val = cutoff * sinc(M_PI * sinc_phase * cutoff) * - window_function(window_phase) / window_mod; + kaiser_window_function(window_phase, resamp->kaiser_beta) / window_mod; phase_table[i * stride * taps + j] = val; } } @@ -499,36 +493,137 @@ static void sinc_init_table(rarch_sinc_resampler_t *resamp, double cutoff, sinc_phase = sidelobes * window_phase; val = cutoff * sinc(M_PI * sinc_phase * cutoff) * - window_function(window_phase) / window_mod; + kaiser_window_function(window_phase, resamp->kaiser_beta) / window_mod; delta = (val - phase_table[phase * stride * taps + j]); phase_table[(phase * stride + 1) * taps + j] = delta; } } } -static void resampler_sinc_free(void *data) +static void sinc_init_table_lanczos(rarch_sinc_resampler_t *resamp, double cutoff, + float *phase_table, int phases, int taps, bool calculate_delta) { - rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)data; - if (resamp) - memalign_free(resamp->main_buffer); - free(resamp); + int i, j; + double window_mod = lanzcos_window_function(0.0); /* Need to normalize w(0) to 1.0. */ + int stride = calculate_delta ? 2 : 1; + double sidelobes = taps / 2.0; + + for (i = 0; i < phases; i++) + { + for (j = 0; j < taps; j++) + { + double sinc_phase; + float val; + int n = j * phases + i; + double window_phase = (double)n / (phases * taps); /* [0, 1). */ + window_phase = 2.0 * window_phase - 1.0; /* [-1, 1) */ + sinc_phase = sidelobes * window_phase; + val = cutoff * sinc(M_PI * sinc_phase * cutoff) * + lanzcos_window_function(window_phase) / window_mod; + phase_table[i * stride * taps + j] = val; + } + } + + if (calculate_delta) + { + int phase; + int p; + + for (p = 0; p < phases - 1; p++) + { + for (j = 0; j < taps; j++) + { + float delta = phase_table[(p + 1) * stride * taps + j] - + phase_table[p * stride * taps + j]; + phase_table[(p * stride + 1) * taps + j] = delta; + } + } + + phase = phases - 1; + for (j = 0; j < taps; j++) + { + float val, delta; + double sinc_phase; + int n = j * phases + (phase + 1); + double window_phase = (double)n / (phases * taps); /* (0, 1]. */ + window_phase = 2.0 * window_phase - 1.0; /* (-1, 1] */ + sinc_phase = sidelobes * window_phase; + + val = cutoff * sinc(M_PI * sinc_phase * cutoff) * + lanzcos_window_function(window_phase) / window_mod; + delta = (val - phase_table[phase * stride * taps + j]); + phase_table[(phase * stride + 1) * taps + j] = delta; + } + } } static void *resampler_sinc_new(const struct resampler_config *config, - double bandwidth_mod, resampler_simd_mask_t mask) + double bandwidth_mod, enum resampler_quality quality, + resampler_simd_mask_t mask) { - double cutoff; - size_t phase_elems, elems; - rarch_sinc_resampler_t *re = (rarch_sinc_resampler_t*) + double cutoff = 0.0; + size_t phase_elems = 0; + size_t elems = 0; + unsigned sidelobes = 0; + rarch_sinc_resampler_t *re = (rarch_sinc_resampler_t*) calloc(1, sizeof(*re)); if (!re) return NULL; - (void)config; + re->window_type = SINC_WINDOW_NONE; - re->taps = TAPS; - cutoff = CUTOFF; + switch (quality) + { + case RESAMPLER_QUALITY_LOWEST: + cutoff = 0.98; + sidelobes = 2; + re->phase_bits = 12; + re->subphase_bits = 10; + re->window_type = SINC_WINDOW_LANCZOS; + re->enable_avx = 0; + break; + case RESAMPLER_QUALITY_LOWER: + cutoff = 0.98; + sidelobes = 4; + re->phase_bits = 12; + re->subphase_bits = 10; + re->window_type = SINC_WINDOW_LANCZOS; + re->enable_avx = 0; + break; + case RESAMPLER_QUALITY_HIGHER: + cutoff = 0.90; + sidelobes = 32; + re->phase_bits = 10; + re->subphase_bits = 14; + re->window_type = SINC_WINDOW_KAISER; + re->kaiser_beta = 10.5; + re->enable_avx = 1; + break; + case RESAMPLER_QUALITY_HIGHEST: + cutoff = 0.962; + sidelobes = 128; + re->phase_bits = 10; + re->subphase_bits = 14; + re->window_type = SINC_WINDOW_KAISER; + re->kaiser_beta = 14.5; + re->enable_avx = 1; + break; + case RESAMPLER_QUALITY_NORMAL: + case RESAMPLER_QUALITY_DONTCARE: + cutoff = 0.825; + sidelobes = 8; + re->phase_bits = 8; + re->subphase_bits = 16; + re->window_type = SINC_WINDOW_KAISER; + re->kaiser_beta = 5.5; + re->enable_avx = 0; + break; + } + + re->subphase_mask = (1 << re->subphase_bits) - 1; + re->subphase_mod = 1.0f / (1 << re->subphase_bits); + re->taps = sidelobes * 2; /* Downsampling, must lower cutoff, and extend number of * taps accordingly to keep same stopband attenuation. */ @@ -539,14 +634,23 @@ static void *resampler_sinc_new(const struct resampler_config *config, } /* Be SIMD-friendly. */ -#if (defined(__AVX__) && ENABLE_AVX) || (defined(__ARM_NEON__)) - re->taps = (re->taps + 7) & ~7; -#else - re->taps = (re->taps + 3) & ~3; +#if defined(__AVX__) + if (re->enable_avx) + re->taps = (re->taps + 7) & ~7; + else #endif + { +#if defined(__ARM_NEON__) + re->taps = (re->taps + 7) & ~7; +#else + re->taps = (re->taps + 3) & ~3; +#endif + } - phase_elems = ((1 << PHASE_BITS) * re->taps) * TAPS_MULT; - elems = phase_elems + 4 * re->taps; + phase_elems = ((1 << re->phase_bits) * re->taps); + if (re->window_type == SINC_WINDOW_KAISER) + phase_elems = phase_elems * 2; + elems = phase_elems + 4 * re->taps; re->main_buffer = (float*)memalign_alloc(128, sizeof(float) * elems); if (!re->main_buffer) @@ -556,21 +660,40 @@ static void *resampler_sinc_new(const struct resampler_config *config, re->buffer_l = re->main_buffer + phase_elems; re->buffer_r = re->buffer_l + 2 * re->taps; - sinc_init_table(re, cutoff, re->phase_table, - 1 << PHASE_BITS, re->taps, SINC_COEFF_LERP); + switch (re->window_type) + { + case SINC_WINDOW_LANCZOS: + sinc_init_table_lanczos(re, cutoff, re->phase_table, + 1 << re->phase_bits, re->taps, false); + break; + case SINC_WINDOW_KAISER: + sinc_init_table_kaiser(re, cutoff, re->phase_table, + 1 << re->phase_bits, re->taps, true); + break; + case SINC_WINDOW_NONE: + goto error; + } sinc_resampler.process = resampler_sinc_process_c; -#if defined(__AVX__) && ENABLE_AVX - if (mask & RESAMPLER_SIMD_AVX) + if (mask & RESAMPLER_SIMD_AVX && re->enable_avx) + { +#if defined(__AVX__) sinc_resampler.process = resampler_sinc_process_avx; -#elif defined(__SSE__) - if (mask & RESAMPLER_SIMD_SSE) +#endif + } + else if (mask & RESAMPLER_SIMD_SSE) + { +#if defined(__SSE__) sinc_resampler.process = resampler_sinc_process_sse; -#elif defined(__ARM_NEON__) && !defined(SINC_COEFF_LERP) - if (mask & RESAMPLER_SIMD_NEON) +#endif + } + else if (mask & RESAMPLER_SIMD_NEON && re->window_type != SINC_WINDOW_KAISER) + { +#if defined(__ARM_NEON__) sinc_resampler.process = resampler_sinc_process_neon; #endif + } return re; diff --git a/file/config_file.c b/file/config_file.c index fac7cca..c432d77 100644 --- a/file/config_file.c +++ b/file/config_file.c @@ -44,6 +44,7 @@ #include #include #include +#include #define MAX_INCLUDE_DEPTH 16 @@ -77,40 +78,6 @@ struct config_file static config_file_t *config_file_new_internal( const char *path, unsigned depth); -static char *getaline(FILE *file) -{ - char* newline = (char*)malloc(9); - char* newline_tmp = NULL; - size_t cur_size = 8; - size_t idx = 0; - int in = fgetc(file); - - if (!newline) - return NULL; - - while (in != EOF && in != '\n') - { - if (idx == cur_size) - { - cur_size *= 2; - newline_tmp = (char*)realloc(newline, cur_size + 1); - - if (!newline_tmp) - { - free(newline); - return NULL; - } - - newline = newline_tmp; - } - - newline[idx++] = in; - in = fgetc(file); - } - newline[idx] = '\0'; - return newline; -} - static char *strip_comment(char *str) { /* Remove everything after comment. @@ -174,16 +141,17 @@ static char *extract_value(char *line, bool is_value) if (*line == '"') { line++; + if (*line == '"') return strdup(""); tok = strtok_r(line, "\"", &save); - goto end; } else if (*line == '\0') /* Nothing */ return NULL; + else + { + /* We don't have that. Read until next space. */ + tok = strtok_r(line, " \n\t\f\r\v", &save); + } - /* We don't have that. Read until next space. */ - tok = strtok_r(line, " \n\t\f\r\v", &save); - -end: if (tok) return strdup(tok); return NULL; @@ -223,7 +191,7 @@ static void add_child_list(config_file_t *parent, config_file_t *child) /* Rebase tail. */ if (parent->entries) { - struct config_entry_list *head = + struct config_entry_list *head = (struct config_entry_list*)parent->entries; while (head->next) @@ -307,8 +275,8 @@ static bool parse_line(config_file_t *conf, comment = strip_comment(line); - /* Starting line with # and include includes config files. */ - if ((comment == line) && (conf->include_depth < MAX_INCLUDE_DEPTH)) + /* Starting line with #include includes config files. */ + if (comment == line) { comment++; if (strstr(comment, "include ") == comment) @@ -316,14 +284,15 @@ static bool parse_line(config_file_t *conf, char *line = comment + strlen("include "); char *path = extract_value(line, false); if (path) - add_sub_conf(conf, path); + { + if (conf->include_depth >= MAX_INCLUDE_DEPTH) + fprintf(stderr, "!!! #include depth exceeded for config. Might be a cycle.\n"); + else + add_sub_conf(conf, path); + } goto error; } } - else if (conf->include_depth >= MAX_INCLUDE_DEPTH) - { - fprintf(stderr, "!!! #include depth exceeded for config. Might be a cycle.\n"); - } /* Skips to first character. */ while (isspace((int)*line)) @@ -344,10 +313,11 @@ static bool parse_line(config_file_t *conf, key[idx++] = *line++; } - key[idx] = '\0'; - list->key = key; + key[idx] = '\0'; + list->key = key; + + list->value = extract_value(line, true); - list->value = extract_value(line, true); if (!list->value) { list->key = NULL; @@ -364,7 +334,7 @@ error: static config_file_t *config_file_new_internal( const char *path, unsigned depth) { - FILE *file = NULL; + RFILE *file = NULL; struct config_file *conf = (struct config_file*)malloc(sizeof(*conf)); if (!conf) return NULL; @@ -386,7 +356,9 @@ static config_file_t *config_file_new_internal( goto error; conf->include_depth = depth; - file = fopen_utf8(path, "r"); + file = filestream_open(path, + RETRO_VFS_FILE_ACCESS_READ, + RETRO_VFS_FILE_ACCESS_HINT_NONE); if (!file) { @@ -394,7 +366,7 @@ static config_file_t *config_file_new_internal( goto error; } - while (!feof(file)) + while (!filestream_eof(file)) { char *line = NULL; struct config_entry_list *list = (struct config_entry_list*)malloc(sizeof(*list)); @@ -402,7 +374,7 @@ static config_file_t *config_file_new_internal( if (!list) { config_file_free(conf); - fclose(file); + filestream_close(file); return NULL; } @@ -411,7 +383,7 @@ static config_file_t *config_file_new_internal( list->value = NULL; list->next = NULL; - line = getaline(file); + line = filestream_getline(file); if (!line) { @@ -435,7 +407,7 @@ static config_file_t *config_file_new_internal( free(list); } - fclose(file); + filestream_close(file); return conf; @@ -520,7 +492,7 @@ config_file_t *config_file_new_from_string(const char *from_string) conf->tail = NULL; conf->includes = NULL; conf->include_depth = 0; - + lines = string_split(from_string, "\n"); if (!lines) return conf; @@ -572,11 +544,8 @@ config_file_t *config_file_new(const char *path) static struct config_entry_list *config_get_entry(const config_file_t *conf, const char *key, struct config_entry_list **prev) { - struct config_entry_list *entry; - struct config_entry_list *previous = NULL; - - if (prev) - previous = *prev; + struct config_entry_list *entry = NULL; + struct config_entry_list *previous = prev ? *prev : NULL; for (entry = conf->entries; entry; entry = entry->next) { @@ -902,9 +871,32 @@ void config_set_bool(config_file_t *conf, const char *key, bool val) config_set_string(conf, key, val ? "true" : "false"); } -/* Dump the current config to an already opened file. - * Does not close the file. */ -static void config_file_dump(config_file_t *conf, FILE *file) +bool config_file_write(config_file_t *conf, const char *path) +{ + if (!string_is_empty(path)) + { + void* buf = NULL; + FILE *file = fopen_utf8(path, "wb"); + if (!file) + return false; + + /* TODO: this is only useful for a few platforms, find which and add ifdef */ + buf = calloc(1, 0x4000); + setvbuf(file, (char*)buf, _IOFBF, 0x4000); + + config_file_dump(conf, file); + + if (file != stdout) + fclose(file); + free(buf); + } + else + config_file_dump(conf, stdout); + + return true; +} + +void config_file_dump(config_file_t *conf, FILE *file) { struct config_entry_list *list = NULL; struct config_include_list *includes = conf->includes; @@ -925,33 +917,6 @@ static void config_file_dump(config_file_t *conf, FILE *file) } } -bool config_file_write(config_file_t *conf, const char *path) -{ - void* buf = NULL; - FILE *file = !string_is_empty(path) ? fopen_utf8(path, "wb") : stdout; - - if (!file) - return false; - - /* TODO: this is only useful for a few platforms, find which and add ifdef */ - if (file != stdout) - { - buf = calloc(1, 0x4000); - setvbuf(file, (char*)buf, _IOFBF, 0x4000); - } - - config_file_dump(conf, file); - - if (file != stdout) - { - fclose(file); - free(buf); - } - - return true; -} - - bool config_entry_exists(config_file_t *conf, const char *entry) { struct config_entry_list *list = conf->entries; diff --git a/include/audio/audio_resampler.h b/include/audio/audio_resampler.h index 5f02232..d950586 100644 --- a/include/audio/audio_resampler.h +++ b/include/audio/audio_resampler.h @@ -47,6 +47,16 @@ RETRO_BEGIN_DECLS #define RESAMPLER_SIMD_VFPU (1 << 13) #define RESAMPLER_SIMD_PS (1 << 14) +enum resampler_quality +{ + RESAMPLER_QUALITY_DONTCARE = 0, + RESAMPLER_QUALITY_LOWEST, + RESAMPLER_QUALITY_LOWER, + RESAMPLER_QUALITY_NORMAL, + RESAMPLER_QUALITY_HIGHER, + RESAMPLER_QUALITY_HIGHEST +}; + /* A bit-mask of all supported SIMD instruction sets. * Allows an implementation to pick different * resampler_implementation structs. @@ -108,7 +118,8 @@ struct resampler_config /* Bandwidth factor. Will be < 1.0 for downsampling, > 1.0 for upsampling. * Corresponds to expected resampling ratio. */ typedef void *(*resampler_init_t)(const struct resampler_config *config, - double bandwidth_mod, resampler_simd_mask_t mask); + double bandwidth_mod, enum resampler_quality quality, + resampler_simd_mask_t mask); /* Frees the handle. */ typedef void (*resampler_free_t)(void *data); @@ -177,7 +188,7 @@ const char *audio_resampler_driver_find_ident(int index); * Returns: true (1) if successful, otherwise false (0). **/ bool retro_resampler_realloc(void **re, const retro_resampler_t **backend, - const char *ident, double bw_ratio); + const char *ident, enum resampler_quality quality, double bw_ratio); RETRO_END_DECLS diff --git a/include/file/config_file.h b/include/file/config_file.h index 2cbe46b..9a19bed 100644 --- a/include/file/config_file.h +++ b/include/file/config_file.h @@ -154,6 +154,10 @@ void config_set_bool(config_file_t *conf, const char *entry, bool val); /* Write the current config to a file. */ bool config_file_write(config_file_t *conf, const char *path); +/* Dump the current config to an already opened file. + * Does not close the file. */ +void config_file_dump(config_file_t *conf, FILE *file); + bool config_file_exists(const char *path); RETRO_END_DECLS diff --git a/include/queues/fifo_queue.h b/include/queues/fifo_queue.h index 911da76..f1a3cae 100644 --- a/include/queues/fifo_queue.h +++ b/include/queues/fifo_queue.h @@ -25,6 +25,7 @@ #include #include +#include #include #include diff --git a/include/retro_common_api.h b/include/retro_common_api.h index d637b96..c2eeac0 100644 --- a/include/retro_common_api.h +++ b/include/retro_common_api.h @@ -82,8 +82,15 @@ typedef int ssize_t; #define PRIuPTR "Iu" #endif #else +/* C++11 says this one isn't needed, but apparently (some versions of) mingw require it anyways */ +/* https://stackoverflow.com/questions/8132399/how-to-printf-uint64-t-fails-with-spurious-trailing-in-format */ +/* https://github.com/libretro/RetroArch/issues/6009 */ +#define __STDC_FORMAT_MACROS #include #endif +#ifndef PRId64 +#error "inttypes.h is being screwy" +#endif #define STRING_REP_INT64 "%" PRId64 #define STRING_REP_UINT64 "%" PRIu64 #define STRING_REP_USIZE "%" PRIuPTR diff --git a/streams/file_stream.c b/streams/file_stream.c index 593a735..2e72402 100644 --- a/streams/file_stream.c +++ b/streams/file_stream.c @@ -58,37 +58,37 @@ struct RFILE void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info) { - const struct retro_vfs_interface* vfs_iface; + const struct retro_vfs_interface* vfs_iface; - filestream_get_path_cb = NULL; - filestream_open_cb = NULL; - filestream_close_cb = NULL; - filestream_tell_cb = NULL; - filestream_size_cb = NULL; - filestream_seek_cb = NULL; - filestream_read_cb = NULL; - filestream_write_cb = NULL; - filestream_flush_cb = NULL; - filestream_remove_cb = NULL; - filestream_rename_cb = NULL; + filestream_get_path_cb = NULL; + filestream_open_cb = NULL; + filestream_close_cb = NULL; + filestream_tell_cb = NULL; + filestream_size_cb = NULL; + filestream_seek_cb = NULL; + filestream_read_cb = NULL; + filestream_write_cb = NULL; + filestream_flush_cb = NULL; + filestream_remove_cb = NULL; + filestream_rename_cb = NULL; - vfs_iface = vfs_info->iface; + vfs_iface = vfs_info->iface; - if (vfs_info->required_interface_version < FILESTREAM_REQUIRED_VFS_VERSION - || !vfs_iface) - return; + if (vfs_info->required_interface_version < FILESTREAM_REQUIRED_VFS_VERSION + || !vfs_iface) + return; - filestream_get_path_cb = vfs_iface->get_path; - filestream_open_cb = vfs_iface->open; - filestream_close_cb = vfs_iface->close; - filestream_size_cb = vfs_iface->size; - filestream_tell_cb = vfs_iface->tell; - filestream_seek_cb = vfs_iface->seek; - filestream_read_cb = vfs_iface->read; - filestream_write_cb = vfs_iface->write; - filestream_flush_cb = vfs_iface->flush; - filestream_remove_cb = vfs_iface->remove; - filestream_rename_cb = vfs_iface->rename; + filestream_get_path_cb = vfs_iface->get_path; + filestream_open_cb = vfs_iface->open; + filestream_close_cb = vfs_iface->close; + filestream_size_cb = vfs_iface->size; + filestream_tell_cb = vfs_iface->tell; + filestream_seek_cb = vfs_iface->seek; + filestream_read_cb = vfs_iface->read; + filestream_write_cb = vfs_iface->write; + filestream_flush_cb = vfs_iface->flush; + filestream_remove_cb = vfs_iface->remove; + filestream_rename_cb = vfs_iface->rename; } /* Callback wrappers */ @@ -98,7 +98,7 @@ bool filestream_exists(const char *path) if (!path || !*path) return false; - + dummy = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); @@ -137,23 +137,23 @@ int64_t filestream_get_size(RFILE *stream) RFILE *filestream_open(const char *path, unsigned mode, unsigned hints) { struct retro_vfs_file_handle *fp = NULL; - RFILE* output = NULL; + RFILE* output = NULL; - if (filestream_open_cb != NULL) - fp = (struct retro_vfs_file_handle*) + if (filestream_open_cb != NULL) + fp = (struct retro_vfs_file_handle*) filestream_open_cb(path, mode, hints); - else - fp = (struct retro_vfs_file_handle*) + else + fp = (struct retro_vfs_file_handle*) retro_vfs_file_open_impl(path, mode, hints); - if (!fp) - return NULL; + if (!fp) + return NULL; - output = (RFILE*)malloc(sizeof(RFILE)); - output->error_flag = false; - output->eof_flag = false; - output->hfile = fp; - return output; + output = (RFILE*)malloc(sizeof(RFILE)); + output->error_flag = false; + output->eof_flag = false; + output->hfile = fp; + return output; } char *filestream_gets(RFILE *stream, char *s, size_t len) @@ -313,30 +313,30 @@ int filestream_putc(RFILE *stream, int c) char c_char = (char)c; if (!stream) return EOF; - return filestream_write(stream, &c_char, 1)==1 ? c : EOF; + return filestream_write(stream, &c_char, 1)==1 ? c : EOF; } int filestream_vprintf(RFILE *stream, const char* format, va_list args) { - static char buffer[8 * 1024]; - int num_chars = vsprintf(buffer, format, args); + static char buffer[8 * 1024]; + int num_chars = vsprintf(buffer, format, args); - if (num_chars < 0) - return -1; - else if (num_chars == 0) - return 0; + if (num_chars < 0) + return -1; + else if (num_chars == 0) + return 0; - return filestream_write(stream, buffer, num_chars); + return filestream_write(stream, buffer, num_chars); } int filestream_printf(RFILE *stream, const char* format, ...) { - va_list vl; + va_list vl; int result; - va_start(vl, format); - result = filestream_vprintf(stream, format, vl); - va_end(vl); - return result; + va_start(vl, format); + result = filestream_vprintf(stream, format, vl); + va_end(vl); + return result; } int filestream_error(RFILE *stream) @@ -396,7 +396,7 @@ int filestream_read_file(const char *path, void **buf, ssize_t *len) if (!content_buf) goto error; - if ((size_t)(content_buf_size + 1) != (content_buf_size + 1)) + if ((int64_t)(size_t)(content_buf_size + 1) != (content_buf_size + 1)) goto error; ret = filestream_read(file, content_buf, (int64_t)content_buf_size); diff --git a/vfs/vfs_implementation.c b/vfs/vfs_implementation.c index 4595aa9..21eeea9 100644 --- a/vfs/vfs_implementation.c +++ b/vfs/vfs_implementation.c @@ -129,7 +129,7 @@ int64_t retro_vfs_file_seek_internal(libretro_vfs_implementation_file *stream, i { /* fseek() returns error on under/overflow but * allows cursor > EOF for - read-only file descriptors. */ + read-only file descriptors. */ switch (whence) { case SEEK_SET: @@ -187,7 +187,7 @@ libretro_vfs_implementation_file *retro_vfs_file_open_impl(const char *path, uns const char *dumb_prefix = "vfsonly://"; if (!memcmp(path, dumb_prefix, strlen(dumb_prefix))) - path += strlen(dumb_prefix); + path += strlen(dumb_prefix); #endif if (!stream) @@ -387,15 +387,15 @@ int64_t retro_vfs_file_seek_impl(libretro_vfs_implementation_file *stream, int64 int whence = -1; switch (seek_position) { - case RETRO_VFS_SEEK_POSITION_START: - whence = SEEK_SET; - break; - case RETRO_VFS_SEEK_POSITION_CURRENT: - whence = SEEK_CUR; - break; - case RETRO_VFS_SEEK_POSITION_END: - whence = SEEK_END; - break; + case RETRO_VFS_SEEK_POSITION_START: + whence = SEEK_SET; + break; + case RETRO_VFS_SEEK_POSITION_CURRENT: + whence = SEEK_CUR; + break; + case RETRO_VFS_SEEK_POSITION_END: + whence = SEEK_END; + break; } return retro_vfs_file_seek_internal(stream, offset, whence);