Added Sinc resampler to SNSF plugin, as well as fixed issue with there being garbage at the start of an SNSF when played after one has finished.
--- a/src/in_snsf/XSFConfig_SNSF.cpp
+++ b/src/in_snsf/XSFConfig_SNSF.cpp
@@ -1,7 +1,7 @@
/*
* xSF - SNSF configuration
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
- * Last modification on 2013-04-12
+ * Last modification on 2013-05-08
*
* Partially based on the vio*sf framework
*
@@ -98,6 +98,7 @@
SendMessageW(GetDlgItem(hwndDlg, idResampler), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Hermite Resampler"));
SendMessageW(GetDlgItem(hwndDlg, idResampler), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Bspline Resampler"));
SendMessageW(GetDlgItem(hwndDlg, idResampler), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Osculating Resampler"));
+ SendMessageW(GetDlgItem(hwndDlg, idResampler), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Sinc Resampler"));
SendMessageW(GetDlgItem(hwndDlg, idResampler), CB_SETCURSEL, this->resampler, 0);
// Mutes
for (int x = 0, numMutes = this->mutes.size(); x < numMutes; ++x)
--- a/src/in_snsf/XSFPlayer_SNSF.cpp
+++ b/src/in_snsf/XSFPlayer_SNSF.cpp
@@ -1,7 +1,7 @@
/*
* xSF - SNSF Player
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
- * Last modification on 2013-04-23
+ * Last modification on 2013-05-08
*
* Based on a modified in_snsf by Caitsith2
* http://snsf.caitsith2.net/
@@ -26,6 +26,7 @@
#include "snes9x/apu/hermite_resampler.h"
#include "snes9x/apu/bspline_resampler.h"
#include "snes9x/apu/osculating_resampler.h"
+#include "snes9x/apu/sinc_resampler.h"
#include "snes9x/memmap.h"
class XSFPlayer_SNSF : public XSFPlayer
@@ -71,8 +72,11 @@
BUFFER() : buf(), fil(0), cur(0), len(0) { }
bool Init()
{
+ if (!this->buf.empty())
+ this->buf.clear();
this->len = 2 * 2 * 48000 / 5;
- buf.resize(len);
+ this->buf.resize(len, 0);
+ this->fil = this->cur = 0;
return true;
}
void Fill()
@@ -230,9 +234,11 @@
S9xInitAPU();
XSFConfig_SNSF *xSFConfig_SNSF = dynamic_cast<XSFConfig_SNSF *>(xSFConfig);
- if (xSFConfig_SNSF->resampler == 3)
+ if (xSFConfig_SNSF->resampler == 4)
+ S9xInitSound<SincResampler>(10, 0);
+ else if (xSFConfig_SNSF->resampler == 3)
S9xInitSound<OsculatingResampler>(10, 0);
- if (xSFConfig_SNSF->resampler == 2)
+ else if (xSFConfig_SNSF->resampler == 2)
S9xInitSound<BsplineResampler>(10, 0);
else if (xSFConfig_SNSF->resampler == 1)
S9xInitSound<HermiteResampler>(10, 0);
--- a/src/in_snsf/snes9x/apu/apu.cpp
+++ b/src/in_snsf/snes9x/apu/apu.cpp
@@ -184,6 +184,10 @@
#include "hermite_resampler.h"
#include "bspline_resampler.h"
#include "osculating_resampler.h"
+#include "sinc_resampler.h"
+
+bool SincResampler::initializedLUTs = false;
+double SincResampler::sinc_lut[SincResampler::SINC_SAMPLES + 1];
#define APU_DEFAULT_INPUT_RATE 32000
#define APU_MINIMUM_SAMPLE_COUNT 512
@@ -479,6 +483,7 @@
template bool S9xInitSound<HermiteResampler>(int, int);
template bool S9xInitSound<BsplineResampler>(int, int);
template bool S9xInitSound<OsculatingResampler>(int, int);
+template bool S9xInitSound<SincResampler>(int, int);
void S9xSetSoundControl (uint8_t voice_switch)
{
--- a/src/in_snsf/snes9x/apu/bspline_resampler.h
+++ b/src/in_snsf/snes9x/apu/bspline_resampler.h
@@ -8,8 +8,6 @@
#undef CLAMP
#undef SHORT_CLAMP
-template<typename T1, typename T2> static inline T1 CLAMP(T1 x, T2 low, T2 high) { return x > high ? high : (x < low ? low : x); }
-template<typename T> static inline short SHORT_CLAMP(T n) { return static_cast<short>(CLAMP(n, -32768, 32767)); }
class BsplineResampler : public Resampler
{
@@ -17,6 +15,9 @@
double r_step;
double r_frac;
int r_left[6], r_right[6];
+
+ template<typename T1, typename T2> static T1 CLAMP(T1 x, T2 low, T2 high) { return x > high ? high : (x < low ? low : x); }
+ template<typename T> static short SHORT_CLAMP(T n) { return static_cast<short>(CLAMP(n, -32768, 32767)); }
double bspline(double x, double a, double b, double c, double d, double e, double f)
{
--- a/src/in_snsf/snes9x/apu/osculating_resampler.h
+++ b/src/in_snsf/snes9x/apu/osculating_resampler.h
@@ -8,8 +8,6 @@
#undef CLAMP
#undef SHORT_CLAMP
-//template<typename T1, typename T2> static inline T1 CLAMP(T1 x, T2 low, T2 high) { return x > high ? high : (x < low ? low : x); }
-//template<typename T> static inline short SHORT_CLAMP(T n) { return static_cast<short>(CLAMP(n, -32768, 32767)); }
class OsculatingResampler : public Resampler
{
@@ -17,6 +15,9 @@
double r_step;
double r_frac;
int r_left[6], r_right[6];
+
+ template<typename T1, typename T2> static T1 CLAMP(T1 x, T2 low, T2 high) { return x > high ? high : (x < low ? low : x); }
+ template<typename T> static short SHORT_CLAMP(T n) { return static_cast<short>(CLAMP(n, -32768, 32767)); }
double osculating(double x, double a, double b, double c, double d, double e, double f)
{
--- /dev/null
+++ b/src/in_snsf/snes9x/apu/sinc_resampler.h
@@ -1,1 +1,159 @@
+/* Simple resampler based on bsnes's ruby audio library */
+#ifndef __SINC_RESAMPLER_H
+#define __SINC_RESAMPLER_H
+
+#include <algorithm>
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include "resampler.h"
+
+#undef CLAMP
+#undef SHORT_CLAMP
+
+#ifndef M_PI
+const double M_PI = 3.14159265358979323846;
+#endif
+
+class SincResampler : public Resampler
+{
+protected:
+ static bool initializedLUTs;
+ static const unsigned SINC_RESOLUTION = 8192;
+ static const unsigned SINC_WIDTH = 8;
+ static const unsigned SINC_SAMPLES = SINC_RESOLUTION * SINC_WIDTH;
+ static double sinc_lut[SINC_SAMPLES + 1];
+
+ double r_step;
+ double r_frac;
+ int r_left[SINC_WIDTH * 2], r_right[SINC_WIDTH * 2];
+
+ template<typename T1, typename T2> static T1 CLAMP(T1 x, T2 low, T2 high) { return x > high ? high : (x < low ? low : x); }
+ template<typename T> static short SHORT_CLAMP(T n) { return static_cast<short>(CLAMP(n, -32768, 32767)); }
+
+ // Code from http://learningcppisfun.blogspot.com/2010/04/comparing-floating-point-numbers.html
+ template<typename T> static bool fEqual(T x, T y, int N = 1)
+ {
+ T diff = std::abs(x - y);
+ T tolerance = N * std::numeric_limits<T>::epsilon();
+ return diff <= tolerance * std::abs(x) && diff <= tolerance * std::abs(y);
+ }
+
+ static inline double sinc(double x)
+ {
+ return fEqual(x, 0.0) ? 1.0 : std::sin(x * M_PI) / (x * M_PI);
+ }
+
+ double sinc(const int *data)
+ {
+ double kernel[SINC_WIDTH * 2], kernel_sum = 0.0;
+ int i = SINC_WIDTH, shift = static_cast<int>(std::floor(this->r_frac * SINC_RESOLUTION));
+ int step = this->r_step > 1.0 ? static_cast<int>(SINC_RESOLUTION / this->r_step) : SINC_RESOLUTION;
+ int shift_adj = shift * step / SINC_RESOLUTION;
+ for (; i >= -static_cast<int>(SINC_WIDTH - 1); --i)
+ {
+ int pos = i * step;
+ kernel_sum += kernel[i + SINC_WIDTH - 1] = this->sinc_lut[std::abs(shift_adj - pos)];
+ }
+ double sum = 0.0;
+ for (i = 0; i < static_cast<int>(SINC_WIDTH * 2); ++i)
+ sum += data[i] * kernel[i];
+ return sum / kernel_sum;
+ }
+
+public:
+ SincResampler(int num_samples) : Resampler(num_samples)
+ {
+ if (!this->initializedLUTs)
+ {
+ double dx = static_cast<double>(SINC_WIDTH) / SINC_SAMPLES, x = 0.0;
+ for (unsigned i = 0; i <= SINC_SAMPLES; ++i, x += dx)
+ this->sinc_lut[i] = std::abs(x) < SINC_WIDTH ? sinc(x) * (0.5 * (1.0 + std::cos((M_PI * x) / SINC_WIDTH))) : 0.0;
+ this->initializedLUTs = true;
+ }
+ this->clear();
+ }
+
+ void time_ratio(double ratio)
+ {
+ this->r_step = ratio;
+ this->clear();
+ }
+
+ void clear()
+ {
+ ring_buffer::clear ();
+ this->r_frac = 1.0;
+ std::fill(&this->r_left[0], &this->r_left[SINC_WIDTH * 2], 0);
+ std::fill(&this->r_right[0], &this->r_right[SINC_WIDTH * 2], 0);
+ }
+
+ void read(short *data, int num_samples)
+ {
+ int i_position = this->start >> 1;
+ short *internal_buffer = reinterpret_cast<short *>(this->buffer);
+ int o_position = 0;
+ int consumed = 0;
+
+ while (o_position < num_samples && consumed < this->buffer_size)
+ {
+ int s_left = internal_buffer[i_position];
+ int s_right = internal_buffer[i_position + 1];
+ int max_samples = this->buffer_size >> 1;
+ const double margin_of_error = 1.0e-10;
+
+ if (std::abs(this->r_step - 1.0) < margin_of_error)
+ {
+ data[o_position] = static_cast<short>(s_left);
+ data[o_position + 1] = static_cast<short>(s_right);
+
+ o_position += 2;
+ i_position += 2;
+ if (i_position >= max_samples)
+ i_position -= max_samples;
+ consumed += 2;
+
+ continue;
+ }
+
+ while (this->r_frac <= 1.0 && o_position < num_samples)
+ {
+ data[o_position] = SHORT_CLAMP(sinc(this->r_left));
+ data[o_position + 1] = SHORT_CLAMP(sinc(this->r_right));
+
+ o_position += 2;
+
+ this->r_frac += this->r_step;
+ }
+
+ if (this->r_frac > 1.0)
+ {
+ std::copy(&this->r_left[1], &this->r_left[SINC_WIDTH * 2], &this->r_left[0]);
+ this->r_left[SINC_WIDTH * 2 - 1] = s_left;
+
+ std::copy(&this->r_right[1], &this->r_right[SINC_WIDTH * 2], &this->r_right[0]);
+ this->r_right[SINC_WIDTH * 2 - 1] = s_right;
+
+ this->r_frac -= 1.0;
+
+ i_position += 2;
+ if (i_position >= max_samples)
+ i_position -= max_samples;
+ consumed += 2;
+ }
+ }
+
+ this->size -= consumed << 1;
+ this->start += consumed << 1;
+ if (this->start >= this->buffer_size)
+ this->start -= this->buffer_size;
+ }
+
+ inline int avail()
+ {
+ return static_cast<int>(std::floor(((this->size >> 2) - this->r_frac) / this->r_step) * 2);
+ }
+};
+
+#endif /* __SINC_RESAMPLER_H */
+