MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Filter.hpp
Go to the documentation of this file.
1#pragma once
2
4
6
7enum coefficients : uint8_t {
10 ALL
11};
12
13/**
14 * @class FilterContext
15 * @brief Specialized context for filter node callbacks
16 *
17 * FilterContext extends the base NodeContext to provide filter-specific
18 * information to callbacks. It includes references to the filter's internal
19 * state, including input and output history buffers and coefficient vectors.
20 *
21 * This rich context enables callbacks to perform sophisticated analysis and
22 * monitoring of filter behavior, such as:
23 * - Detecting resonance conditions or instability
24 * - Analyzing filter response characteristics in real-time
25 * - Implementing adaptive processing based on filter state
26 * - Visualizing filter behavior through external interfaces
27 * - Recording filter state for later analysis or debugging
28 */
29class MAYAFLUX_API FilterContext : public NodeContext {
30public:
31 /**
32 * @brief Constructs a FilterContext with the current filter state
33 * @param value Current output sample value
34 * @param input_history Reference to the filter's input history buffer
35 * @param output_history Reference to the filter's output history buffer
36 * @param coefs_a Reference to the filter's feedback coefficients
37 * @param coefs_b Reference to the filter's feedforward coefficients
38 *
39 * Creates a context object that provides a complete snapshot of the
40 * filter's current state, including its most recent output value,
41 * history buffers, and coefficient vectors.
42 */
43 FilterContext(double value,
44 const std::vector<double>& input_history,
45 const std::vector<double>& output_history,
46 const std::vector<double>& coefs_a,
47 const std::vector<double>& coefs_b)
48 : NodeContext(value)
49 , input_history(input_history)
50 , output_history(output_history)
51 , coefs_a(coefs_a)
52 , coefs_b(coefs_b)
53 {
54 }
55
56 /**
57 * @brief Current input history buffer
58 *
59 * Contains the most recent input samples processed by the filter,
60 * with the newest sample at index 0. The size of this buffer depends
61 * on the filter's feedforward path configuration.
62 */
63 const std::vector<double>& input_history;
64
65 /**
66 * @brief Current output history buffer
67 *
68 * Contains the most recent output samples processed by the filter,
69 * with the newest sample at index 0. The size of this buffer depends
70 * on the filter's feedback path configuration.
71 */
72 const std::vector<double>& output_history;
73
74 /**
75 * @brief Current coefficients for input
76 */
77 const std::vector<double>& coefs_a;
78
79 /**
80 * @brief Current coefficients for output
81 */
82 const std::vector<double>& coefs_b;
83};
84
85/**
86 * @class FilterContextGpu
87 * @brief GPU-augmented filter context for callbacks
88 *
89 * Extends FilterContext to include GPU-uploadable data, allowing
90 * callbacks to access both CPU-side filter state and GPU-resident
91 * data for advanced processing and visualization.
92 */
93class MAYAFLUX_API FilterContextGpu : public FilterContext, public GpuVectorData {
94public:
95 FilterContextGpu(double value,
96 const std::vector<double>& input_history,
97 const std::vector<double>& output_history,
98 const std::vector<double>& coefs_a,
99 const std::vector<double>& coefs_b,
100 std::span<const float> gpu_data)
101 : FilterContext(value, input_history, output_history, coefs_a, coefs_b)
102 , GpuVectorData(gpu_data)
103 {
104 }
105
106 friend class Filter;
107
108 std::vector<float> gpu_float_buffer;
109};
110
111/**
112 * @class Filter
113 * @brief Base class for computational signal transformers implementing difference equations
114 *
115 * The Filter class provides a comprehensive framework for implementing digital
116 * transformations based on difference equations. It transcends traditional audio
117 * filtering concepts, offering a flexible foundation for:
118 *
119 * - Complex resonant systems for generative synthesis
120 * - Computational physical modeling components
121 * - Recursive signal transformation algorithms
122 * - Data-driven transformation design from frequency responses
123 * - Dynamic coefficient modulation for emergent behaviors
124 *
125 * At its core, the Filter implements the general difference equation:
126 * y[n] = (b₀x[n] + b₁x[n-1] + ... + bₘx[n-m]) - (a₁y[n-1] + ... + aₙy[n-n])
127 *
128 * Where:
129 * - x[n] are input values
130 * - y[n] are output values
131 * - b coefficients apply to input values (feedforward)
132 * - a coefficients apply to previous output values (feedback)
133 *
134 * This mathematical structure can represent virtually any linear time-invariant system,
135 * making it a powerful tool for signal transformation across domains. The same
136 * equations can process audio, control data, or even be applied to visual or
137 * physical simulation parameters.
138 */
139class MAYAFLUX_API Filter : public Node {
140public:
141 /**
142 * @brief Constructor using explicit coefficient vectors
143 * @param input Source node providing input samples
144 * @param a_coef Feedback (denominator) coefficients
145 * @param b_coef Feedforward (numerator) coefficients
146 *
147 * Creates a filter with the specified input node and coefficient vectors.
148 * This allows direct specification of filter coefficients for precise
149 * control over filter behavior.
150 */
151 Filter(const std::shared_ptr<Node>& input, const std::vector<double>& a_coef, const std::vector<double>& b_coef);
152
153 /**
154 * @brief Constructor using explicit coefficient vectors (no input node)
155 * @param a_coef Feedback (denominator) coefficients
156 * @param b_coef Feedforward (numerator) coefficients
157 *
158 * Creates a filter with the specified coefficient vectors but no input node.
159 * This can be used in scenarios where the filter operates on external data
160 * or is part of a larger processing chain.
161 */
162 Filter(const std::vector<double>& a_coef, const std::vector<double>& b_coef);
163
164 /**
165 * @brief Virtual destructor
166 */
167 ~Filter() override = default;
168
169 /**
170 * @brief Gets the current processing latency of the filter
171 * @return Latency in samples
172 *
173 * The latency is determined by the maximum of the input and output
174 * buffer sizes, representing how many samples of delay the filter introduces.
175 */
176 [[nodiscard]] inline int get_current_latency() const
177 {
178 return static_cast<int>(std::max(m_coef_b.size(), m_coef_a.size())) - 1;
179 }
180
181 /**
182 * @brief Updates filter coefficients
183 * @param new_coefs New coefficient values
184 * @param type Which set of coefficients to update (input, output, or both)
185 *
186 * Provides a flexible way to update filter coefficients, allowing
187 * dynamic modification of filter characteristics during processing.
188 */
189 void set_coefs(const std::vector<double>& new_coefs, coefficients type = coefficients::ALL);
190
191 /**
192 * @brief Updates coefficients from another node's output
193 * @param length Number of coefficients to update
194 * @param source Node providing coefficient values
195 * @param type Which set of coefficients to update (input, output, or both)
196 *
197 * Enables cross-domain interaction by deriving transformation coefficients from
198 * another node's output. This creates dynamic relationships between different
199 * parts of the computational graph, allowing one signal path to influence
200 * the behavior of another - perfect for generative systems where parameters
201 * evolve based on the system's own output.
202 */
203 void update_coefs_from_node(int length, const std::shared_ptr<Node>& source, coefficients type = coefficients::ALL);
204
205 /**
206 * @brief Updates coefficients from the filter's own input
207 * @param length Number of coefficients to update
208 * @param type Which set of coefficients to update (input, output, or both)
209 *
210 * Creates a self-modifying transformation where the input signal itself
211 * influences the transformation characteristics. This enables complex
212 * emergent behaviors and feedback systems where the signal's own properties
213 * determine how it will be processed, leading to evolving, non-linear responses.
214 */
215 void update_coef_from_input(int length, coefficients type = coefficients::ALL);
216
217 /**
218 * @brief Modifies a specific coefficient
219 * @param index Index of the coefficient to modify
220 * @param value New value for the coefficient
221 * @param type Which set of coefficients to update (input, output, or both)
222 *
223 * Allows precise control over individual coefficients, useful for
224 * fine-tuning filter behavior or implementing parameter automation.
225 */
226 void add_coef(int index, double value, coefficients type = coefficients::ALL);
227
228 /**
229 * @brief Resets the filter's internal state
230 *
231 * Clears the input and output history buffers, effectively
232 * resetting the filter to its initial state. This is useful
233 * when switching between audio segments to prevent artifacts.
234 */
235 virtual void reset();
236
237 /**
238 * @brief Sets the filter's output gain
239 * @param new_gain New gain value
240 *
241 * Adjusts the overall output level of the filter without
242 * changing its frequency response characteristics.
243 */
244 inline void set_gain(double new_gain) { m_gain = new_gain; }
245
246 /**
247 * @brief Gets the current gain value
248 * @return Current gain value
249 */
250 [[nodiscard]] inline double get_gain() const { return m_gain; }
251
252 /**
253 * @brief Enables or disables filter bypass
254 * @param enable True to enable bypass, false to disable
255 *
256 * When bypass is enabled, the filter passes input directly to output
257 * without applying any filtering, useful for A/B testing or
258 * temporarily disabling filters.
259 */
260 inline void set_bypass(bool enable) { m_bypass_enabled = enable; }
261
262 /**
263 * @brief Checks if bypass is currently enabled
264 * @return True if bypass is enabled, false otherwise
265 */
266 [[nodiscard]] inline bool is_bypass_enabled() const { return m_bypass_enabled; }
267
268 /**
269 * @brief Gets the filter's order
270 * @return Maximum order of the filter
271 *
272 * The filter order is determined by the maximum of the input and output
273 * coefficient counts minus one, representing the highest power of z⁻¹
274 * in the filter's transfer function.
275 */
276 [[nodiscard]] inline int get_order() const { return std::max(m_coef_a.size() - 1, m_coef_b.size() - 1); }
277
278 /**
279 * @brief Gets the input history buffer
280 * @return Constant reference to the input history vector
281 *
282 * Provides access to the filter's internal input history buffer,
283 * useful for analysis and visualization.
284 */
285 [[nodiscard]] inline const std::vector<double>& get_input_history() const { return m_input_history; }
286
287 /**
288 * @brief Gets the output history buffer
289 * @return Constant reference to the output history vector
290 *
291 * Provides access to the filter's internal output history buffer,
292 * useful for analysis and visualization.
293 */
294 [[nodiscard]] inline const std::vector<double>& get_output_history() const { return m_output_history; }
295
296 /**
297 * @brief Normalizes filter coefficients
298 * @param type Which set of coefficients to normalize (input, output, or both)
299 *
300 * Scales coefficients to ensure a[0] = 1.0 and/or maintain consistent
301 * gain at DC or Nyquist, depending on the filter type.
302 */
303 void normalize_coefficients(coefficients type = coefficients::ALL);
304
305 /**
306 * @brief Calculates the complex frequency response at a specific frequency
307 * @param frequency Frequency in Hz to analyze
308 * @param sample_rate Sample rate in Hz
309 * @return Complex frequency response (magnitude and phase)
310 *
311 * Computes the transformation's complex response at the specified frequency.
312 * This mathematical analysis can be used for visualization, further algorithmic
313 * processing, or to inform cross-domain mappings between audio properties
314 * and other computational parameters.
315 */
316 [[nodiscard]] std::complex<double> get_frequency_response(double frequency, double sample_rate) const;
317
318 /**
319 * @brief Calculates the magnitude response at a specific frequency
320 * @param frequency Frequency in Hz to analyze
321 * @param sample_rate Sample rate in Hz
322 * @return Magnitude response in linear scale
323 *
324 * Computes the filter's magnitude response at the specified frequency,
325 * representing how much the filter amplifies or attenuates that frequency.
326 */
327 [[nodiscard]] double get_magnitude_response(double frequency, double sample_rate) const;
328
329 /**
330 * @brief Calculates the phase response at a specific frequency
331 * @param frequency Frequency in Hz to analyze
332 * @param sample_rate Sample rate in Hz
333 * @return Phase response in radians
334 *
335 * Computes the filter's phase response at the specified frequency,
336 * representing the phase shift introduced by the filter at that frequency.
337 */
338 [[nodiscard]] double get_phase_response(double frequency, double sample_rate) const;
339
340 /**
341 * @brief Processes a single sample through the filter
342 * @param input The input sample
343 * @return The filtered output sample
344 *
345 * This is the core processing method that implements the difference
346 * equation for a single sample. It must be implemented by derived
347 * filter classes to define their specific filtering behavior.
348 */
349 double process_sample(double input = 0.) override = 0;
350
351 /**
352 * @brief Calculates the phase response at a specific frequency
353 * @param frequency Frequency in Hz to analyze
354 * @param sample_rate Sample rate in Hz
355 * @return Phase response in radians
356 *
357 * Computes the filter's phase response at the specified frequency,
358 * representing the phase shift introduced by the filter at that frequency.
359 */
360 std::vector<double> process_batch(unsigned int num_samples) override;
361
362 /**
363 * @brief Sets the input node for the filter
364 * @param input_node Node providing input samples
365 *
366 * Connects the filter to a source of input samples, allowing
367 * filters to be chained together or connected to generators.
368 */
369 inline void set_input_node(const std::shared_ptr<Node>& input_node) { m_input_node = input_node; }
370
371 /**
372 * @brief Gets the input node for the filter
373 * @return Node providing input samples
374 */
375 inline std::shared_ptr<Node> get_input_node() { return m_input_node; }
376
377 /**
378 * @brief Updates the feedback (denominator) coefficients
379 * @param new_coefs New coefficient values
380 *
381 * Sets the 'a' coefficients in the difference equation, which
382 * are applied to previous output samples. The method ensures
383 * proper normalization and buffer sizing.
384 */
385 void setACoefficients(const std::vector<double>& new_coefs);
386
387 /**
388 * @brief Updates the feedforward (numerator) coefficients
389 * @param new_coefs New coefficient values
390 *
391 * Sets the 'b' coefficients in the difference equation, which
392 * are applied to current and previous input samples. The method
393 * ensures proper buffer sizing.
394 */
395 void setBCoefficients(const std::vector<double>& new_coefs);
396
397 /**
398 * @brief Gets the feedback (denominator) coefficients
399 * @return Constant reference to the 'a' coefficient vector
400 *
401 * Provides access to the filter's feedback coefficients for
402 * analysis and visualization.
403 */
404 [[nodiscard]] inline const std::vector<double>& getACoefficients() const { return m_coef_a; }
405
406 /**
407 * @brief Gets the feedforward (numerator) coefficients
408 * @return Constant reference to the 'b' coefficient vector
409 *
410 * Provides access to the filter's feedforward coefficients for
411 * analysis and visualization.
412 */
413 [[nodiscard]] inline const std::vector<double>& getBCoefficients() const { return m_coef_b; }
414
415 /**
416 * @brief Provide external buffer context for input history
417 * @param context View into buffer data to use instead of internal input accumulation
418 */
419 inline void set_input_context(std::span<double> context)
420 {
421 m_external_input_context = context;
422 m_use_external_input_context = true;
423 }
424
425 /**
426 * @brief Clear external input context, resume internal accumulation
427 */
429 {
430 m_use_external_input_context = false;
431 m_external_input_context = {};
432 }
433
434 [[nodiscard]] inline bool using_external_input_context() const
435 {
436 return m_use_external_input_context;
437 }
438
439 /**
440 * @brief Gets the last created context object
441 * @return Reference to the last FilterContext object
442 */
443 NodeContext& get_last_context() override;
444
445 void set_gpu_compatible(bool compatible) override
446 {
447 Node::set_gpu_compatible(compatible);
448 if (compatible) {
449 m_node_capability |= NodeCapability::VECTOR;
450 } else {
451 m_node_capability &= ~NodeCapability::VECTOR;
452 }
453 }
454
455 /**
456 * @brief Registers a callback to be called on each tick with the filter context
457 * @param callback Typed hook that receives a FilterContext object
458 */
459 void on_tick(const TypedHook<FilterContext>& callback);
460
461 /**
462 * @brief Registers a conditional callback to be called on each tick if the condition is met
463 * @param condition NodeCondition that determines whether the callback should be called
464 * @param callback Typed hook that receives a FilterContext object
465 */
466 void on_tick_if(const NodeCondition& condition, const TypedHook<FilterContext>& callback);
467
468 /**
469 * @brief Retrieves the current modulators connected to this node
470 * @return Vector of pairs containing the modulator role and the corresponding node
471 */
472 [[nodiscard]] std::vector<std::pair<ModulatorRole, std::shared_ptr<Node>>> get_modulators() const override;
473
474protected:
475 /**
476 * @brief Modifies a specific coefficient in a coefficient buffer
477 * @param index Index of the coefficient to modify
478 * @param value New value for the coefficient
479 * @param buffer Reference to the coefficient buffer to modify
480 *
481 * Internal implementation for adding or modifying a coefficient
482 * in either the 'a' or 'b' coefficient vectors.
483 */
484 void add_coef_internal(uint64_t index, double value, std::vector<double>& buffer);
485
486 /**
487 * @brief Updates the input history buffer with a new sample
488 * @param current_sample The new input sample
489 *
490 * Shifts the input history buffer and adds the new sample at the
491 * beginning. This maintains the history of input samples needed
492 * for the filter's feedforward path.
493 */
494 virtual void update_inputs(double current_sample);
495
496 /**
497 * @brief Updates the output history buffer with a new sample
498 * @param current_sample The new output sample
499 *
500 * Shifts the output history buffer and adds the new sample at the
501 * beginning. This maintains the history of output samples needed
502 * for the filter's feedback path.
503 */
504 virtual void update_outputs(double current_sample);
505
506 /**
507 * @brief Updates filter-specific context object
508 * @param value The current output sample value
509 *
510 * Updates FilterContext object that contains information about the filter's
511 * current state, including the current sample value, input/output history buffers,
512 * and coefficients. This context is passed to callbacks and conditions to provide
513 * them with the information they need to execute properly.
514 *
515 * The FilterContext allows callbacks to access filter-specific information
516 * beyond just the current sample value, enabling more sophisticated monitoring
517 * and analysis of filter behavior.
518 */
519 void update_context(double value) override;
520
521 /**
522 * @brief Notifies all registered callbacks with the current filter context
523 * @param value The current output sample value
524 *
525 * This method is called by the filter implementation when a new output value
526 * is produced. It creates a FilterContext object using create_context(), then
527 * calls all registered callbacks with that context.
528 *
529 * For unconditional callbacks (registered with on_tick()), the callback
530 * is always called. For conditional callbacks (registered with on_tick_if()),
531 * the callback is called only if its condition returns true.
532 *
533 * Filter implementations should call this method at appropriate points in their
534 * processing flow to trigger callbacks, typically after computing a new output value.
535 */
536 void notify_tick(double value) override;
537
538 /**
539 * @brief Builds input history from external context or internal accumulation
540 * @param current_sample Current input sample being processed
541 */
542 void build_input_history(double current_sample);
543
544 /**
545 * @brief The most recent sample value generated by this oscillator
546 *
547 * This value is updated each time process_sample() is called and can be
548 * accessed via get_last_output() without triggering additional processing.
549 * It's useful for monitoring the oscillator's state and for implementing
550 * feedback loops.
551 */
552 // double m_last_output;
553
554 /**
555 * @brief Input node providing samples to filter
556 *
557 * The filter processes samples from this node, allowing filters
558 * to be chained together or connected to generators.
559 */
560 std::shared_ptr<Node> m_input_node;
561
562 /**
563 * @brief Buffer storing previous input samples
564 *
565 * Maintains a history of input samples needed for the filter's
566 * feedforward path (b coefficients).
567 */
568 std::vector<double> m_input_history;
569
570 /**
571 * @brief Buffer storing previous output samples
572 *
573 * Maintains a history of output samples needed for the filter's
574 * feedback path (a coefficients).
575 */
576 std::vector<double> m_output_history;
577
578 /**
579 * @brief External input context for input history
580 *
581 * If set, the filter uses this external context for input history
582 * instead of its internal buffer. This allows sharing input history
583 * across multiple filters or nodes or from AudioBuffer sources.
584 */
585 std::span<double> m_external_input_context;
586
587 /**
588 * @brief Feedback (denominator) coefficients
589 *
590 * The 'a' coefficients in the difference equation, applied to
591 * previous output samples. a[0] is typically normalized to 1.0.
592 */
593 std::vector<double> m_coef_a;
594
595 /**
596 * @brief Feedforward (numerator) coefficients
597 *
598 * The 'b' coefficients in the difference equation, applied to
599 * current and previous input samples.
600 */
601 std::vector<double> m_coef_b;
602
603 /**
604 * @brief Overall gain factor applied to the filter output
605 *
606 * Provides a simple way to adjust the filter's output level
607 * without changing its frequency response characteristics.
608 */
609 double m_gain = 1.0;
610
611 /**
612 * @brief Flag to bypass filter processing
613 *
614 * When enabled, the filter passes input directly to output
615 * without applying any filtering.
616 */
617 bool m_bypass_enabled {};
618
619 std::vector<double> m_saved_input_history;
620 std::vector<double> m_saved_output_history;
621
622 bool m_use_external_input_context {};
623
626};
627}
Core::GlobalInputConfig input
Definition Config.cpp:36
double frequency
FilterContextGpu(double value, const std::vector< double > &input_history, const std::vector< double > &output_history, const std::vector< double > &coefs_a, const std::vector< double > &coefs_b, std::span< const float > gpu_data)
Definition Filter.hpp:95
GPU-augmented filter context for callbacks.
Definition Filter.hpp:93
const std::vector< double > & coefs_b
Current coefficients for output.
Definition Filter.hpp:82
const std::vector< double > & input_history
Current input history buffer.
Definition Filter.hpp:63
const std::vector< double > & coefs_a
Current coefficients for input.
Definition Filter.hpp:77
FilterContext(double value, const std::vector< double > &input_history, const std::vector< double > &output_history, const std::vector< double > &coefs_a, const std::vector< double > &coefs_b)
Constructs a FilterContext with the current filter state.
Definition Filter.hpp:43
const std::vector< double > & output_history
Current output history buffer.
Definition Filter.hpp:72
Specialized context for filter node callbacks.
Definition Filter.hpp:29
void set_gain(double new_gain)
Sets the filter's output gain.
Definition Filter.hpp:244
std::vector< double > m_saved_output_history
Definition Filter.hpp:620
void set_gpu_compatible(bool compatible) override
Sets whether the node is compatible with GPU processing.
Definition Filter.hpp:445
void set_input_context(std::span< double > context)
Provide external buffer context for input history.
Definition Filter.hpp:419
std::vector< double > m_coef_b
Feedforward (numerator) coefficients.
Definition Filter.hpp:601
std::vector< double > m_output_history
Buffer storing previous output samples.
Definition Filter.hpp:576
const std::vector< double > & get_input_history() const
Gets the input history buffer.
Definition Filter.hpp:285
const std::vector< double > & getBCoefficients() const
Gets the feedforward (numerator) coefficients.
Definition Filter.hpp:413
int get_order() const
Gets the filter's order.
Definition Filter.hpp:276
bool is_bypass_enabled() const
Checks if bypass is currently enabled.
Definition Filter.hpp:266
std::shared_ptr< Node > m_input_node
The most recent sample value generated by this oscillator.
Definition Filter.hpp:560
std::vector< double > m_saved_input_history
Definition Filter.hpp:619
const std::vector< double > & getACoefficients() const
Gets the feedback (denominator) coefficients.
Definition Filter.hpp:404
~Filter() override=default
Virtual destructor.
void set_input_node(const std::shared_ptr< Node > &input_node)
Sets the input node for the filter.
Definition Filter.hpp:369
int get_current_latency() const
Gets the current processing latency of the filter.
Definition Filter.hpp:176
double get_gain() const
Gets the current gain value.
Definition Filter.hpp:250
std::vector< double > m_input_history
Buffer storing previous input samples.
Definition Filter.hpp:568
void clear_input_context()
Clear external input context, resume internal accumulation.
Definition Filter.hpp:428
FilterContextGpu m_context_gpu
Definition Filter.hpp:625
std::span< double > m_external_input_context
External input context for input history.
Definition Filter.hpp:585
const std::vector< double > & get_output_history() const
Gets the output history buffer.
Definition Filter.hpp:294
std::vector< double > m_coef_a
Feedback (denominator) coefficients.
Definition Filter.hpp:593
double process_sample(double input=0.) override=0
Processes a single sample through the filter.
void set_bypass(bool enable)
Enables or disables filter bypass.
Definition Filter.hpp:260
std::shared_ptr< Node > get_input_node()
Gets the input node for the filter.
Definition Filter.hpp:375
bool using_external_input_context() const
Definition Filter.hpp:434
Base class for computational signal transformers implementing difference equations.
Definition Filter.hpp:139
GPU-uploadable 1D array data interface.
Base context class for node callbacks.
Definition Node.hpp:53
Base interface for all computational processing nodes.
Definition Node.hpp:126
NodeCapability
Bitmask flags declaring what data shapes a node's context can produce.
Definition NodeSpec.hpp:104
std::function< void(ContextT &)> TypedHook
Callback function type for node processing events, parameterised on context type.
Definition NodeUtils.hpp:28
std::function< bool(NodeContext &)> NodeCondition
Predicate function type for conditional callbacks.
Definition NodeUtils.hpp:54