Browse code

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.

Naram Qashat authored on 2013/05/08 03:42:24
Showing 6 changed files
... ...
@@ -1,7 +1,7 @@
1 1
 /*
2 2
  * xSF - SNSF configuration
3 3
  * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
4
- * Last modification on 2013-04-12
4
+ * Last modification on 2013-05-08
5 5
  *
6 6
  * Partially based on the vio*sf framework
7 7
  *
... ...
@@ -98,6 +98,7 @@ INT_PTR CALLBACK XSFConfig_SNSF::ConfigDialogProc(HWND hwndDlg, UINT uMsg, WPARA
98 98
 			SendMessageW(GetDlgItem(hwndDlg, idResampler), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Hermite Resampler"));
99 99
 			SendMessageW(GetDlgItem(hwndDlg, idResampler), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Bspline Resampler"));
100 100
 			SendMessageW(GetDlgItem(hwndDlg, idResampler), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Osculating Resampler"));
101
+			SendMessageW(GetDlgItem(hwndDlg, idResampler), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Sinc Resampler"));
101 102
 			SendMessageW(GetDlgItem(hwndDlg, idResampler), CB_SETCURSEL, this->resampler, 0);
102 103
 			// Mutes
103 104
 			for (int x = 0, numMutes = this->mutes.size(); x < numMutes; ++x)
... ...
@@ -1,7 +1,7 @@
1 1
 /*
2 2
  * xSF - SNSF Player
3 3
  * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
4
- * Last modification on 2013-04-23
4
+ * Last modification on 2013-05-08
5 5
  *
6 6
  * Based on a modified in_snsf by Caitsith2
7 7
  * http://snsf.caitsith2.net/
... ...
@@ -26,6 +26,7 @@
26 26
 #include "snes9x/apu/hermite_resampler.h"
27 27
 #include "snes9x/apu/bspline_resampler.h"
28 28
 #include "snes9x/apu/osculating_resampler.h"
29
+#include "snes9x/apu/sinc_resampler.h"
29 30
 #include "snes9x/memmap.h"
30 31
 
31 32
 class XSFPlayer_SNSF : public XSFPlayer
... ...
@@ -71,8 +72,11 @@ public:
71 72
 	BUFFER() : buf(), fil(0), cur(0), len(0) { }
72 73
 	bool Init()
73 74
 	{
75
+		if (!this->buf.empty())
76
+			this->buf.clear();
74 77
 		this->len = 2 * 2 * 48000 / 5;
75
-		buf.resize(len);
78
+		this->buf.resize(len, 0);
79
+		this->fil = this->cur = 0;
76 80
 		return true;
77 81
 	}
78 82
 	void Fill()
... ...
@@ -230,9 +234,11 @@ bool XSFPlayer_SNSF::Load()
230 234
 
231 235
 	S9xInitAPU();
232 236
 	XSFConfig_SNSF *xSFConfig_SNSF = dynamic_cast<XSFConfig_SNSF *>(xSFConfig);
233
-	if (xSFConfig_SNSF->resampler == 3)
237
+	if (xSFConfig_SNSF->resampler == 4)
238
+		S9xInitSound<SincResampler>(10, 0);
239
+	else if (xSFConfig_SNSF->resampler == 3)
234 240
 		S9xInitSound<OsculatingResampler>(10, 0);
235
-	if (xSFConfig_SNSF->resampler == 2)
241
+	else if (xSFConfig_SNSF->resampler == 2)
236 242
 		S9xInitSound<BsplineResampler>(10, 0);
237 243
 	else if (xSFConfig_SNSF->resampler == 1)
238 244
 		S9xInitSound<HermiteResampler>(10, 0);
... ...
@@ -184,6 +184,10 @@
184 184
 #include "hermite_resampler.h"
185 185
 #include "bspline_resampler.h"
186 186
 #include "osculating_resampler.h"
187
+#include "sinc_resampler.h"
188
+
189
+bool SincResampler::initializedLUTs = false;
190
+double SincResampler::sinc_lut[SincResampler::SINC_SAMPLES + 1];
187 191
 
188 192
 #define APU_DEFAULT_INPUT_RATE		32000
189 193
 #define APU_MINIMUM_SAMPLE_COUNT	512
... ...
@@ -479,6 +483,7 @@ template bool S9xInitSound<LinearResampler>(int, int);
479 483
 template bool S9xInitSound<HermiteResampler>(int, int);
480 484
 template bool S9xInitSound<BsplineResampler>(int, int);
481 485
 template bool S9xInitSound<OsculatingResampler>(int, int);
486
+template bool S9xInitSound<SincResampler>(int, int);
482 487
 
483 488
 void S9xSetSoundControl (uint8_t voice_switch)
484 489
 {
... ...
@@ -8,8 +8,6 @@
8 8
 
9 9
 #undef CLAMP
10 10
 #undef SHORT_CLAMP
11
-template<typename T1, typename T2> static inline T1 CLAMP(T1 x, T2 low, T2 high) { return x > high ? high : (x < low ? low : x); }
12
-template<typename T> static inline short SHORT_CLAMP(T n) { return static_cast<short>(CLAMP(n, -32768, 32767)); }
13 11
 
14 12
 class BsplineResampler : public Resampler
15 13
 {
... ...
@@ -18,6 +16,9 @@ protected:
18 16
 	double r_frac;
19 17
 	int r_left[6], r_right[6];
20 18
 
19
+	template<typename T1, typename T2> static T1 CLAMP(T1 x, T2 low, T2 high) { return x > high ? high : (x < low ? low : x); }
20
+	template<typename T> static short SHORT_CLAMP(T n) { return static_cast<short>(CLAMP(n, -32768, 32767)); }
21
+
21 22
 	double bspline(double x, double a, double b, double c, double d, double e, double f)
22 23
 	{
23 24
 		float ym2py2 = a + e, ym1py1 = b + d;
... ...
@@ -8,8 +8,6 @@
8 8
 
9 9
 #undef CLAMP
10 10
 #undef SHORT_CLAMP
11
-//template<typename T1, typename T2> static inline T1 CLAMP(T1 x, T2 low, T2 high) { return x > high ? high : (x < low ? low : x); }
12
-//template<typename T> static inline short SHORT_CLAMP(T n) { return static_cast<short>(CLAMP(n, -32768, 32767)); }
13 11
 
14 12
 class OsculatingResampler : public Resampler
15 13
 {
... ...
@@ -18,6 +16,9 @@ protected:
18 16
 	double r_frac;
19 17
 	int r_left[6], r_right[6];
20 18
 
19
+	template<typename T1, typename T2> static T1 CLAMP(T1 x, T2 low, T2 high) { return x > high ? high : (x < low ? low : x); }
20
+	template<typename T> static short SHORT_CLAMP(T n) { return static_cast<short>(CLAMP(n, -32768, 32767)); }
21
+
21 22
 	double osculating(double x, double a, double b, double c, double d, double e, double f)
22 23
 	{
23 24
 		double z = x - 0.5;
24 25
new file mode 100644
... ...
@@ -0,0 +1,158 @@
1
+/* Simple resampler based on bsnes's ruby audio library */
2
+
3
+#ifndef __SINC_RESAMPLER_H
4
+#define __SINC_RESAMPLER_H
5
+
6
+#include <algorithm>
7
+#define _USE_MATH_DEFINES
8
+#include <cmath>
9
+#include "resampler.h"
10
+
11
+#undef CLAMP
12
+#undef SHORT_CLAMP
13
+
14
+#ifndef M_PI
15
+const double M_PI = 3.14159265358979323846;
16
+#endif
17
+
18
+class SincResampler : public Resampler
19
+{
20
+protected:
21
+	static bool initializedLUTs;
22
+	static const unsigned SINC_RESOLUTION = 8192;
23
+	static const unsigned SINC_WIDTH = 8;
24
+	static const unsigned SINC_SAMPLES = SINC_RESOLUTION * SINC_WIDTH;
25
+	static double sinc_lut[SINC_SAMPLES + 1];
26
+
27
+	double r_step;
28
+	double r_frac;
29
+	int r_left[SINC_WIDTH * 2], r_right[SINC_WIDTH * 2];
30
+
31
+	template<typename T1, typename T2> static T1 CLAMP(T1 x, T2 low, T2 high) { return x > high ? high : (x < low ? low : x); }
32
+	template<typename T> static short SHORT_CLAMP(T n) { return static_cast<short>(CLAMP(n, -32768, 32767)); }
33
+
34
+	// Code from http://learningcppisfun.blogspot.com/2010/04/comparing-floating-point-numbers.html
35
+	template<typename T> static bool fEqual(T x, T y, int N = 1)
36
+	{
37
+		T diff = std::abs(x - y);
38
+		T tolerance = N * std::numeric_limits<T>::epsilon();
39
+		return diff <= tolerance * std::abs(x) && diff <= tolerance * std::abs(y);
40
+	}
41
+
42
+	static inline double sinc(double x)
43
+	{
44
+		return fEqual(x, 0.0) ? 1.0 : std::sin(x * M_PI) / (x * M_PI);
45
+	}
46
+
47
+	double sinc(const int *data)
48
+	{
49
+		double kernel[SINC_WIDTH * 2], kernel_sum = 0.0;
50
+		int i = SINC_WIDTH, shift = static_cast<int>(std::floor(this->r_frac * SINC_RESOLUTION));
51
+		int step = this->r_step > 1.0 ? static_cast<int>(SINC_RESOLUTION / this->r_step) : SINC_RESOLUTION;
52
+		int shift_adj = shift * step / SINC_RESOLUTION;
53
+		for (; i >= -static_cast<int>(SINC_WIDTH - 1); --i)
54
+		{
55
+			int pos = i * step;
56
+			kernel_sum += kernel[i + SINC_WIDTH - 1] = this->sinc_lut[std::abs(shift_adj - pos)];
57
+		}
58
+		double sum = 0.0;
59
+		for (i = 0; i < static_cast<int>(SINC_WIDTH * 2); ++i)
60
+			sum += data[i] * kernel[i];
61
+		return sum / kernel_sum;
62
+	}
63
+
64
+public:
65
+	SincResampler(int num_samples) : Resampler(num_samples)
66
+	{
67
+		if (!this->initializedLUTs)
68
+		{
69
+			double dx = static_cast<double>(SINC_WIDTH) / SINC_SAMPLES, x = 0.0;
70
+			for (unsigned i = 0; i <= SINC_SAMPLES; ++i, x += dx)
71
+				this->sinc_lut[i] = std::abs(x) < SINC_WIDTH ? sinc(x) * (0.5 * (1.0 + std::cos((M_PI * x) / SINC_WIDTH))) : 0.0;
72
+			this->initializedLUTs = true;
73
+		}
74
+		this->clear();
75
+	}
76
+
77
+	void time_ratio(double ratio)
78
+	{
79
+		this->r_step = ratio;
80
+		this->clear();
81
+	}
82
+
83
+	void clear()
84
+	{
85
+		ring_buffer::clear ();
86
+		this->r_frac = 1.0;
87
+		std::fill(&this->r_left[0], &this->r_left[SINC_WIDTH * 2], 0);
88
+		std::fill(&this->r_right[0], &this->r_right[SINC_WIDTH * 2], 0);
89
+	}
90
+
91
+	void read(short *data, int num_samples)
92
+	{
93
+		int i_position = this->start >> 1;
94
+		short *internal_buffer = reinterpret_cast<short *>(this->buffer);
95
+		int o_position = 0;
96
+		int consumed = 0;
97
+
98
+		while (o_position < num_samples && consumed < this->buffer_size)
99
+		{
100
+			int s_left = internal_buffer[i_position];
101
+			int s_right = internal_buffer[i_position + 1];
102
+			int max_samples = this->buffer_size >> 1;
103
+			const double margin_of_error = 1.0e-10;
104
+
105
+			if (std::abs(this->r_step - 1.0) < margin_of_error)
106
+			{
107
+				data[o_position] = static_cast<short>(s_left);
108
+				data[o_position + 1] = static_cast<short>(s_right);
109
+
110
+				o_position += 2;
111
+				i_position += 2;
112
+				if (i_position >= max_samples)
113
+					i_position -= max_samples;
114
+				consumed += 2;
115
+
116
+				continue;
117
+			}
118
+
119
+			while (this->r_frac <= 1.0 && o_position < num_samples)
120
+			{
121
+				data[o_position] = SHORT_CLAMP(sinc(this->r_left));
122
+				data[o_position + 1] = SHORT_CLAMP(sinc(this->r_right));
123
+
124
+				o_position += 2;
125
+
126
+				this->r_frac += this->r_step;
127
+			}
128
+
129
+			if (this->r_frac > 1.0)
130
+			{
131
+				std::copy(&this->r_left[1], &this->r_left[SINC_WIDTH * 2], &this->r_left[0]);
132
+				this->r_left[SINC_WIDTH * 2 - 1] = s_left;
133
+
134
+				std::copy(&this->r_right[1], &this->r_right[SINC_WIDTH * 2], &this->r_right[0]);
135
+				this->r_right[SINC_WIDTH * 2 - 1] = s_right;
136
+
137
+				this->r_frac -= 1.0;
138
+
139
+				i_position += 2;
140
+				if (i_position >= max_samples)
141
+					i_position -= max_samples;
142
+				consumed += 2;
143
+			}
144
+		}
145
+
146
+		this->size -= consumed << 1;
147
+		this->start += consumed << 1;
148
+		if (this->start >= this->buffer_size)
149
+			this->start -= this->buffer_size;
150
+	}
151
+
152
+	inline int avail()
153
+	{
154
+		return static_cast<int>(std::floor(((this->size >> 2) - this->r_frac) / this->r_step) * 2);
155
+	}
156
+};
157
+
158
+#endif /* __SINC_RESAMPLER_H */