From 087ac22029efd7e09ab560fc187408c9ccd774b4 Mon Sep 17 00:00:00 2001 From: bladeoner Date: Sun, 3 Mar 2019 17:28:18 +0100 Subject: [PATCH] Backport and adjusted the Refactored APU Resampler (#821) --- source/snes9x/apu/apu.cpp | 10 +- source/snes9x/apu/hermite_resampler.h | 143 ---------------- source/snes9x/apu/resampler.h | 237 +++++++++++++++++++++----- source/snes9x/apu/ring_buffer.h | 115 ------------- source/snes9x/snes9x.h | 2 +- 5 files changed, 202 insertions(+), 305 deletions(-) delete mode 100644 source/snes9x/apu/hermite_resampler.h delete mode 100644 source/snes9x/apu/ring_buffer.h diff --git a/source/snes9x/apu/apu.cpp b/source/snes9x/apu/apu.cpp index bd4e045..383b37a 100644 --- a/source/snes9x/apu/apu.cpp +++ b/source/snes9x/apu/apu.cpp @@ -10,7 +10,7 @@ #include "../msu1.h" #include "../snapshot.h" #include "../display.h" -#include "hermite_resampler.h" +#include "resampler.h" #define APU_DEFAULT_INPUT_RATE 32040 #define APU_MINIMUM_SAMPLE_COUNT 512 @@ -62,7 +62,7 @@ namespace spc static uint32 ratio_denominator = APU_DENOMINATOR_NTSC; static double dynamic_rate_multiplier = 1.0; -} +} // namespace spc namespace msu { @@ -71,7 +71,7 @@ namespace msu static Resampler *resampler = NULL; static int resample_buffer_size = -1; static uint8 *resample_buffer = NULL; -} +} // namespace msu static void EightBitize (uint8 *, int); static void DeStereo (uint8 *, int); @@ -359,7 +359,7 @@ bool8 S9xInitSound (int buffer_ms, int lag_ms) arguments. Use 2x in the resampler for buffer leveling with SoundSync */ if (!spc::resampler) { - spc::resampler = new HermiteResampler(spc::buffer_size >> (Settings.SoundSync ? 0 : 1)); + spc::resampler = new Resampler(spc::buffer_size >> (Settings.SoundSync ? 0 : 1)); if (!spc::resampler) { delete[] spc::landing_buffer; @@ -372,7 +372,7 @@ bool8 S9xInitSound (int buffer_ms, int lag_ms) if (!msu::resampler) { - msu::resampler = new HermiteResampler(msu::buffer_size >> (Settings.SoundSync ? 0 : 1)); + msu::resampler = new Resampler(msu::buffer_size >> (Settings.SoundSync ? 0 : 1)); if (!msu::resampler) { delete[] msu::landing_buffer; diff --git a/source/snes9x/apu/hermite_resampler.h b/source/snes9x/apu/hermite_resampler.h deleted file mode 100644 index a0a8ec8..0000000 --- a/source/snes9x/apu/hermite_resampler.h +++ /dev/null @@ -1,143 +0,0 @@ -/*****************************************************************************\ - Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. - This file is licensed under the Snes9x License. - For further information, consult the LICENSE file in the root directory. -\*****************************************************************************/ - -#ifndef __HERMITE_RESAMPLER_H -#define __HERMITE_RESAMPLER_H - -#include "resampler.h" - -#undef CLAMP -#undef SHORT_CLAMP -#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) -#define SHORT_CLAMP(n) ((short) CLAMP((n), -32768, 32767)) - -class HermiteResampler : public Resampler -{ - protected: - - float r_step; - float r_frac; - int r_left[4], r_right[4]; - - static inline float - hermite (float mu1, float a, float b, float c, float d) - { - float mu2, mu3, m0, m1, a0, a1, a2, a3; - - mu2 = mu1 * mu1; - mu3 = mu2 * mu1; - - m0 = (c - a) * 0.5; - m1 = (d - b) * 0.5; - - a0 = +2 * mu3 - 3 * mu2 + 1; - a1 = mu3 - 2 * mu2 + mu1; - a2 = mu3 - mu2; - a3 = -2 * mu3 + 3 * mu2; - - return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c); - } - - public: - HermiteResampler (int num_samples) : Resampler (num_samples) - { - clear (); - } - - ~HermiteResampler () - { - } - - void - time_ratio (double ratio) - { - r_step = ratio; - } - - void - clear (void) - { - ring_buffer::clear (); - r_frac = 1.0; - r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0; - r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0; - } - - void - read (short *data, int num_samples) - { - //If we are outputting the exact same ratio as the input, pull directly from the input buffer - if (r_step == 1.0) - { - ring_buffer::pull((unsigned char*)data, num_samples * sizeof(short)); - return; - } - - int i_position = start >> 1; - int max_samples = buffer_size >> 1; - short *internal_buffer = (short *) buffer; - int o_position = 0; - int consumed = 0; - - while (o_position < num_samples && consumed < buffer_size) - { - int s_left = internal_buffer[i_position]; - int s_right = internal_buffer[i_position + 1]; - float hermite_val[2]; - - while (r_frac <= 1.0 && o_position < num_samples) - { - hermite_val[0] = hermite (r_frac, r_left [0], r_left [1], r_left [2], r_left [3]); - hermite_val[1] = hermite (r_frac, r_right[0], r_right[1], r_right[2], r_right[3]); - data[o_position] = SHORT_CLAMP (hermite_val[0]); - data[o_position + 1] = SHORT_CLAMP (hermite_val[1]); - - o_position += 2; - - r_frac += r_step; - } - - if (r_frac > 1.0) - { - r_left [0] = r_left [1]; - r_left [1] = r_left [2]; - r_left [2] = r_left [3]; - r_left [3] = s_left; - - r_right[0] = r_right[1]; - r_right[1] = r_right[2]; - r_right[2] = r_right[3]; - r_right[3] = s_right; - - r_frac -= 1.0; - - i_position += 2; - if (i_position >= max_samples) - i_position -= max_samples; - consumed += 2; - } - } - - size -= consumed << 1; - start += consumed << 1; - if (start >= buffer_size) - start -= buffer_size; - } - - inline int - avail (void) - { - //If we are outputting the exact same ratio as the input, find out directly from the input buffer - if (r_step == 1.0) - { - return (ring_buffer::space_filled() + sizeof(short) - 1) / sizeof(short); - } - - return (int) floor (((size >> 2) - r_frac) / r_step) * 2; - } -}; - -#endif /* __HERMITE_RESAMPLER_H */ diff --git a/source/snes9x/apu/resampler.h b/source/snes9x/apu/resampler.h index d1125ba..ff20c92 100644 --- a/source/snes9x/apu/resampler.h +++ b/source/snes9x/apu/resampler.h @@ -4,61 +4,216 @@ For further information, consult the LICENSE file in the root directory. \*****************************************************************************/ -#ifndef __RESAMPLER_H -#define __RESAMPLER_H +#ifndef __NEW_RESAMPLER_H +#define __NEW_RESAMPLER_H -#include "ring_buffer.h" +#include +#include +#if __cplusplus >= 201103L +#include +#else +#include +#endif +#include -class Resampler : public ring_buffer +class Resampler { - public: - virtual void clear (void) = 0; - virtual void time_ratio (double) = 0; - virtual void read (short *, int) = 0; - virtual int avail (void) = 0; + public: + int size; + int buffer_size; + int start; + int16_t *buffer; - Resampler (int num_samples) : ring_buffer (num_samples << 1) + float r_step; + float r_frac; + int r_left[4], r_right[4]; + + static inline int16_t short_clamp(int n) + { + return (int16_t)(((int16_t)n != n) ? (n >> 31) ^ 0x7fff : n); + } + + static inline int min(int a, int b) + { + return ((a) < (b) ? (a) : (b)); + } + + static inline float hermite(float mu1, float a, float b, float c, float d) + { + float mu2, mu3, m0, m1, a0, a1, a2, a3; + + mu2 = mu1 * mu1; + mu3 = mu2 * mu1; + + m0 = (c - a) * 0.5; + m1 = (d - b) * 0.5; + + a0 = +2 * mu3 - 3 * mu2 + 1; + a1 = mu3 - 2 * mu2 + mu1; + a2 = mu3 - mu2; + a3 = -2 * mu3 + 3 * mu2; + + return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c); + } + + Resampler(int num_samples) + { + this->buffer_size = num_samples; + buffer = new int16_t[this->buffer_size]; + r_step = 1.0; + clear(); + } + + ~Resampler() + { + delete[] buffer; + buffer = NULL; + } + + inline void time_ratio(double ratio) + { + r_step = ratio; + } + + inline void clear(void) + { + start = 0; + size = 0; + memset(buffer, 0, buffer_size * 2); + + r_frac = 0.0; + r_left[0] = r_left[1] = r_left[2] = r_left[3] = 0; + r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0; + } + + inline bool pull(int16_t *dst, int num_samples) + { + if (space_filled() < num_samples) + return false; + + memcpy(dst, buffer + start, min(num_samples, buffer_size - start) * 2); + + if (num_samples > (buffer_size - start)) + memcpy(dst + (buffer_size - start), buffer, (num_samples - (buffer_size - start)) * 2); + + start = (start + num_samples) % buffer_size; + size -= num_samples; + + return true; + } + + inline void push_sample(int16_t l, int16_t r) + { + if (space_empty() >= 2) { + int end = start + size; + if (end >= buffer_size) + end -= buffer_size; + buffer[end] = l; + buffer[end + 1] = r; + size += 2; + } + } + + inline bool push(int16_t *src, int num_samples) + { + if (space_empty() < num_samples) + return false; + + int end = start + size; + if (end > buffer_size) + end -= buffer_size; + int first_write_size = min(num_samples, buffer_size - end); + + memcpy(buffer + end, src, first_write_size * 2); + + if (num_samples > first_write_size) + memcpy(buffer, src + first_write_size, (num_samples - first_write_size) * 2); + + size += num_samples; + + return true; + } + + void read(int16_t *data, int num_samples) + { + //If we are outputting the exact same ratio as the input, pull directly from the input buffer + if (r_step == 1.0) + { + pull(data, num_samples); + return; } - virtual ~Resampler () + assert((num_samples & 1) == 0); // resampler always processes both stereo samples + int o_position = 0; + + while (o_position < num_samples && size > 0) { + int s_left = buffer[start]; + int s_right = buffer[start + 1]; + int hermite_val[2]; + + while (r_frac <= 1.0 && o_position < num_samples) + { + hermite_val[0] = (int)hermite(r_frac, r_left[0], r_left[1], r_left[2], r_left[3]); + hermite_val[1] = (int)hermite(r_frac, r_right[0], r_right[1], r_right[2], r_right[3]); + data[o_position] = short_clamp(hermite_val[0]); + data[o_position + 1] = short_clamp(hermite_val[1]); + + o_position += 2; + + r_frac += r_step; + } + + if (r_frac > 1.0) + { + r_left[0] = r_left[1]; + r_left[1] = r_left[2]; + r_left[2] = r_left[3]; + r_left[3] = s_left; + + r_right[0] = r_right[1]; + r_right[1] = r_right[2]; + r_right[2] = r_right[3]; + r_right[3] = s_right; + + r_frac -= 1.0; + + start += 2; + if (start >= buffer_size) + start -= buffer_size; + size -= 2; + } } + } - inline bool - push (short *src, int num_samples) - { - if (max_write () < num_samples) - return false; + inline int space_empty(void) const + { + return buffer_size - size; + } - !num_samples || ring_buffer::push ((unsigned char *) src, num_samples << 1); + inline int space_filled(void) const + { + return size; + } - return true; - } - - inline int - space_empty (void) const - { - return buffer_size - size; - } - - inline int - space_filled (void) const - { + inline int avail(void) + { + //If we are outputting the exact same ratio as the input, find out directly from the input buffer + if (r_step == 1.0) return size; - } - inline int - max_write (void) const - { - return space_empty () >> 1; - } + return (int)trunc(((size >> 1) - r_frac) / r_step) * 2; + } - inline void - resize (int num_samples) - { - ring_buffer::resize (num_samples << 1); - } + void resize(int num_samples) + { + if (buffer) + delete[] buffer; + buffer_size = num_samples; + buffer = new int16_t[buffer_size]; + clear(); + } }; -#endif /* __RESAMPLER_H */ +#endif /* __NEW_RESAMPLER_H */ \ No newline at end of file diff --git a/source/snes9x/apu/ring_buffer.h b/source/snes9x/apu/ring_buffer.h deleted file mode 100644 index fb9cf58..0000000 --- a/source/snes9x/apu/ring_buffer.h +++ /dev/null @@ -1,115 +0,0 @@ -/*****************************************************************************\ - Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. - This file is licensed under the Snes9x License. - For further information, consult the LICENSE file in the root directory. -\*****************************************************************************/ - -#ifndef __RING_BUFFER_H -#define __RING_BUFFER_H - -#include - -#undef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -class ring_buffer -{ -protected: - int size; - int buffer_size; - int start; - unsigned char *buffer; - -public: - ring_buffer (int buffer_size) - { - this->buffer_size = buffer_size; - buffer = new unsigned char[this->buffer_size]; - memset (buffer, 0, this->buffer_size); - - size = 0; - start = 0; - } - - ~ring_buffer (void) - { - delete[] buffer; - } - - bool - push (unsigned char *src, int bytes) - { - if (space_empty () < bytes) - return false; - - int end = (start + size) % buffer_size; - int first_write_size = MIN (bytes, buffer_size - end); - - memcpy (buffer + end, src, first_write_size); - - if (bytes > first_write_size) - memcpy (buffer, src + first_write_size, bytes - first_write_size); - - size += bytes; - - return true; - } - - bool - pull (unsigned char *dst, int bytes) - { - if (space_filled () < bytes) - return false; - - memcpy (dst, buffer + start, MIN (bytes, buffer_size - start)); - - if (bytes > (buffer_size - start)) - memcpy (dst + (buffer_size - start), buffer, bytes - (buffer_size - start)); - - start = (start + bytes) % buffer_size; - size -= bytes; - - return true; - } - - inline int - space_empty (void) const - { - return buffer_size - size; - } - - inline int - space_filled (void) const - { - return size; - } - - void - clear (void) - { - start = 0; - size = 0; - memset (buffer, 0, buffer_size); - } - - void - resize (int size) - { - delete[] buffer; - buffer_size = size; - buffer = new unsigned char[buffer_size]; - memset (buffer, 0, this->buffer_size); - - this->size = 0; - start = 0; - } - - inline void - cache_silence (void) - { - clear (); - size = buffer_size; - } -}; - -#endif diff --git a/source/snes9x/snes9x.h b/source/snes9x/snes9x.h index 3fb647a..18a961f 100644 --- a/source/snes9x/snes9x.h +++ b/source/snes9x/snes9x.h @@ -9,7 +9,7 @@ #define _SNES9X_H_ #ifndef VERSION -#define VERSION "1.59" +#define VERSION "1.59.2" #endif #include "port.h"