From 91d2cea259143814e2f38ed1b611d0bde43d7833 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 6 Feb 2011 18:38:04 +0100 Subject: [PATCH] Make the hermite resampling core more robust. --- audio/hermite.c | 57 ++++++++++++++++++++++++++++++------------------- audio/hermite.h | 20 +++++++++++------ driver.c | 2 +- ssnes.c | 28 +++++------------------- 4 files changed, 55 insertions(+), 52 deletions(-) diff --git a/audio/hermite.c b/audio/hermite.c index 64f4105008..27643de140 100644 --- a/audio/hermite.c +++ b/audio/hermite.c @@ -20,10 +20,13 @@ #include "hermite.h" #include +#define MAX_CHANS 8 + struct hermite_resampler { - float chan_data[2][4]; + float chan_data[MAX_CHANS][4]; double r_frac; + unsigned chans; }; static inline float hermite_kernel(float mu1, float a, float b, float c, float d) @@ -44,50 +47,60 @@ static inline float hermite_kernel(float mu1, float a, float b, float c, float d return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c); } -hermite_resampler_t *hermite_new(void) +hermite_resampler_t *hermite_new(unsigned channels) { + if (channels > MAX_CHANS) + return NULL; + hermite_resampler_t *re = calloc(1, sizeof(*re)); if (!re) return NULL; + re->chans = channels; return re; } -size_t hermite_process(hermite_resampler_t *re, const struct hermite_data *data) +void hermite_process(hermite_resampler_t *re, struct hermite_data *data) { - double r_step = 1.0 / data->ratio; - size_t processed = 0; + double r_step = 1.0 / data->src_ratio; + size_t processed_out = 0; + size_t processed_in = 0; - size_t in_frames = data->in_frames; - const float *in_data = data->in_data; - float *out_data = data->out_data; + size_t in_frames = data->input_frames; + size_t out_frames = data->output_frames; + const float *in_data = data->data_in; + float *out_data = data->data_out; - while (in_frames > 0) + while (processed_in < in_frames && processed_out < out_frames) { - while (re->r_frac <= 1.0) + while (re->r_frac <= 1.0 && processed_out < out_frames) { re->r_frac += r_step; - for (unsigned i = 0; i < 2; i++) + for (unsigned i = 0; i < re->chans; i++) { float res = hermite_kernel(re->r_frac, re->chan_data[i][0], re->chan_data[i][1], re->chan_data[i][2], re->chan_data[i][3]); *out_data++ = res; } - processed++; + processed_out++; } - re->r_frac -= 1.0; - - for (unsigned i = 0; i < 2; i++) + if (re->r_frac >= 1.0) { - re->chan_data[i][0] = re->chan_data[i][1]; - re->chan_data[i][1] = re->chan_data[i][2]; - re->chan_data[i][2] = re->chan_data[i][3]; - re->chan_data[i][3] = *in_data++; - } + re->r_frac -= 1.0; - in_frames--; + for (unsigned i = 0; i < re->chans; i++) + { + re->chan_data[i][0] = re->chan_data[i][1]; + re->chan_data[i][1] = re->chan_data[i][2]; + re->chan_data[i][2] = re->chan_data[i][3]; + re->chan_data[i][3] = *in_data++; + } + processed_in++; + } } - return processed; + + data->input_frames_used = processed_in; + data->output_frames_gen = processed_out; } void hermite_free(hermite_resampler_t *re) diff --git a/audio/hermite.h b/audio/hermite.h index 11cc9dc4e7..c7cd65b733 100644 --- a/audio/hermite.h +++ b/audio/hermite.h @@ -16,26 +16,34 @@ */ // Hermite resampler based on bsnes' audio library. +// Attempts to be similiar to the libsamplerate process interface. #ifndef __SSNES_HERMITE_H #define __SSNES_HERMITE_H #include +#include typedef struct hermite_resampler hermite_resampler_t; -hermite_resampler_t *hermite_new(void); +hermite_resampler_t *hermite_new(unsigned channels); struct hermite_data { - const float *in_data; - float *out_data; // We make it really simple and assume that there is always enough space. :) + const float *data_in; + float *data_out; - size_t in_frames; - double ratio; + size_t input_frames; + size_t output_frames; + + size_t input_frames_used; + size_t output_frames_gen; + + bool end_of_input; // Just used to clone the SRC API. + double src_ratio; }; -size_t hermite_process(hermite_resampler_t *re, const struct hermite_data *data); +void hermite_process(hermite_resampler_t *re, struct hermite_data *data); void hermite_free(hermite_resampler_t *re); #endif diff --git a/driver.c b/driver.c index ccd59e8cd5..82fbe6bcf9 100644 --- a/driver.c +++ b/driver.c @@ -172,7 +172,7 @@ void init_audio(void) if (!g_extern.audio_data.source) g_extern.audio_active = false; #else - g_extern.audio_data.source = hermite_new(); + g_extern.audio_data.source = hermite_new(2); if (!g_extern.audio_data.source) g_extern.audio_active = false; #endif diff --git a/ssnes.c b/ssnes.c index 6f50e0e88b..f5c53af2ed 100644 --- a/ssnes.c +++ b/ssnes.c @@ -190,6 +190,9 @@ static void audio_sample(uint16_t left, uint16_t right) #ifdef HAVE_SRC SRC_DATA src_data = { +#else + struct hermite_data src_data = { +#endif .data_in = g_extern.audio_data.data, .data_out = g_extern.audio_data.outsamples, .input_frames = g_extern.audio_data.chunk_size / 2, @@ -197,28 +200,15 @@ static void audio_sample(uint16_t left, uint16_t right) .end_of_input = 0, .src_ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate, }; -#else - struct hermite_data re_data = { - .in_data = g_extern.audio_data.data, - .out_data = g_extern.audio_data.outsamples, - .in_frames = g_extern.audio_data.chunk_size / 2, - .ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate, - }; -#endif #ifdef HAVE_SRC src_process(g_extern.audio_data.source, &src_data); #else - size_t output_frames_gen = hermite_process(g_extern.audio_data.source, &re_data); + hermite_process(g_extern.audio_data.source, &src_data); #endif - if (g_extern.audio_data.use_float) { -#ifdef HAVE_SRC if (driver.audio->write(driver.audio_data, g_extern.audio_data.outsamples, src_data.output_frames_gen * sizeof(float) * 2) < 0) -#else - if (driver.audio->write(driver.audio_data, g_extern.audio_data.outsamples, output_frames_gen * sizeof(float) * 2) < 0) -#endif { fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n"); g_extern.audio_active = false; @@ -226,21 +216,13 @@ static void audio_sample(uint16_t left, uint16_t right) } else { -#ifdef HAVE_SRC - src_float_to_short_array(g_extern.audio_data.outsamples, g_extern.audio_data.conv_outsamples, src_data.output_frames_gen * 2); -#else - for (unsigned i = 0; i < output_frames_gen * 2; i++) + for (unsigned i = 0; i < src_data.output_frames_gen * 2; i++) { int32_t val = g_extern.audio_data.outsamples[i] * 0x8000; g_extern.audio_data.conv_outsamples[i] = (val > 0x7FFF) ? 0x7FFF : (val < -0x8000 ? -0x8000 : (int16_t)val); } -#endif -#ifdef HAVE_SRC if (driver.audio->write(driver.audio_data, g_extern.audio_data.conv_outsamples, src_data.output_frames_gen * sizeof(int16_t) * 2) < 0) -#else - if (driver.audio->write(driver.audio_data, g_extern.audio_data.conv_outsamples, output_frames_gen * sizeof(int16_t) * 2) < 0) -#endif { fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n"); g_extern.audio_active = false;