Rewind audio! :D

This commit is contained in:
Themaister 2011-10-15 14:33:41 +02:00
parent 9c79bf0ba2
commit b2dd12d186
5 changed files with 63 additions and 14 deletions

View File

@ -61,7 +61,7 @@ hermite_resampler_t *hermite_new(unsigned channels)
void hermite_process(hermite_resampler_t *re, struct hermite_data *data)
{
double r_step = 1.0 / data->src_ratio;
double r_step = 1.0 / data->ratio;
size_t processed_out = 0;
size_t processed_in = 0;

View File

@ -39,8 +39,7 @@ struct hermite_data
size_t input_frames_used;
size_t output_frames_gen;
bool end_of_input; // Just used to clone the SRC API.
double src_ratio;
double ratio;
};
void hermite_process(hermite_resampler_t *re, struct hermite_data *data);

View File

@ -258,12 +258,18 @@ void init_audio(void)
size_t max_bufsamples = g_extern.audio_data.block_chunk_size > g_extern.audio_data.nonblock_chunk_size ?
g_extern.audio_data.block_chunk_size : g_extern.audio_data.nonblock_chunk_size;
assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO);
max_bufsamples *= 2; // Accomodate rewind since at some point we might have two full buffers.
assert((g_extern.audio_data.data = malloc(max_bufsamples * sizeof(float))));
g_extern.audio_data.data_ptr = 0;
assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO);
assert((g_extern.audio_data.outsamples = malloc(max_bufsamples * sizeof(float) * AUDIO_MAX_RATIO)));
assert((g_extern.audio_data.conv_outsamples = malloc(max_bufsamples * sizeof(int16_t) * AUDIO_MAX_RATIO)));
// Needs to be able to hold full content of a full max_bufsamples in addition to its own.
assert((g_extern.audio_data.rewind_buf = malloc(max_bufsamples * sizeof(int16_t))));
g_extern.audio_data.rewind_size = max_bufsamples;
init_dsp_plugin();
}
@ -284,6 +290,7 @@ void uninit_audio(void)
free(g_extern.audio_data.data); g_extern.audio_data.data = NULL;
free(g_extern.audio_data.outsamples); g_extern.audio_data.outsamples = NULL;
free(g_extern.audio_data.conv_outsamples); g_extern.audio_data.conv_outsamples = NULL;
free(g_extern.audio_data.rewind_buf); g_extern.audio_data.rewind_buf = NULL;
deinit_dsp_plugin();
}

View File

@ -218,6 +218,10 @@ struct global
float *outsamples;
int16_t *conv_outsamples;
int16_t *rewind_buf;
size_t rewind_ptr;
size_t rewind_size;
dylib_t dsp_lib;
const ssnes_dsp_plugin_t *dsp_plugin;
void *dsp_handle;

59
ssnes.c
View File

@ -187,11 +187,8 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
static bool audio_flush(const int16_t *data, unsigned samples)
{
for (unsigned i = 0; i < samples; i += 2)
{
g_extern.audio_data.data[i + 0] = (float)data[i + 0] / 0x8000;
g_extern.audio_data.data[i + 1] = (float)data[i + 1] / 0x8000;
}
for (unsigned i = 0; i < samples; i++)
g_extern.audio_data.data[i] = (float)data[i] / 0x8000;
const float *output_data = NULL;
unsigned output_frames = 0;
@ -224,10 +221,12 @@ static bool audio_flush(const int16_t *data, unsigned samples)
.data_out = g_extern.audio_data.outsamples,
.input_frames = dsp_output.samples ? dsp_output.frames : (samples / 2),
.output_frames = g_extern.audio_data.chunk_size * 8,
.end_of_input = 0,
.src_ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate,
.ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate,
};
if (g_extern.frame_is_reverse)
src_data.output_frames = (samples) * src_data.ratio;
if (dsp_output.should_resample)
{
hermite_process(g_extern.audio_data.source, &src_data);
@ -267,6 +266,15 @@ static bool audio_flush(const int16_t *data, unsigned samples)
return true;
}
static void audio_sample_rewind(uint16_t left, uint16_t right)
{
if (!g_extern.audio_active)
return;
g_extern.audio_data.rewind_buf[--g_extern.audio_data.rewind_ptr] = right;
g_extern.audio_data.rewind_buf[--g_extern.audio_data.rewind_ptr] = left;
}
static void audio_sample(uint16_t left, uint16_t right)
{
if (!g_extern.audio_active)
@ -278,7 +286,9 @@ static void audio_sample(uint16_t left, uint16_t right)
if (g_extern.audio_data.data_ptr < g_extern.audio_data.chunk_size)
return;
g_extern.audio_active = audio_flush(g_extern.audio_data.conv_outsamples, g_extern.audio_data.data_ptr);
g_extern.audio_active = audio_flush(g_extern.audio_data.conv_outsamples,
g_extern.audio_data.data_ptr);
g_extern.audio_data.data_ptr = 0;
}
@ -1331,9 +1341,36 @@ static void check_input_rate(void)
}
}
static inline void flush_rewind_audio(void)
{
if (g_extern.frame_is_reverse) // We just rewound. Flush rewind audio buffer.
{
g_extern.audio_active = audio_flush(g_extern.audio_data.rewind_buf + g_extern.audio_data.rewind_ptr,
g_extern.audio_data.rewind_size - g_extern.audio_data.rewind_ptr);
}
}
static inline void setup_rewind_audio(void)
{
// Push audio ready to be played.
g_extern.audio_data.rewind_ptr = g_extern.audio_data.rewind_size;
for (unsigned i = 0; i < g_extern.audio_data.data_ptr; i += 2)
{
g_extern.audio_data.rewind_buf[--g_extern.audio_data.rewind_ptr] =
g_extern.audio_data.conv_outsamples[i + 1];
g_extern.audio_data.rewind_buf[--g_extern.audio_data.rewind_ptr] =
g_extern.audio_data.conv_outsamples[i + 0];
}
g_extern.audio_data.data_ptr = 0;
}
static void check_rewind(void)
{
flush_rewind_audio();
g_extern.frame_is_reverse = false;
static bool first = true;
if (first)
{
@ -1351,13 +1388,13 @@ static void check_rewind(void)
if (state_manager_pop(g_extern.state_manager, &buf))
{
g_extern.frame_is_reverse = true;
setup_rewind_audio();
msg_queue_push(g_extern.msg_queue, "Rewinding!", 0, 30);
psnes_unserialize(buf, psnes_serialize_size());
if (g_extern.bsv_movie)
{
bsv_movie_frame_rewind(g_extern.bsv_movie);
}
}
else
msg_queue_push(g_extern.msg_queue, "Reached end of rewind buffer!", 0, 30);
@ -1372,6 +1409,8 @@ static void check_rewind(void)
state_manager_push(g_extern.state_manager, g_extern.state_buf);
}
}
psnes_set_audio_sample(g_extern.frame_is_reverse ? audio_sample_rewind : audio_sample);
}
static void check_movie_record(void)