2 #include "bsinc_tables.h"
12 #include "math_defs.h"
18 /* The max points includes the doubling for downsampling, so the maximum number
19 * of base sample points is 24, which is 23rd order.
21 constexpr int BSincPointsMax
{BSINC_POINTS_MAX
};
22 constexpr int BSincPointsHalf
{BSincPointsMax
/ 2};
24 constexpr int BSincPhaseCount
{BSINC_PHASE_COUNT
};
25 constexpr int BSincScaleCount
{BSINC_SCALE_COUNT
};
29 constexpr std::enable_if_t
<std::is_floating_point
<T
>::value
,T
> sqrt(T x
)
31 if(!(x
>= 0 && x
< std::numeric_limits
<double>::infinity()))
32 throw std::domain_error
{"Invalid sqrt value"};
38 cur
= 0.5f
*(cur
+ x
/cur
);
44 constexpr std::enable_if_t
<std::is_floating_point
<T
>::value
,T
> sin(T x
)
46 if(x
>= al::MathDefs
<T
>::Tau())
49 throw std::domain_error
{"Invalid sin value"};
51 x
-= al::MathDefs
<T
>::Tau();
52 } while(x
>= al::MathDefs
<T
>::Tau());
57 throw std::domain_error
{"Invalid sin value"};
59 x
+= al::MathDefs
<T
>::Tau();
83 /* This is the normalized cardinal sine (sinc) function.
85 * sinc(x) = { 1, x = 0
86 * { sin(pi x) / (pi x), otherwise.
88 constexpr double Sinc(const double x
)
90 if(!(x
> 1e-15 || x
< -1e-15))
92 return sin(al::MathDefs
<double>::Pi()*x
) / (al::MathDefs
<double>::Pi()*x
);
95 /* The zero-order modified Bessel function of the first kind, used for the
98 * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
99 * = sum_{k=0}^inf ((x / 2)^k / k!)^2
101 constexpr double BesselI_0(const double x
)
103 /* Start at k=1 since k=0 is trivial. */
104 const double x2
{x
/ 2.0};
110 /* Let the integration converge until the term of the sum is no longer
114 const double y
{x2
/ k
};
119 } while(sum
!= last_sum
);
124 /* Calculate a Kaiser window from the given beta value and a normalized k
127 * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1
130 * Where k can be calculated as:
132 * k = i / l, where -l <= i <= l.
136 * k = 2 i / M - 1, where 0 <= i <= M.
138 constexpr double Kaiser(const double beta
, const double k
, const double besseli_0_beta
)
140 if(!(k
>= -1.0 && k
<= 1.0))
142 return BesselI_0(beta
* sqrt(1.0 - k
*k
)) / besseli_0_beta
;
145 /* Calculates the (normalized frequency) transition width of the Kaiser window.
146 * Rejection is in dB.
148 constexpr double CalcKaiserWidth(const double rejection
, const int order
)
150 if(rejection
> 21.19)
151 return (rejection
- 7.95) / (order
* 2.285 * al::MathDefs
<double>::Tau());
152 /* This enforces a minimum rejection of just above 21.18dB */
153 return 5.79 / (order
* al::MathDefs
<double>::Tau());
156 /* Calculates the beta value of the Kaiser window. Rejection is in dB. */
157 constexpr double CalcKaiserBeta(const double rejection
)
160 return 0.1102 * (rejection
-8.7);
161 else if(rejection
>= 21.0)
162 return (0.5842 * std::pow(rejection
-21.0, 0.4)) + (0.07886 * (rejection
-21.0));
172 double besseli_0_beta
;
174 int a
[BSINC_SCALE_COUNT
];
178 constexpr BSincHeader
GenerateBSincHeader(int Rejection
, int Order
)
181 ret
.width
= CalcKaiserWidth(Rejection
, Order
);
182 ret
.beta
= CalcKaiserBeta(Rejection
);
183 ret
.scaleBase
= ret
.width
/ 2.0;
184 ret
.scaleRange
= 1.0 - ret
.scaleBase
;
185 ret
.besseli_0_beta
= BesselI_0(ret
.beta
);
187 int num_points
{Order
+1};
188 for(int si
{0};si
< BSincScaleCount
;++si
)
190 const double scale
{ret
.scaleBase
+ (ret
.scaleRange
* si
/ (BSincScaleCount
- 1))};
191 const int a
{std::min(static_cast<int>(num_points
/ 2.0 / scale
), num_points
)};
195 ret
.total_size
+= 4 * BSincPhaseCount
* ((m
+3) & ~3);
201 /* 11th and 23rd order filters (12 and 24-point respectively) with a 60dB drop
202 * at nyquist. Each filter will scale up the order when downsampling, to 23rd
203 * and 47th order respectively.
205 constexpr BSincHeader bsinc12_hdr
{GenerateBSincHeader(60, 11)};
206 constexpr BSincHeader bsinc24_hdr
{GenerateBSincHeader(60, 23)};
209 /* FIXME: This should be constexpr, but the temporary filter arrays are too
210 * big. This requires using heap space, which is not allowed in a constexpr
211 * function (maybe in C++20).
213 template<size_t total_size
>
214 std::array
<float,total_size
> GenerateBSincCoeffs(const BSincHeader
&hdr
)
216 auto filter
= std::make_unique
<double[][BSincPhaseCount
+1][BSincPointsMax
]>(BSincScaleCount
);
218 /* Calculate the Kaiser-windowed Sinc filter coefficients for each scale
221 for(unsigned int si
{0};si
< BSincScaleCount
;++si
)
223 const int m
{hdr
.a
[si
] * 2};
224 const int o
{BSincPointsHalf
- (m
/2)};
225 const int l
{hdr
.a
[si
] - 1};
226 const int a
{hdr
.a
[si
]};
227 const double scale
{hdr
.scaleBase
+ (hdr
.scaleRange
* si
/ (BSincScaleCount
- 1))};
228 const double cutoff
{scale
- (hdr
.scaleBase
* std::max(0.5, scale
) * 2.0)};
230 /* Do one extra phase index so that the phase delta has a proper target
231 * for its last index.
233 for(int pi
{0};pi
<= BSincPhaseCount
;++pi
)
235 const double phase
{l
+ (pi
/double{BSincPhaseCount
})};
237 for(int i
{0};i
< m
;++i
)
239 const double x
{i
- phase
};
240 filter
[si
][pi
][o
+i
] = Kaiser(hdr
.beta
, x
/a
, hdr
.besseli_0_beta
) * cutoff
*
246 auto ret
= std::make_unique
<std::array
<float,total_size
>>();
249 for(unsigned int si
{0};si
< BSincScaleCount
-1;++si
)
251 const int m
{((hdr
.a
[si
]*2) + 3) & ~3};
252 const int o
{BSincPointsHalf
- (m
/2)};
254 for(int pi
{0};pi
< BSincPhaseCount
;++pi
)
256 /* Write out the filter. Also calculate and write out the phase and
259 for(int i
{0};i
< m
;++i
)
260 (*ret
)[idx
++] = static_cast<float>(filter
[si
][pi
][o
+i
]);
262 /* Linear interpolation between phases is simplified by pre-
263 * calculating the delta (b - a) in: x = a + f (b - a)
265 for(int i
{0};i
< m
;++i
)
267 const double phDelta
{filter
[si
][pi
+1][o
+i
] - filter
[si
][pi
][o
+i
]};
268 (*ret
)[idx
++] = static_cast<float>(phDelta
);
271 /* Linear interpolation between scales is also simplified.
273 * Given a difference in points between scales, the destination
274 * points will be 0, thus: x = a + f (-a)
276 for(int i
{0};i
< m
;++i
)
278 const double scDelta
{filter
[si
+1][pi
][o
+i
] - filter
[si
][pi
][o
+i
]};
279 (*ret
)[idx
++] = static_cast<float>(scDelta
);
282 /* This last simplification is done to complete the bilinear
283 * equation for the combination of phase and scale.
285 for(int i
{0};i
< m
;++i
)
287 const double spDelta
{(filter
[si
+1][pi
+1][o
+i
] - filter
[si
+1][pi
][o
+i
]) -
288 (filter
[si
][pi
+1][o
+i
] - filter
[si
][pi
][o
+i
])};
289 (*ret
)[idx
++] = static_cast<float>(spDelta
);
294 /* The last scale index doesn't have any scale or scale-phase deltas. */
295 const unsigned int si
{BSincScaleCount
- 1};
296 const int m
{((hdr
.a
[si
]*2) + 3) & ~3};
297 const int o
{BSincPointsHalf
- (m
/2)};
299 for(int pi
{0};pi
< BSincPhaseCount
;++pi
)
301 for(int i
{0};i
< m
;++i
)
302 (*ret
)[idx
++] = static_cast<float>(filter
[si
][pi
][o
+i
]);
303 for(int i
{0};i
< m
;++i
)
305 const double phDelta
{filter
[si
][pi
+1][o
+i
] - filter
[si
][pi
][o
+i
]};
306 (*ret
)[idx
++] = static_cast<float>(phDelta
);
308 for(int i
{0};i
< m
;++i
)
309 (*ret
)[idx
++] = 0.0f
;
310 for(int i
{0};i
< m
;++i
)
311 (*ret
)[idx
++] = 0.0f
;
314 assert(idx
== total_size
);
319 /* FIXME: These can't be constexpr due to the calls reaching the compiler's
322 alignas(16) const auto bsinc12_table
= GenerateBSincCoeffs
<bsinc12_hdr
.total_size
>(bsinc12_hdr
);
323 alignas(16) const auto bsinc24_table
= GenerateBSincCoeffs
<bsinc24_hdr
.total_size
>(bsinc24_hdr
);
326 constexpr BSincTable
GenerateBSincTable(const BSincHeader
&hdr
, const float *tab
)
329 ret
.scaleBase
= static_cast<float>(hdr
.scaleBase
);
330 ret
.scaleRange
= static_cast<float>(1.0 / hdr
.scaleRange
);
331 for(int i
{0};i
< BSincScaleCount
;++i
)
332 ret
.m
[i
] = static_cast<unsigned int>(((hdr
.a
[i
]*2) + 3) & ~3);
333 ret
.filterOffset
[0] = 0;
334 for(int i
{1};i
< BSincScaleCount
;++i
)
335 ret
.filterOffset
[i
] = ret
.filterOffset
[i
-1] + ret
.m
[i
-1]*4*BSincPhaseCount
;
342 const BSincTable bsinc12
{GenerateBSincTable(bsinc12_hdr
, &bsinc12_table
.front())};
343 const BSincTable bsinc24
{GenerateBSincTable(bsinc24_hdr
, &bsinc24_table
.front())};