Make the hermite resampling core more robust.
This commit is contained in:
parent
d9e2072733
commit
91d2cea259
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
2
driver.c
2
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
|
||||
|
|
28
ssnes.c
28
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;
|
||||
|
|
Loading…
Reference in New Issue