11 std::span<const double>
src,
12 std::span<const double> kernel,
16 if (
src.empty() || kernel.empty())
17 return std::vector<double>(
src.size(), 0.0);
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;
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());
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);
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));
42 processor(sig, ker, res);
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]);
54 const double inv = 1.0 /
static_cast<double>(fft_size);
57 Eigen::VectorXd time_result;
58 fft.inv(time_result, full_fft);
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)); });
69std::vector<double>
convolve(std::span<const double>
src, std::span<const double> ir)
72 std::vector<double>
out(
src.size());
73 const double g = ir[0];
74 std::transform(std::execution::par_unseq,
src.begin(),
src.end(),
out.begin(),
75 [g](
double x) { return x * g; });
80 [](
const auto& s,
const auto& k,
auto& r) {
81 std::transform(std::execution::par_unseq, s.begin(), s.end(), k.begin(), r.begin(),
82 [](
const std::complex<double>&
a,
const std::complex<double>&
b) {
89 std::span<const double>
src,
90 std::span<const double> tmpl,
93 std::vector<double> reversed(tmpl.rbegin(), tmpl.rend());
96 [](
const auto& s,
const auto& k,
auto& r) {
97 std::transform(std::execution::par_unseq, s.begin(), s.end(), k.begin(), r.begin(),
98 [](
const std::complex<double>&
a,
const std::complex<double>&
b) {
99 return a * std::conj(b);
104 const auto [mn, mx] = std::ranges::minmax_element(
out);
105 const double peak = std::max(std::abs(*mn), std::abs(*mx));
107 std::transform(std::execution::par_unseq,
out.begin(),
out.end(),
out.begin(),
108 [
peak](
double v) { return v / peak; });
123 std::span<const double>
src,
124 std::span<const double> ir,
125 double regularization)
128 [regularization](
const auto& s,
const auto& k,
auto& r) {
129 std::transform(std::execution::par_unseq, s.begin(), s.end(), k.begin(), r.begin(),
130 [regularization](
const std::complex<double>& sig,
const std::complex<double>& ker) {
131 const double mag_sq = std::norm(ker);
132 if (mag_sq < regularization)
133 return std::complex<double> {};
134 return sig * std::conj(ker) / (mag_sq + regularization);
142 [](
const auto& s,
const auto&,
auto& r) {
143 std::transform(std::execution::par_unseq, s.begin(), s.end(), s.begin(), r.begin(),
144 [](
const std::complex<double>&
a,
const std::complex<double>&
b) {
145 return a * std::conj(b);
150 const double peak = *std::max_element(
out.begin(),
out.end());
152 std::transform(std::execution::par_unseq,
out.begin(),
out.end(),
out.begin(),
153 [
peak](
double v) { return v / peak; });
std::vector< double > apply_convolution(std::span< const double > src, std::span< const double > kernel, const ConvolutionProcessor &processor, bool full_size)
Core FFT convolution engine.
std::vector< double > peak(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Peak amplitude per window.
void normalize(std::span< double > data, double target_min, double target_max) noexcept
Normalize to [target_min, target_max] in-place Single-pass min/max reduction followed by a single tra...
std::function< void(const std::vector< std::complex< double > > &signal_fft, const std::vector< std::complex< double > > &kernel_fft, std::vector< std::complex< double > > &result_fft)> ConvolutionProcessor
Frequency-domain processor callback for apply_convolution.