Add optional fixed point resampler.

This commit is contained in:
Themaister 2012-07-06 17:36:37 +02:00
parent 0649d36c7d
commit 7b12182a0f
9 changed files with 150 additions and 45 deletions

View File

@ -15,7 +15,6 @@ OBJ = retroarch.o \
patch.o \
compat/compat.o \
screenshot.o \
audio/utils.o \
audio/null.o \
input/null.o \
gfx/null.o
@ -213,6 +212,10 @@ else
OBJ += audio/hermite.o
endif
ifneq ($(HAVE_FIXED_POINT), 1)
OBJ += audio/utils.o
endif
ifneq ($(V),1)
Q := @
endif

View File

@ -20,6 +20,14 @@
#include <math.h>
#include "../boolean.h"
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#ifdef HAVE_FIXED_POINT
#error "Hermite doesn't support fixed point resampling."
#endif
#define CHANNELS 2
struct rarch_resampler

View File

@ -17,7 +17,12 @@
#ifndef __RARCH_RESAMPLER_H
#define __RARCH_RESAMPLER_H
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include <stddef.h>
#include <stdint.h>
#include <math.h>
// M_PI is left out of ISO C99 :(
@ -27,10 +32,16 @@
typedef struct rarch_resampler rarch_resampler_t;
#ifdef HAVE_FIXED_POINT
typedef int16_t sample_t;
#else
typedef float sample_t;
#endif
struct resampler_data
{
const float *data_in;
float *data_out;
const sample_t *data_in;
sample_t *data_out;
size_t input_frames;
size_t output_frames;

View File

@ -33,7 +33,7 @@
#endif
#define PHASE_BITS 8
#define SUBPHASE_BITS 16
#define SUBPHASE_BITS 15
#define PHASES (1 << PHASE_BITS)
#define PHASES_SHIFT (SUBPHASE_BITS)
@ -52,12 +52,11 @@
struct rarch_resampler
{
float phase_table[PHASES][2][TAPS];
float buffer_l[2 * TAPS];
float buffer_r[2 * TAPS];
sample_t phase_table[PHASES][2][TAPS];
sample_t buffer_l[2 * TAPS];
sample_t buffer_r[2 * TAPS];
unsigned ptr;
uint32_t time;
};
@ -83,8 +82,13 @@ static void init_sinc_table(rarch_resampler_t *resamp)
{
double p = (double)i / PHASES;
double sinc_phase = M_PI * (p + (SIDELOBES - 1 - j));
resamp->phase_table[i][PHASE_INDEX][j] = CUTOFF * sinc(CUTOFF * sinc_phase) *
lanzcos(sinc_phase / SIDELOBES);
float val = CUTOFF * sinc(CUTOFF * sinc_phase) * lanzcos(sinc_phase / SIDELOBES);
#ifdef HAVE_FIXED_POINT
resamp->phase_table[i][PHASE_INDEX][j] = (int16_t)(val * 0x7fff);
#else
resamp->phase_table[i][PHASE_INDEX][j] = val;
#endif
}
}
@ -93,8 +97,13 @@ static void init_sinc_table(rarch_resampler_t *resamp)
{
for (int j = 0; j < TAPS; j++)
{
#ifdef HAVE_FIXED_POINT
resamp->phase_table[i][DELTA_INDEX][j] =
(resamp->phase_table[i + 1][PHASE_INDEX][j] - resamp->phase_table[i][PHASE_INDEX][j]);
#else
resamp->phase_table[i][DELTA_INDEX][j] =
(resamp->phase_table[i + 1][PHASE_INDEX][j] - resamp->phase_table[i][PHASE_INDEX][j]) / SUBPHASES;
#endif
}
}
@ -105,26 +114,31 @@ static void init_sinc_table(rarch_resampler_t *resamp)
double sinc_phase = M_PI * (p + (SIDELOBES - 1 - j));
double phase = CUTOFF * sinc(CUTOFF * sinc_phase) * lanzcos(sinc_phase / SIDELOBES);
#ifdef HAVE_FIXED_POINT
int16_t result = 0x7fff * phase - resamp->phase_table[PHASES - 1][PHASE_INDEX][j];
#else
float result = (phase - resamp->phase_table[PHASES - 1][PHASE_INDEX][j]) / SUBPHASES;
#endif
resamp->phase_table[PHASES - 1][DELTA_INDEX][j] = result;
}
}
// No memalign() for us on Win32 ...
static void *aligned_alloc(size_t boundary, size_t size)
static void *aligned_alloc__(size_t boundary, size_t size)
{
void *ptr = malloc(boundary + size + sizeof(uintptr_t));
if (!ptr)
return NULL;
uintptr_t addr = ((uintptr_t)ptr + sizeof(uintptr_t) + boundary) & ~(boundary - 1);
void **place = (void**)addr;
place[-1] = ptr;
void **place = (void**)addr;
place[-1] = ptr;
return (void*)addr;
}
static void aligned_free(void *ptr)
static void aligned_free__(void *ptr)
{
void **p = (void**)ptr;
free(p[-1]);
@ -132,7 +146,7 @@ static void aligned_free(void *ptr)
rarch_resampler_t *resampler_new(void)
{
rarch_resampler_t *re = (rarch_resampler_t*)aligned_alloc(16, sizeof(*re));
rarch_resampler_t *re = (rarch_resampler_t*)aligned_alloc__(16, sizeof(*re));
if (!re)
return NULL;
@ -140,7 +154,9 @@ rarch_resampler_t *resampler_new(void)
init_sinc_table(re);
#if __SSE__
#ifdef HAVE_FIXED_POINT
RARCH_LOG("Sinc resampler [Fixed]\n");
#elif __SSE__
RARCH_LOG("Sinc resampler [SSE]\n");
#else
RARCH_LOG("Sinc resampler [C]\n");
@ -149,7 +165,41 @@ rarch_resampler_t *resampler_new(void)
return re;
}
#if __SSE__
#ifdef HAVE_FIXED_POINT
static inline int16_t saturate(int32_t val)
{
if (val > 0x7fff)
return 0x7fff;
else if (val < -0x8000)
return -0x8000;
else
return val;
}
static void process_sinc(rarch_resampler_t *resamp, int16_t *out_buffer)
{
int32_t sum_l = 0;
int32_t sum_r = 0;
const int16_t *buffer_l = resamp->buffer_l + resamp->ptr;
const int16_t *buffer_r = resamp->buffer_r + resamp->ptr;
unsigned phase = resamp->time >> PHASES_SHIFT;
unsigned delta = (resamp->time >> SUBPHASES_SHIFT) & SUBPHASES_MASK;
const int16_t *phase_table = resamp->phase_table[phase][PHASE_INDEX];
const int16_t *delta_table = resamp->phase_table[phase][DELTA_INDEX];
for (unsigned i = 0; i < TAPS; i++)
{
int16_t sinc_val = phase_table[i] + ((delta * delta_table[i] + 0x4000) >> 15);
sum_l += (buffer_l[i] * sinc_val + 0x4000) >> 15;
sum_r += (buffer_r[i] * sinc_val + 0x4000) >> 15;
}
out_buffer[0] = saturate(sum_l);
out_buffer[1] = saturate(sum_r);
}
#elif __SSE__
static void process_sinc(rarch_resampler_t *resamp, float *out_buffer)
{
__m128 sum_l = _mm_setzero_ps();
@ -231,10 +281,10 @@ void resampler_process(rarch_resampler_t *re, struct resampler_data *data)
{
uint32_t ratio = PHASES_WRAP / data->ratio;
const float *input = data->data_in;
float *output = data->data_out;
size_t frames = data->input_frames;
size_t out_frames = 0;
const sample_t *input = data->data_in;
sample_t *output = data->data_out;
size_t frames = data->input_frames;
size_t out_frames = 0;
while (frames)
{
@ -259,6 +309,6 @@ void resampler_process(rarch_resampler_t *re, struct resampler_data *data)
void resampler_free(rarch_resampler_t *re)
{
aligned_free(re);
aligned_free__(re);
}

View File

@ -209,6 +209,10 @@ static void init_dsp_plugin(void)
{
if (!(*g_settings.audio.dsp_plugin))
return;
#ifdef HAVE_FIXED_POINT
RARCH_WARN("DSP plugins are not available in fixed point mode.\n");
#else
rarch_dsp_info_t info = {0};
g_extern.audio_data.dsp_lib = dylib_load(g_settings.audio.dsp_plugin);
@ -261,6 +265,7 @@ error:
dylib_close(g_extern.audio_data.dsp_lib);
g_extern.audio_data.dsp_plugin = NULL;
g_extern.audio_data.dsp_lib = NULL;
#endif
}
static void deinit_dsp_plugin(void)
@ -302,13 +307,13 @@ void init_audio(void)
// Used for recording even if audio isn't enabled.
rarch_assert(g_extern.audio_data.conv_outsamples = (int16_t*)malloc(outsamples_max * sizeof(int16_t)));
g_extern.audio_data.block_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING;
g_extern.audio_data.block_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING;
g_extern.audio_data.nonblock_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING;
g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size;
g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size;
// Needs to be able to hold full content of a full max_bufsamples in addition to its own.
rarch_assert(g_extern.audio_data.rewind_buf = (int16_t*)malloc(max_bufsamples * sizeof(int16_t)));
g_extern.audio_data.rewind_size = max_bufsamples;
g_extern.audio_data.rewind_size = max_bufsamples;
if (!g_settings.audio.enable)
{
@ -340,11 +345,14 @@ void init_audio(void)
if (!g_extern.audio_data.source)
g_extern.audio_active = false;
#ifndef HAVE_FIXED_POINT
rarch_assert(g_extern.audio_data.data = (float*)malloc(max_bufsamples * sizeof(float)));
#endif
g_extern.audio_data.data_ptr = 0;
rarch_assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO);
rarch_assert(g_extern.audio_data.outsamples = (float*)malloc(outsamples_max * sizeof(float)));
rarch_assert(g_extern.audio_data.outsamples = (sample_t*)malloc(outsamples_max * sizeof(sample_t)));
g_extern.audio_data.orig_src_ratio =
g_extern.audio_data.src_ratio =
@ -370,7 +378,7 @@ void uninit_audio(void)
{
free(g_extern.audio_data.conv_outsamples);
g_extern.audio_data.conv_outsamples = NULL;
g_extern.audio_data.data_ptr = 0;
g_extern.audio_data.data_ptr = 0;
free(g_extern.audio_data.rewind_buf);
g_extern.audio_data.rewind_buf = NULL;
@ -387,8 +395,10 @@ void uninit_audio(void)
if (g_extern.audio_data.source)
resampler_free(g_extern.audio_data.source);
#ifndef HAVE_FIXED_POINT
free(g_extern.audio_data.data);
g_extern.audio_data.data = NULL;
#endif
free(g_extern.audio_data.outsamples);
g_extern.audio_data.outsamples = NULL;

View File

@ -339,7 +339,10 @@ struct global
{
rarch_resampler_t *source;
#ifndef HAVE_FIXED_POINT
float *data;
#endif
size_t data_ptr;
size_t chunk_size;
size_t nonblock_chunk_size;
@ -350,7 +353,7 @@ struct global
bool use_float;
bool mute;
float *outsamples;
sample_t *outsamples;
int16_t *conv_outsamples;
int16_t *rewind_buf;

View File

@ -140,6 +140,6 @@ check_pkgconf PYTHON python3
add_define_make OS "$OS"
# Creates config.mk and config.h.
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE LIBPNG DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY NETWORK_CMD SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 X264RGB SINC BSV_MOVIE RPI"
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE LIBPNG DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY NETWORK_CMD SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 X264RGB SINC FIXED_POINT BSV_MOVIE RPI"
create_config_make config.mk $VARS
create_config_header config.h $VARS

View File

@ -24,4 +24,5 @@ HAVE_SDL_IMAGE=auto # Enable SDL_image support
HAVE_LIBPNG=auto # Enable libpng support
HAVE_PYTHON=auto # Enable Python 3 support for shaders
HAVE_SINC=yes # Disable SINC resampler
HAVE_FIXED_POINT=no # Enable fixed-point resampler
HAVE_BSV_MOVIE=yes # Disable BSV movie support

View File

@ -278,8 +278,9 @@ static bool audio_flush(const int16_t *data, size_t samples)
if (g_extern.recording)
{
struct ffemu_audio_data ffemu_data = {0};
ffemu_data.data = data;
ffemu_data.frames = samples / 2;
ffemu_data.data = data;
ffemu_data.frames = samples / 2;
ffemu_push_audio(g_extern.rec, &ffemu_data);
}
#endif
@ -289,18 +290,20 @@ static bool audio_flush(const int16_t *data, size_t samples)
if (!g_extern.audio_active)
return false;
const float *output_data = NULL;
unsigned output_frames = 0;
const sample_t *output_data = NULL;
unsigned output_frames = 0;
#ifndef HAVE_FIXED_POINT
audio_convert_s16_to_float(g_extern.audio_data.data, data, samples);
#endif
#ifdef HAVE_DYLIB
#if defined(HAVE_DYLIB) && !defined(HAVE_FIXED_POINT)
rarch_dsp_output_t dsp_output = {0};
dsp_output.should_resample = RARCH_TRUE;
dsp_output.should_resample = RARCH_TRUE;
rarch_dsp_input_t dsp_input = {0};
dsp_input.samples = g_extern.audio_data.data;
dsp_input.frames = samples / 2;
dsp_input.samples = g_extern.audio_data.data;
dsp_input.frames = samples >> 1;
if (g_extern.audio_data.dsp_plugin)
g_extern.audio_data.dsp_plugin->process(g_extern.audio_data.dsp_handle, &dsp_output, &dsp_input);
@ -309,13 +312,18 @@ static bool audio_flush(const int16_t *data, size_t samples)
{
#endif
struct resampler_data src_data = {0};
#ifdef HAVE_DYLIB
src_data.data_in = dsp_output.samples ? dsp_output.samples : g_extern.audio_data.data;
src_data.input_frames = dsp_output.samples ? dsp_output.frames : (samples / 2);
#ifdef HAVE_FIXED_POINT
src_data.data_in = data;
src_data.input_frames = samples >> 1;
#elif defined(HAVE_DYLIB)
src_data.data_in = dsp_output.samples ? dsp_output.samples : g_extern.audio_data.data;
src_data.input_frames = dsp_output.samples ? dsp_output.frames : (samples >> 1);
#else
src_data.data_in = g_extern.audio_data.data;
src_data.input_frames = (samples / 2);
src_data.data_in = g_extern.audio_data.data;
src_data.input_frames = samples >> 1;
#endif
src_data.data_out = g_extern.audio_data.outsamples;
if (g_extern.audio_data.rate_control)
@ -327,13 +335,13 @@ static bool audio_flush(const int16_t *data, size_t samples)
resampler_process(g_extern.audio_data.source, &src_data);
output_data = g_extern.audio_data.outsamples;
output_data = g_extern.audio_data.outsamples;
output_frames = src_data.output_frames;
#ifdef HAVE_DYLIB
#if defined(HAVE_DYLIB) && !defined(HAVE_FIXED_POINT)
}
else
{
output_data = dsp_output.samples;
output_data = dsp_output.samples;
output_frames = dsp_output.frames;
}
#endif
@ -344,6 +352,16 @@ static bool audio_flush(const int16_t *data, size_t samples)
int16_t i[0x10000 * sizeof(float) / sizeof(int16_t)];
} static empty_buf; // Const here would require us to statically initialize it, bloating the binary.
#ifdef HAVE_FIXED_POINT
if (g_extern.audio_data.mute)
output_data = empty_buf.i;
if (audio_write_func(output_data, output_frames * sizeof(int16_t) * 2) < 0)
{
fprintf(stderr, "RetroArch [ERROR]: Audio backend failed to write. Will continue without sound.\n");
return false;
}
#else
if (g_extern.audio_data.use_float)
{
if (audio_write_func(g_extern.audio_data.mute ? empty_buf.f : output_data,
@ -368,6 +386,7 @@ static bool audio_flush(const int16_t *data, size_t samples)
return false;
}
}
#endif
return true;
}