mirror of
https://github.com/libretro/libretro-common.git
synced 2025-04-02 10:31:51 -04:00
Update
This commit is contained in:
parent
36defa5e6c
commit
b4e2f2580f
13 changed files with 451 additions and 332 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#if defined(__AVX__) && ENABLE_AVX
|
||||
#if defined(__AVX__)
|
||||
#include <immintrin.h>
|
||||
#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;
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include <file/file_path.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <retro_common_api.h>
|
||||
#include <retro_inline.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 <inttypes.h>
|
||||
#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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue