MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
NodeStructure.cpp
Go to the documentation of this file.
1#include "NodeStructure.hpp"
4
6
7namespace MayaFlux::Nodes {
8
9ChainNode::ChainNode(const std::shared_ptr<Node>& source, const std::shared_ptr<Node>& target)
10 : m_Source(source)
11 , m_Target(target)
12 , m_is_initialized(false)
13{
14 if (!m_Source || !m_Target) {
15 error<std::invalid_argument>(
17 std::source_location::current(),
18 "ChainNode requires both source and target nodes to be non-null");
19 }
20}
21
23{
24 if (!m_is_initialized) {
25 auto self = shared_from_this();
26
27 if (m_Target) {
28 for (auto& channel : get_active_channels(m_Target, 0)) {
29 MayaFlux::register_audio_node(self, channel);
30 }
31 } else {
33 }
34 m_is_initialized = true;
35 }
36
38 switch (semantics) {
40 if (m_Target) {
41 for (auto& channel : get_active_channels(m_Target, 0)) {
43 }
44 }
45 break;
47 if (m_Source) {
48 for (auto& channel : get_active_channels(m_Source, 0)) {
50 }
51 }
52 if (m_Target) {
53 for (auto& channel : get_active_channels(m_Target, 0)) {
55 }
56 }
57 break;
59 default:
60 break;
61 }
62}
63
64double ChainNode::process_sample(double input)
65{
66 if (!m_Source || !m_Target) {
67 return input;
68 }
69 if (!is_initialized())
70 initialize();
71
72 atomic_inc_modulator_count(m_Source->m_modulator_count, 1);
73 atomic_inc_modulator_count(m_Target->m_modulator_count, 1);
74
75 m_last_output = input;
76
77 uint32_t sstate = m_Source->m_state.load();
78 if (sstate & Utils::NodeState::PROCESSED) {
79 m_last_output = input + m_Source->get_last_output();
80 } else {
81 m_last_output = m_Source->process_sample(input);
83 }
84
85 uint32_t tstate = m_Target->m_state.load();
86 if (tstate & Utils::NodeState::PROCESSED) {
87 m_last_output += m_Target->get_last_output();
88 } else {
89 m_last_output = m_Target->process_sample(m_last_output);
90 tstate = tstate | Utils::NodeState::PROCESSED;
92 }
93
94 atomic_dec_modulator_count(m_Source->m_modulator_count, 1);
95 atomic_dec_modulator_count(m_Target->m_modulator_count, 1);
96
99
100 return m_last_output;
101}
102
103std::vector<double> ChainNode::process_batch(unsigned int num_samples)
104{
105 std::vector<double> output(num_samples);
106 for (size_t i = 0; i < num_samples; i++) {
107 output[i] = process_sample(0.F);
108 }
109 return output;
110}
111
113{
115 if (m_Source)
116 m_Source->reset_processed_state();
117 if (m_Target)
118 m_Target->reset_processed_state();
119}
120
122{
123 if (m_Source)
124 m_Source->save_state();
125 if (m_Target)
126 m_Target->save_state();
127
128 m_state_saved = true;
129}
130
132{
133 if (m_Source)
134 m_Source->restore_state();
135 if (m_Target)
136 m_Target->restore_state();
137
138 m_state_saved = false;
139}
140
142{
143 auto sState = m_Source->m_state.load();
144 auto tState = m_Target->m_state.load();
145
146 bool is_source_registered = m_Source ? (sState & Utils::NodeState::ACTIVE) : false;
147 bool is_target_registered = m_Target ? (tState & Utils::NodeState::ACTIVE) : false;
148 return !is_source_registered && !is_target_registered && (m_state.load() & Utils::NodeState::ACTIVE);
149}
150
152{
153 if (!m_Target) {
154 error<std::runtime_error>(
156 std::source_location::current(),
157 "ChainNode target node is null when retrieving last context");
158 }
159 return m_Target->get_last_context();
160}
161
162BinaryOpContext::BinaryOpContext(double value, double lhs_value, double rhs_value)
163 : NodeContext(value, typeid(BinaryOpContext).name())
164 , lhs_value(lhs_value)
165 , rhs_value(rhs_value)
166{
167}
168
169BinaryOpContextGpu::BinaryOpContextGpu(double value, double lhs_value, double rhs_value, std::span<const float> gpu_data)
170 : BinaryOpContext(value, lhs_value, rhs_value)
171 , GpuVectorData(gpu_data)
172{
173 type_id = typeid(BinaryOpContextGpu).name();
174}
175
176BinaryOpNode::BinaryOpNode(const std::shared_ptr<Node>& lhs, const std::shared_ptr<Node>& rhs, CombineFunc func)
177 : m_lhs(lhs)
178 , m_rhs(rhs)
179 , m_func(std::move(func))
180 , m_context(0.0, 0.0, 0.0)
181 , m_context_gpu(0.0, 0.0, 0.0, get_gpu_data_buffer())
182{
183 if (!m_lhs || !m_rhs) {
184 error<std::invalid_argument>(
186 std::source_location::current(),
187 "BinaryOpNode requires both lhs and rhs nodes to be non-null");
188 }
189}
190
192{
193 if (!m_is_initialized) {
194 auto self = shared_from_this();
195 uint32_t lhs_mask = m_lhs ? m_lhs->get_channel_mask().load() : 0;
196 uint32_t rhs_mask = m_rhs ? m_rhs->get_channel_mask().load() : 0;
197 uint32_t combined_mask = lhs_mask | rhs_mask;
198
199 if (combined_mask != 0) {
200 for (auto& channel : get_active_channels(combined_mask, 0)) {
201 MayaFlux::register_audio_node(self, channel);
202 }
203 } else {
205 }
206 m_is_initialized = true;
207 }
208
210 switch (semantics) {
212 if (m_lhs) {
213 for (auto& channel : get_active_channels(m_lhs, 0)) {
215 }
216 }
217 if (m_rhs) {
218 for (auto& channel : get_active_channels(m_rhs, 0)) {
220 }
221 }
222 break;
224 default:
225 break;
226 }
227}
228
230{
231 if (!m_lhs || !m_rhs) {
232 return input;
233 }
234
235 if (!is_initialized())
236 initialize();
237
238 atomic_inc_modulator_count(m_lhs->m_modulator_count, 1);
239 atomic_inc_modulator_count(m_rhs->m_modulator_count, 1);
240
241 uint32_t lstate = m_lhs->m_state.load();
242 if (lstate & Utils::NodeState::PROCESSED) {
243 m_last_lhs_value = input + m_lhs->get_last_output();
244 } else {
245 m_last_lhs_value = m_lhs->process_sample(input);
247 }
248
249 uint32_t rstate = m_rhs->m_state.load();
250 if (rstate & Utils::NodeState::PROCESSED) {
251 m_last_rhs_value = input + m_rhs->get_last_output();
252 } else {
253 m_last_rhs_value = m_rhs->process_sample(input);
255 }
256
258
261
262 atomic_dec_modulator_count(m_lhs->m_modulator_count, 1);
263 atomic_dec_modulator_count(m_rhs->m_modulator_count, 1);
264
267
268 return m_last_output;
269}
270
271std::vector<double> BinaryOpNode::process_batch(unsigned int num_samples)
272{
273 std::vector<double> output(num_samples);
274 for (unsigned int i = 0; i < num_samples; ++i) {
275 output[i] = process_sample(0.0);
276 }
277 return output;
278}
279
281{
282 update_context(value);
283 auto& ctx = get_last_context();
284
285 for (const auto& callback : m_callbacks) {
286 callback(ctx);
287 }
288
289 for (const auto& [callback, condition] : m_conditional_callbacks) {
290 if (condition(ctx)) {
291 callback(ctx);
292 }
293 }
294}
295
297{
299 if (m_lhs)
300 m_lhs->reset_processed_state();
301 if (m_rhs)
302 m_rhs->reset_processed_state();
303}
304
306{
309
310 if (m_lhs)
311 m_lhs->save_state();
312 if (m_rhs)
313 m_rhs->save_state();
314
315 m_state_saved = true;
316}
317
319{
322
323 if (m_lhs)
324 m_lhs->restore_state();
325 if (m_rhs)
326 m_rhs->restore_state();
327
328 m_state_saved = false;
329}
330
343
345{
346 if (m_gpu_compatible) {
347 return m_context_gpu;
348 }
349 return m_context;
350}
351
353{
354 auto lstate = m_lhs->m_state.load();
355 auto rstate = m_rhs->m_state.load();
356 bool is_lhs_registered = m_lhs ? (lstate & Utils::NodeState::ACTIVE) : false;
357 bool is_rhs_registered = m_rhs ? (rstate & Utils::NodeState::ACTIVE) : false;
358 return !is_lhs_registered && !is_rhs_registered;
359}
360
361} // namespace MayaFlux::Nodes
BinaryOpContextGpu(double value, double lhs_value, double rhs_value, std::span< const float > gpu_data)
GPU-compatible context for binary operation callbacks.
double rhs_value
The value from the right-hand side node.
BinaryOpContext(double value, double lhs_value, double rhs_value)
Constructs a BinaryOpContext with the current operation state.
double lhs_value
The value from the left-hand side node.
Specialized context for binary operation callbacks.
bool m_is_initialized
Flag indicating whether the binary operator has been properly initialized.
void restore_state() override
Restores the node's state from the last save Recursively cascades through all connected modulator nod...
void update_context(double value) override
updates context object for callbacks
void initialize()
Initializes the binary operation node.
NodeContext & get_last_context() override
Retrieves the last created context object.
void notify_tick(double value) override
Notifies all registered callbacks about a new output value.
std::shared_ptr< Node > m_lhs
The left-hand side node.
std::function< double(double, double)> CombineFunc
Function type for combining two node outputs.
void reset_processed_state() override
Resets the processed state of the node and any attached input nodes.
CombineFunc m_func
The function used to combine the outputs of both nodes.
double process_sample(double input=0.) override
Processes a single sample through both nodes and combines the results.
void save_state() override
Saves the node's current state for later restoration Recursively cascades through all connected modul...
std::vector< double > process_batch(unsigned int num_samples) override
Processes multiple samples through both nodes and combines the results.
double m_last_rhs_value
The last output value from the right-hand side node.
double m_last_lhs_value
The last output value from the left-hand side node.
BinaryOpNode(const std::shared_ptr< Node > &lhs, const std::shared_ptr< Node > &rhs, CombineFunc func)
Creates a new binary operation node.
std::shared_ptr< Node > m_rhs
The right-hand side node.
NodeContext & get_last_context() override
Retrieves the last created context object.
std::shared_ptr< Node > m_Source
The upstream node that processes input first.
std::shared_ptr< Node > m_Target
The downstream node that processes the source's output.
void initialize()
Initializes the chain node.
std::vector< double > process_batch(unsigned int num_samples) override
Processes multiple samples through the chain.
bool m_is_initialized
Flag indicating whether the chain has been properly initialized.
double process_sample(double input=0.) override
Processes a single sample through the chain.
void restore_state() override
Restores the node's state from the last save Recursively cascades through all connected modulator nod...
void save_state() override
Saves the node's current state for later restoration Recursively cascades through all connected modul...
void reset_processed_state() override
Resets the processed state of the node and any attached input nodes.
ChainNode(const std::shared_ptr< Node > &source, const std::shared_ptr< Node > &target)
Creates a new chain connecting source to target.
GPU-uploadable 1D array data interface.
std::string type_id
Type identifier for runtime type checking.
Definition Node.hpp:48
double value
Current sample value.
Definition Node.hpp:40
Base context class for node callbacks.
Definition Node.hpp:30
std::vector< NodeHook > m_callbacks
Collection of standard callback functions.
Definition Node.hpp:403
double m_last_output
The most recent sample value generated by this oscillator.
Definition Node.hpp:374
bool m_fire_events_during_snapshot
Internal flag controlling whether notify_tick fires during state snapshots Default: false (events don...
Definition Node.hpp:448
std::vector< std::pair< NodeHook, NodeCondition > > m_conditional_callbacks
Collection of conditional callback functions with their predicates.
Definition Node.hpp:413
std::atomic< Utils::NodeState > m_state
Atomic state flag tracking the node's processing status.
Definition Node.hpp:463
bool m_gpu_compatible
Flag indicating if the node supports GPU processing This flag is set by derived classes to indicate w...
Definition Node.hpp:383
GraphConfig & get_graph_config()
Definition Config.cpp:42
@ Init
Engine/subsystem initialization.
@ Runtime
General runtime operations (default fallback)
@ Nodes
DSP Generator and Filter Nodes, graph pipeline, node management.
void atomic_add_flag(std::atomic< Utils::NodeState > &state, Utils::NodeState flag)
Atomically adds a flag to a node state.
Definition NodeUtils.cpp:96
std::vector< uint32_t > get_active_channels(const std::shared_ptr< Nodes::Node > &node, uint32_t fallback_channel)
Extracts active channel list from a node's channel mask.
void try_reset_processed_state(std::shared_ptr< Node > node)
Attempts to reset the processed state of a node.
void atomic_inc_modulator_count(std::atomic< uint32_t > &count, int amount)
Atomically increments the modulator count by a specified amount.
void atomic_dec_modulator_count(std::atomic< uint32_t > &count, int amount)
Atomically decrements the modulator count by a specified amount.
void atomic_remove_flag(std::atomic< Utils::NodeState > &state, Utils::NodeState flag)
Atomically removes a flag from a node state.
Contains the node-based computational processing system components.
Definition Chronie.hpp:5
@ PRESERVE_BOTH
Preserve both nodes in the chain, add new chain node to root, i.e doubling the target signal.
Definition Utils.hpp:47
@ REPLACE_TARGET
Unregister the target and register with the new chain node.
Definition Utils.hpp:46
@ ONLY_CHAIN
Only keep the new chain node, unregistering the source and target.
Definition Utils.hpp:48
@ KEEP
Preserve both nodes in the binary op, add new binary op node to root, i.e doubling the signal.
Definition Utils.hpp:57
@ REPLACE
Unregister both nodes and register with the new binary op node.
Definition Utils.hpp:56
@ ACTIVE
Engine is processing this node.
Definition Utils.hpp:30
@ PROCESSED
Node has been processed this cycle.
Definition Utils.hpp:34
void register_audio_node(const std::shared_ptr< Nodes::Node > &node, uint32_t channel)
Adds a node to the root node of a specific channel.
Definition Graph.cpp:36
void unregister_audio_node(const std::shared_ptr< Nodes::Node > &node, uint32_t channel)
Removes a node from the root node of a specific channel.
Definition Graph.cpp:58
Utils::NodeBinaryOpSemantics binary_op_semantics
Definition Config.hpp:31
Utils::NodeChainSemantics chain_semantics
Definition Config.hpp:30