-
Notifications
You must be signed in to change notification settings - Fork 0
/
PreEncodingFilters.cs
122 lines (103 loc) · 3.83 KB
/
PreEncodingFilters.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
// BRR Suite is licensed under the MIT license.
namespace BRRSuite;
/// <summary>
/// Contains functionality for creating filters that adjust audio waveforms before encoding to BRR.
/// </summary>
public static class PreEncodingFilters {
/// <summary>
/// Encapsulates a filter that does nothing.
/// </summary>
public static readonly PreEncodingFilter NoFilter = _ => { };
// ported from BRRtools
/// <summary>
/// Creates a filter for treble boosting a waveform to compensate for the SNES Gaussian interpolation filter.
/// </summary>
/// <param name="matrix">A convolution matrix to run over each sample.</param>
/// <returns>An anonymous function encapsulating the filter.</returns>
public static PreEncodingFilter GetTrebleBoostFilter(double[] matrix) {
int depth = matrix.Length;
return (samples) => {
int length = samples.Length;
int[] samplesCopy = [.. samples];
for (int i = 0; i < length; i++) {
double acc = samples[i] * matrix[0];
for (int k = depth - 1; k > 0; k--) {
acc += matrix[k] * ((i + k < length) ? samplesCopy[i + k] : samplesCopy[^1]);
acc += matrix[k] * ((i - k >= 0) ? samplesCopy[i - k] : samplesCopy[0]);
}
samples[i] = (int) acc;
}
};
}
// ported from BRRtools
// Tepples' coefficient multiplied by 0.6 to avoid overflow in most cases <- idk what this means
/// <summary>
/// Returns a new convolution matrix containing the values used by the original BRRtools treble boost filter.
/// </summary>
/// <returns>An array of <see cref="double"/> values.</returns>
public static double[] GetBRRtoolsTrebleMatrix() => [
0.912962, -0.16199, -0.0153283, 0.0426783, -0.0372004, 0.023436, -0.0105816, 0.00250474
];
/// <summary>
/// Creates a convolution matrix for Guassian compensation.
/// </summary>
/// <remarks>
/// This is a formula discovered by Drexxx using the function <c>a*b^|x|</c>, where:
/// <list type="bullet">
/// <item>a = 16/sqrt(70)</item>
/// <item>b = (16*sqrt(70)-16)/193</item>
/// </list>
/// </remarks>
/// <param name="depth">The desired size of the output matrix. A recommended value is 4.</param>
/// <returns>An array of <see cref="double"/> values.</returns>
public static double[] GetDrexxxMatrix(int depth) {
int length = depth;
double[] ret = new double[length];
for (int i = 0; i < length; i++) {
// a b x
ret[i] = 1.9123657749350298 * Math.Pow(-0.3132730726295474, Math.Abs(i - depth));
}
return ret;
}
/// <summary>
/// Creates a filter for boosting the amplitude of a waveform by a specified amount in a linear fashion.
/// </summary>
/// <param name="boost">The value to multiply every sample by, where 1.0 indicates no change in amplitude.</param>
/// <returns>An anonymous function encapsulating the filter.</returns>
public static PreEncodingFilter GetLinearAmplitudeFilter(double boost) {
return (samples) => {
int len = samples.Length;
int[] ret = new int[len];
for (int i = 0; i < len; i++) {
samples[i] = (int) Math.Round(samples[i] * boost);
}
};
}
/// <summary>
/// Finds the largest linear amplitude boost ratio of a waveform such that the largest magnitude of the waveform—positive or negative—will be
/// transformed to a magnitude of 32600 (or close to it).
/// </summary>
/// <param name="samples">The set of samples to check.</param>
/// <returns>A <see langword="double"/> ratio.</returns>
public static double GetLinearBoostFactor(int[] samples) {
int max = int.MinValue;
int min = int.MaxValue;
// find the largest numbers
foreach (var n in samples) {
if (n > max) {
max = n;
}
if (n < min) {
min = n;
}
}
// get their absolute values
max = Math.Abs(max);
min = Math.Abs(min);
// get the larger magnitude
if (min > max) {
max = min;
}
return 32600D / max;
}
}