MayaFlux 0.4.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 */
103 NodeContext(double value)
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 Mark the specificed channel as a processor/user
271 * @param channel_id The ID of the channel to register
272 *
273 * This method uses a bitmask to track which channels are currently using this node.
274 * It allows the node to manage its state based on channel usage, which is important
275 * for the audio engine's processing lifecycle. When a channel registers usage,
276 * the node can adjust its processing state accordingly, such as preventing state resets
277 * within the same cycle, or use the same output for multiple channels
278 */
279 void register_channel_usage(uint32_t channel_id);
280
281 /**
282 * @brief Removes the specified channel from the usage tracking
283 * @param channel_id The ID of the channel to unregister
284 */
285 void unregister_channel_usage(uint32_t channel_id);
286
287 /**
288 * @brief Checks if the node is currently used by a specific channel
289 * @param channel_id The ID of the channel to check
290 */
291 [[nodiscard]] bool is_used_by_channel(uint32_t channel_id) const;
292
293 /**
294 * @brief Requests a reset of the processed state from a specific channel
295 * @param channel_id The ID of the channel requesting the reset
296 *
297 * This method is called by channels to signal that they have completed their
298 * processing and that the node's processed state should be reset. It uses a bitmask
299 * to track pending resets and ensures that all channels have completed before
300 * actually resetting the node's state.
301 */
302 void request_reset_from_channel(uint32_t channel_id);
303
304 /**
305 * @brief Retrieves the current bitmask of active channels using this node
306 * @return Bitmask where each bit represents an active channel
307 *
308 * This method returns the current bitmask that tracks which channels
309 * are actively using this node. Each bit in the mask corresponds to a
310 * specific channel ID, allowing the node to manage its state based on
311 * channel usage.
312 */
313 [[nodiscard]] const inline std::atomic<uint32_t>& get_channel_mask() const { return m_active_channels_mask; }
314
315 /**
316 * @brief Updates the context object with the current node state
317 * @param value The current sample value
318 *
319 * This method is responsible for updating the NodeContext object
320 * with the latest state information from the node. It is called
321 * internally whenever a new output value is produced, ensuring that
322 * the context reflects the current state of the node for use in callbacks.
323 */
324 virtual void update_context(double value) = 0;
325
326 /**
327 * @brief Retrieves the last created context object
328 * @return Reference to the last NodeContext object
329 *
330 * This method provides access to the most recent NodeContext object
331 * created by the node. This context contains information about the
332 * node's state at the time of the last output generation.
333 */
335
336 /**
337 * @brief Sets whether the node is compatible with GPU processing
338 * @param compatible True if the node supports GPU processing, false otherwise
339 */
340 virtual void set_gpu_compatible(bool compatible)
341 {
342 m_gpu_compatible = compatible;
343 if (compatible) {
344 m_timing_rate = m_frame_rate; // Use frame rate for timing calculations if GPU compatible
345 } else {
346 m_timing_rate = m_sample_rate; // Use sample rate for timing calculations if not GPU compatible
347 }
348 }
349
350 /**
351 * @brief Checks if the node supports GPU processing
352 * @return True if the node is GPU compatible, false otherwise
353 */
354 [[nodiscard]] bool is_gpu_compatible() const { return m_gpu_compatible; }
355
356 /**
357 * @brief Provides access to the GPU data buffer
358 * @return Span of floats representing the GPU data buffer
359 * This method returns a span of floats that represents the GPU data buffer
360 * associated with this node. The buffer contains data that can be uploaded to the GPU
361 * for processing, enabling efficient execution in GPU-accelerated pipelines.
362 */
363 [[nodiscard]] std::span<const float> get_gpu_data_buffer() const;
364
365 void set_sample_rate(uint32_t sample_rate) { m_sample_rate = sample_rate; }
366 [[nodiscard]] uint32_t get_sample_rate() const { return m_sample_rate; }
367
368 void set_frame_rate(uint32_t frame_rate) { m_frame_rate = frame_rate; }
369 [[nodiscard]] uint32_t get_frame_rate() const { return m_frame_rate; }
370
371protected:
372 /**
373 * @brief Notifies all registered callbacks with the current context
374 * @param value The current sample value
375 *
376 * This method is called by the node implementation when a new output value
377 * is produced. It creates a context object using create_context(), then
378 * calls all registered callbacks with that context.
379 *
380 * For unconditional callbacks (registered with on_tick()), the callback
381 * is always called. For conditional callbacks (registered with on_tick_if()),
382 * the callback is called only if its condition returns true.
383 *
384 * Node implementations should call this method at appropriate points in their
385 * processing flow to trigger callbacks.
386 */
387 virtual void notify_tick(double value) = 0;
388
389 /**
390 * @brief Resets the processed state of the node directly
391 *
392 * Unlike reset_processed_state(), this method is called internally
393 * and does not perform any checks or state transitions.
394 */
395 virtual void reset_processed_state_internal();
396
397 /**
398 * @brief The most recent sample value generated by this oscillator
399 *
400 * This value is updated each time process_sample() is called and can be
401 * accessed via get_last_output() without triggering additional processing.
402 * It's useful for monitoring the oscillator's state and for implementing
403 * feedback loops.
404 */
405 double m_last_output { 0 };
406
407 /**
408 * @brief Flag indicating if the node supports GPU processing
409 * This flag is set by derived classes to indicate whether
410 * the node can be processed on the GPU. Nodes that support GPU
411 * processing can provide GPU-compatible context data for
412 * efficient execution in GPU-accelerated pipelines.
413 */
414 bool m_gpu_compatible {};
415
416 /**
417 * @brief GPU data buffer for context objects
418 *
419 * This buffer is used to store float data that can be uploaded
420 * to the GPU for nodes that support GPU processing. It provides
421 * a contiguous array of floats that can be bound to GPU descriptors,
422 * enabling efficient data transfer and processing on the GPU.
423 */
424 std::vector<float> m_gpu_data_buffer;
425
426 /**
427 * @brief Collection of standard callback functions
428 *
429 * Stores the registered callback functions that will be notified
430 * whenever the binary operation produces a new output value. These callbacks
431 * enable external components to monitor and react to the combined output
432 * without interrupting the processing flow.
433 */
434 std::vector<NodeHook> m_callbacks;
435
436 /**
437 * @brief Collection of conditional callback functions with their predicates
438 *
439 * Stores pairs of callback functions and their associated condition predicates.
440 * These callbacks are only invoked when their condition evaluates to true
441 * for a combined output value, enabling selective monitoring of specific
442 * conditions or patterns in the combined signal.
443 */
444 std::vector<std::pair<NodeHook, NodeCondition>> m_conditional_callbacks;
445
446 /**
447 * @brief Flag indicating if the node is part of a NodeNetwork
448 * This flag is used to disable event firing when the node is
449 * managed within a NodeNetwork, preventing redundant or conflicting
450 * event notifications.
451 */
452 bool m_networked_node {};
453
454 /**
455 @brief tracks if the node's state has been saved by a snapshot operation
456 */
457 bool m_state_saved {};
458
459 uint32_t m_sample_rate { 48000 }; ///< Sample rate for audio processing, used for normalization
460
461 uint32_t m_frame_rate { 60 }; ///< Frame rate for time-based processing, used for normalization
462
463 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)
464
465 uint8_t m_node_capability { NodeCapability::SCALAR }; ///< Bitmask of capabilities declared by this node
466
467public:
468 /**
469 * @brief Saves the node's current state for later restoration
470 * Recursively cascades through all connected modulator nodes
471 * Protected - only NodeSourceProcessor and NodeBuffer can call
472 */
473 virtual void save_state() = 0;
474
475 /**
476 * @brief Restores the node's state from the last save
477 * Recursively cascades through all connected modulator nodes
478 * Protected - only NodeSourceProcessor and NodeBuffer can call
479 */
480 virtual void restore_state() = 0;
481
482 /**
483 * @brief Internal flag controlling whether notify_tick fires during state snapshots
484 * Default: false (events don't fire during isolated buffer processing)
485 * Can be exposed in future if needed via concrete implementation in parent
486 */
487 bool m_fire_events_during_snapshot = false;
488
489 /**
490 * @brief Atomic state flag tracking the node's processing status
491 *
492 * This atomic state variable tracks the node's current operational status using
493 * bit flags defined in NodeState. It indicates whether the node is:
494 * - ACTIVE: Currently part of the processing graph
495 * - PROCESSED: Has been processed in the current cycle
496 * - PENDING_REMOVAL: Marked for removal from the processing graph
497 * - MOCK_PROCESS: Should be processed but output ignored
498 *
499 * The atomic nature ensures thread-safe state transitions, allowing the audio
500 * engine to safely coordinate processing across multiple threads without data races.
501 */
502 std::atomic<NodeState> m_state { NodeState::INACTIVE };
503
504 /**
505 * @brief Counter tracking how many other nodes are using this node as a modulator
506 *
507 * This counter is incremented when another node begins using this node as a
508 * modulation source, and decremented when that relationship ends. It's critical
509 * for the node lifecycle management system, as it prevents premature state resets
510 * when a node's output is still needed by downstream nodes.
511 *
512 * When this counter is non-zero, the node's processed state will not be reset
513 * automatically after processing, ensuring that all dependent nodes can access
514 * its output before it's cleared.
515 */
516 std::atomic<uint32_t> m_modulator_count { 0 };
517
518 /**
519 * @brief Attempt to claim snapshot context for this processing cycle
520 * @param context_id Unique context identifier for this buffer processing
521 * @return true if this caller claimed the context (should call save_state)
522 *
523 * This method enables multiple NodeBuffers referencing the same node to
524 * coordinate save/restore state operations. Only the first caller per
525 * processing context will claim the snapshot, preventing nested state saves.
526 */
527 bool try_claim_snapshot_context(uint64_t context_id);
528
529 /**
530 * @brief Check if currently in a snapshot context
531 * @param context_id Context to check
532 * @return true if this context is active
533 *
534 * Used by secondary callers to detect when the primary snapshot holder
535 * has completed processing and released the context.
536 */
537 [[nodiscard]] bool is_in_snapshot_context(uint64_t context_id) const;
538
539 /**
540 * @brief Release snapshot context
541 * @param context_id Context to release
542 *
543 * Called by the snapshot owner after restore_state() completes,
544 * allowing other buffers to proceed with their own snapshots.
545 */
546 void release_snapshot_context(uint64_t context_id);
547
548 /**
549 * @brief Check if node is currently being snapshotted by any context
550 * @return true if a snapshot is in progress
551 */
552 [[nodiscard]] bool has_active_snapshot() const;
553
554 /**
555 * @brief Get the active snapshot context ID
556 * @return The current active snapshot context ID, or 0 if none
557 */
558 [[nodiscard]] inline uint64_t get_active_snapshot_context() const
559 {
560 return m_snapshot_context_id.load(std::memory_order_acquire);
561 }
562
563 /**
564 * @brief Increments the buffer reference count
565 * This method is called when a new buffer starts using this node
566 * to ensure proper lifecycle management.
567 */
568 void add_buffer_reference();
569
570 /**
571 * @brief Decrements the buffer reference count
572 * This method is called when a buffer stops using this node
573 * to ensure proper lifecycle management.
574 */
575 void remove_buffer_reference();
576
577 /**
578 * @brief Marks the node as having been processed by a buffer
579 * @return true if the buffer was successfully marked as processed
580 *
581 * This method checks if the node can be marked as processed based on
582 * the current buffer count and node state. If conditions are met,
583 * it updates the processed flag and increments the reset counter.
584 */
585 bool mark_buffer_processed();
586
587 /**
588 * @brief Requests a reset of the buffer state
589 *
590 * This method is called to signal that the buffer's processed state
591 * should be reset. It increments the reset counter, which is used to
592 * determine when it's safe to clear the processed state.
593 */
594 void request_buffer_reset();
595
596 /**
597 * @brief Checks if the buffer has been processed
598 * @return true if the buffer is marked as processed
599 */
600 [[nodiscard]] inline bool is_buffer_processed() const
601 {
602 return m_buffer_processed.load(std::memory_order_acquire);
603 }
604
605 /**
606 * @brief Sets whether the node is part of a NodeNetwork
607 * @param networked True if the node is managed within a NodeNetwork
608 *
609 * This method sets a flag indicating whether the node is part of a
610 * NodeNetwork. When set, certain behaviors such as event firing
611 * may be disabled to prevent redundant or conflicting notifications.
612 */
613 [[nodiscard]] inline bool is_in_network() const { return m_networked_node; }
614
615 /**
616 * @brief Marks the node as being part of a NodeNetwork
617 * @param networked True if the node is managed within a NodeNetwork
618 *
619 * This method sets a flag indicating whether the node is part of a
620 * NodeNetwork. When set, certain behaviors such as event firing
621 * may be disabled to prevent redundant or conflicting notifications.
622 */
623 void set_in_network(bool networked) { m_networked_node = networked; }
624
625 /**
626 * @brief Retrieves the current routing state of the network
627 * @return Reference to the current RoutingState structure
628 *
629 * This method provides access to the network's current routing state, which
630 * includes information about fade-in/out (Active) phases, channel counts, and elapsed cycles.
631 * The routing state is used to manage smooth transitions when routing changes occur,
632 * ensuring seamless audio output during dynamic reconfigurations of the processing graph.
633 */
634 [[nodiscard]] const RoutingState& get_routing_state() const { return m_routing_state; }
635
636 /**
637 * @brief Retrieves the current routing state of the network (non-const)
638 * @return Reference to the current RoutingState structure
639 */
640 RoutingState& get_routing_state() { return m_routing_state; }
641
642 /**
643 * @brief Checks if the network is currently in a routing transition phase
644 * @return true if the network is in a fade-in or fade-out (Active) phase
645 *
646 * This method checks the network's routing state to determine if it is currently
647 * undergoing a routing transition, such as fading in or out. This information
648 * can be used by processing algorithms to adjust their behavior during transitions,
649 * ensuring smooth audio output without artifacts.
650 */
651 [[nodiscard]] bool needs_channel_routing() const
652 {
653 return m_routing_state.phase & (RoutingState::ACTIVE | RoutingState::COMPLETED);
654 }
655
656 /**
657 * @brief Declare which data shapes this node's context can produce.
658 *
659 * Override to advertise capabilities beyond SCALAR. The default reflects
660 * the Node interface guarantee: every node produces a scalar.
661 * Combine flags with bitwise OR for nodes whose context implements
662 * multiple GpuContext mixins.
663 *
664 * @return Bitmask of NodeCapability flags.
665 */
666 [[nodiscard]] virtual uint8_t node_capabilities() const { return m_node_capability; }
667
668 /**
669 * @brief Query a single capability.
670 * @param cap Capability flag to test.
671 * @return true if this node supports the requested data shape.
672 */
673 [[nodiscard]] bool has_capability(NodeCapability cap) const
674 {
675 return (m_node_capability & cap) != 0U;
676 }
677
678 /**
679 * @brief Returns direct modulator nodes and their roles.
680 *
681 * Each entry pairs the role the modulator plays in this node's processing
682 * with the modulator node itself. Default returns empty. Concrete nodes
683 * override to expose their held modulator references.
684 */
685 [[nodiscard]] virtual std::vector<std::pair<ModulatorRole, std::shared_ptr<Node>>>
686 get_modulators() const { return {}; }
687
688 /**
689 * @brief Returns the full modulator tree rooted at this node.
690 *
691 * Calls get_modulators() on this node, then recursively on each returned
692 * node, building the complete tree. The caller receives the entire
693 * hierarchy in one call.
694 */
695 [[nodiscard]] std::vector<ModulatorTree> get_modulator_tree() const
696 {
697 std::vector<ModulatorTree> result;
698 for (auto& [role, node] : get_modulators()) {
699 ModulatorTree entry;
700 entry.role = role;
701 entry.node = node;
702 entry.modulators = node->get_modulator_tree();
703 result.push_back(std::move(entry));
704 }
705 return result;
706 }
707
708private:
709 /**
710 * @brief Bitmask tracking which channels are currently using this node
711 */
712 std::atomic<uint32_t> m_active_channels_mask { 0 };
713
714 /**
715 * @brief Bitmask tracking which channels have requested a reset
716 *
717 * This mask is used to track which channels have requested the node's processed
718 * state to be reset. When all channels that are currently using the node have
719 * requested a reset, the node can safely clear its processed state.
720 */
721 std::atomic<uint32_t> m_pending_reset_mask { 0 };
722
723 /**
724 * @brief Unique identifier for the current snapshot context
725 *
726 * This atomic variable holds the unique identifier of the current
727 * snapshot context that has claimed ownership of this node's state.
728 * It ensures that only one processing context can perform save/restore
729 * operations at a time, preventing nested snapshots and ensuring
730 * consistent state management.
731 */
732 std::atomic<uint64_t> m_snapshot_context_id { 0 };
733
734 /**
735 * @brief Counter tracking how many buffers are using this node
736 * This counter is incremented when a buffer starts using this node
737 * and decremented when the buffer stops using it. It helps manage
738 * the node's lifecycle in relation to buffer usage.
739 */
740 std::atomic<uint32_t> m_buffer_count { 0 };
741
742 /**
743 * @brief Flag indicating whether the buffer has been processed
744 * This atomic flag is set when the buffer has been successfully
745 * processed and is used to prevent redundant processing.
746 */
747 std::atomic<bool> m_buffer_processed { false };
748
749 /**
750 * @brief Counter tracking how many buffers have requested a reset
751 *
752 * When all buffers using this node have requested a reset, the node's
753 * processed state can be safely cleared. This counter helps coordinate
754 * that process.
755 */
756 std::atomic<uint32_t> m_buffer_reset_count { 0 };
757
758 /**
759 * @brief Internal state tracking for routing transitions
760 *
761 * This structure tracks the state of routing transitions,
762 * such as fade-in and fade-out phases, channel counts, and elapsed cycles.
763 * It's used to manage smooth transitions when routing changes occur, ensuring
764 * that audio output remains seamless during dynamic reconfigurations of the processing graph.
765 */
767};
768}
Core::GlobalInputConfig input
Definition Config.cpp:36
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:600
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:613
uint32_t get_frame_rate() const
Definition Node.hpp:369
std::vector< NodeHook > m_callbacks
Collection of standard callback functions.
Definition Node.hpp:434
void set_in_network(bool networked)
Marks the node as being part of a NodeNetwork.
Definition Node.hpp:623
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:558
const std::atomic< uint32_t > & get_channel_mask() const
Retrieves the current bitmask of active channels using this node.
Definition Node.hpp:313
bool needs_channel_routing() const
Checks if the network is currently in a routing transition phase.
Definition Node.hpp:651
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:340
void set_sample_rate(uint32_t sample_rate)
Definition Node.hpp:365
bool is_gpu_compatible() const
Checks if the node supports GPU processing.
Definition Node.hpp:354
std::vector< std::pair< NodeHook, NodeCondition > > m_conditional_callbacks
Collection of conditional callback functions with their predicates.
Definition Node.hpp:444
virtual ~Node()=default
Virtual destructor for proper cleanup of derived classes.
uint32_t get_sample_rate() const
Definition Node.hpp:366
RoutingState & get_routing_state()
Retrieves the current routing state of the network (non-const)
Definition Node.hpp:640
bool has_capability(NodeCapability cap) const
Query a single capability.
Definition Node.hpp:673
std::vector< ModulatorTree > get_modulator_tree() const
Returns the full modulator tree rooted at this node.
Definition Node.hpp:695
void set_frame_rate(uint32_t frame_rate)
Definition Node.hpp:368
virtual std::vector< std::pair< ModulatorRole, std::shared_ptr< Node > > > get_modulators() const
Returns direct modulator nodes and their roles.
Definition Node.hpp:686
std::vector< float > m_gpu_data_buffer
GPU data buffer for context objects.
Definition Node.hpp:424
RoutingState m_routing_state
Internal state tracking for routing transitions.
Definition Node.hpp:766
virtual uint8_t node_capabilities() const
Declare which data shapes this node's context can produce.
Definition Node.hpp:666
const RoutingState & get_routing_state() const
Retrieves the current routing state of the network.
Definition Node.hpp:634
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