-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathResamplingAlgorithms.cs
177 lines (145 loc) · 5.09 KB
/
ResamplingAlgorithms.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// BRR Suite is licensed under the MIT license.
namespace BRRSuite;
/// <summary>
/// Contains methods for creating <see cref="ResamplingAlgorithm"/> delegates.
/// </summary>
public static class ResamplingAlgorithms {
/// <summary>
/// Tests the parameters of a resampling invocation, throwing an error if they are not valid.
/// </summary>
/// <remarks>
/// Throws an <see cref="ArgumentException"/> if any of the following is true:<br/>
/// - <paramref name="inLength"/> > <paramref name="samplesLength"/><br/>
/// - <paramref name="inLength"/> < 1<br/>
/// - <paramref name="outLength"/> < 1
/// </remarks>
/// <param name="samplesLength">The length of the input data.</param>
/// <param name="inLength"><inheritdoc cref="ResamplingAlgorithm" path="/param[@name='inLength']"/></param>
/// <param name="outLength"><inheritdoc cref="ResamplingAlgorithm" path="/param[@name='outLength']"/></param>
/// <exception cref="ArgumentException">If a problem occurs</exception>
public static void ThrowIfInvalid(int samplesLength, int inLength, int outLength) {
if (inLength > samplesLength) {
throw new ArgumentException(
$"The input length for resampling should not be larger than the size of the data: {inLength}/{samplesLength}.",
nameof(inLength));
}
if (inLength < 1) {
throw new ArgumentException("The input length should not be 0 or negative.", nameof(inLength));
}
if (outLength < 1) {
throw new ArgumentException("The output length should not be 0 or negative.", nameof(outLength));
}
}
// ported from BRRtools
/// <summary>
/// A resampling algorithm that uses nearest-neighbor interpolation.
/// </summary>
public static readonly ResamplingAlgorithm NoInterpolation =
(samples, inLength, outLength) => {
ThrowIfInvalid(samples.Length, inLength, outLength);
// Fast copy
if (inLength == outLength) {
return samples[..inLength];
}
double ratio = ((double) inLength) / outLength;
int[] outBuf = new int[outLength];
for (int i = 0; i < outLength; i++) {
outBuf[i] = samples[(int) (i * ratio)];
}
return outBuf;
};
// ported from BRRtools
/// <summary>
/// A resampling algorithm that uses cubic interpolation.
/// </summary>
public static readonly ResamplingAlgorithm CubicInterpolation =
(samples, inLength, outLength) => {
ThrowIfInvalid(samples.Length, inLength, outLength);
// Fast copy
if (inLength == outLength) {
return samples[..inLength];
}
double ratio = ((double) inLength) / outLength;
int[] outBuf = new int[outLength];
for (int i = 0; i < outLength; i++) {
int a = (int) (i * ratio);
short s0 = (short) ((a == 0) ? samples[0] : samples[a - 1]);
short s1 = (short) samples[a];
short s2 = (short) ((a + 1 >= inLength) ? samples[inLength - 1] : samples[a + 1]);
short s3 = (short) ((a + 2 >= inLength) ? samples[inLength - 1] : samples[a + 2]);
double a0 = s3 - s2 - s0 + s1;
double a1 = s0 - s1 - a0;
double a2 = s2 - s0;
double b0 = i * ratio - a;
double b2 = b0 * b0;
double b3 = b2 * b0;
outBuf[i] = (int) (b3 * a0 + b2 * a1 + b0 * a2 + s1);
}
return outBuf;
};
// ported from BRRtools
/// <summary>
/// A resampling algorithm that uses bandlimited interpolation.
/// </summary>
public static readonly ResamplingAlgorithm BandlimitedInterpolation =
(samples, inLength, outLength) => {
ThrowIfInvalid(samples.Length, inLength, outLength);
// Fast copy
if (inLength == outLength) {
return samples[..inLength];
}
double ratio = ((double) inLength) / outLength;
int[] outBuf = new int[outLength];
const int FIROrder = 15;
if (ratio > 1.0) {
int[] samples_antialiased = new int[inLength];
double[] fir_coefs = new double[FIROrder + 1];
// Compute FIR coefficients
for (int k = 0; k <= FIROrder; k++) {
fir_coefs[k] = Sinc(k / ratio) / ratio;
}
// Apply FIR filter to samples
for (int i = 0; i < inLength; i++) {
double acc = samples[i] * fir_coefs[0];
for (int k = FIROrder; k > 0; k--) {
acc += fir_coefs[k] * ((i + k < inLength) ? samples[i + k] : samples[inLength - 1]);
acc += fir_coefs[k] * ((i - k >= 0) ? samples[i - k] : samples[0]);
}
samples_antialiased[i] = (int) acc;
}
samples = samples_antialiased;
}
// Actual resampling using sinc interpolation
for (int i = 0; i < outLength; i++) {
double a = i * ratio;
double acc = 0.0;
int aend = (int) (a + FIROrder) + 1;
for (int j = (int) (a - FIROrder); j < aend; j++) {
int sample;
if (j >= 0) {
if (j < inLength) {
sample = samples[j];
} else {
sample = samples[inLength - 1];
}
} else {
sample = samples[0];
}
acc += sample * Sinc(a - j);
}
outBuf[i] = (int) acc;
}
return outBuf;
};
/// <summary>
/// Performs the normalized sinc function on a value.
/// </summary>
/// <param name="x">The argument of the sinc function.</param>
/// <returns>The result of sinc(x).</returns>
public static double Sinc(double x) {
if (x == 0D) {
return 1D;
}
return Math.Sin(Math.PI * x) / (Math.PI * x);
}
}