Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
fir_filter.hpp
Go to the documentation of this file.
1#pragma once
2
5
6#include <pulp/simd/simd.hpp>
7
8#include <algorithm>
9#include <cmath>
10#include <cstddef>
11#include <cstring>
12#include <numeric>
13#include <vector>
14
15namespace pulp::signal {
16
38template <typename SampleType = float>
40public:
43 static constexpr std::size_t kBlockCapacity = 512;
44
47 static constexpr std::size_t kInlineTaps = 8;
48
49 FirFilterT() = default;
50
52 void set_coefficients(std::vector<SampleType> coeffs) {
53 coefficients_ = std::move(coeffs);
54 reversed_.assign(coefficients_.rbegin(), coefficients_.rend());
55 const std::size_t taps = coefficients_.size();
56 history_.assign(taps == 0 ? 0 : (taps - 1) + kBlockCapacity, SampleType{0});
57 head_ = taps == 0 ? 0 : taps - 1;
58 }
59
61 int order() const { return static_cast<int>(coefficients_.size()); }
62
64 const std::vector<SampleType>& coefficients() const noexcept {
65 return coefficients_;
66 }
67
69 SampleType process(SampleType input) {
70 const std::size_t taps = coefficients_.size();
71 if (taps == 0)
72 return input;
73 if (head_ == history_.size())
74 compact();
75 history_[head_] = input;
76 const SampleType* window = history_.data() + head_ + 1 - taps;
77 ++head_;
78 if (taps <= kInlineTaps) {
79 SampleType output = SampleType{0};
80 for (std::size_t k = 0; k < taps; ++k)
81 output += window[k] * reversed_[k];
82 return output;
83 }
84 return pulp::simd::dot(window, reversed_.data(), taps);
85 }
86
88 void process(SampleType* data, int num_samples) {
89 process(data, data, num_samples);
90 }
91
98 void process(const SampleType* input, SampleType* output, int num_samples) {
99 const std::size_t taps = coefficients_.size();
100 if (num_samples <= 0)
101 return;
102 if (taps == 0) {
103 if (input != output)
104 std::copy_n(input, num_samples, output);
105 return;
106 }
107 const auto total = static_cast<std::size_t>(num_samples);
108 std::size_t done = 0;
109 while (done < total) {
110 if (head_ == history_.size())
111 compact();
112 const std::size_t count = std::min(history_.size() - head_, total - done);
113 std::copy_n(input + done, count, history_.data() + head_);
114 pulp::simd::correlate(history_.data() + head_ + 1 - taps, reversed_.data(),
115 output + done, count, taps);
116 head_ += count;
117 done += count;
118 }
119 }
120
122 void reset() {
123 std::fill(history_.begin(), history_.end(), SampleType{0.0f});
124 head_ = coefficients_.empty() ? 0 : coefficients_.size() - 1;
125 }
126
127 // ── Coefficient generators ──────────────────────────────────────────
128
133 static std::vector<SampleType> lowpass(
134 int num_taps, SampleType cutoff_hz, SampleType sample_rate) {
135 std::vector<SampleType> h(static_cast<size_t>(num_taps));
136 SampleType fc = cutoff_hz / sample_rate;
137 int m = num_taps - 1;
138 constexpr SampleType pi = SampleType{3.14159265358979323846f};
139
140 for (int i = 0; i <= m; ++i) {
141 SampleType n = static_cast<SampleType>(i) -
142 static_cast<SampleType>(m) / SampleType{2.0f};
143 if (std::abs(n) < SampleType{1e-6f}) {
144 h[static_cast<size_t>(i)] = SampleType{2.0f} * fc;
145 } else {
146 h[static_cast<size_t>(i)] =
147 std::sin(SampleType{2.0f} * pi * fc * n) / (pi * n);
148 }
149 // Hamming window
150 SampleType w = SampleType{0.54f} -
151 SampleType{0.46f} *
152 std::cos(SampleType{2.0f} * pi * static_cast<SampleType>(i) /
153 static_cast<SampleType>(m));
154 h[static_cast<size_t>(i)] *= w;
155 }
156
157 // Normalize
158 SampleType sum = std::accumulate(h.begin(), h.end(), SampleType{0.0f});
159 if (sum > SampleType{0.0f}) {
160 for (auto& v : h) v /= sum;
161 }
162 return h;
163 }
164
166 static std::vector<SampleType> highpass(
167 int num_taps, SampleType cutoff_hz, SampleType sample_rate) {
168 auto h = lowpass(num_taps, cutoff_hz, sample_rate);
169 for (auto& v : h) v = -v;
170 h[static_cast<size_t>(num_taps / 2)] += SampleType{1.0f};
171 return h;
172 }
173
174private:
175 // Keeps the newest taps - 1 inputs and makes room at the end.
176 void compact() noexcept {
177 const std::size_t keep = coefficients_.size() - 1;
178 std::copy(history_.begin() + static_cast<std::ptrdiff_t>(head_ - keep),
179 history_.begin() + static_cast<std::ptrdiff_t>(head_), history_.begin());
180 head_ = keep;
181 }
182
183 std::vector<SampleType> coefficients_;
184 std::vector<SampleType> reversed_;
185 // [0, head_) holds past input, oldest first; head_ >= taps - 1 always.
186 std::vector<SampleType> history_;
187 std::size_t head_ = 0;
188};
189
192
193} // namespace pulp::signal
FIR filter with arbitrary coefficients.
Definition fir_filter.hpp:39
int order() const
Get current coefficient count (filter order + 1).
Definition fir_filter.hpp:61
static std::vector< SampleType > highpass(int num_taps, SampleType cutoff_hz, SampleType sample_rate)
Generate highpass FIR coefficients using spectral inversion.
Definition fir_filter.hpp:166
static std::vector< SampleType > lowpass(int num_taps, SampleType cutoff_hz, SampleType sample_rate)
Generate lowpass FIR coefficients using windowed-sinc method.
Definition fir_filter.hpp:133
static constexpr std::size_t kBlockCapacity
Definition fir_filter.hpp:43
static constexpr std::size_t kInlineTaps
Definition fir_filter.hpp:47
void process(SampleType *data, int num_samples)
Process a buffer of samples in-place.
Definition fir_filter.hpp:88
SampleType process(SampleType input)
Process a single sample.
Definition fir_filter.hpp:69
const std::vector< SampleType > & coefficients() const noexcept
The taps in time order (h[0] multiplies the newest input).
Definition fir_filter.hpp:64
void reset()
Reset the internal delay buffer to zero.
Definition fir_filter.hpp:122
void set_coefficients(std::vector< SampleType > coeffs)
Set filter coefficients. The number of taps equals coefficients.size().
Definition fir_filter.hpp:52
void process(const SampleType *input, SampleType *output, int num_samples)
Process a buffer into a separate output buffer without an intermediate copy.
Definition fir_filter.hpp:98
Definition additive_bank.hpp:218
@ i
Definition formant_filter_bank.hpp:20
FirFilterT< double > FirFilter64
Definition fir_filter.hpp:191
FirFilterT< float > FirFilter
Definition fir_filter.hpp:190