2 #include "bsinc_tables.h"
12 #include "alnumbers.h"
13 #include "core/mixer/defs.h"
18 using uint
= unsigned int;
21 /* This is the normalized cardinal sine (sinc) function.
23 * sinc(x) = { 1, x = 0
24 * { sin(pi x) / (pi x), otherwise.
26 constexpr double Sinc(const double x
)
28 constexpr double epsilon
{std::numeric_limits
<double>::epsilon()};
29 if(!(x
> epsilon
|| x
< -epsilon
))
31 return std::sin(al::numbers::pi
*x
) / (al::numbers::pi
*x
);
34 /* The zero-order modified Bessel function of the first kind, used for the
37 * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
38 * = sum_{k=0}^inf ((x / 2)^k / k!)^2
40 constexpr double BesselI_0(const double x
) noexcept
42 /* Start at k=1 since k=0 is trivial. */
43 const double x2
{x
/ 2.0};
49 /* Let the integration converge until the term of the sum is no longer
53 const double y
{x2
/ k
};
58 } while(sum
!= last_sum
);
63 /* Calculate a Kaiser window from the given beta value and a normalized k
66 * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1
69 * Where k can be calculated as:
71 * k = i / l, where -l <= i <= l.
75 * k = 2 i / M - 1, where 0 <= i <= M.
77 constexpr double Kaiser(const double beta
, const double k
, const double besseli_0_beta
)
79 if(!(k
>= -1.0 && k
<= 1.0))
81 return BesselI_0(beta
* std::sqrt(1.0 - k
*k
)) / besseli_0_beta
;
84 /* Calculates the (normalized frequency) transition width of the Kaiser window.
87 constexpr double CalcKaiserWidth(const double rejection
, const uint order
) noexcept
90 return (rejection
- 7.95) / (2.285 * al::numbers::pi
*2.0 * order
);
91 /* This enforces a minimum rejection of just above 21.18dB */
92 return 5.79 / (al::numbers::pi
*2.0 * order
);
95 /* Calculates the beta value of the Kaiser window. Rejection is in dB. */
96 constexpr double CalcKaiserBeta(const double rejection
)
99 return 0.1102 * (rejection
-8.7);
100 else if(rejection
>= 21.0)
101 return (0.5842 * std::pow(rejection
-21.0, 0.4)) + (0.07886 * (rejection
-21.0));
111 double besseli_0_beta
{};
113 uint a
[BSincScaleCount
]{};
116 constexpr BSincHeader(uint Rejection
, uint Order
) noexcept
118 width
= CalcKaiserWidth(Rejection
, Order
);
119 beta
= CalcKaiserBeta(Rejection
);
120 scaleBase
= width
/ 2.0;
121 scaleRange
= 1.0 - scaleBase
;
122 besseli_0_beta
= BesselI_0(beta
);
124 uint num_points
{Order
+1};
125 for(uint si
{0};si
< BSincScaleCount
;++si
)
127 const double scale
{scaleBase
+ (scaleRange
* (si
+1) / BSincScaleCount
)};
128 const uint a_
{std::min(static_cast<uint
>(num_points
/ 2.0 / scale
), num_points
)};
129 const uint m
{2 * a_
};
132 total_size
+= 4 * BSincPhaseCount
* ((m
+3) & ~3u);
137 /* 11th and 23rd order filters (12 and 24-point respectively) with a 60dB drop
138 * at nyquist. Each filter will scale up the order when downsampling, to 23rd
139 * and 47th order respectively.
141 constexpr BSincHeader bsinc12_hdr
{60, 11};
142 constexpr BSincHeader bsinc24_hdr
{60, 23};
145 /* NOTE: GCC 5 has an issue with BSincHeader objects being in an anonymous
146 * namespace while also being used as non-type template parameters.
148 #if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6
150 /* The number of sample points is double the a value (rounded up to a multiple
151 * of 4), and scale index 0 includes the doubling for downsampling. bsinc24 is
152 * currently the highest quality filter, and will use the most sample points.
154 constexpr uint BSincPointsMax
{(bsinc24_hdr
.a
[0]*2 + 3) & ~3u};
155 static_assert(BSincPointsMax
<= MaxResamplerPadding
, "MaxResamplerPadding is too small");
157 template<size_t total_size
>
158 struct BSincFilterArray
{
159 alignas(16) std::array
<float, total_size
> mTable
;
160 const BSincHeader
&hdr
;
162 BSincFilterArray(const BSincHeader
&hdr_
) : hdr
{hdr_
}
165 template<const BSincHeader
&hdr
>
166 struct BSincFilterArray
{
167 alignas(16) std::array
<float, hdr
.total_size
> mTable
{};
171 constexpr uint BSincPointsMax
{(hdr
.a
[0]*2 + 3) & ~3u};
172 static_assert(BSincPointsMax
<= MaxResamplerPadding
, "MaxResamplerPadding is too small");
174 using filter_type
= double[BSincPhaseCount
+1][BSincPointsMax
];
175 auto filter
= std::make_unique
<filter_type
[]>(BSincScaleCount
);
177 /* Calculate the Kaiser-windowed Sinc filter coefficients for each
178 * scale and phase index.
180 for(uint si
{0};si
< BSincScaleCount
;++si
)
182 const uint m
{hdr
.a
[si
] * 2};
183 const size_t o
{(BSincPointsMax
-m
) / 2};
184 const double scale
{hdr
.scaleBase
+ (hdr
.scaleRange
* (si
+1) / BSincScaleCount
)};
185 const double cutoff
{scale
- (hdr
.scaleBase
* std::max(1.0, scale
*2.0))};
186 const auto a
= static_cast<double>(hdr
.a
[si
]);
187 const double l
{a
- 1.0/BSincPhaseCount
};
189 /* Do one extra phase index so that the phase delta has a proper
190 * target for its last index.
192 for(uint pi
{0};pi
<= BSincPhaseCount
;++pi
)
194 const double phase
{std::floor(l
) + (pi
/double{BSincPhaseCount
})};
196 for(uint i
{0};i
< m
;++i
)
198 const double x
{i
- phase
};
199 filter
[si
][pi
][o
+i
] = Kaiser(hdr
.beta
, x
/l
, hdr
.besseli_0_beta
) * cutoff
*
206 for(size_t si
{0};si
< BSincScaleCount
;++si
)
208 const size_t m
{((hdr
.a
[si
]*2) + 3) & ~3u};
209 const size_t o
{(BSincPointsMax
-m
) / 2};
211 /* Write out each phase index's filter and phase delta for this
214 for(size_t pi
{0};pi
< BSincPhaseCount
;++pi
)
216 for(size_t i
{0};i
< m
;++i
)
217 mTable
[idx
++] = static_cast<float>(filter
[si
][pi
][o
+i
]);
219 /* Linear interpolation between phases is simplified by pre-
220 * calculating the delta (b - a) in: x = a + f (b - a)
222 for(size_t i
{0};i
< m
;++i
)
224 const double phDelta
{filter
[si
][pi
+1][o
+i
] - filter
[si
][pi
][o
+i
]};
225 mTable
[idx
++] = static_cast<float>(phDelta
);
228 /* Calculate and write out each phase index's filter quality scale
229 * deltas. The last scale index doesn't have any scale or scale-
232 if(si
== BSincScaleCount
-1)
234 for(size_t i
{0};i
< BSincPhaseCount
*m
*2;++i
)
235 mTable
[idx
++] = 0.0f
;
237 else for(size_t pi
{0};pi
< BSincPhaseCount
;++pi
)
239 /* Linear interpolation between scales is also simplified.
241 * Given a difference in the number of points between scales,
242 * the destination points will be 0, thus: x = a + f (-a)
244 for(size_t i
{0};i
< m
;++i
)
246 const double scDelta
{filter
[si
+1][pi
][o
+i
] - filter
[si
][pi
][o
+i
]};
247 mTable
[idx
++] = static_cast<float>(scDelta
);
250 /* This last simplification is done to complete the bilinear
251 * equation for the combination of phase and scale.
253 for(size_t i
{0};i
< m
;++i
)
255 const double spDelta
{(filter
[si
+1][pi
+1][o
+i
] - filter
[si
+1][pi
][o
+i
]) -
256 (filter
[si
][pi
+1][o
+i
] - filter
[si
][pi
][o
+i
])};
257 mTable
[idx
++] = static_cast<float>(spDelta
);
261 assert(idx
== hdr
.total_size
);
264 constexpr const BSincHeader
&getHeader() const noexcept
{ return hdr
; }
265 constexpr const float *getTable() const noexcept
{ return &mTable
.front(); }
268 #if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6
269 const BSincFilterArray
<bsinc12_hdr
.total_size
> bsinc12_filter
{bsinc12_hdr
};
270 const BSincFilterArray
<bsinc24_hdr
.total_size
> bsinc24_filter
{bsinc24_hdr
};
272 const BSincFilterArray
<bsinc12_hdr
> bsinc12_filter
{};
273 const BSincFilterArray
<bsinc24_hdr
> bsinc24_filter
{};
277 constexpr BSincTable
GenerateBSincTable(const T
&filter
)
280 const BSincHeader
&hdr
= filter
.getHeader();
281 ret
.scaleBase
= static_cast<float>(hdr
.scaleBase
);
282 ret
.scaleRange
= static_cast<float>(1.0 / hdr
.scaleRange
);
283 for(size_t i
{0};i
< BSincScaleCount
;++i
)
284 ret
.m
[i
] = ((hdr
.a
[i
]*2) + 3) & ~3u;
285 ret
.filterOffset
[0] = 0;
286 for(size_t i
{1};i
< BSincScaleCount
;++i
)
287 ret
.filterOffset
[i
] = ret
.filterOffset
[i
-1] + ret
.m
[i
-1]*4*BSincPhaseCount
;
288 ret
.Tab
= filter
.getTable();
294 const BSincTable bsinc12
{GenerateBSincTable(bsinc12_filter
)};
295 const BSincTable bsinc24
{GenerateBSincTable(bsinc24_filter
)};