Make the hermite resampling core more robust.

This commit is contained in:
Themaister 2011-02-06 18:38:04 +01:00
parent d9e2072733
commit 91d2cea259
4 changed files with 55 additions and 52 deletions

View File

@ -20,10 +20,13 @@
#include "hermite.h"
#include <stdlib.h>
#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)

View File

@ -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 <stddef.h>
#include <stdbool.h>
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

View File

@ -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

28
ssnes.c
View File

@ -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;