Wayverb
multiband_filter.h
1 #pragma once
2 
3 #include "frequency_domain/envelope.h"
4 #include "frequency_domain/filter.h"
5 
6 #include "utilities/foldl.h"
7 #include "utilities/map.h"
8 #include "utilities/map_to_vector.h"
9 #include "utilities/mapping_iterator_adapter.h"
10 #include "utilities/range.h"
11 
12 namespace frequency_domain {
13 
14 template <size_t N>
15 struct edges_and_width_factor final {
16  std::array<double, N> edges;
17  double width_factor;
18 };
19 
20 template <size_t N>
21 constexpr auto make_edges_and_width_factor(const std::array<double, N>& edges,
22  double width_factor) {
23  return edges_and_width_factor<N>{edges, width_factor};
24 }
25 
26 template <size_t N>
27 auto compute_multiband_params(util::range<double> audible_range,
28  double overlap) {
29  return make_edges_and_width_factor(band_edges<N>(audible_range),
30  width_factor(audible_range, N, overlap));
31 }
32 
34 
35 template <typename T>
36 size_t next_power_of_two(T t) {
37  return std::ceil(std::log2(t));
38 }
39 
40 template <typename T>
41 size_t best_fft_length(T t) {
42  return std::pow(2, next_power_of_two(t));
43 }
44 
46 
47 template <size_t bands_plus_one, typename It, typename Callback>
48 auto multiband_filter(It b,
49  It e,
51  const Callback& callback,
52  size_t l = 0) {
53  constexpr auto bands = bands_plus_one - 1;
54 
55  // A bit of extra padding here so that discontinuities at the end get
56  // truncated away.
57  const auto bins = best_fft_length(std::distance(b, e)) << 2;
58  filter filt{bins};
59 
60  // Will store the area under each frequency-domain window.
61  std::array<double, bands> summed_squared{};
62  std::array<double, bands> integrated_envelopes{};
63 
64  for (auto i = 0ul; i != bands; ++i) {
65  const auto mapping_b = callback(b, i);
66  const auto mapping_e = callback(e, i);
67  filt.run(mapping_b, mapping_e, mapping_b, [&](auto cplx, auto freq) {
68  const auto amp = compute_bandpass_magnitude(
69  freq,
70  util::make_range(params.edges[i + 0], params.edges[i + 1]),
71  params.width_factor,
72  l);
73  integrated_envelopes[i] += amp;
74  const auto ret = cplx * static_cast<float>(amp);
75  const auto abs_ret = std::abs(ret);
76  summed_squared[i] += abs_ret * abs_ret;
77  return ret;
78  });
79  }
80 
81  std::array<double, bands> normalized_rms{};
82  for (auto i = 0; i != bands; ++i) {
83  normalized_rms[i] = integrated_envelopes[i] ?
84  std::sqrt(summed_squared[i] / integrated_envelopes[i]) : 0;
85  }
86 
87  return normalized_rms;
88 }
89 
90 template <typename It>
91 auto square_sum(It b, It e) {
92  return std::accumulate(
93  b, e, std::decay_t<decltype(*b)>(), [](auto a, auto b) {
94  return a + b * b;
95  });
96 }
97 
98 template <typename It>
99 auto rms(It b, It e) {
100  using std::sqrt;
101  return sqrt(square_sum(b, e));
102 }
103 
104 template <size_t bands, typename T>
105 auto init_array(const T& t) {
106  std::array<T, bands> ret{};
107  std::fill(begin(ret), end(ret), t);
108  return ret;
109 }
110 
111 template <size_t bands, typename It>
112 auto make_multiband(It begin, It end) {
113  return util::map_to_vector(
114  begin, end, [](const auto& i) { return init_array<bands>(i); });
115 }
116 
117 class indexer final {
118 public:
119  constexpr explicit indexer(size_t index)
120  : index_{index} {}
121 
122  template <typename T>
123  constexpr auto& operator()(T& t) const {
124  return t[index_];
125  }
126 
127  template <typename T>
128  constexpr const auto& operator()(const T& t) const {
129  return t[index_];
130  }
131 
132 private:
133  size_t index_;
134 };
135 
136 struct make_indexer_iterator final {
137  template <typename It>
138  constexpr auto operator()(It it, size_t index) const {
139  return util::make_mapping_iterator_adapter(std::move(it),
140  indexer{index});
141  }
142 };
143 
144 template <size_t bands_plus_one, typename It>
145 auto per_band_energy(It begin,
146  It end,
148  constexpr auto bands = bands_plus_one - 1;
149 
150  auto multiband = make_multiband<bands>(begin, end);
151 
152  const auto rms = multiband_filter(std::begin(multiband),
153  std::end(multiband),
154  params,
156 
157  return rms;
158 }
159 
160 } // namespace frequency_domain
Structured this way so that I can keep all fftw linkage internal.
Definition: filter.h:12
Definition: buffer.h:5
Definition: multiband_filter.h:15
Definition: multiband_filter.h:136
Definition: multiband_filter.h:117