MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Filter.cpp
Go to the documentation of this file.
1#include "Filter.hpp"
2
3#include <algorithm>
4
6
7std::pair<int, int> shift_parser(const std::string& str)
8{
9 size_t underscore = str.find('_');
10 if (underscore == std::string::npos) {
11 throw std::invalid_argument("Invalid format. Supply numberical format of nInputs_nOutpits like 25_2");
12 }
13
14 int inputs = std::stoi(str.substr(0, underscore));
15 int outputs = std::stoi(str.substr(underscore + 1));
16 return std::make_pair(inputs, outputs);
17}
18
19Filter::Filter(const std::shared_ptr<Node>& input, const std::string& zindex_shifts)
20 : m_input_node(input)
21 , m_context(0.0, m_input_history, m_output_history, m_coef_a, m_coef_b)
22 , m_context_gpu(0.0, m_input_history, m_output_history, m_coef_a, m_coef_b, get_gpu_data_buffer())
23{
24 m_shift_config = shift_parser(zindex_shifts);
26 m_coef_b.resize(m_input_history.size(), 1);
27 m_coef_a.resize(m_output_history.size(), 1);
28}
29
30Filter::Filter(const std::shared_ptr<Node>& input, const std::vector<double>& a_coef, const std::vector<double>& b_coef)
31 : m_input_node(input)
32 , m_coef_a(a_coef)
33 , m_coef_b(b_coef)
34 , m_context(0.0, m_input_history, m_output_history, m_coef_a, m_coef_b)
35 , m_context_gpu(0.0, m_input_history, m_output_history, m_coef_a, m_coef_b, get_gpu_data_buffer())
36{
37 m_shift_config = shift_parser(std::to_string(b_coef.size() - 1) + "_" + std::to_string(a_coef.size() - 1));
38
39 if (m_coef_a.empty() || m_coef_b.empty()) {
40 throw std::invalid_argument("IIR coefficients cannot be empty");
41 }
42 if (m_coef_a[0] == 0.0F) {
43 throw std::invalid_argument("First denominator coefficient (a[0]) cannot be zero");
44 }
45
47}
48
49Filter::Filter(const std::vector<double>& a_coef, const std::vector<double>& b_coef)
50 : Filter(nullptr, a_coef, b_coef)
51{
52}
53
55{
56 m_input_history.resize(m_shift_config.first + 1, 0.0F);
57 m_output_history.resize(m_shift_config.second + 1, 0.0F);
58}
59
60void Filter::set_coefs(const std::vector<double>& new_coefs, coefficients type)
61{
62 if (type == coefficients::OUTPUT) {
63 setACoefficients(new_coefs);
64 } else if (type == coefficients::INPUT) {
65 setBCoefficients(new_coefs);
66 } else {
67 setACoefficients(new_coefs);
68 setBCoefficients(new_coefs);
69 }
70}
71
72void Filter::build_input_history(double current_sample)
73{
75 size_t available = m_external_input_context.size();
76 size_t lookback = std::min(available, m_input_history.size() - 1);
77
78 m_input_history[0] = current_sample;
79
80 for (size_t i = 0; i < lookback; ++i) {
81 m_input_history[i + 1] = m_external_input_context[available - 1 - i];
82 }
83 } else {
84 update_inputs(current_sample);
85 }
86}
87
88void Filter::update_inputs(double current_sample)
89{
90 for (unsigned int i = m_input_history.size() - 1; i > 0; i--) {
92 }
93 m_input_history[0] = current_sample;
94}
95
96void Filter::update_outputs(double current_sample)
97{
98 m_output_history[0] = current_sample;
99 for (unsigned int i = m_output_history.size() - 1; i > 0; i--) {
101 }
102}
103
104void Filter::setACoefficients(const std::vector<double>& new_coefs)
105{
106 if (new_coefs.empty()) {
107 throw std::invalid_argument("Denominator coefficients cannot be empty");
108 }
109 if (new_coefs[0] == 0.0F) {
110 throw std::invalid_argument("First denominator coefficient (a[0]) cannot be zero");
111 }
112
113 m_coef_a = new_coefs;
114
115 if (static_cast<int>(m_coef_a.size()) - 1 != m_shift_config.second) {
116 m_shift_config.second = static_cast<int>(m_coef_a.size()) - 1;
118 }
119}
120
121void Filter::setBCoefficients(const std::vector<double>& new_coefs)
122{
123 if (new_coefs.empty()) {
124 throw std::invalid_argument("Numerator coefficients cannot be empty");
125 }
126
127 m_coef_b = new_coefs;
128
129 if (static_cast<int>(m_coef_b.size()) - 1 != m_shift_config.first) {
130 m_shift_config.first = static_cast<int>(m_coef_b.size()) - 1;
132 }
133}
134
135void Filter::update_coefs_from_node(int length, const std::shared_ptr<Node>& source, coefficients type)
136{
137 std::vector<double> samples = source->process_batch(length);
138 set_coefs(samples, type);
139}
140
142{
143 if (m_input_node) {
144 std::vector<double> samples = m_input_node->process_batch(length);
145 set_coefs(samples, type);
146 } else {
147 std::cerr << "No input node set for Filter. Use Filter::setInputNode() to set an input node.\n Alternatively, use Filter::updateCoefficientsFromNode() to specify a different source node.\n";
148 }
149}
150
151void Filter::add_coef_internal(uint64_t index, double value, std::vector<double>& buffer)
152{
153 if (index > buffer.size()) {
154 buffer.resize(index + 1, 1.F);
155 }
156 buffer.at(index) = value;
157}
158
159void Filter::add_coef(int index, double value, coefficients type)
160{
161 switch (type) {
163 add_coef_internal(index, value, m_coef_a);
164 break;
166 add_coef_internal(index, value, m_coef_b);
167 break;
168 default:
169 add_coef_internal(index, value, m_coef_a);
170 add_coef_internal(index, value, m_coef_b);
171 break;
172 }
173}
174
176{
177 std::ranges::fill(m_input_history, 0.0);
178 std::ranges::fill(m_output_history, 0.0);
179}
180
182{
183 if (type == coefficients::OUTPUT || type == coefficients::ALL) {
184 if (!m_coef_a.empty() && m_coef_a[0] != 0.0) {
185 double a0 = m_coef_a[0];
186 for (auto& coef : m_coef_a) {
187 coef /= a0;
188 }
189 }
190 }
191
192 if (type == coefficients::INPUT || type == coefficients::ALL) {
193 if (!m_coef_b.empty()) {
194 double max_coef = 0.0;
195 for (const auto& coef : m_coef_b) {
196 max_coef = std::max(max_coef, std::abs(coef));
197 }
198
199 if (max_coef > 0.0) {
200 for (auto& coef : m_coef_b) {
201 coef /= max_coef;
202 }
203 }
204 }
205 }
206}
207
208std::complex<double> Filter::get_frequency_response(double frequency, double sample_rate) const
209{
210 double omega = 2.0 * M_PI * frequency / sample_rate;
211 std::complex<double> z = std::exp(std::complex<double>(0, omega));
212
213 std::complex<double> numerator = 0.0;
214 for (size_t i = 0; i < m_coef_b.size(); ++i) {
215 numerator += m_coef_b[i] * std::pow(z, -static_cast<int>(i));
216 }
217
218 std::complex<double> denominator = 0.0;
219 for (size_t i = 0; i < m_coef_a.size(); ++i) {
220 denominator += m_coef_a[i] * std::pow(z, -static_cast<int>(i));
221 }
222
223 return numerator / denominator;
224}
225
226double Filter::get_magnitude_response(double frequency, double sample_rate) const
227{
228 return std::abs(get_frequency_response(frequency, sample_rate));
229}
230
231double Filter::get_phase_response(double frequency, double sample_rate) const
232{
233 return std::arg(get_frequency_response(frequency, sample_rate));
234}
235
236std::vector<double> Filter::process_batch(unsigned int num_samples)
237{
238 std::vector<double> output(num_samples);
239 for (unsigned int i = 0; i < num_samples; ++i) {
240 output[i] = process_sample(0.0); // The input value doesn't matter since we have an input node
241 }
242 return output;
243}
244
245void Filter::update_context(double value)
246{
247 if (m_gpu_compatible) {
248 m_context_gpu.value = value;
249 } else {
250 m_context.value = value;
251 }
252}
253
254void Filter::notify_tick(double value)
255{
256 update_context(value);
257 auto& ctx = get_last_context();
258
259 for (auto& callback : m_callbacks) {
260 callback(ctx);
261 }
262 for (auto& [callback, condition] : m_conditional_callbacks) {
263 if (condition(ctx)) {
264 callback(ctx);
265 }
266 }
267}
268
270{
271 if (m_gpu_compatible) {
272 return m_context_gpu;
273 }
274 return m_context;
275}
276
277}
void add_coef_internal(uint64_t index, double value, std::vector< double > &buffer)
Modifies a specific coefficient in a coefficient buffer.
Definition Filter.cpp:151
void add_coef(int index, double value, coefficients type=coefficients::ALL)
Modifies a specific coefficient.
Definition Filter.cpp:159
std::vector< double > process_batch(unsigned int num_samples) override
Calculates the phase response at a specific frequency.
Definition Filter.cpp:236
virtual void reset()
Resets the filter's internal state.
Definition Filter.cpp:175
void build_input_history(double current_sample)
Builds input history from external context or internal accumulation.
Definition Filter.cpp:72
std::vector< double > m_coef_b
Feedforward (numerator) coefficients.
Definition Filter.hpp:630
std::vector< double > m_output_history
Buffer storing previous output samples.
Definition Filter.hpp:605
virtual void update_outputs(double current_sample)
Updates the output history buffer with a new sample.
Definition Filter.cpp:96
std::shared_ptr< Node > m_input_node
The most recent sample value generated by this oscillator.
Definition Filter.hpp:581
void update_coef_from_input(int length, coefficients type=coefficients::ALL)
Updates coefficients from the filter's own input.
Definition Filter.cpp:141
virtual void initialize_shift_buffers()
Initializes the input and output history buffers.
Definition Filter.cpp:54
std::complex< double > get_frequency_response(double frequency, double sample_rate) const
Calculates the complex frequency response at a specific frequency.
Definition Filter.cpp:208
void set_coefs(const std::vector< double > &new_coefs, coefficients type=coefficients::ALL)
Updates filter coefficients.
Definition Filter.cpp:60
Filter(const std::shared_ptr< Node > &input, const std::string &zindex_shifts)
Constructor using string-based filter configuration.
Definition Filter.cpp:19
void normalize_coefficients(coefficients type=coefficients::ALL)
Normalizes filter coefficients.
Definition Filter.cpp:181
void setACoefficients(const std::vector< double > &new_coefs)
Updates the feedback (denominator) coefficients.
Definition Filter.cpp:104
std::vector< double > m_input_history
Buffer storing previous input samples.
Definition Filter.hpp:597
void setBCoefficients(const std::vector< double > &new_coefs)
Updates the feedforward (numerator) coefficients.
Definition Filter.cpp:121
virtual void update_inputs(double current_sample)
Updates the input history buffer with a new sample.
Definition Filter.cpp:88
void update_coefs_from_node(int length, const std::shared_ptr< Node > &source, coefficients type=coefficients::ALL)
Updates coefficients from another node's output.
Definition Filter.cpp:135
void notify_tick(double value) override
Notifies all registered callbacks with the current filter context.
Definition Filter.cpp:254
FilterContextGpu m_context_gpu
Definition Filter.hpp:654
std::pair< int, int > m_shift_config
Configuration for input and output buffer sizes.
Definition Filter.hpp:589
std::span< double > m_external_input_context
External input context for input history.
Definition Filter.hpp:614
double get_phase_response(double frequency, double sample_rate) const
Calculates the phase response at a specific frequency.
Definition Filter.cpp:231
void update_context(double value) override
Updates filter-specific context object.
Definition Filter.cpp:245
std::vector< double > m_coef_a
Feedback (denominator) coefficients.
Definition Filter.hpp:622
double get_magnitude_response(double frequency, double sample_rate) const
Calculates the magnitude response at a specific frequency.
Definition Filter.cpp:226
double process_sample(double input=0.) override=0
Processes a single sample through the filter.
NodeContext & get_last_context() override
Gets the last created context object.
Definition Filter.cpp:269
Base class for computational signal transformers implementing difference equations.
Definition Filter.hpp:145
double value
Current sample value.
Definition Node.hpp:40
Base context class for node callbacks.
Definition Node.hpp:30
std::vector< NodeHook > m_callbacks
Collection of standard callback functions.
Definition Node.hpp:403
std::vector< std::pair< NodeHook, NodeCondition > > m_conditional_callbacks
Collection of conditional callback functions with their predicates.
Definition Node.hpp:413
bool m_gpu_compatible
Flag indicating if the node supports GPU processing This flag is set by derived classes to indicate w...
Definition Node.hpp:383
std::pair< int, int > shift_parser(const std::string &str)
Parses a string representation of filter order into input/output shift configuration.
Definition Filter.cpp:7