MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
NodeStructure.hpp
Go to the documentation of this file.
1#pragma once
2#include "Node.hpp"
3
4namespace MayaFlux::Nodes {
5
6/**
7 * @class ChainNode
8 * @brief Connects two nodes in series to form a processing chain
9 *
10 * The ChainNode implements the Node interface and represents a connection
11 * between two nodes where the output of the source node becomes the input
12 * to the target node. This is the implementation behind the '>>' operator
13 * for nodes.
14 *
15 * When processed, the ChainNode:
16 * 1. Passes the input to the source node
17 * 2. Takes the output from the source node
18 * 3. Passes that as input to the target node
19 * 4. Returns the output from the target node
20 */
21class MAYAFLUX_API ChainNode : public Node, public std::enable_shared_from_this<ChainNode> {
22public:
23 /**
24 * @brief Creates a new chain connecting source to target
25 * @param source The upstream node that processes input first
26 * @param target The downstream node that processes the source's output
27 */
28 ChainNode(const std::shared_ptr<Node>& source, const std::shared_ptr<Node>& target);
29
30 /**
31 * @brief Initializes the chain node
32 *
33 * This method performs necessary setup for the chain node, ensuring both
34 * the source and target nodes are properly initialized and registered.
35 * It should be called before the chain is used for processing to ensure
36 * correct signal flow through the connected nodes.
37 *
38 * Initialization is particularly important for chains to ensure that the
39 * signal path is properly established before processing begins.
40 */
41 void initialize();
42
43 /**
44 * @brief Processes a single sample through the chain
45 * @param input The input sample
46 * @return The output after processing through both nodes
47 *
48 * The input is first processed by the source node, and the result
49 * is then processed by the target node.
50 */
51 double process_sample(double input = 0.) override;
52
53 /**
54 * @brief Processes multiple samples through the chain
55 * @param num_samples Number of samples to process
56 * @return Vector of processed samples
57 *
58 * Each sample is processed through the source node and then
59 * through the target node.
60 */
61 std::vector<double> process_batch(unsigned int num_samples) override;
62
63 /**
64 * @brief Registers a callback for every output sample
65 * @param callback Function to call when a new sample is output
66 *
67 * This method delegates to the target node's on_tick method,
68 * ensuring that callbacks are triggered based on the final
69 * output of the chain rather than intermediate values.
70 * The callback receives the context from the target node.
71 */
72 inline void on_tick(const NodeHook& callback) override
73 {
74 m_Target->on_tick(callback);
75 }
76
77 /**
78 * @brief Registers a conditional callback for output samples
79 * @param callback Function to call when condition is met
80 * @param condition Predicate that determines when callback is triggered
81 *
82 * This method delegates to the target node's on_tick_if method,
83 * ensuring that conditional callbacks are evaluated based on the
84 * final output of the chain. The callback and condition both
85 * receive the context from the target node.
86 */
87 inline void on_tick_if(const NodeCondition& condition, const NodeHook& callback) override
88 {
89 m_Target->on_tick_if(condition, callback);
90 }
91
92 /**
93 * @brief Removes a previously registered callback
94 * @param callback The callback function to remove
95 * @return True if the callback was found and removed, false otherwise
96 *
97 * This method delegates to the target node's remove_hook method,
98 * removing the callback from the target node's notification system.
99 */
100 inline bool remove_hook(const NodeHook& callback) override
101 {
102 return m_Target->remove_hook(callback);
103 }
104
105 /**
106 * @brief Removes a previously registered conditional callback
107 * @param callback The condition function to remove
108 * @return True if the callback was found and removed, false otherwise
109 *
110 * This method delegates to the target node's remove_conditional_hook method,
111 * removing the conditional callback from the target node's notification system.
112 */
113 inline bool remove_conditional_hook(const NodeCondition& callback) override
114 {
115 return m_Target->remove_conditional_hook(callback);
116 }
117
118 /**
119 * @brief Removes all registered callbacks
120 *
121 * This method delegates to the target node's remove_all_hooks method,
122 * clearing all callbacks from the target node's notification system.
123 * After calling this method, no callbacks will be triggered for this chain.
124 */
125 inline void remove_all_hooks() override
126 {
127 m_Target->remove_all_hooks();
128 }
129
130 /**
131 * @brief Resets the processed state of the node and any attached input nodes
132 *
133 * This method is used by the processing system to reset the processed state
134 * of the node at the end of each processing cycle. This ensures that
135 * all nodes are marked as unprocessed before the cycle next begins, allowing
136 * the system to correctly identify which nodes need to be processed.
137 */
138 void reset_processed_state() override;
139
140 NodeContext& get_last_context() override;
141 void save_state() override;
142 void restore_state() override;
143
144protected:
145 /**
146 * @brief Empty implementation of notify_tick
147 * @param value The output value
148 *
149 * ChainNode doesn't implement its own notification system,
150 * instead delegating all callback handling to the target node.
151 * This method is a placeholder to satisfy the Node interface.
152 */
153 inline void notify_tick(double) override { }
154
155 /**
156 * @brief Empty implementation of update_context
157 * @param value The output value
158 *
159 * ChainNode doesn't create its own contexts for callbacks,
160 * instead relying on the target node to provide appropriate contexts.
161 * This method is a placeholder to satisfy the Node interface.
162 */
163 inline void update_context(double /*value*/) override { }
164
165private:
166 /**
167 * @brief The upstream node that processes input first
168 */
169 std::shared_ptr<Node> m_Source;
170
171 /**
172 * @brief The downstream node that processes the source's output
173 */
174 std::shared_ptr<Node> m_Target;
175
176 /**
177 * @brief Flag indicating whether the chain has been properly initialized
178 *
179 * This flag is set to true when both the source and target nodes have been
180 * registered for processing and the chain itself is registered. It's used
181 * to ensure that the chain doesn't attempt to process signals before all
182 * components are ready, preventing potential null pointer issues or
183 * processing inconsistencies.
184 */
186
187 bool m_state_saved {};
188
189public:
190 bool is_initialized() const;
191};
192
193/**
194 * @class BinaryOpContext
195 * @brief Specialized context for binary operation callbacks
196 *
197 * BinaryOpContext extends the base NodeContext to provide detailed information
198 * about a binary operation's inputs and output to callbacks. It includes the
199 * individual values from both the left and right nodes that were combined to
200 * produce the final output value.
201 *
202 * This rich context enables callbacks to perform sophisticated analysis and
203 * monitoring of signal combinations, such as:
204 * - Tracking the relative contributions of each input signal
205 * - Implementing adaptive responses based on input relationships
206 * - Detecting specific interaction patterns between signals
207 * - Creating visualizations that show both inputs and their combination
208 */
209class MAYAFLUX_API BinaryOpContext : public NodeContext {
210public:
211 /**
212 * @brief Constructs a BinaryOpContext with the current operation state
213 * @param value The combined output value
214 * @param lhs_value The value from the left-hand side node
215 * @param rhs_value The value from the right-hand side node
216 *
217 * Creates a context object that provides a complete snapshot of the
218 * binary operation's current state, including both input values and
219 * the resulting output value after combination.
220 */
221 BinaryOpContext(double value, double lhs_value, double rhs_value);
222
223 /**
224 * @brief The value from the left-hand side node
225 *
226 * This is the output value from the left node before combination.
227 * It allows callbacks to analyze the individual contribution of
228 * the left node to the final combined output.
229 */
230 double lhs_value;
231
232 /**
233 * @brief The value from the right-hand side node
234 *
235 * This is the output value from the right node before combination.
236 * It allows callbacks to analyze the individual contribution of
237 * the right node to the final combined output.
238 */
239 double rhs_value;
240};
241
242/**
243 * @class BinaryOpContextGpu
244 * @brief GPU-compatible context for binary operation callbacks
245 *
246 * BinaryOpContextGpu extends BinaryOpContext and implements GpuVectorData
247 * to provide GPU-compatible data for binary operation callbacks. It includes
248 * the individual values from both the left and right nodes, as well as a
249 * span of float data that can be uploaded to the GPU for efficient processing.
250 *
251 * This context enables GPU-accelerated callbacks to analyze and respond to
252 * binary operations in high-performance scenarios, such as:
253 * - Real-time audio processing on the GPU
254 * - Complex signal interactions in visual effects
255 * - High-throughput data transformations in compute shaders
256 */
257class MAYAFLUX_API BinaryOpContextGpu : public BinaryOpContext, public GpuVectorData {
258public:
259 BinaryOpContextGpu(double value, double lhs_value, double rhs_value, std::span<const float> gpu_data);
260};
261
262/**
263 * @class BinaryOpNode
264 * @brief Combines the outputs of two nodes using a binary operation
265 *
266 * The BinaryOpNode implements the Node interface and represents a combination
267 * of two nodes where both nodes process the same input, and their outputs
268 * are combined using a specified binary operation (like addition or multiplication).
269 * This is the implementation behind the '+' and '*' operators for nodes.
270 *
271 * When processed, the BinaryOpNode:
272 * 1. Passes the input to both the left and right nodes
273 * 2. Takes the outputs from both nodes
274 * 3. Combines the outputs using the specified function
275 * 4. Returns the combined result
276 */
277class MAYAFLUX_API BinaryOpNode : public Node, public std::enable_shared_from_this<BinaryOpNode> {
278public:
279 /**
280 * @typedef CombineFunc
281 * @brief Function type for combining two node outputs
282 *
283 * A function that takes two double values (the outputs from the left
284 * and right nodes) and returns a single double value (the combined result).
285 */
286 using CombineFunc = std::function<double(double, double)>;
287
288 /**
289 * @brief Creates a new binary operation node
290 * @param lhs The left-hand side node
291 * @param rhs The right-hand side node
292 * @param func The function to combine the outputs of both nodes
293 *
294 * Common combine functions include:
295 * - Addition: [](double a, double b) { return a + b; }
296 * - Multiplication: [](double a, double b) { return a * b; }
297 */
298 BinaryOpNode(const std::shared_ptr<Node>& lhs, const std::shared_ptr<Node>& rhs, CombineFunc func);
299
300 /**
301 * @brief Initializes the binary operation node
302 *
303 * This method performs any necessary setup for the binary operation node,
304 * such as ensuring both input nodes are properly initialized and registered.
305 * It should be called before the node is used for processing to ensure
306 * correct operation.
307 */
308 void initialize();
309
310 /**
311 * @brief Processes a single sample through both nodes and combines the results
312 * @param input The input sample
313 * @return The combined output after processing through both nodes
314 *
315 * The input is processed by both the left and right nodes, and their
316 * outputs are combined using the specified function.
317 */
318 double process_sample(double input = 0.) override;
319
320 /**
321 * @brief Processes multiple samples through both nodes and combines the results
322 * @param num_samples Number of samples to process
323 * @return Vector of combined processed samples
324 *
325 * Each sample is processed through both the left and right nodes, and
326 * their outputs are combined using the specified function.
327 */
328 std::vector<double> process_batch(unsigned int num_samples) override;
329
330 /**
331 * @brief Resets the processed state of the node and any attached input nodes
332 *
333 * This method is used by the processing system to reset the processed state
334 * of the node at the end of each processing cycle. This ensures that
335 * all nodes are marked as unprocessed before the cycle next begins, allowing
336 * the system to correctly identify which nodes need to be processed.
337 */
338 void reset_processed_state() override;
339
340 void save_state() override;
341 void restore_state() override;
342
343protected:
344 /**
345 * @brief Notifies all registered callbacks about a new output value
346 * @param value The newly combined output value
347 *
348 * This method is called internally whenever a new value is produced,
349 * creating the appropriate context with both input values and the output,
350 * and invoking all registered callbacks that should receive notification.
351 */
352 void notify_tick(double value) override;
353
354 /**
355 * @brief updates context object for callbacks
356 * @param value The current combined output value
357 *
358 * This method updates the specialized context object containing
359 * the combined output value and the individual values from both
360 * input nodes, providing callbacks with rich information about
361 * the operation's inputs and output.
362 */
363 void update_context(double value) override;
364
365 /**
366 * @brief Retrieves the last created context object
367 * @return Reference to the last BinaryOpContext object
368 *
369 * This method provides access to the most recent BinaryOpContext object
370 * created by the binary operation node. This context contains information
371 * about both input values and the combined output value.
372 */
373 NodeContext& get_last_context() override;
374
375private:
376 /**
377 * @brief The left-hand side node
378 */
379 std::shared_ptr<Node> m_lhs;
380
381 /**
382 * @brief The right-hand side node
383 */
384 std::shared_ptr<Node> m_rhs;
385
386 /**
387 * @brief The function used to combine the outputs of both nodes
388 */
390
391 /**
392 * @brief The last output value from the left-hand side node
393 *
394 * This value is stored to provide context information to callbacks,
395 * allowing them to access not just the combined result but also
396 * the individual contributions from each input node.
397 */
398 double m_last_lhs_value {};
399
400 /**
401 * @brief The last output value from the right-hand side node
402 *
403 * This value is stored to provide context information to callbacks,
404 * allowing them to access not just the combined result but also
405 * the individual contributions from each input node.
406 */
407 double m_last_rhs_value {};
408
409 /**
410 * @brief Flag indicating whether the binary operator has been properly initialized
411 *
412 * This flag is set to true when both the lhs and rhs nodes have been
413 * registered for processing and the connector itself is registered. It's used
414 * to ensure that the operator func doesn't attempt to process signals before all
415 * components are ready, preventing potential null pointer issues or
416 * processing inconsistencies.
417 */
418 bool m_is_initialized {};
419
420 bool m_state_saved {};
421 double m_saved_last_lhs_value {};
422 double m_saved_last_rhs_value {};
423
426
427public:
428 bool is_initialized() const;
429};
430}
GPU-compatible context for binary operation callbacks.
double rhs_value
The value from the right-hand side node.
double lhs_value
The value from the left-hand side node.
Specialized context for binary operation callbacks.
std::shared_ptr< Node > m_lhs
The left-hand side node.
std::function< double(double, double)> CombineFunc
Function type for combining two node outputs.
CombineFunc m_func
The function used to combine the outputs of both nodes.
std::shared_ptr< Node > m_rhs
The right-hand side node.
Combines the outputs of two nodes using a binary operation.
std::shared_ptr< Node > m_Source
The upstream node that processes input first.
std::shared_ptr< Node > m_Target
The downstream node that processes the source's output.
void update_context(double) override
Empty implementation of update_context.
void remove_all_hooks() override
Removes all registered callbacks.
void on_tick(const NodeHook &callback) override
Registers a callback for every output sample.
bool m_is_initialized
Flag indicating whether the chain has been properly initialized.
bool remove_hook(const NodeHook &callback) override
Removes a previously registered callback.
bool remove_conditional_hook(const NodeCondition &callback) override
Removes a previously registered conditional callback.
void on_tick_if(const NodeCondition &condition, const NodeHook &callback) override
Registers a conditional callback for output samples.
void notify_tick(double) override
Empty implementation of notify_tick.
Connects two nodes in series to form a processing chain.
GPU-uploadable 1D array data interface.
Base context class for node callbacks.
Definition Node.hpp:30
Base interface for all computational processing nodes.
Definition Node.hpp:109
void initialize()
Definition main.cpp:11
std::function< void(NodeContext &)> NodeHook
Callback function type for node processing events.
Definition NodeUtils.hpp:25
std::function< bool(NodeContext &)> NodeCondition
Predicate function type for conditional callbacks.
Definition NodeUtils.hpp:43
Contains the node-based computational processing system components.
Definition Chronie.hpp:5
bool is_initialized()
Checks if the default engine has been initialized.
Definition Core.cpp:50