MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
NodeNetwork.cpp
Go to the documentation of this file.
1#include "NodeNetwork.hpp"
2
5
7
9{
10 if (!m_initialized) {
11 initialize();
12 m_initialized = true;
13 }
14}
15
16std::optional<std::vector<double>> NodeNetwork::get_audio_buffer() const
17{
19 return std::nullopt;
20
21 while (m_audio_buffer_lock.test_and_set(std::memory_order_acquire))
22 std::this_thread::yield();
23
24 auto result = m_last_audio_buffer.empty()
25 ? std::nullopt
26 : std::optional<std::vector<double>> { m_last_audio_buffer };
27 m_audio_buffer_lock.clear(std::memory_order_release);
28
29 return result;
30}
31
33{
34 if (m_output_scale == 1.0)
35 return;
36
37 for (auto& s : m_last_audio_buffer)
38 s *= m_output_scale;
39}
40
42 const std::shared_ptr<Nodes::Node>& node,
43 std::vector<double>& buffer,
44 size_t& buffer_pos,
45 size_t num_samples)
46{
47 buffer.assign(num_samples, 0.0);
48 buffer_pos = 0;
49
50 if (!node)
51 return;
52
53 static std::atomic<uint64_t> s_ctx { 1 };
54 uint64_t my_ctx = s_ctx.fetch_add(1, std::memory_order_relaxed);
55
56 const auto state = node->m_state.load(std::memory_order_acquire);
57
58 if (state == NodeState::INACTIVE && !node->is_buffer_processed()) {
59 for (size_t i = 0; i < num_samples; ++i)
60 buffer[i] = node->process_sample(0.0);
61 node->mark_buffer_processed();
62 return;
63 }
64
65 bool claimed = node->try_claim_snapshot_context(my_ctx);
66
67 if (claimed) {
68 try {
69 node->save_state();
70 for (size_t i = 0; i < num_samples; ++i)
71 buffer[i] = node->process_sample(0.0);
72 node->restore_state();
73 if (node->is_buffer_processed())
74 node->request_buffer_reset();
75 node->release_snapshot_context(my_ctx);
76 } catch (...) {
77 node->release_snapshot_context(my_ctx);
78 buffer.assign(num_samples, 0.0);
79 }
80 } else {
81 uint64_t active = node->get_active_snapshot_context();
83 return;
84 node->save_state();
85 for (size_t i = 0; i < num_samples; ++i)
86 buffer[i] = node->process_sample(0.0);
87 node->restore_state();
88 if (node->is_buffer_processed())
89 node->request_buffer_reset();
90 }
91}
92
93[[nodiscard]] std::unordered_map<std::string, std::string>
95{
96 return { { "topology", topology_to_string(m_topology) },
97 { "output_mode", output_mode_to_string(m_output_mode) },
98 { "node_count", std::to_string(get_node_count()) },
99 { "enabled", m_enabled ? "true" : "false" } };
100}
101
102void NodeNetwork::map_parameter(const std::string& param_name,
103 const std::shared_ptr<Node>& source,
104 MappingMode mode)
105{
106 m_parameter_mappings.push_back({ .param_name = param_name,
107 .mode = mode,
108 .broadcast_source = source,
109 .network_source = nullptr });
110}
111
112void NodeNetwork::map_parameter(const std::string& param_name,
113 const std::shared_ptr<NodeNetwork>& source_network)
114{
115 m_parameter_mappings.push_back(
116 { .param_name = param_name,
118 .broadcast_source = nullptr,
119 .network_source = source_network });
120}
121
122void NodeNetwork::unmap_parameter(const std::string& param_name)
123{
124 std::erase_if(m_parameter_mappings,
125 [&](const auto& m) { return m.param_name == param_name; });
126}
127
129{
130 return m_processing_state.load(std::memory_order_acquire);
131}
132
133void NodeNetwork::add_channel_usage(uint32_t channel_id)
134{
135 if (channel_id < 32) {
136 m_channel_mask |= (1U << channel_id);
137 }
138}
139
140void NodeNetwork::remove_channel_usage(uint32_t channel_id)
141{
142 if (channel_id < 32) {
143 m_channel_mask &= ~(1U << channel_id);
144 }
145}
146
147bool NodeNetwork::is_registered_on_channel(uint32_t channel_id) const
148{
149 if (channel_id < 32) {
150 return (m_channel_mask & (1U << channel_id)) != 0;
151 }
152 return false;
153}
154
155std::vector<uint32_t> NodeNetwork::get_registered_channels() const
156{
157 std::vector<uint32_t> channels;
158 for (uint32_t i = 0; i < 32; ++i) {
159 if (m_channel_mask & (1U << i)) {
160 channels.push_back(i);
161 }
162 }
163 return channels;
164}
165
166void NodeNetwork::mark_processing(bool processing)
167{
168 m_processing_state.store(processing, std::memory_order_release);
169}
170
172{
173 return m_processed_this_cycle.load(std::memory_order_acquire);
174}
175
176void NodeNetwork::mark_processed(bool processed)
177{
178 m_processed_this_cycle.store(processed, std::memory_order_release);
179}
180
182{
183 if (channel_id >= 32)
184 return;
185
186 auto channel_bit = static_cast<uint32_t>(1ULL << channel_id);
187 uint32_t old_pending = m_pending_reset_mask.fetch_or(channel_bit,
188 std::memory_order_acq_rel);
189 uint32_t new_pending = old_pending | channel_bit;
190 uint32_t active_channels = m_channel_mask.load(std::memory_order_acquire);
191
192 if ((new_pending & active_channels) == active_channels && active_channels != 0) {
193 uint32_t expected = new_pending;
194 if (m_pending_reset_mask.compare_exchange_strong(expected, 0,
195 std::memory_order_acq_rel)) {
196 mark_processed(false);
197 }
198 }
199}
200
202{
203 return std::string(MayaFlux::Reflect::enum_to_string(topo));
204}
205
207{
208 return std::string(MayaFlux::Reflect::enum_to_string(mode));
209}
210
211} // namespace MayaFlux::Nodes::Network
std::vector< ParameterMapping > m_parameter_mappings
virtual void unmap_parameter(const std::string &param_name)
Remove parameter mapping.
bool is_processed_this_cycle() const
Check if network has been processed this cycle (lock-free)
std::atomic< bool > m_processing_state
Per-channel processing state (lock-free atomic flags)
virtual void map_parameter(const std::string &param_name, const std::shared_ptr< Node > &source, MappingMode mode=MappingMode::BROADCAST)
Map external node output to network parameter.
void apply_output_scale()
Apply m_output_scale to m_last_audio_buffer.
static void extract_node_samples(const std::shared_ptr< Nodes::Node > &node, std::vector< double > &buffer, size_t &buffer_pos, size_t num_samples)
Extract num_samples from node into buffer using snapshot guard.
std::vector< uint32_t > get_registered_channels() const
Get all channels this network is registered on.
virtual void initialize()
Called once before first process_batch()
void mark_processing(bool processing)
Mark network as processing or not (lock-free)
virtual std::optional< std::vector< double > > get_audio_buffer() const
Get cached audio buffer from last process_batch()
static std::string topology_to_string(Topology topo)
void add_channel_usage(uint32_t channel_id)
Register network usage on a specific channel.
void remove_channel_usage(uint32_t channel_id)
Unregister network from a specific channel.
void mark_processed(bool processed)
Mark network as processed this cycle (lock-free)
double m_output_scale
Post-processing scalar applied to m_last_audio_buffer each batch.
bool is_registered_on_channel(uint32_t channel_id) const
Check if network is registered on a channel.
bool is_processing() const
Check if network is currently processing (lock-free)
std::atomic< uint32_t > m_channel_mask
Bitfield of channels this network is registered on.
std::atomic_flag m_audio_buffer_lock
Spinlock guarding m_last_audio_buffer.
void ensure_initialized()
Ensure initialize() is called exactly once.
void request_reset_from_channel(uint32_t channel_id)
Request a reset from a specific channel.
static std::string output_mode_to_string(OutputMode mode)
virtual std::unordered_map< std::string, std::string > get_metadata() const
Get network metadata for debugging/visualization.
std::vector< double > m_last_audio_buffer
std::atomic< uint32_t > m_pending_reset_mask
virtual size_t get_node_count() const =0
Get the number of nodes in the network.
bool wait_for_snapshot_completion(const std::shared_ptr< Nodes::Node > &node, uint64_t active_context_id, int max_spins)
Wait for an active snapshot context to complete using exponential backoff.
Topology
Defines the structural relationships between nodes in the network.
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)
OutputMode
Defines how the network's computational results are exposed.
@ AUDIO_COMPUTE
processed each cycle but not sent to output
@ AUDIO_SINK
Aggregated audio samples sent to output.
@ INACTIVE
Engine is not processing this node.
Definition NodeSpec.hpp:44
constexpr std::string_view enum_to_string(EnumType value) noexcept
Universal enum to string converter using magic_enum (original case)