MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
NodeNetwork.hpp
Go to the documentation of this file.
1#pragma once
2
5
7
8/**
9 * @enum Topology
10 * @brief Defines the structural relationships between nodes in the network
11 */
12enum class Topology : uint8_t {
13 INDEPENDENT, ///< No connections, nodes process independently
14 CHAIN, ///< Linear sequence: node[i] → node[i+1]
15 RING, ///< Circular: last node connects to first
16 GRID_2D, ///< 2D lattice with 4-connectivity
17 GRID_3D, ///< 3D lattice with 6-connectivity
18 SPATIAL, ///< Dynamic proximity-based (nodes within radius interact)
19 CUSTOM ///< User-defined arbitrary topology
20};
21
22/**
23 * @enum OutputMode
24 * @brief Defines how the network's computational results are exposed
25 */
26enum class OutputMode : uint8_t {
27 NONE, ///< Pure internal state, no external output
28 AUDIO_SINK, ///< Aggregated audio samples sent to output
29 AUDIO_COMPUTE, ///< processed each cycle but not sent to output
30 GRAPHICS_BIND, ///< State available for visualization (read-only)
31 CUSTOM ///< User-defined output handling via callbacks
32};
33
34/**
35 * @enum MappingMode
36 * @brief Defines how nodes map to external entities (e.g., audio channels,
37 * graphics objects)
38 */
39enum class MappingMode : uint8_t {
40 BROADCAST, ///< One node → all network nodes
41 ONE_TO_ONE ///< Node array/network → network nodes (must match count)
42};
43
44/**
45 * @class NodeNetwork
46 * @brief Abstract base class for structured collections of nodes with defined
47 * relationships
48 *
49 * DESIGN PRINCIPLES:
50 * =================
51 *
52 * 1. OWNERSHIP: Networks own their nodes exclusively. Nodes within a network
53 * cannot be independently attached to NodeGraphManager channels.
54 *
55 * 2. PROCESSING: Networks are processed directly by NodeGraphManager, parallel
56 * to RootNodes. They are NOT summed through RootNode but manage their own
57 * internal processing pipeline.
58 *
59 * 3. OUTPUT ROUTING: Networks explicitly declare their output mode:
60 * - NONE: Pure internal computation (e.g., particle physics state)
61 * - AUDIO_SINK: Contributes to audio output (aggregated samples)
62 * - GRAPHICS_BIND: Data available for visualization (read-only)
63 * - CUSTOM: User-defined handling via callbacks
64 *
65 * 4. TOPOLOGY: Networks define relationships between nodes:
66 * - INDEPENDENT: No inter-node connections
67 * - CHAIN: Linear sequence (node[i] → node[i+1])
68 * - RING: Circular (last connects to first)
69 * - GRID_2D/3D: Spatial lattice with neighbor connections
70 * - SPATIAL: Dynamic proximity-based relationships
71 * - CUSTOM: Arbitrary user-defined topology
72 *
73 * 5. EXTENSIBILITY: Subclasses define:
74 * - Internal node data structure (spatial, physical, abstract)
75 * - Interaction behavior (forces, coupling, feedback)
76 * - Aggregation logic (how to combine node outputs)
77 * - Initialization patterns (how to populate the network)
78 *
79 * PHILOSOPHY:
80 * ===========
81 *
82 * NodeNetworks embody "structure IS content" - the relationships between
83 * nodes define emergent behavior. They bridge individual node computation
84 * with collective, coordinated behavior patterns (swarms, resonances,
85 * waveguides, recursive growth).
86 *
87 * Networks are NOT:
88 * - Buffers (no sequential data storage)
89 * - Processors (no transformation pipelines)
90 * - Simple node containers (relationships matter)
91 *
92 * Networks ARE:
93 * - Relational structures for coordinated node behavior
94 * - Generators of emergent complexity from simple rules
95 * - Cross-domain abstractions (audio, visual, control unified)
96 *
97 * USAGE PATTERN:
98 * ==============
99 *
100 * ```cpp
101 * // Create network via builder or subclass constructor
102 * auto particles = std::make_shared<ParticleNetwork>(1000);
103 * particles->set_output_mode(OutputMode::GRAPHICS_BIND);
104 * particles->initialize_random_positions();
105 *
106 * // Register with NodeGraphManager (NOT RootNode)
107 * node_graph_manager->add_network(particles, ProcessingToken::VISUAL_RATE);
108 *
109 * // NodeGraphManager calls process_batch() each frame
110 * // Graphics nodes can read network state for visualization
111 * auto geom = std::make_shared<NetworkGeometryNode>(particles);
112 * ```
113 */
114class MAYAFLUX_API NodeNetwork {
115public:
116 virtual ~NodeNetwork() = default;
117
118 //-------------------------------------------------------------------------
119 // Core Abstract Interface (MUST be implemented by subclasses)
120 //-------------------------------------------------------------------------
121
122 /**
123 * @brief Process the network for the given number of samples
124 * @param num_samples Number of samples/frames to process
125 *
126 * Subclasses implement their specific processing logic:
127 * 1. Update internal state (physics, relationships, etc.)
128 * 2. Process individual nodes
129 * 3. Apply inter-node interactions
130 * 4. Aggregate outputs if needed
131 *
132 * Called by NodeGraphManager during token processing.
133 */
134 virtual void process_batch(unsigned int num_samples) = 0;
135
136 /**
137 * @brief Get the number of nodes in the network
138 * @return Total node count
139 *
140 * Used for introspection, visualization, and validation.
141 */
142 [[nodiscard]] virtual size_t get_node_count() const = 0;
143
144 //-------------------------------------------------------------------------
145 // Output Interface (Default implementations provided)
146 //-------------------------------------------------------------------------
147
148 /**
149 * @brief Get cached audio buffer from last process_batch()
150 * @return Optional vector of samples
151 *
152 * Returns the buffer generated by the most recent process_batch() call.
153 * All channels requesting this network's output get the same buffer.
154 */
155 [[nodiscard]] virtual std::optional<std::vector<double>> get_audio_buffer() const;
156
157 /**
158 * @brief Get output of specific internal node as audio buffer (for ONE_TO_ONE mapping)
159 * @param index Index of node in network
160 * @return Optional span of samples for this node, or nullopt if not applicable
161 *
162 * For networks with multiple nodes, this allows external entities to access
163 * individual node outputs as separate audio buffers. Useful for granular
164 * synthesis, multi-voice processing, etc.
165 */
166 [[nodiscard]] virtual std::optional<std::span<const double>>
167 get_node_audio_buffer(size_t /*index*/) const { return std::nullopt; }
168
169 //-------------------------------------------------------------------------
170 // Configuration (Non-virtual, base class managed)
171 //-------------------------------------------------------------------------
172
173 /**
174 * @brief Set the network's output routing mode
175 */
176 void set_output_mode(OutputMode mode) { m_output_mode = mode; }
177
178 /**
179 * @brief Get the current output routing mode
180 */
181 [[nodiscard]] OutputMode get_output_mode() const { return m_output_mode; }
182
183 /**
184 * @brief Set the network's topology
185 */
186 virtual void set_topology(Topology topology) { m_topology = topology; }
187
188 /**
189 * @brief Get the current topology
190 */
191 [[nodiscard]] Topology get_topology() const { return m_topology; }
192
193 /**
194 * @brief Enable/disable the network
195 *
196 * Disabled networks skip processing but maintain state.
197 */
198 void set_enabled(bool enabled) { m_enabled = enabled; }
199
200 /**
201 * @brief Check if network is enabled
202 */
203 [[nodiscard]] bool is_enabled() const { return m_enabled; }
204
205 //-------------------------------------------------------------------------
206 // Introspection (Optional overrides for subclass-specific data)
207 //-------------------------------------------------------------------------
208
209 /**
210 * @brief Get network metadata for debugging/visualization
211 * @return Map of property names to string representations
212 *
213 * Subclasses can override to expose internal state:
214 * - Particle count, average velocity
215 * - Modal frequencies, decay times
216 * - Waveguide delay lengths
217 * etc.
218 */
219 [[nodiscard]] virtual std::unordered_map<std::string, std::string>
220 get_metadata() const;
221
222 /**
223 * @brief Get output of specific internal node (for ONE_TO_ONE mapping)
224 * @param index Index of node in network
225 * @return Output value, or nullopt if not applicable
226 */
227 [[nodiscard]] virtual std::optional<double> get_node_output(size_t index) const
228 {
229 return std::nullopt; // Default: not supported
230 }
231
232 //-------------------------------------------------------------------------
233 // Lifecycle Hooks (Optional overrides)
234 //-------------------------------------------------------------------------
235
236 /**
237 * @brief Called once before first process_batch()
238 *
239 * Use for expensive one-time initialization:
240 * - Building neighbor maps
241 * - Allocating buffers
242 * - Computing lookup tables
243 */
244 virtual void initialize() { }
245
246 /**
247 * @brief Reset network to initial state
248 *
249 * Override to implement network-specific reset logic:
250 * - Clear particle velocities
251 * - Reset modal phases
252 * - Rebuild topology
253 */
254 virtual void reset() { }
255
256 //-------------------------------------------------------------------------
257 // Mapping Hooks
258 //-------------------------------------------------------------------------
259
260 /**
261 * @brief Map external node output to network parameter
262 * @param param_name Parameter name (network-specific, e.g., "brightness",
263 * "frequency")
264 * @param source Single node for BROADCAST
265 * @param mode Mapping mode
266 * @note Default implementation stores mapping; subclasses handle in
267 * process_batch(). This methoud SHOULD BE OVERRIDDEN by child classes that
268 * need to handle parameter mappings.
269 */
270 virtual void map_parameter(const std::string& param_name,
271 const std::shared_ptr<Node>& source,
272 MappingMode mode = MappingMode::BROADCAST);
273 /**
274 * @brief Map external node network to network parameters (ONE_TO_ONE)
275 * @param param_name Parameter name
276 * @param source_network NodeNetwork with matching node count
277 * @note Default implementation stores mapping; subclasses handle in
278 * process_batch(). This methoud SHOULD BE OVERRIDDEN by child classes that
279 * need to handle parameter mappings.
280 */
281 virtual void
282 map_parameter(const std::string& param_name,
283 const std::shared_ptr<NodeNetwork>& source_network);
284 /**
285 * @brief Remove parameter mapping
286 */
287 virtual void unmap_parameter(const std::string& param_name);
288
289 /**
290 * @brief Set the scalar multiplier applied to the network's output buffer after processing
291 * @param scale Linear scale factor (1.0 = unity, 0.0 = silence, >1.0 = amplify)
292 */
293 void set_output_scale(double scale) { m_output_scale = scale; }
294
295 /**
296 * @brief Get the current output scale factor
297 */
298 [[nodiscard]] double get_output_scale() const { return m_output_scale; }
299
300 //-------------------------------------------------------------------------
301 // Channel Registration (Mirrors Node interface)
302 //-------------------------------------------------------------------------
303
304 /**
305 * @brief Register network usage on a specific channel
306 * @param channel_id Channel index
307 *
308 * Networks can be registered to multiple channels like regular nodes.
309 * Channel registration determines where network output is routed.
310 */
311 void add_channel_usage(uint32_t channel_id);
312
313 /**
314 * @brief Unregister network from a specific channel
315 * @param channel_id Channel index
316 */
317 void remove_channel_usage(uint32_t channel_id);
318
319 /**
320 * @brief Check if network is registered on a channel
321 * @param channel_id Channel index
322 * @return true if registered on this channel
323 */
324 [[nodiscard]] bool is_registered_on_channel(uint32_t channel_id) const;
325
326 /**
327 * @brief Get all channels this network is registered on
328 * @return Vector of channel indices
329 */
330 [[nodiscard]] std::vector<uint32_t> get_registered_channels() const;
331
332 /**
333 * @brief Get channel mask (bitfield of registered channels)
334 */
335 [[nodiscard]] uint32_t get_channel_mask() const { return m_channel_mask; }
336
337 /**
338 * @brief Set channel mask directly
339 */
340 void set_channel_mask(uint32_t mask) { m_channel_mask = mask; }
341
342 /**
343 * @brief Check if network has been processed this cycle (lock-free)
344 * @return true if processed this cycle
345 */
346 [[nodiscard]] bool is_processed_this_cycle() const;
347
348 /**
349 * @brief Mark network as processing or not (lock-free)
350 */
351 void mark_processing(bool processing);
352
353 /**
354 * @brief Mark network as processed this cycle (lock-free)
355 */
356 void mark_processed(bool processed);
357
358 /**
359 * @brief Check if network is currently processing (lock-free)
360 * @return true if currently processing
361 */
362 [[nodiscard]] bool is_processing() const;
363
364 /**
365 * @brief Request a reset from a specific channel
366 * @param channel_id Channel index
367 */
368 void request_reset_from_channel(uint32_t channel_id);
369
370 virtual NetworkOperator* get_operator() { return nullptr; }
371 virtual const NetworkOperator* get_operator() const { return nullptr; }
372
373 virtual bool has_operator() const { return false; }
374
375 /**
376 * @brief Retrieves the current routing state of the network
377 * @return Reference to the current RoutingState structure
378 *
379 * This method provides access to the network's current routing state, which
380 * includes information about fade-in/out (Active) phases, channel counts, and elapsed cycles.
381 * The routing state is used to manage smooth transitions when routing changes occur,
382 * ensuring seamless audio output during dynamic reconfigurations of the processing graph.
383 */
384 [[nodiscard]] const RoutingState& get_routing_state() const { return m_routing_state; }
385
386 /**
387 * @brief Retrieves the current routing state of the network (non-const)
388 * @return Reference to the current RoutingState structure
389 */
390 RoutingState& get_routing_state() { return m_routing_state; }
391
392 /**
393 * @brief Checks if the network is currently in a routing transition phase
394 * @return true if the network is in a fade-in or fade-out (Active) phase
395 *
396 * This method checks the network's routing state to determine if it is currently
397 * undergoing a routing transition, such as fading in or out. This information
398 * can be used by processing algorithms to adjust their behavior during transitions,
399 * ensuring smooth audio output without artifacts.
400 */
401 [[nodiscard]] bool needs_channel_routing() const
402 {
403 return m_routing_state.phase & (RoutingState::ACTIVE | RoutingState::COMPLETED);
404 }
405
406 void set_sample_rate(uint32_t sample_rate) { m_sample_rate = sample_rate; }
407 [[nodiscard]] uint32_t get_sample_rate() const { return m_sample_rate; }
408 void set_block_size(uint32_t block_size) { m_block_size = block_size; }
409 [[nodiscard]] uint32_t get_block_size() const { return m_block_size; }
410
411protected:
412 //-------------------------------------------------------------------------
413 // Protected State (Accessible to subclasses)
414 //-------------------------------------------------------------------------
415
416 Topology m_topology = Topology::INDEPENDENT;
417 OutputMode m_output_mode = OutputMode::NONE;
418 bool m_enabled = true;
419 bool m_initialized = false;
420 uint32_t m_sample_rate { 48000 };
421 uint32_t m_block_size { 512 };
422
423 /**
424 * @brief Ensure initialize() is called exactly once
425 */
426 void ensure_initialized();
427
428 //-------------------------------------------------------------------------
429 // Utility Functions (Helper methods for subclasses)
430 //-------------------------------------------------------------------------
431
432 /**
433 * @brief Build neighbor map for GRID_2D topology
434 * @param width Grid width
435 * @param height Grid height
436 * @return Map of node index to neighbor indices (4-connectivity)
437 */
438 static std::unordered_map<size_t, std::vector<size_t>>
439 build_grid_2d_neighbors(size_t width, size_t height);
440
441 /**
442 * @brief Build neighbor map for GRID_3D topology
443 * @param width Grid width
444 * @param height Grid height
445 * @param depth Grid depth
446 * @return Map of node index to neighbor indices (6-connectivity)
447 */
448 static std::unordered_map<size_t, std::vector<size_t>>
449 build_grid_3d_neighbors(size_t width, size_t height, size_t depth);
450
451 /**
452 * @brief Build neighbor map for RING topology
453 * @param count Total node count
454 * @return Map of node index to [prev, next] indices
455 */
456 static std::unordered_map<size_t, std::vector<size_t>>
458
459 /**
460 * @brief Build neighbor map for CHAIN topology
461 * @param count Total node count
462 * @return Map of node index to [next] or [prev] index
463 */
464 static std::unordered_map<size_t, std::vector<size_t>>
466
467 /**
468 * @brief Apply m_output_scale to m_last_audio_buffer
469 *
470 * Call at the end of each concrete process_batch() after all samples are written.
471 */
472 void apply_output_scale();
473
475 std::string param_name;
477
478 // Storage for mapped sources
479 std::shared_ptr<Node> broadcast_source; // For BROADCAST
480 std::shared_ptr<NodeNetwork> network_source; // For ONE_TO_ONE
481 };
482
483 std::vector<ParameterMapping> m_parameter_mappings;
484
485 //-------------------------------------------------------------------------
486 // Channel State (Same as Node)
487 //-------------------------------------------------------------------------
488
489 /// Bitfield of channels this network is registered on
490 std::atomic<uint32_t> m_channel_mask { 0 };
491 std::atomic<uint32_t> m_pending_reset_mask { 0 };
492
493 /// Per-channel processing state (lock-free atomic flags)
494 std::atomic<bool> m_processing_state { false };
495 std::atomic<bool> m_processed_this_cycle { false };
496
497 // Cached buffer from last process_batch() call
498 mutable std::vector<double> m_last_audio_buffer;
499 double m_output_scale { 1.0 }; ///< Post-processing scalar applied to m_last_audio_buffer each batch
500
501private:
502 //-------------------------------------------------------------------------
503 // String Conversion (For metadata/debugging)
504 //-------------------------------------------------------------------------
505
506 static std::string topology_to_string(Topology topo);
507
508 static std::string output_mode_to_string(OutputMode mode);
509
511};
512
513} // namespace MayaFlux::Nodes::Network
Eigen::Index count
Domain-agnostic interpretive lens for network processing.
double get_output_scale() const
Get the current output scale factor.
virtual void reset()
Reset network to initial state.
std::vector< ParameterMapping > m_parameter_mappings
Topology get_topology() const
Get the current topology.
OutputMode get_output_mode() const
Get the current output routing mode.
static std::unordered_map< size_t, std::vector< size_t > > build_grid_3d_neighbors(size_t width, size_t height, size_t depth)
Build neighbor map for GRID_3D topology.
virtual void set_topology(Topology topology)
Set the network's topology.
virtual void initialize()
Called once before first process_batch()
bool is_enabled() const
Check if network is enabled.
void set_output_scale(double scale)
Set the scalar multiplier applied to the network's output buffer after processing.
void set_block_size(uint32_t block_size)
virtual std::optional< double > get_node_output(size_t index) const
Get output of specific internal node (for ONE_TO_ONE mapping)
virtual void process_batch(unsigned int num_samples)=0
Process the network for the given number of samples.
void set_sample_rate(uint32_t sample_rate)
const RoutingState & get_routing_state() const
Retrieves the current routing state of the network.
static std::unordered_map< size_t, std::vector< size_t > > build_ring_neighbors(size_t count)
Build neighbor map for RING topology.
bool needs_channel_routing() const
Checks if the network is currently in a routing transition phase.
void set_channel_mask(uint32_t mask)
Set channel mask directly.
static std::unordered_map< size_t, std::vector< size_t > > build_chain_neighbors(size_t count)
Build neighbor map for CHAIN topology.
uint32_t get_channel_mask() const
Get channel mask (bitfield of registered channels)
static std::unordered_map< size_t, std::vector< size_t > > build_grid_2d_neighbors(size_t width, size_t height)
Build neighbor map for GRID_2D topology.
virtual std::optional< std::span< const double > > get_node_audio_buffer(size_t) const
Get output of specific internal node as audio buffer (for ONE_TO_ONE mapping)
RoutingState & get_routing_state()
Retrieves the current routing state of the network (non-const)
virtual const NetworkOperator * get_operator() const
std::vector< double > m_last_audio_buffer
virtual size_t get_node_count() const =0
Get the number of nodes in the network.
virtual NetworkOperator * get_operator()
void set_enabled(bool enabled)
Enable/disable the network.
void set_output_mode(OutputMode mode)
Set the network's output routing mode.
Abstract base class for structured collections of nodes with defined relationships.
Topology
Defines the structural relationships between nodes in the network.
@ CHAIN
Linear sequence: node[i] → node[i+1].
@ GRID_2D
2D lattice with 4-connectivity
@ INDEPENDENT
No connections, nodes process independently.
@ CUSTOM
User-defined arbitrary topology.
@ GRID_3D
3D lattice with 6-connectivity
@ SPATIAL
Dynamic proximity-based (nodes within radius interact)
@ RING
Circular: last node connects to first.
MappingMode
Defines how nodes map to external entities (e.g., audio channels, graphics objects)
@ ONE_TO_ONE
Node array/network → network nodes (must match count)
@ BROADCAST
One node → all network nodes.
OutputMode
Defines how the network's computational results are exposed.
@ GRAPHICS_BIND
State available for visualization (read-only)
@ AUDIO_COMPUTE
processed each cycle but not sent to output
@ NONE
Pure internal state, no external output.
@ AUDIO_SINK
Aggregated audio samples sent to output.
Represents the state of routing transitions for a node.
Definition NodeSpec.hpp:64