MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Node.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "GpuContext.hpp"
5
6/**
7 * @namespace MayaFlux::Nodes
8 * @brief Contains the node-based computational processing system components
9 *
10 * The Nodes namespace provides a flexible, composable processing architecture
11 * based on the concept of interconnected computational nodes. Each node represents a
12 * discrete transformation unit that can be connected to other nodes to form
13 * complex processing networks and computational graphs.
14 */
15namespace MayaFlux::Nodes {
16
17/**
18 * @enum ModulatorRole
19 * @brief Describes the role a modulator node plays relative to its owner.
20 */
21enum class ModulatorRole : uint8_t {
25 Lhs,
26 Rhs,
28};
29
30/**
31 * @struct ModulatorTree
32 * @brief Recursive tree node describing a modulator and all of its own modulators.
33 */
36 std::shared_ptr<Node> node;
37 std::vector<ModulatorTree> modulators;
38};
39
40/**
41 * @class NodeContext
42 * @brief Base context class for node callbacks
43 *
44 * Provides basic context information for callbacks and can be
45 * extended by specific node types to include additional context.
46 * The NodeContext serves as a container for node state information
47 * that is passed to callback functions, allowing callbacks to
48 * access relevant node data during execution.
49 *
50 * Node implementations can extend this class to provide type-specific
51 * context information, which can be safely accessed using the as<T>() method.
52 */
53class MAYAFLUX_API NodeContext {
54public:
55 virtual ~NodeContext() = default;
56
57 /**
58 * @brief Current sample value
59 *
60 * The most recent output value produced by the node.
61 * This is the primary data point that callbacks will typically use.
62 */
63 double value;
64
65 /**
66 * @brief Safely cast to a derived context type
67 * @tparam T The derived context type to cast to
68 * @return Pointer to the derived context or nullptr if types don't match
69 *
70 * Provides type-safe access to derived context classes. If the requested
71 * type matches the actual type of this context, returns a properly cast
72 * pointer. Otherwise, returns nullptr to prevent unsafe access.
73 *
74 * Example:
75 * ```cpp
76 * if (auto filter_ctx = ctx.as<FilterContext>()) {
77 * // Access filter-specific context members
78 * double gain = filter_ctx->gain;
79 * }
80 * ```
81 */
82 template <typename T>
83 T* as()
84 {
85 return dynamic_cast<T*>(this);
86 }
87
88 template <typename T>
89 const T* as() const
90 {
91 return dynamic_cast<const T*>(this);
92 }
93
94protected:
95 /**
96 * @brief Protected constructor for NodeContext
97 * @param value The current sample value
98 * @param type String identifier for the context type
99 *
100 * This constructor is protected to ensure that only derived classes
101 * can create context objects, with proper type identification.
102 */
104 : value(value)
105 {
106 }
107};
108
109/**
110 * @class Node
111 * @brief Base interface for all computational processing nodes
112 *
113 * The Node class defines the fundamental interface for all processing components
114 * in the MayaFlux engine. Nodes are the basic building blocks of transformation chains
115 * and can be connected together to create complex computational graphs.
116 *
117 * Each node processes data on a sample-by-sample basis, allowing for flexible
118 * real-time processing. Nodes can be:
119 * - Connected in series (output of one feeding into input of another)
120 * - Combined in parallel (outputs mixed together)
121 * - Multiplied (outputs multiplied together)
122 *
123 * The node system supports both single-sample processing for real-time applications
124 * and batch processing for more efficient offline processing.
125 */
126class MAYAFLUX_API Node {
127
128public:
129 /**
130 * @brief Virtual destructor for proper cleanup of derived classes
131 */
132 virtual ~Node() = default;
133
134 /**
135 * @brief Processes a single data sample
136 * @param input The input sample value
137 * @return The processed output sample value
138 *
139 * This is the core processing method that all nodes must implement.
140 * It takes a single input value, applies the node's transformation algorithm,
141 * and returns the resulting output value.
142 *
143 * For generator nodes that don't require input (like oscillators or stochastic generators),
144 * the input parameter may be ignored.
145 * Note: This method does NOT mark the node as processed. That responsibility
146 * belongs to the caller, typically a chained parent node or the root node.
147 */
148 virtual double process_sample(double input = 0.) = 0;
149
150 /**
151 * @brief Processes multiple samples at once
152 * @param num_samples Number of samples to process
153 * @return Vector containing the processed samples
154 *
155 * This method provides batch processing capability for more efficient
156 * processing of multiple samples. The default implementation typically
157 * calls process_sample() for each sample, but specialized nodes can
158 * override this with more optimized batch processing algorithms.
159 */
160 virtual std::vector<double> process_batch(unsigned int num_samples) = 0;
161
162 /**
163 * @brief Registers a callback to be called on each tick
164 * @param callback Function to call with the current node context
165 *
166 * Registers a callback function that will be called each time the node
167 * produces a new output value. The callback receives a NodeContext object
168 * containing information about the node's current state.
169 *
170 * This mechanism enables external components to monitor and react to
171 * the node's activity without interrupting the processing flow.
172 *
173 * Example:
174 * ```cpp
175 * node->on_tick([](NodeContext& ctx) {
176 * std::cout << "Node produced value: " << ctx.value << std::endl;
177 * });
178 * ```
179 */
180 virtual void on_tick(const NodeHook& callback);
181
182 /**
183 * @brief Registers a conditional callback
184 * @param condition Predicate that determines when callback should be triggered
185 * @param callback Function to call when condition is met
186 *
187 * Registers a callback function that will be called only when the specified
188 * condition is met. The condition is evaluated each time the node produces
189 * a new output value, and the callback is triggered only if the condition
190 * returns true.
191 *
192 * This mechanism enables selective monitoring and reaction to specific
193 * node states or events, such as threshold crossings or pattern detection.
194 *
195 * Example:
196 * ```cpp
197 * node->on_tick_if(
198 * [](NodeContext& ctx) { return ctx.value > 0.8; },
199 * [](NodeContext& ctx) { std::cout << "Threshold exceeded!" << std::endl; }
200 * );
201 * ```
202 */
203 virtual void on_tick_if(const NodeCondition& condition, const NodeHook& callback);
204
205 /**
206 * @brief Removes a previously registered callback
207 * @param callback The callback to remove
208 * @return True if the callback was found and removed, false otherwise
209 *
210 * Unregisters a callback that was previously registered with on_tick().
211 * After removal, the callback will no longer be triggered when the node
212 * produces new output values.
213 *
214 * This method is useful for cleaning up callbacks when they are no longer
215 * needed, preventing memory leaks and unnecessary processing.
216 */
217 virtual bool remove_hook(const NodeHook& callback);
218
219 /**
220 * @brief Removes a previously registered conditional callback
221 * @param callback The callback part of the conditional callback to remove
222 * @return True if the callback was found and removed, false otherwise
223 *
224 * Unregisters a conditional callback that was previously registered with
225 * on_tick_if(). After removal, the callback will no longer be triggered
226 * even when its condition is met.
227 *
228 * This method is useful for cleaning up conditional callbacks when they
229 * are no longer needed, preventing memory leaks and unnecessary processing.
230 */
231 virtual bool remove_conditional_hook(const NodeCondition& callback);
232
233 /**
234 * @brief Removes all registered callbacks
235 *
236 * Unregisters all callbacks that were previously registered with on_tick()
237 * and on_tick_if(). After calling this method, no callbacks will be triggered
238 * when the node produces new output values.
239 *
240 * This method is useful for completely resetting the node's callback system,
241 * such as when repurposing a node or preparing for cleanup.
242 */
243 virtual void remove_all_hooks();
244
245 /**
246 * @brief Resets the processed state of the node and any attached input nodes
247 *
248 * This method is used by the processing system to reset the processed state
249 * of the node at the end of each processing cycle. This ensures that
250 * all nodes are marked as unprocessed before the cycle next begins, allowing
251 * the system to correctly identify which nodes need to be processed.
252 */
253 virtual void reset_processed_state();
254
255 /**
256 * @brief Retrieves the most recent output value produced by the node
257 * @return The last output sample value
258 *
259 * This method provides access to the node's most recent output without
260 * triggering additional processing. It's useful for monitoring node state,
261 * debugging, and for implementing feedback loops where a node needs to
262 * access its previous output.
263 *
264 * The returned value represents the last sample that was produced by
265 * the node's process_sample() method.
266 */
267 inline virtual double get_last_output() { return m_last_output; }
268
269 /**
270 * @brief Allows RootNode to process the Generator without using the processed sample
271 * @param bMock_process True to mock process, false to process normally
272 *
273 * NOTE: This has no effect on the behaviour of process_sample (or process_batch).
274 * This is ONLY used by the RootNode when processing the node graph.
275 * If the output of the Generator needs to be ignored elsewhere, simply discard the return value.
276 *
277 * Calling process manually can be cumbersome. Using a coroutine just to call process
278 * is overkill. This method allows the RootNode to process the Generator without
279 * using the processed sample, which is useful for mocking processing.
280 */
281 virtual void enable_mock_process(bool mock_process);
282
283 /**
284 * @brief Checks if the generator should mock process
285 * @return True if the generator should mock process, false otherwise
286 */
287 [[nodiscard]] virtual bool should_mock_process() const;
288
289 /**
290 * @brief Mark the specificed channel as a processor/user
291 * @param channel_id The ID of the channel to register
292 *
293 * This method uses a bitmask to track which channels are currently using this node.
294 * It allows the node to manage its state based on channel usage, which is important
295 * for the audio engine's processing lifecycle. When a channel registers usage,
296 * the node can adjust its processing state accordingly, such as preventing state resets
297 * within the same cycle, or use the same output for multiple channels
298 */
299 void register_channel_usage(uint32_t channel_id);
300
301 /**
302 * @brief Removes the specified channel from the usage tracking
303 * @param channel_id The ID of the channel to unregister
304 */
305 void unregister_channel_usage(uint32_t channel_id);
306
307 /**
308 * @brief Checks if the node is currently used by a specific channel
309 * @param channel_id The ID of the channel to check
310 */
311 [[nodiscard]] bool is_used_by_channel(uint32_t channel_id) const;
312
313 /**
314 * @brief Requests a reset of the processed state from a specific channel
315 * @param channel_id The ID of the channel requesting the reset
316 *
317 * This method is called by channels to signal that they have completed their
318 * processing and that the node's processed state should be reset. It uses a bitmask
319 * to track pending resets and ensures that all channels have completed before
320 * actually resetting the node's state.
321 */
322 void request_reset_from_channel(uint32_t channel_id);
323
324 /**
325 * @brief Retrieves the current bitmask of active channels using this node
326 * @return Bitmask where each bit represents an active channel
327 *
328 * This method returns the current bitmask that tracks which channels
329 * are actively using this node. Each bit in the mask corresponds to a
330 * specific channel ID, allowing the node to manage its state based on
331 * channel usage.
332 */
333 [[nodiscard]] const inline std::atomic<uint32_t>& get_channel_mask() const { return m_active_channels_mask; }
334
335 /**
336 * @brief Updates the context object with the current node state
337 * @param value The current sample value
338 *
339 * This method is responsible for updating the NodeContext object
340 * with the latest state information from the node. It is called
341 * internally whenever a new output value is produced, ensuring that
342 * the context reflects the current state of the node for use in callbacks.
343 */
344 virtual void update_context(double value) = 0;
345
346 /**
347 * @brief Retrieves the last created context object
348 * @return Reference to the last NodeContext object
349 *
350 * This method provides access to the most recent NodeContext object
351 * created by the node. This context contains information about the
352 * node's state at the time of the last output generation.
353 */
355
356 /**
357 * @brief Sets whether the node is compatible with GPU processing
358 * @param compatible True if the node supports GPU processing, false otherwise
359 */
360 virtual void set_gpu_compatible(bool compatible)
361 {
362 m_gpu_compatible = compatible;
363 if (compatible) {
364 m_timing_rate = m_frame_rate; // Use frame rate for timing calculations if GPU compatible
365 } else {
366 m_timing_rate = m_sample_rate; // Use sample rate for timing calculations if not GPU compatible
367 }
368 }
369
370 /**
371 * @brief Checks if the node supports GPU processing
372 * @return True if the node is GPU compatible, false otherwise
373 */
374 [[nodiscard]] bool is_gpu_compatible() const { return m_gpu_compatible; }
375
376 /**
377 * @brief Provides access to the GPU data buffer
378 * @return Span of floats representing the GPU data buffer
379 * This method returns a span of floats that represents the GPU data buffer
380 * associated with this node. The buffer contains data that can be uploaded to the GPU
381 * for processing, enabling efficient execution in GPU-accelerated pipelines.
382 */
383 [[nodiscard]] std::span<const float> get_gpu_data_buffer() const;
384
385 void set_sample_rate(uint32_t sample_rate) { m_sample_rate = sample_rate; }
386 [[nodiscard]] uint32_t get_sample_rate() const { return m_sample_rate; }
387
388 void set_frame_rate(uint32_t frame_rate) { m_frame_rate = frame_rate; }
389 [[nodiscard]] uint32_t get_frame_rate() const { return m_frame_rate; }
390
391protected:
392 /**
393 * @brief Notifies all registered callbacks with the current context
394 * @param value The current sample value
395 *
396 * This method is called by the node implementation when a new output value
397 * is produced. It creates a context object using create_context(), then
398 * calls all registered callbacks with that context.
399 *
400 * For unconditional callbacks (registered with on_tick()), the callback
401 * is always called. For conditional callbacks (registered with on_tick_if()),
402 * the callback is called only if its condition returns true.
403 *
404 * Node implementations should call this method at appropriate points in their
405 * processing flow to trigger callbacks.
406 */
407 virtual void notify_tick(double value) = 0;
408
409 /**
410 * @brief Resets the processed state of the node directly
411 *
412 * Unlike reset_processed_state(), this method is called internally
413 * and does not perform any checks or state transitions.
414 */
415 virtual void reset_processed_state_internal();
416
417 /**
418 * @brief The most recent sample value generated by this oscillator
419 *
420 * This value is updated each time process_sample() is called and can be
421 * accessed via get_last_output() without triggering additional processing.
422 * It's useful for monitoring the oscillator's state and for implementing
423 * feedback loops.
424 */
425 double m_last_output { 0 };
426
427 /**
428 * @brief Flag indicating if the node supports GPU processing
429 * This flag is set by derived classes to indicate whether
430 * the node can be processed on the GPU. Nodes that support GPU
431 * processing can provide GPU-compatible context data for
432 * efficient execution in GPU-accelerated pipelines.
433 */
434 bool m_gpu_compatible {};
435
436 /**
437 * @brief GPU data buffer for context objects
438 *
439 * This buffer is used to store float data that can be uploaded
440 * to the GPU for nodes that support GPU processing. It provides
441 * a contiguous array of floats that can be bound to GPU descriptors,
442 * enabling efficient data transfer and processing on the GPU.
443 */
444 std::vector<float> m_gpu_data_buffer;
445
446 /**
447 * @brief Collection of standard callback functions
448 *
449 * Stores the registered callback functions that will be notified
450 * whenever the binary operation produces a new output value. These callbacks
451 * enable external components to monitor and react to the combined output
452 * without interrupting the processing flow.
453 */
454 std::vector<NodeHook> m_callbacks;
455
456 /**
457 * @brief Collection of conditional callback functions with their predicates
458 *
459 * Stores pairs of callback functions and their associated condition predicates.
460 * These callbacks are only invoked when their condition evaluates to true
461 * for a combined output value, enabling selective monitoring of specific
462 * conditions or patterns in the combined signal.
463 */
464 std::vector<std::pair<NodeHook, NodeCondition>> m_conditional_callbacks;
465
466 /**
467 * @brief Flag indicating if the node is part of a NodeNetwork
468 * This flag is used to disable event firing when the node is
469 * managed within a NodeNetwork, preventing redundant or conflicting
470 * event notifications.
471 */
472 bool m_networked_node {};
473
474 /**
475 @brief tracks if the node's state has been saved by a snapshot operation
476 */
477 bool m_state_saved {};
478
479 uint32_t m_sample_rate { 48000 }; ///< Sample rate for audio processing, used for normalization
480
481 uint32_t m_frame_rate { 60 }; ///< Frame rate for time-based processing, used for normalization
482
483 uint32_t m_timing_rate { m_sample_rate }; ///< Current timing rate for the node, used for timing calculations (can be sample rate or frame rate)
484
485 uint8_t m_node_capability { NodeCapability::SCALAR }; ///< Bitmask of capabilities declared by this node
486
487public:
488 /**
489 * @brief Saves the node's current state for later restoration
490 * Recursively cascades through all connected modulator nodes
491 * Protected - only NodeSourceProcessor and NodeBuffer can call
492 */
493 virtual void save_state() = 0;
494
495 /**
496 * @brief Restores the node's state from the last save
497 * Recursively cascades through all connected modulator nodes
498 * Protected - only NodeSourceProcessor and NodeBuffer can call
499 */
500 virtual void restore_state() = 0;
501
502 /**
503 * @brief Internal flag controlling whether notify_tick fires during state snapshots
504 * Default: false (events don't fire during isolated buffer processing)
505 * Can be exposed in future if needed via concrete implementation in parent
506 */
507 bool m_fire_events_during_snapshot = false;
508
509 /**
510 * @brief Atomic state flag tracking the node's processing status
511 *
512 * This atomic state variable tracks the node's current operational status using
513 * bit flags defined in NodeState. It indicates whether the node is:
514 * - ACTIVE: Currently part of the processing graph
515 * - PROCESSED: Has been processed in the current cycle
516 * - PENDING_REMOVAL: Marked for removal from the processing graph
517 * - MOCK_PROCESS: Should be processed but output ignored
518 *
519 * The atomic nature ensures thread-safe state transitions, allowing the audio
520 * engine to safely coordinate processing across multiple threads without data races.
521 */
522 std::atomic<NodeState> m_state { NodeState::INACTIVE };
523
524 /**
525 * @brief Counter tracking how many other nodes are using this node as a modulator
526 *
527 * This counter is incremented when another node begins using this node as a
528 * modulation source, and decremented when that relationship ends. It's critical
529 * for the node lifecycle management system, as it prevents premature state resets
530 * when a node's output is still needed by downstream nodes.
531 *
532 * When this counter is non-zero, the node's processed state will not be reset
533 * automatically after processing, ensuring that all dependent nodes can access
534 * its output before it's cleared.
535 */
536 std::atomic<uint32_t> m_modulator_count { 0 };
537
538 /**
539 * @brief Attempt to claim snapshot context for this processing cycle
540 * @param context_id Unique context identifier for this buffer processing
541 * @return true if this caller claimed the context (should call save_state)
542 *
543 * This method enables multiple NodeBuffers referencing the same node to
544 * coordinate save/restore state operations. Only the first caller per
545 * processing context will claim the snapshot, preventing nested state saves.
546 */
547 bool try_claim_snapshot_context(uint64_t context_id);
548
549 /**
550 * @brief Check if currently in a snapshot context
551 * @param context_id Context to check
552 * @return true if this context is active
553 *
554 * Used by secondary callers to detect when the primary snapshot holder
555 * has completed processing and released the context.
556 */
557 [[nodiscard]] bool is_in_snapshot_context(uint64_t context_id) const;
558
559 /**
560 * @brief Release snapshot context
561 * @param context_id Context to release
562 *
563 * Called by the snapshot owner after restore_state() completes,
564 * allowing other buffers to proceed with their own snapshots.
565 */
566 void release_snapshot_context(uint64_t context_id);
567
568 /**
569 * @brief Check if node is currently being snapshotted by any context
570 * @return true if a snapshot is in progress
571 */
572 [[nodiscard]] bool has_active_snapshot() const;
573
574 /**
575 * @brief Get the active snapshot context ID
576 * @return The current active snapshot context ID, or 0 if none
577 */
578 [[nodiscard]] inline uint64_t get_active_snapshot_context() const
579 {
580 return m_snapshot_context_id.load(std::memory_order_acquire);
581 }
582
583 /**
584 * @brief Increments the buffer reference count
585 * This method is called when a new buffer starts using this node
586 * to ensure proper lifecycle management.
587 */
588 void add_buffer_reference();
589
590 /**
591 * @brief Decrements the buffer reference count
592 * This method is called when a buffer stops using this node
593 * to ensure proper lifecycle management.
594 */
595 void remove_buffer_reference();
596
597 /**
598 * @brief Marks the node as having been processed by a buffer
599 * @return true if the buffer was successfully marked as processed
600 *
601 * This method checks if the node can be marked as processed based on
602 * the current buffer count and node state. If conditions are met,
603 * it updates the processed flag and increments the reset counter.
604 */
605 bool mark_buffer_processed();
606
607 /**
608 * @brief Requests a reset of the buffer state
609 *
610 * This method is called to signal that the buffer's processed state
611 * should be reset. It increments the reset counter, which is used to
612 * determine when it's safe to clear the processed state.
613 */
614 void request_buffer_reset();
615
616 /**
617 * @brief Checks if the buffer has been processed
618 * @return true if the buffer is marked as processed
619 */
620 [[nodiscard]] inline bool is_buffer_processed() const
621 {
622 return m_buffer_processed.load(std::memory_order_acquire);
623 }
624
625 /**
626 * @brief Sets whether the node is part of a NodeNetwork
627 * @param networked True if the node is managed within a NodeNetwork
628 *
629 * This method sets a flag indicating whether the node is part of a
630 * NodeNetwork. When set, certain behaviors such as event firing
631 * may be disabled to prevent redundant or conflicting notifications.
632 */
633 [[nodiscard]] inline bool is_in_network() const { return m_networked_node; }
634
635 /**
636 * @brief Marks the node as being part of a NodeNetwork
637 * @param networked True if the node is managed within a NodeNetwork
638 *
639 * This method sets a flag indicating whether the node is part of a
640 * NodeNetwork. When set, certain behaviors such as event firing
641 * may be disabled to prevent redundant or conflicting notifications.
642 */
643 void set_in_network(bool networked) { m_networked_node = networked; }
644
645 /**
646 * @brief Retrieves the current routing state of the network
647 * @return Reference to the current RoutingState structure
648 *
649 * This method provides access to the network's current routing state, which
650 * includes information about fade-in/out (Active) phases, channel counts, and elapsed cycles.
651 * The routing state is used to manage smooth transitions when routing changes occur,
652 * ensuring seamless audio output during dynamic reconfigurations of the processing graph.
653 */
654 [[nodiscard]] const RoutingState& get_routing_state() const { return m_routing_state; }
655
656 /**
657 * @brief Retrieves the current routing state of the network (non-const)
658 * @return Reference to the current RoutingState structure
659 */
660 RoutingState& get_routing_state() { return m_routing_state; }
661
662 /**
663 * @brief Checks if the network is currently in a routing transition phase
664 * @return true if the network is in a fade-in or fade-out (Active) phase
665 *
666 * This method checks the network's routing state to determine if it is currently
667 * undergoing a routing transition, such as fading in or out. This information
668 * can be used by processing algorithms to adjust their behavior during transitions,
669 * ensuring smooth audio output without artifacts.
670 */
671 [[nodiscard]] bool needs_channel_routing() const
672 {
673 return m_routing_state.phase & (RoutingState::ACTIVE | RoutingState::COMPLETED);
674 }
675
676 /**
677 * @brief Declare which data shapes this node's context can produce.
678 *
679 * Override to advertise capabilities beyond SCALAR. The default reflects
680 * the Node interface guarantee: every node produces a scalar.
681 * Combine flags with bitwise OR for nodes whose context implements
682 * multiple GpuContext mixins.
683 *
684 * @return Bitmask of NodeCapability flags.
685 */
686 [[nodiscard]] virtual uint8_t node_capabilities() const { return m_node_capability; }
687
688 /**
689 * @brief Query a single capability.
690 * @param cap Capability flag to test.
691 * @return true if this node supports the requested data shape.
692 */
693 [[nodiscard]] bool has_capability(NodeCapability cap) const
694 {
695 return (m_node_capability & cap) != 0U;
696 }
697
698 /**
699 * @brief Returns direct modulator nodes and their roles.
700 *
701 * Each entry pairs the role the modulator plays in this node's processing
702 * with the modulator node itself. Default returns empty. Concrete nodes
703 * override to expose their held modulator references.
704 */
705 [[nodiscard]] virtual std::vector<std::pair<ModulatorRole, std::shared_ptr<Node>>>
706 get_modulators() const { return {}; }
707
708 /**
709 * @brief Returns the full modulator tree rooted at this node.
710 *
711 * Calls get_modulators() on this node, then recursively on each returned
712 * node, building the complete tree. The caller receives the entire
713 * hierarchy in one call.
714 */
715 [[nodiscard]] std::vector<ModulatorTree> get_modulator_tree() const
716 {
717 std::vector<ModulatorTree> result;
718 for (auto& [role, node] : get_modulators()) {
719 ModulatorTree entry;
720 entry.role = role;
721 entry.node = node;
722 entry.modulators = node->get_modulator_tree();
723 result.push_back(std::move(entry));
724 }
725 return result;
726 }
727
728private:
729 /**
730 * @brief Bitmask tracking which channels are currently using this node
731 */
732 std::atomic<uint32_t> m_active_channels_mask { 0 };
733
734 /**
735 * @brief Bitmask tracking which channels have requested a reset
736 *
737 * This mask is used to track which channels have requested the node's processed
738 * state to be reset. When all channels that are currently using the node have
739 * requested a reset, the node can safely clear its processed state.
740 */
741 std::atomic<uint32_t> m_pending_reset_mask { 0 };
742
743 /**
744 * @brief Unique identifier for the current snapshot context
745 *
746 * This atomic variable holds the unique identifier of the current
747 * snapshot context that has claimed ownership of this node's state.
748 * It ensures that only one processing context can perform save/restore
749 * operations at a time, preventing nested snapshots and ensuring
750 * consistent state management.
751 */
752 std::atomic<uint64_t> m_snapshot_context_id { 0 };
753
754 /**
755 * @brief Counter tracking how many buffers are using this node
756 * This counter is incremented when a buffer starts using this node
757 * and decremented when the buffer stops using it. It helps manage
758 * the node's lifecycle in relation to buffer usage.
759 */
760 std::atomic<uint32_t> m_buffer_count { 0 };
761
762 /**
763 * @brief Flag indicating whether the buffer has been processed
764 * This atomic flag is set when the buffer has been successfully
765 * processed and is used to prevent redundant processing.
766 */
767 std::atomic<bool> m_buffer_processed { false };
768
769 /**
770 * @brief Counter tracking how many buffers have requested a reset
771 *
772 * When all buffers using this node have requested a reset, the node's
773 * processed state can be safely cleared. This counter helps coordinate
774 * that process.
775 */
776 std::atomic<uint32_t> m_buffer_reset_count { 0 };
777
778 /**
779 * @brief Internal state tracking for routing transitions
780 *
781 * This structure tracks the state of routing transitions,
782 * such as fade-in and fade-out phases, channel counts, and elapsed cycles.
783 * It's used to manage smooth transitions when routing changes occur, ensuring
784 * that audio output remains seamless during dynamic reconfigurations of the processing graph.
785 */
787};
788}
Core::GlobalInputConfig input
Definition Config.cpp:38
float value
NodeContext(double value)
Protected constructor for NodeContext.
Definition Node.hpp:103
virtual ~NodeContext()=default
double value
Current sample value.
Definition Node.hpp:63
T * as()
Safely cast to a derived context type.
Definition Node.hpp:83
const T * as() const
Definition Node.hpp:89
Base context class for node callbacks.
Definition Node.hpp:53
virtual double process_sample(double input=0.)=0
Processes a single data sample.
virtual double get_last_output()
Retrieves the most recent output value produced by the node.
Definition Node.hpp:267
virtual std::vector< double > process_batch(unsigned int num_samples)=0
Processes multiple samples at once.
bool is_buffer_processed() const
Checks if the buffer has been processed.
Definition Node.hpp:620
virtual void save_state()=0
Saves the node's current state for later restoration Recursively cascades through all connected modul...
bool is_in_network() const
Sets whether the node is part of a NodeNetwork.
Definition Node.hpp:633
uint32_t get_frame_rate() const
Definition Node.hpp:389
std::vector< NodeHook > m_callbacks
Collection of standard callback functions.
Definition Node.hpp:454
void set_in_network(bool networked)
Marks the node as being part of a NodeNetwork.
Definition Node.hpp:643
virtual void restore_state()=0
Restores the node's state from the last save Recursively cascades through all connected modulator nod...
uint64_t get_active_snapshot_context() const
Get the active snapshot context ID.
Definition Node.hpp:578
const std::atomic< uint32_t > & get_channel_mask() const
Retrieves the current bitmask of active channels using this node.
Definition Node.hpp:333
bool needs_channel_routing() const
Checks if the network is currently in a routing transition phase.
Definition Node.hpp:671
virtual NodeContext & get_last_context()=0
Retrieves the last created context object.
virtual void notify_tick(double value)=0
Notifies all registered callbacks with the current context.
virtual void update_context(double value)=0
Updates the context object with the current node state.
virtual void set_gpu_compatible(bool compatible)
Sets whether the node is compatible with GPU processing.
Definition Node.hpp:360
void set_sample_rate(uint32_t sample_rate)
Definition Node.hpp:385
bool is_gpu_compatible() const
Checks if the node supports GPU processing.
Definition Node.hpp:374
std::vector< std::pair< NodeHook, NodeCondition > > m_conditional_callbacks
Collection of conditional callback functions with their predicates.
Definition Node.hpp:464
virtual ~Node()=default
Virtual destructor for proper cleanup of derived classes.
uint32_t get_sample_rate() const
Definition Node.hpp:386
RoutingState & get_routing_state()
Retrieves the current routing state of the network (non-const)
Definition Node.hpp:660
bool has_capability(NodeCapability cap) const
Query a single capability.
Definition Node.hpp:693
std::vector< ModulatorTree > get_modulator_tree() const
Returns the full modulator tree rooted at this node.
Definition Node.hpp:715
void set_frame_rate(uint32_t frame_rate)
Definition Node.hpp:388
virtual std::vector< std::pair< ModulatorRole, std::shared_ptr< Node > > > get_modulators() const
Returns direct modulator nodes and their roles.
Definition Node.hpp:706
std::vector< float > m_gpu_data_buffer
GPU data buffer for context objects.
Definition Node.hpp:444
RoutingState m_routing_state
Internal state tracking for routing transitions.
Definition Node.hpp:786
virtual uint8_t node_capabilities() const
Declare which data shapes this node's context can produce.
Definition Node.hpp:686
const RoutingState & get_routing_state() const
Retrieves the current routing state of the network.
Definition Node.hpp:654
Base interface for all computational processing nodes.
Definition Node.hpp:126
TypedHook<> NodeHook
Alias for TypedHook<NodeContext>.
Definition NodeUtils.hpp:38
ModulatorRole
Describes the role a modulator node plays relative to its owner.
Definition Node.hpp:21
NodeCapability
Bitmask flags declaring what data shapes a node's context can produce.
Definition NodeSpec.hpp:104
std::function< bool(NodeContext &)> NodeCondition
Predicate function type for conditional callbacks.
Definition NodeUtils.hpp:54
Contains the node-based computational processing system components.
Definition Chronie.hpp:14
std::vector< ModulatorTree > modulators
Definition Node.hpp:37
std::shared_ptr< Node > node
Definition Node.hpp:36
Recursive tree node describing a modulator and all of its own modulators.
Definition Node.hpp:34
Represents the state of routing transitions for a node.
Definition NodeSpec.hpp:64