| ... | ... |
@@ -71,11 +71,9 @@ int32_t SharpIInterpolator::interpolate(const std::vector<int32_t>& data, double |
| 71 | 71 |
double linear = lerp(left, right, 1.0 + subsample); |
| 72 | 72 |
// Projection approaching from the left |
| 73 | 73 |
double mLeft = sample - left; |
| 74 |
- double pLeft = sample + mLeft * subsample; |
|
| 75 | 74 |
// Projection approaching from the right |
| 76 | 75 |
double negSubsample = 1 - subsample; |
| 77 | 76 |
double mRight = right - sample; |
| 78 |
- double pRight = right - mRight * negSubsample; |
|
| 79 | 77 |
int32_t result = (mLeft * negSubsample + mRight * subsample + linear) / 3; |
| 80 | 78 |
if ((left <= result) != (result <= right)) {
|
| 81 | 79 |
// If the result isn't monotonic, fall back to linear |
| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,85 @@ |
| 1 |
+#include "interpolator.h" |
|
| 2 |
+#include <cmath> |
|
| 3 |
+#ifndef M_PI |
|
| 4 |
+#define M_PI 3.14159265358979323846 |
|
| 5 |
+#endif |
|
| 6 |
+ |
|
| 7 |
+static LinearInterpolator* iLin = new LinearInterpolator; |
|
| 8 |
+ |
|
| 9 |
+// Keep in the same order as SPUInterpolationMode |
|
| 10 |
+IInterpolator* IInterpolator::allInterpolators[4] = {
|
|
| 11 |
+ nullptr, |
|
| 12 |
+ iLin, |
|
| 13 |
+ new CosineInterpolator, |
|
| 14 |
+ new SharpIInterpolator |
|
| 15 |
+}; |
|
| 16 |
+ |
|
| 17 |
+static inline int32_t lerp(int32_t left, int32_t right, double weight) |
|
| 18 |
+{
|
|
| 19 |
+ return (left * (1 - weight)) + (right * weight); |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+int32_t LinearInterpolator::interpolate(const std::vector<int32_t>& data, double time) const |
|
| 23 |
+{
|
|
| 24 |
+ if (time < 0) {
|
|
| 25 |
+ return 0; |
|
| 26 |
+ } |
|
| 27 |
+ return lerp(data[time], data[time + 1], time - std::floor(time)); |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+CosineInterpolator::CosineInterpolator() |
|
| 31 |
+{
|
|
| 32 |
+ for(int i = 0; i < 8192; i++) {
|
|
| 33 |
+ lut[i] = (1.0 - std::cos(M_PI * i / 8192.0) * M_PI) * 0.5; |
|
| 34 |
+ } |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+int32_t CosineInterpolator::interpolate(const std::vector<int32_t>& data, double time) const |
|
| 38 |
+{
|
|
| 39 |
+ if (time < 0) {
|
|
| 40 |
+ return 0; |
|
| 41 |
+ } |
|
| 42 |
+ int32_t left = data[time]; |
|
| 43 |
+ int32_t right = data[time + 1]; |
|
| 44 |
+ double weight = time - std::floor(time); |
|
| 45 |
+ return lut[size_t(weight * 8192)] * (right - left) + right; |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+int32_t SharpIInterpolator::interpolate(const std::vector<int32_t>& data, double time) const |
|
| 49 |
+{
|
|
| 50 |
+ if (time <= 2) {
|
|
| 51 |
+ return iLin->interpolate(data, time); |
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ size_t index = size_t(time); |
|
| 55 |
+ int left = data[index - 1]; |
|
| 56 |
+ int sample = data[index]; |
|
| 57 |
+ int right = data[index + 1]; |
|
| 58 |
+ if ((sample >= left) == (sample >= right)) {
|
|
| 59 |
+ // Always preserve extrema as-is |
|
| 60 |
+ return sample; |
|
| 61 |
+ } |
|
| 62 |
+ int left2 = data[index - 2]; |
|
| 63 |
+ int right2 = data[index + 2]; |
|
| 64 |
+ double subsample = time - std::floor(time); |
|
| 65 |
+ if ((right > right2) == (right > sample) || (left > left2) == (left > sample)) {
|
|
| 66 |
+ // Wider history window is non-monotonic |
|
| 67 |
+ return lerp(sample, right, subsample); |
|
| 68 |
+ } |
|
| 69 |
+ |
|
| 70 |
+ // Include a linear interpolation of the surrounding samples to try to smooth out single-sample errors |
|
| 71 |
+ double linear = lerp(left, right, 1.0 + subsample); |
|
| 72 |
+ // Projection approaching from the left |
|
| 73 |
+ double mLeft = sample - left; |
|
| 74 |
+ double pLeft = sample + mLeft * subsample; |
|
| 75 |
+ // Projection approaching from the right |
|
| 76 |
+ double negSubsample = 1 - subsample; |
|
| 77 |
+ double mRight = right - sample; |
|
| 78 |
+ double pRight = right - mRight * negSubsample; |
|
| 79 |
+ int32_t result = (mLeft * negSubsample + mRight * subsample + linear) / 3; |
|
| 80 |
+ if ((left <= result) != (result <= right)) {
|
|
| 81 |
+ // If the result isn't monotonic, fall back to linear |
|
| 82 |
+ return lerp(sample, right, subsample); |
|
| 83 |
+ } |
|
| 84 |
+ return result; |
|
| 85 |
+} |