2 #include "bsinc_tables.h"
13 #include "alnumbers.h"
14 #include "alnumeric.h"
16 #include "bsinc_defs.h"
17 #include "opthelpers.h"
18 #include "resampler_limits.h"
23 using uint
= unsigned int;
26 /* The zero-order modified Bessel function of the first kind, used for the
29 * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
30 * = sum_{k=0}^inf ((x / 2)^k / k!)^2
32 * This implementation only handles nu = 0, and isn't the most precise (it
33 * starts with the largest value and accumulates successively smaller values,
34 * compounding the rounding and precision error), but it's good enough.
36 template<typename T
, typename U
>
37 constexpr auto cyl_bessel_i(T nu
, U x
) -> U
40 throw std::runtime_error
{"cyl_bessel_i: nu != 0"};
42 /* Start at k=1 since k=0 is trivial. */
43 const double x2
{x
/2.0};
48 /* Let the integration converge until the term of the sum is no longer
53 const double y
{x2
/ k
};
58 } while(sum
!= last_sum
);
59 return static_cast<U
>(sum
);
62 /* This is the normalized cardinal sine (sinc) function.
64 * sinc(x) = { 1, x = 0
65 * { sin(pi x) / (pi x), otherwise.
67 constexpr double Sinc(const double x
)
69 constexpr double epsilon
{std::numeric_limits
<double>::epsilon()};
70 if(!(x
> epsilon
|| x
< -epsilon
))
72 return std::sin(al::numbers::pi
*x
) / (al::numbers::pi
*x
);
75 /* Calculate a Kaiser window from the given beta value and a normalized k
78 * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1
81 * Where k can be calculated as:
83 * k = i / l, where -l <= i <= l.
87 * k = 2 i / M - 1, where 0 <= i <= M.
89 constexpr double Kaiser(const double beta
, const double k
, const double besseli_0_beta
)
91 if(!(k
>= -1.0 && k
<= 1.0))
93 return ::cyl_bessel_i(0, beta
* std::sqrt(1.0 - k
*k
)) / besseli_0_beta
;
96 /* Calculates the (normalized frequency) transition width of the Kaiser window.
99 constexpr double CalcKaiserWidth(const double rejection
, const uint order
) noexcept
101 if(rejection
> 21.19)
102 return (rejection
- 7.95) / (2.285 * al::numbers::pi
*2.0 * order
);
103 /* This enforces a minimum rejection of just above 21.18dB */
104 return 5.79 / (al::numbers::pi
*2.0 * order
);
107 /* Calculates the beta value of the Kaiser window. Rejection is in dB. */
108 constexpr double CalcKaiserBeta(const double rejection
)
111 return 0.1102 * (rejection
-8.7);
112 if(rejection
>= 21.0)
113 return (0.5842 * std::pow(rejection
-21.0, 0.4)) + (0.07886 * (rejection
-21.0));
123 std::array
<uint
,BSincScaleCount
> a
{};
126 constexpr BSincHeader(uint Rejection
, uint Order
) noexcept
127 : width
{CalcKaiserWidth(Rejection
, Order
)}, beta
{CalcKaiserBeta(Rejection
)}
128 , scaleBase
{width
/ 2.0}
130 uint num_points
{Order
+1};
131 for(uint si
{0};si
< BSincScaleCount
;++si
)
133 const double scale
{lerpd(scaleBase
, 1.0, (si
+1) / double{BSincScaleCount
})};
134 const uint a_
{std::min(static_cast<uint
>(num_points
/ 2.0 / scale
), num_points
)};
135 const uint m
{2 * a_
};
138 total_size
+= 4 * BSincPhaseCount
* ((m
+3) & ~3u);
143 /* 11th and 23rd order filters (12 and 24-point respectively) with a 60dB drop
144 * at nyquist. Each filter will scale up the order when downsampling, to 23rd
145 * and 47th order respectively.
147 constexpr BSincHeader bsinc12_hdr
{60, 11};
148 constexpr BSincHeader bsinc24_hdr
{60, 23};
151 template<const BSincHeader
&hdr
>
152 struct SIMDALIGN BSincFilterArray
{
153 alignas(16) std::array
<float, hdr
.total_size
> mTable
{};
157 static constexpr uint BSincPointsMax
{(hdr
.a
[0]*2u + 3u) & ~3u};
158 static_assert(BSincPointsMax
<= MaxResamplerPadding
, "MaxResamplerPadding is too small");
160 using filter_type
= std::array
<std::array
<double,BSincPointsMax
>,BSincPhaseCount
>;
161 auto filter
= std::vector
<filter_type
>(BSincScaleCount
);
163 const double besseli_0_beta
{::cyl_bessel_i(0, hdr
.beta
)};
165 /* Calculate the Kaiser-windowed Sinc filter coefficients for each
166 * scale and phase index.
168 for(uint si
{0};si
< BSincScaleCount
;++si
)
170 const uint m
{hdr
.a
[si
] * 2};
171 const size_t o
{(BSincPointsMax
-m
) / 2};
172 const double scale
{lerpd(hdr
.scaleBase
, 1.0, (si
+1) / double{BSincScaleCount
})};
173 const double cutoff
{scale
- (hdr
.scaleBase
* std::max(1.0, scale
*2.0))};
174 const auto a
= static_cast<double>(hdr
.a
[si
]);
175 const double l
{a
- 1.0/BSincPhaseCount
};
177 for(uint pi
{0};pi
< BSincPhaseCount
;++pi
)
179 const double phase
{std::floor(l
) + (pi
/double{BSincPhaseCount
})};
181 for(uint i
{0};i
< m
;++i
)
183 const double x
{i
- phase
};
184 filter
[si
][pi
][o
+i
] = Kaiser(hdr
.beta
, x
/l
, besseli_0_beta
) * cutoff
*
191 for(size_t si
{0};si
< BSincScaleCount
;++si
)
193 const size_t m
{((hdr
.a
[si
]*2) + 3) & ~3u};
194 const size_t o
{(BSincPointsMax
-m
) / 2};
196 /* Write out each phase index's filter and phase delta for this
199 for(size_t pi
{0};pi
< BSincPhaseCount
;++pi
)
201 for(size_t i
{0};i
< m
;++i
)
202 mTable
[idx
++] = static_cast<float>(filter
[si
][pi
][o
+i
]);
204 /* Linear interpolation between phases is simplified by pre-
205 * calculating the delta (b - a) in: x = a + f (b - a)
207 if(pi
< BSincPhaseCount
-1)
209 for(size_t i
{0};i
< m
;++i
)
211 const double phDelta
{filter
[si
][pi
+1][o
+i
] - filter
[si
][pi
][o
+i
]};
212 mTable
[idx
++] = static_cast<float>(phDelta
);
217 /* The delta target for the last phase index is the first
218 * phase index with the coefficients offset by one. The
219 * first delta targets 0, as it represents a coefficient
220 * for a sample that won't be part of the filter.
222 mTable
[idx
++] = static_cast<float>(0.0 - filter
[si
][pi
][o
]);
223 for(size_t i
{1};i
< m
;++i
)
225 const double phDelta
{filter
[si
][0][o
+i
-1] - filter
[si
][pi
][o
+i
]};
226 mTable
[idx
++] = static_cast<float>(phDelta
);
231 /* Now write out each phase index's scale and phase+scale deltas,
232 * to complete the bilinear equation for the combination of phase
235 if(si
< BSincScaleCount
-1)
237 for(size_t pi
{0};pi
< BSincPhaseCount
;++pi
)
239 for(size_t i
{0};i
< m
;++i
)
241 const double scDelta
{filter
[si
+1][pi
][o
+i
] - filter
[si
][pi
][o
+i
]};
242 mTable
[idx
++] = static_cast<float>(scDelta
);
245 if(pi
< BSincPhaseCount
-1)
247 for(size_t i
{0};i
< m
;++i
)
249 const double spDelta
{(filter
[si
+1][pi
+1][o
+i
]-filter
[si
+1][pi
][o
+i
]) -
250 (filter
[si
][pi
+1][o
+i
]-filter
[si
][pi
][o
+i
])};
251 mTable
[idx
++] = static_cast<float>(spDelta
);
256 mTable
[idx
++] = static_cast<float>((0.0 - filter
[si
+1][pi
][o
]) -
257 (0.0 - filter
[si
][pi
][o
]));
258 for(size_t i
{1};i
< m
;++i
)
260 const double spDelta
{(filter
[si
+1][0][o
+i
-1] - filter
[si
+1][pi
][o
+i
]) -
261 (filter
[si
][0][o
+i
-1] - filter
[si
][pi
][o
+i
])};
262 mTable
[idx
++] = static_cast<float>(spDelta
);
269 /* The last scale index doesn't have scale-related deltas. */
270 for(size_t i
{0};i
< BSincPhaseCount
*m
*2;++i
)
271 mTable
[idx
++] = 0.0f
;
274 assert(idx
== hdr
.total_size
);
277 [[nodiscard
]] constexpr auto getHeader() const noexcept
-> const BSincHeader
& { return hdr
; }
278 [[nodiscard
]] constexpr auto getTable() const noexcept
{ return al::span
{mTable
}; }
281 const BSincFilterArray
<bsinc12_hdr
> bsinc12_filter
{};
282 const BSincFilterArray
<bsinc24_hdr
> bsinc24_filter
{};
285 constexpr BSincTable
GenerateBSincTable(const T
&filter
)
288 const BSincHeader
&hdr
= filter
.getHeader();
289 ret
.scaleBase
= static_cast<float>(hdr
.scaleBase
);
290 ret
.scaleRange
= static_cast<float>(1.0 / (1.0 - hdr
.scaleBase
));
291 for(size_t i
{0};i
< BSincScaleCount
;++i
)
292 ret
.m
[i
] = ((hdr
.a
[i
]*2) + 3) & ~3u;
293 ret
.filterOffset
[0] = 0;
294 for(size_t i
{1};i
< BSincScaleCount
;++i
)
295 ret
.filterOffset
[i
] = ret
.filterOffset
[i
-1] + ret
.m
[i
-1]*4*BSincPhaseCount
;
296 ret
.Tab
= filter
.getTable();
302 const BSincTable gBSinc12
{GenerateBSincTable(bsinc12_filter
)};
303 const BSincTable gBSinc24
{GenerateBSincTable(bsinc24_filter
)};