2 #include "polyphase_resampler.h"
7 #include "opthelpers.h"
13 #define M_PI 3.14159265358979323846
18 using uint
= unsigned int;
20 /* This is the normalized cardinal sine (sinc) function.
22 * sinc(x) = { 1, x = 0
23 * { sin(pi x) / (pi x), otherwise.
25 double Sinc(const double x
)
27 if UNLIKELY(std::abs(x
) < EPSILON
)
29 return std::sin(M_PI
* x
) / (M_PI
* x
);
32 /* The zero-order modified Bessel function of the first kind, used for the
35 * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
36 * = sum_{k=0}^inf ((x / 2)^k / k!)^2
38 double BesselI_0(const double x
)
40 double term
, sum
, x2
, y
, last_sum
;
43 // Start at k=1 since k=0 is trivial.
49 // Let the integration converge until the term of the sum is no longer
57 } while(sum
!= last_sum
);
61 /* Calculate a Kaiser window from the given beta value and a normalized k
64 * w(k) = { I_0(B sqrt(1 - k^2)) / I_0(B), -1 <= k <= 1
67 * Where k can be calculated as:
69 * k = i / l, where -l <= i <= l.
73 * k = 2 i / M - 1, where 0 <= i <= M.
75 double Kaiser(const double b
, const double k
)
77 if(!(k
>= -1.0 && k
<= 1.0))
79 return BesselI_0(b
* std::sqrt(1.0 - k
*k
)) / BesselI_0(b
);
82 // Calculates the greatest common divisor of a and b.
83 uint
Gcd(uint x
, uint y
)
94 /* Calculates the size (order) of the Kaiser window. Rejection is in dB and
95 * the transition width is normalized frequency (0.5 is nyquist).
97 * M = { ceil((r - 7.95) / (2.285 2 pi f_t)), r > 21
98 * { ceil(5.79 / 2 pi f_t), r <= 21.
101 uint
CalcKaiserOrder(const double rejection
, const double transition
)
103 double w_t
= 2.0 * M_PI
* transition
;
104 if LIKELY(rejection
> 21.0)
105 return static_cast<uint
>(std::ceil((rejection
- 7.95) / (2.285 * w_t
)));
106 return static_cast<uint
>(std::ceil(5.79 / w_t
));
109 // Calculates the beta value of the Kaiser window. Rejection is in dB.
110 double CalcKaiserBeta(const double rejection
)
112 if LIKELY(rejection
> 50.0)
113 return 0.1102 * (rejection
- 8.7);
114 if(rejection
>= 21.0)
115 return (0.5842 * std::pow(rejection
- 21.0, 0.4)) +
116 (0.07886 * (rejection
- 21.0));
120 /* Calculates a point on the Kaiser-windowed sinc filter for the given half-
121 * width, beta, gain, and cutoff. The point is specified in non-normalized
122 * samples, from 0 to M, where M = (2 l + 1).
124 * w(k) 2 p f_t sinc(2 f_t x)
126 * x -- centered sample index (i - l)
127 * k -- normalized and centered window index (x / l)
128 * w(k) -- window function (Kaiser)
129 * p -- gain compensation factor when sampling
130 * f_t -- normalized center frequency (or cutoff; 0.5 is nyquist)
132 double SincFilter(const uint l
, const double b
, const double gain
, const double cutoff
,
135 const double x
{static_cast<double>(i
) - l
};
136 return Kaiser(b
, x
/ l
) * 2.0 * gain
* cutoff
* Sinc(2.0 * cutoff
* x
);
141 // Calculate the resampling metrics and build the Kaiser-windowed sinc filter
142 // that's used to cut frequencies above the destination nyquist.
143 void PPhaseResampler::init(const uint srcRate
, const uint dstRate
)
145 const uint gcd
{Gcd(srcRate
, dstRate
)};
149 /* The cutoff is adjusted by half the transition width, so the transition
150 * ends before the nyquist (0.5). Both are scaled by the downsampling
153 double cutoff
, width
;
164 // A rejection of -180 dB is used for the stop band. Round up when
165 // calculating the left offset to avoid increasing the transition width.
166 const uint l
{(CalcKaiserOrder(180.0, width
)+1) / 2};
167 const double beta
{CalcKaiserBeta(180.0)};
171 for(uint i
{0};i
< mM
;i
++)
172 mF
[i
] = SincFilter(l
, beta
, mP
, cutoff
, i
);
175 // Perform the upsample-filter-downsample resampling operation using a
176 // polyphase filter implementation.
177 void PPhaseResampler::process(const uint inN
, const double *in
, const uint outN
, double *out
)
179 if UNLIKELY(outN
== 0)
182 // Handle in-place operation.
183 std::vector
<double> workspace
;
185 if UNLIKELY(work
== in
)
187 workspace
.resize(outN
);
188 work
= workspace
.data();
191 // Resample the input.
192 const uint p
{mP
}, q
{mQ
}, m
{mM
}, l
{mL
};
193 const double *f
{mF
.data()};
194 for(uint i
{0};i
< outN
;i
++)
196 // Input starts at l to compensate for the filter delay. This will
197 // drop any build-up from the first half of the filter.
198 size_t j_f
{(l
+ q
*i
) % p
};
199 size_t j_s
{(l
+ q
*i
) / p
};
201 // Only take input when 0 <= j_s < inN.
205 size_t filt_len
{(m
-j_f
+p
-1) / p
};
206 if LIKELY(j_s
+1 > inN
)
208 size_t skip
{std::min
<size_t>(j_s
+1 - inN
, filt_len
)};
213 if(size_t todo
{std::min
<size_t>(j_s
+1, filt_len
)})
216 r
+= f
[j_f
] * in
[j_s
];
224 // Clean up after in-place operation.
226 std::copy_n(work
, outN
, out
);