mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-23 16:09:47 +00:00
Add optional fixed point resampler.
This commit is contained in:
parent
0649d36c7d
commit
7b12182a0f
5
Makefile
5
Makefile
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
88
audio/sinc.c
88
audio/sinc.c
@ -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);
|
||||
}
|
||||
|
||||
|
20
driver.c
20
driver.c
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
51
retroarch.c
51
retroarch.c
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user