From b2dd12d186a40be5c899d49fc7f2642b612aceb2 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sat, 15 Oct 2011 14:33:41 +0200 Subject: [PATCH] Rewind audio! :D --- audio/hermite.c | 2 +- audio/hermite.h | 3 +-- driver.c | 9 +++++++- general.h | 4 ++++ ssnes.c | 59 ++++++++++++++++++++++++++++++++++++++++--------- 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/audio/hermite.c b/audio/hermite.c index 27643de140..452aced121 100644 --- a/audio/hermite.c +++ b/audio/hermite.c @@ -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; diff --git a/audio/hermite.h b/audio/hermite.h index c7cd65b733..e6fdbab49d 100644 --- a/audio/hermite.h +++ b/audio/hermite.h @@ -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); diff --git a/driver.c b/driver.c index 65e2b32a58..7dec8de264 100644 --- a/driver.c +++ b/driver.c @@ -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(); } diff --git a/general.h b/general.h index 5073607f3c..9c7ae643d7 100644 --- a/general.h +++ b/general.h @@ -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; diff --git a/ssnes.c b/ssnes.c index d69659d59f..5280504b6f 100644 --- a/ssnes.c +++ b/ssnes.c @@ -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)