MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
NodeGraphManager.cpp
Go to the documentation of this file.
2
4#include "NodeStructure.hpp"
5
7
8namespace MayaFlux::Nodes {
9
14
15void NodeGraphManager::add_to_root(const std::shared_ptr<Node>& node,
17 unsigned int channel)
18{
19 set_channel_mask(node, channel);
20
21 auto& root = get_root_node(token, channel);
22 root.register_node(node);
23}
24
25void NodeGraphManager::remove_from_root(const std::shared_ptr<Node>& node,
27 unsigned int channel)
28{
29 unset_channel_mask(node, channel);
30
31 auto& root = get_root_node(token, channel);
32 root.unregister_node(node);
33}
34
36 std::function<void(std::span<RootNode*>)> processor)
37{
38 m_token_processors[token] = std::move(processor);
39}
40
41const std::unordered_map<unsigned int, std::shared_ptr<RootNode>>& NodeGraphManager::get_all_channel_root_nodes(ProcessingToken token) const
42{
43 static std::unordered_map<unsigned int, std::shared_ptr<RootNode>> audio_roots;
44 audio_roots.clear();
45
46 auto it = m_token_roots.find(token);
47 if (it != m_token_roots.end()) {
48 for (const auto& [channel, root] : it->second) {
49 audio_roots[channel] = root;
50 }
51 }
52 return audio_roots;
53}
54
56{
57 auto& processing_ptr = m_token_network_processing[token];
58
59 if (!processing_ptr) {
60 processing_ptr = std::make_unique<std::atomic<bool>>(false);
61 }
62
63 bool expected = false;
64 return processing_ptr->compare_exchange_strong(
65 expected, true,
66 std::memory_order_acquire,
67 std::memory_order_relaxed);
68}
69
71{
72 if (m_terminate_requested.load())
73 return;
74
75 auto roots = get_all_root_nodes(token);
76
77 if (auto it = m_token_processors.find(token); it != m_token_processors.end()) {
78 it->second(std::span<RootNode*>(roots.data(), roots.size()));
79 return;
80 }
81
83 return;
84 }
85
86 auto it = m_token_networks.find(token);
87 if (it != m_token_networks.end()) {
88 for (auto& network : it->second) {
89 if (!network || !network->is_enabled()) {
90 continue;
91 }
92
93 if (!network->is_processed_this_cycle()) {
94 network->mark_processing(true);
95 network->process_batch(num_samples);
96 network->mark_processing(false);
97 network->mark_processed(true);
98 }
99 }
100 }
101
102 postprocess_networks(token, std::nullopt);
103
105 for (auto* root : roots) {
106 root->process_batch(num_samples);
107 }
108 } else if (token == ProcessingToken::VISUAL_RATE) {
109 for (auto* root : roots) {
110 root->process_batch_frame(num_samples);
111 }
112 }
113}
114
115std::vector<std::vector<double>> NodeGraphManager::process_audio_networks(ProcessingToken token, uint32_t num_samples, uint32_t channel)
116{
118 return {};
119 }
120
121 std::vector<std::vector<double>> all_network_outputs;
122
123 auto audio_it = m_audio_networks.find(token);
124 if (audio_it != m_audio_networks.end()) {
125 for (auto& network : audio_it->second) {
126 if (!network || !network->is_enabled()) {
127 continue;
128 }
129
130 if (!network->is_registered_on_channel(channel)) {
131 continue;
132 }
133
134 if (!network->is_processed_this_cycle()) {
135 network->mark_processing(true);
136 network->process_batch(num_samples);
137 network->mark_processing(false);
138 network->mark_processed(true);
139 }
140
141 const auto& net_buffer = network->get_audio_buffer();
142 if (net_buffer) {
143 all_network_outputs.push_back(*net_buffer);
144 }
145 }
146 }
147
148 postprocess_networks(token, channel);
149 return all_network_outputs;
150}
151
153{
154 if (token == ProcessingToken::AUDIO_RATE && channel.has_value()) {
155 auto ch = channel.value_or(0U);
157 } else if (auto it = m_token_networks.find(token); it != m_token_networks.end()) {
158 for (auto& network : it->second) {
159 if (network && network->is_enabled()) {
160 network->mark_processed(false);
161 }
162 }
163 }
164
165 if (auto it = m_token_network_processing.find(token); it != m_token_network_processing.end()) {
166 it->second->store(false, std::memory_order_release);
167 }
168}
169
171{
172 auto audio_it = m_audio_networks.find(token);
173 if (audio_it != m_audio_networks.end()) {
174 for (auto& network : audio_it->second) {
175 if (network) {
176 if (network->is_registered_on_channel(channel)) {
177 network->request_reset_from_channel(channel);
178 }
179 }
180 }
181 }
182}
183
189
195
197 unsigned int channel, unsigned int num_samples)
198{
199 if (channel == 0) {
201 }
202
203 auto& root = get_root_node(token, channel);
204
205 if (auto it = m_token_channel_processors.find(token); it != m_token_channel_processors.end()) {
206 return it->second(&root, num_samples);
207 }
208
209 std::vector<double> samples = root.process_batch(num_samples);
210
211 uint32_t normalize_coef = root.get_node_size();
212 for (double& sample : samples) {
213 normalize_sample(sample, normalize_coef);
214 }
215 return samples;
216}
217
219{
220 if (m_terminate_requested.load())
221 return 0.0;
222
223 auto& root = get_root_node(token, channel);
224
225 if (auto it = m_token_sample_processors.find(token); it != m_token_sample_processors.end()) {
226 return it->second(&root, channel);
227 }
228
229 double sample = root.process_sample();
230 normalize_sample(sample, root.get_node_size());
231 return sample;
232}
233
234void NodeGraphManager::normalize_sample(double& sample, uint32_t num_nodes)
235{
236 if (num_nodes == 0)
237 return;
238
239 sample /= std::sqrt(static_cast<double>(num_nodes));
240
241 const double threshold = 0.95;
242 const double knee = 0.1;
243 const double abs_sample = std::abs(sample);
244
245 if (abs_sample > threshold) {
246 const double excess = abs_sample - threshold;
247 const double compressed_excess = std::tanh(excess / knee) * knee;
248 const double limited_abs = threshold + compressed_excess;
249 sample = std::copysign(limited_abs, sample);
250 }
251}
252
253std::unordered_map<unsigned int, std::vector<double>> NodeGraphManager::process_token_with_channel_data(
254 ProcessingToken token, unsigned int num_samples)
255{
256 std::unordered_map<unsigned int, std::vector<double>> channel_data;
257
258 auto channels = get_all_channels(token);
259
260 for (unsigned int channel : channels) {
261 channel_data[channel] = process_channel(token, channel, num_samples);
262 }
263
264 return channel_data;
265}
266
268{
269 auto channels = get_all_channels(token);
270 return static_cast<unsigned int>(channels.size());
271}
272
274{
275 std::vector<RootNode*> roots;
276
277 auto it = m_token_roots.find(token);
278 if (it != m_token_roots.end()) {
279 for (auto& [channel, root] : it->second) {
280 roots.push_back(root.get());
281 }
282 }
283
284 return roots;
285}
286
287void NodeGraphManager::process_all_tokens(unsigned int num_samples)
288{
289 for (auto token : get_active_tokens()) {
290 process_token(token, num_samples);
291 }
292}
293
295{
296 ensure_root_exists(token, channel);
297 return *m_token_roots[token][channel];
298}
299
301{
302 if (m_token_roots[token].find(channel) == m_token_roots[token].end()) {
303 m_token_roots[token][channel] = std::make_shared<RootNode>(token, channel);
304 }
305}
306
308{
309 for (uint32_t ch = 0; ch < num_channels; ++ch) {
311 }
312}
313
314void NodeGraphManager::register_global(const std::shared_ptr<Node>& node)
315{
316 if (!is_node_registered(node)) {
317 std::stringstream ss;
318 ss << "node_" << node.get();
319 std::string generated_id = ss.str();
320 m_Node_registry[generated_id] = node;
321 }
322}
323
324void NodeGraphManager::set_channel_mask(const std::shared_ptr<Node>& node, uint32_t channel_id)
325{
326 register_global(node);
327 node->register_channel_usage(channel_id);
328}
329
330void NodeGraphManager::unregister_global(const std::shared_ptr<Node>& node)
331{
332 for (const auto& pair : m_Node_registry) {
333 if (pair.second == node) {
334 m_Node_registry.erase(pair.first);
335 break;
336 }
337 }
338}
339
340void NodeGraphManager::unset_channel_mask(const std::shared_ptr<Node>& node, uint32_t channel_id)
341{
342 unregister_global(node);
343 node->unregister_channel_usage(channel_id);
344}
345
346std::vector<ProcessingToken> NodeGraphManager::get_active_tokens() const
347{
348 std::vector<ProcessingToken> tokens;
349 for (const auto& [token, channels] : m_token_roots) {
350 if (!channels.empty()) {
351 tokens.push_back(token);
352 }
353 }
354 return tokens;
355}
356
358{
359 std::vector<unsigned int> channels;
360 auto it = m_token_roots.find(token);
361 if (it != m_token_roots.end()) {
362 for (const auto& [channel, root] : it->second) {
363 channels.push_back(channel);
364 }
365 }
366 return channels;
367}
368
370{
371 size_t count = 0;
372 auto it = m_token_roots.find(token);
373 if (it != m_token_roots.end()) {
374 for (const auto& [channel, root] : it->second) {
375 count += root->get_node_size();
376 }
377 }
378 return count;
379}
380
382{
385 "=== NodeGraphManager Summary ===");
386
387 for (auto token : get_active_tokens()) {
388 auto channels = get_all_channels(token);
389 size_t total_nodes = get_node_count(token);
390
393 "Token {}: {} nodes across {} channels",
394 static_cast<int>(token), total_nodes, channels.size());
395
396 for (auto channel : channels) {
397 auto& root = const_cast<NodeGraphManager*>(this)->get_root_node(token, channel);
398 auto networks = get_networks(token, channel);
399
402 " Channel {}: {} nodes, {} networks",
403 channel, root.get_node_size(), networks.size());
404
405 for (const auto& network : networks) {
406 if (network) {
409 " Network: {} internal nodes, mode={}, enabled={}",
410 network->get_node_count(),
411 static_cast<int>(network->get_output_mode()),
412 network->is_enabled());
413 }
414 }
415 }
416 }
417}
418
419std::shared_ptr<Node> NodeGraphManager::get_node(const std::string& id)
420{
421 auto it = m_Node_registry.find(id);
422
423 if (it != m_Node_registry.end()) {
424 return it->second;
425 }
426 return nullptr;
427}
428
429bool NodeGraphManager::is_node_registered(const std::shared_ptr<Node>& node)
430{
431 return std::ranges::any_of(m_Node_registry,
432 [&node](const auto& pair) { return pair.second == node; });
433}
434
435void NodeGraphManager::connect(const std::string& source_id, const std::string& target_id)
436{
437 auto source = get_node(source_id);
438 auto target = get_node(target_id);
439
440 if (source && target) {
441 source >> target;
442 }
443}
444
445//-----------------------------------------------------------------------------
446// NodeNetwork Management
447//-----------------------------------------------------------------------------
448
449void NodeGraphManager::add_network(const std::shared_ptr<Network::NodeNetwork>& network,
451{
452 if (!network) {
453 return;
454 }
455
457
458 network->set_enabled(true);
459
460 if (network->get_output_mode() == Network::NodeNetwork::OutputMode::AUDIO_SINK) {
461 uint32_t channel_mask = network->get_channel_mask();
462
463 if (channel_mask == 0) {
464 channel_mask = 1;
465 network->add_channel_usage(0);
466 }
467
468 auto channels = network->get_registered_channels();
469 m_audio_networks[token].push_back(network);
470
471 for (auto ch : channels) {
475 "Added audio network to token {} channel {}: {} nodes",
476 static_cast<int>(token), ch, network->get_node_count());
477 }
478
479 } else {
480 m_token_networks[token].push_back(network);
481
484 "Added network to token {}: {} nodes, mode={}",
485 static_cast<int>(token),
486 network->get_node_count(),
487 static_cast<int>(network->get_output_mode()));
488 }
489}
490
491void NodeGraphManager::remove_network(const std::shared_ptr<Network::NodeNetwork>& network,
493{
494 if (!network) {
495 return;
496 }
497
498 if (network->get_output_mode() == Network::NodeNetwork::OutputMode::AUDIO_SINK) {
499 auto token_it = m_audio_networks.find(token);
500 if (token_it != m_audio_networks.end()) {
501 auto& networks = token_it->second;
502 networks.erase(
503 std::remove(networks.begin(), networks.end(), network),
504 networks.end());
505 }
506 } else {
507 auto it = m_token_networks.find(token);
508 if (it != m_token_networks.end()) {
509 auto& networks = it->second;
510 networks.erase(
511 std::remove(networks.begin(), networks.end(), network),
512 networks.end());
513 }
514 }
515
517}
518
519std::vector<std::shared_ptr<Network::NodeNetwork>>
521{
522 auto token_it = m_audio_networks.find(token);
523 if (token_it != m_audio_networks.end()) {
524 std::vector<std::shared_ptr<Network::NodeNetwork>> networks_on_channel;
525 for (const auto& network : token_it->second) {
526 if (network && network->is_registered_on_channel(channel)) {
527 networks_on_channel.push_back(network);
528 }
529 }
530 return networks_on_channel;
531 }
532 return {};
533}
534
535std::vector<std::shared_ptr<Network::NodeNetwork>>
537{
538 std::vector<std::shared_ptr<Network::NodeNetwork>> all_networks;
539
540 auto audio_it = m_audio_networks.find(token);
541 if (audio_it != m_audio_networks.end()) {
542 all_networks.insert(all_networks.end(),
543 audio_it->second.begin(),
544 audio_it->second.end());
545 }
546
547 auto token_it = m_token_networks.find(token);
548 if (token_it != m_token_networks.end()) {
549 all_networks.insert(all_networks.end(),
550 token_it->second.begin(),
551 token_it->second.end());
552 }
553
554 return all_networks;
555}
556
558{
559 size_t count = 0;
560
561 auto audio_it = m_audio_networks.find(token);
562 if (audio_it != m_audio_networks.end()) {
563 count += audio_it->second.size();
564 }
565
566 auto token_it = m_token_networks.find(token);
567 if (token_it != m_token_networks.end()) {
568 count += token_it->second.size();
569 }
570
571 return count;
572}
573
579
580void NodeGraphManager::register_network_global(const std::shared_ptr<Network::NodeNetwork>& network)
581{
582 if (!is_network_registered(network)) {
583 std::stringstream ss;
584 ss << "network_" << network.get();
585 std::string generated_id = ss.str();
586 m_network_registry[generated_id] = network;
587 }
588}
589
590void NodeGraphManager::unregister_network_global(const std::shared_ptr<Network::NodeNetwork>& network)
591{
592 for (const auto& pair : m_network_registry) {
593 if (pair.second == network) {
594 m_network_registry.erase(pair.first);
595 break;
596 }
597 }
598}
599
600bool NodeGraphManager::is_network_registered(const std::shared_ptr<Network::NodeNetwork>& network)
601{
602 return std::ranges::any_of(m_network_registry,
603 [&network](const auto& pair) { return pair.second == network; });
604}
605
607{
608 if (m_terminate_requested.load())
609 return;
610
611 for (auto& [token, networks] : m_audio_networks) {
612 for (auto& network : networks) {
613 if (network) {
615 }
616 }
617 }
618
619 for (auto& [token, networks] : m_token_networks) {
620 for (auto& network : networks) {
621 if (network) {
623 }
624 }
625 }
626
627 m_terminate_requested.store(true);
628
629 for (auto token : get_active_tokens()) {
630 auto roots = get_all_root_nodes(token);
631 for (auto* root : roots) {
632 root->terminate_all_nodes();
633 }
634 }
635}
636
647
648}
#define MF_INFO(comp, ctx,...)
#define MF_PRINT(comp, ctx,...)
static MayaFlux::Nodes::ProcessingToken token
Definition Timers.cpp:8
@ AUDIO_SINK
Aggregated audio samples sent to output.
void register_token_processor(ProcessingToken token, std::function< void(std::span< RootNode * >)> processor)
Register subsystem processor for a specific token.
void remove_network(const std::shared_ptr< Network::NodeNetwork > &network, ProcessingToken token)
Remove a network from a processing token.
void register_token_sample_processor(ProcessingToken token, TokenSampleProcessor processor)
Register per-sample processor for a specific token.
void terminate_active_processing()
Terminates all active processing across all tokens and channels.
void clear_networks(ProcessingToken token)
Clear all networks from a token.
void print_summary() const
Prints a summary of all tokens, channels, and node counts.
const std::unordered_map< unsigned int, std::shared_ptr< RootNode > > & get_all_channel_root_nodes(ProcessingToken token=ProcessingToken::AUDIO_RATE) const
Gets all channel root nodes for the AUDIO_RATE domain.
std::vector< std::vector< double > > process_audio_networks(ProcessingToken token, uint32_t num_samples, uint32_t channel=0)
Process audio networks for a specific channel.
std::unordered_map< ProcessingToken, std::vector< std::shared_ptr< Network::NodeNetwork > > > m_token_networks
Non-audio networks (token-level processing) For NONE, GRAPHICS_BIND, CUSTOM output modes.
std::atomic< bool > m_terminate_requested
Global termination flag.
std::unordered_map< ProcessingToken, std::function< void(std::span< RootNode * >)> > m_token_processors
Registered custom processors for each processing token.
void add_network(const std::shared_ptr< Network::NodeNetwork > &network, ProcessingToken token)
Add a network to a processing token.
size_t get_network_count(ProcessingToken token) const
Get count of networks for a token.
bool preprocess_networks(ProcessingToken token)
Preprocess networks for a specific token.
void normalize_sample(double &sample, uint32_t num_nodes)
Normalizes a sample to the range [-1, 1] based on the number of nodes.
size_t get_node_count(ProcessingToken token) const
Gets the total number of nodes registered under a given token.
void unregister_global(const std::shared_ptr< Node > &node)
Unregisters a node globally.
unsigned int get_channel_count(ProcessingToken token) const
Get the number of active channels for a specific token.
void unset_channel_mask(const std::shared_ptr< Node > &node, uint32_t channel_id)
Unsets the specified channel mask from a node's global registration.
std::unordered_map< std::string, std::shared_ptr< Network::NodeNetwork > > m_network_registry
Global network registry (like m_Node_registry)
void reset_audio_network_state(ProcessingToken token, uint32_t channel=0)
Resets the processing state of audio networks for a token and channel.
void process_all_tokens(unsigned int num_samples=1)
Process all active tokens sequentially.
void register_global(const std::shared_ptr< Node > &node)
Registers a node globally if not already registered.
std::unordered_map< ProcessingToken, TokenSampleProcessor > m_token_sample_processors
Per-sample processors for each processing token.
std::unordered_map< std::string, std::shared_ptr< Node > > m_Node_registry
Registry of all nodes by their string identifiers.
std::vector< double > process_channel(ProcessingToken token, unsigned int channel, unsigned int num_samples)
Process a specific channel within a token domain.
void register_network_global(const std::shared_ptr< Network::NodeNetwork > &network)
Register network globally (like nodes)
void postprocess_networks(ProcessingToken token, std::optional< uint32_t > channel)
Postprocess networks for a specific token and channel.
void ensure_root_exists(ProcessingToken token, unsigned int channel)
Ensures a root node exists for the given token and channel.
double process_sample(ProcessingToken token, uint32_t channel)
Process a single sample for a specific channel.
void connect(const std::string &source_id, const std::string &target_id)
Connects two nodes by their string identifiers.
void set_channel_mask(const std::shared_ptr< Node > &node, uint32_t channel_id)
Adds the specified channel mask to a node's global registration.
void add_to_root(const std::shared_ptr< Node > &node, ProcessingToken token, unsigned int channel=0)
Add node to specific processing token and channel.
RootNode & get_root_node(ProcessingToken token, unsigned int channel)
Gets or creates the root node for a specific token and channel.
NodeGraphManager()
Creates a new NodeGraphManager.
bool is_node_registered(const std::shared_ptr< Node > &node)
Checks if a node is registered with this manager.
std::unordered_map< ProcessingToken, std::unordered_map< unsigned int, std::shared_ptr< RootNode > > > m_token_roots
Multi-modal map of processing tokens to their channel root nodes.
std::vector< std::shared_ptr< Network::NodeNetwork > > get_all_networks(ProcessingToken token) const
Get all networks for a specific token across all channels.
~NodeGraphManager()
Destroys the NodeGraphManager.
void remove_from_root(const std::shared_ptr< Node > &node, ProcessingToken token, unsigned int channel=0)
Remove node from a specific processing token and channel.
std::unordered_map< unsigned int, std::vector< double > > process_token_with_channel_data(ProcessingToken token, unsigned int num_samples)
Process all channels for a token and return channel-separated data.
std::unordered_map< ProcessingToken, std::vector< std::shared_ptr< Network::NodeNetwork > > > m_audio_networks
Audio-sink networks Only populated for networks with OutputMode::AUDIO_SINK.
std::shared_ptr< Node > get_node(const std::string &id)
Looks up a node by its string identifier.
void unregister_network_global(const std::shared_ptr< Network::NodeNetwork > &network)
Unregister network globally.
std::vector< ProcessingToken > get_active_tokens() const
Gets all currently active processing tokens (domains)
std::unordered_map< ProcessingToken, TokenChannelProcessor > m_token_channel_processors
Per-channel processors for each processing token.
void ensure_token_exists(ProcessingToken token, uint32_t num_channels=1)
Ensures that a processing token entry exists.
bool is_network_registered(const std::shared_ptr< Network::NodeNetwork > &network)
Check if network is registered globally.
std::unordered_map< ProcessingToken, std::unique_ptr< std::atomic< bool > > > m_token_network_processing
Processing flags for each token's networks.
std::vector< std::shared_ptr< Network::NodeNetwork > > get_networks(ProcessingToken token, uint32_t channel=0) const
Get all networks for a specific token.
std::vector< RootNode * > get_all_root_nodes(ProcessingToken token)
Get spans of root nodes for a token (for custom processing)
std::vector< unsigned int > get_all_channels(ProcessingToken token) const
Gets all channel indices for a given processing token.
void process_token(ProcessingToken token, unsigned int num_samples=1)
Process all nodes in a specific token domain Calls registered processor if available,...
void register_token_channel_processor(ProcessingToken token, TokenChannelProcessor processor)
Register per-channel processor for a specific token.
Central manager for the computational processing node graph.
Container for top-level nodes in a processing channel with multi-modal support.
Definition RootNode.hpp:29
@ NodeProcessing
Node graph processing (Nodes::NodeGraphManager)
@ Nodes
DSP Generator and Filter Nodes, graph pipeline, node management.
ProcessingToken
Enumerates the different processing domains for nodes.
@ AUDIO_RATE
Nodes that process at the audio sample rate.
@ VISUAL_RATE
Nodes that process at the visual frame rate.
std::function< double(RootNode *, uint32_t)> TokenSampleProcessor
std::function< std::vector< double >(RootNode *, uint32_t)> TokenChannelProcessor
Contains the node-based computational processing system components.
Definition Chronie.hpp:5