15{
16 if (
src.empty() || kernel.empty())
17 return std::vector<double>(
src.size(), 0.0);
18
19 const size_t conv_len =
src.size() + kernel.size() - 1;
20 const size_t fft_size = std::bit_ceil(std::max(size_t { 256 }, conv_len));
21 const size_t bins = fft_size / 2 + 1;
22
23 Eigen::VectorXd padded_src = Eigen::VectorXd::Zero(static_cast<Eigen::Index>(fft_size));
24 Eigen::VectorXd padded_kernel = Eigen::VectorXd::Zero(static_cast<Eigen::Index>(fft_size));
25 std::ranges::copy(
src, padded_src.begin());
26 std::ranges::copy(kernel, padded_kernel.begin());
27
28 Eigen::FFT<double> fft;
29 Eigen::VectorXcd src_fft, ker_fft;
30 fft.fwd(src_fft, padded_src);
31 fft.fwd(ker_fft, padded_kernel);
32
33 std::vector<std::complex<double>> sig(bins), ker(bins), res(bins);
34 std::for_each(std::execution::par_unseq,
35 std::views::iota(size_t { 0 }, bins).begin(),
36 std::views::iota(size_t { 0 }, bins).end(),
38 sig[
b] = src_fft(
static_cast<Eigen::Index
>(
b));
39 ker[
b] = ker_fft(
static_cast<Eigen::Index
>(
b));
40 });
41
42 processor(sig, ker, res);
43
44 Eigen::VectorXcd full_fft(static_cast<Eigen::Index>(fft_size));
45 std::for_each(std::execution::par_unseq,
46 std::views::iota(size_t { 0 }, fft_size).begin(),
47 std::views::iota(size_t { 0 }, fft_size).end(),
49 full_fft(
static_cast<Eigen::Index
>(
b)) = (
b < bins)
51 :
std::conj(res[fft_size -
b]);
52 });
53
54 const double inv = 1.0 / static_cast<double>(fft_size);
55 full_fft *= inv;
56
57 Eigen::VectorXd time_result;
58 fft.inv(time_result, full_fft);
59
60 const size_t out_len = full_size ? conv_len :
src.size();
61 std::vector<double>
out(out_len);
62 std::for_each(std::execution::par_unseq,
63 std::views::iota(size_t { 0 }, out_len).begin(),
64 std::views::iota(size_t { 0 }, out_len).end(),
65 [&](
size_t i) {
out[i] = time_result(
static_cast<Eigen::Index
>(i)); });
67}
std::vector< float > * out