MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
NodeUtils.cpp
Go to the documentation of this file.
1#include "NodeUtils.hpp"
2
3#include "Node.hpp"
4
5namespace MayaFlux::Nodes {
6
8 const std::vector<HookEntry>* entries;
10};
11
13 : m_owner(&owner)
14{
15 owner.register_hook_list(*this);
16}
17
19{
20 delete m_head.load();
21
22 Retired* list = m_retired.load();
23 while (list) {
24 Retired* next = list->next;
25 delete list->entries;
26 delete list;
27 list = next;
28 }
29}
30
32{
33 if (m_list) {
34 m_list->m_readers.fetch_sub(1);
35 }
36}
37
39{
40 return m_owner->next_hook_id();
41}
42
44{
45 if (m_head.load(std::memory_order_relaxed) == nullptr) {
46 return {};
47 }
48
49 m_readers.fetch_add(1);
50 const auto* entries = m_head.load();
51 if (!entries) {
52 m_readers.fetch_sub(1);
53 return {};
54 }
55 return { this, entries };
56}
57
58bool HookList::empty() const
59{
60 return m_head.load() == nullptr;
61}
62
63size_t HookList::size() const
64{
65 return snapshot().size();
66}
67
69{
70 for (const auto& entry : snapshot()) {
71 if (entry.condition && !(*entry.condition)(ctx)) {
72 continue;
73 }
74 entry.hook(ctx);
75 }
76}
77
78void HookList::retire(const std::vector<HookEntry>* old)
79{
80 auto* node = new Retired { old, m_retired.load() };
81 while (!m_retired.compare_exchange_weak(node->next, node)) {
82 }
83}
84
86{
87 Retired* list = m_retired.exchange(nullptr);
88 if (!list) {
89 return;
90 }
91
92 if (m_readers.load() != 0) {
93 Retired* tail = list;
94 while (tail->next) {
95 tail = tail->next;
96 }
97 tail->next = m_retired.load();
98 while (!m_retired.compare_exchange_weak(tail->next, list)) {
99 }
100 return;
101 }
102
103 while (list) {
104 Retired* next = list->next;
105 delete list->entries;
106 delete list;
107 list = next;
108 }
109}
110
111bool HookList::mutate(const std::function<bool(std::vector<HookEntry>&)>& edit)
112{
113 for (;;) {
114 m_readers.fetch_add(1);
115
116 const std::vector<HookEntry>* current = m_head.load();
117
118 auto next = std::make_unique<std::vector<HookEntry>>();
119 if (current) {
120 *next = *current;
121 }
122
123 const bool changed = edit(*next);
124 bool swapped = false;
125 if (changed) {
126 const std::vector<HookEntry>* replacement = next->empty() ? nullptr : next.get();
127 const std::vector<HookEntry>* expected = current;
128 swapped = m_head.compare_exchange_strong(expected, replacement);
129 if (swapped && replacement) {
130 next.release();
131 }
132 }
133
134 m_readers.fetch_sub(1);
135
136 if (!changed) {
137 return false;
138 }
139 if (swapped) {
140 if (current) {
142 }
143 reclaim();
144 return true;
145 }
146 }
147}
148
149HookId HookList::add(NodeHook hook, const std::type_info& key, uint8_t tag, std::array<double, 2> params)
150{
151 const HookId id = next_id();
152 mutate([&](std::vector<HookEntry>& entries) {
153 entries.push_back(HookEntry { id, &key, tag, params, hook, std::nullopt });
154 return true;
155 });
156 return id;
157}
158
159HookId HookList::add_unique(NodeHook hook, const std::type_info& key, uint8_t tag, std::array<double, 2> params)
160{
161 const HookId fresh = next_id();
162 HookId result = fresh;
163 mutate([&](std::vector<HookEntry>& entries) {
164 const auto it = std::ranges::find_if(entries, [&](const HookEntry& entry) {
165 return *entry.key == key && entry.params == params;
166 });
167 if (it != entries.end()) {
168 result = it->id;
169 return false;
170 }
171 result = fresh;
172 entries.push_back(HookEntry { fresh, &key, tag, params, hook, std::nullopt });
173 return true;
174 });
175 return result;
176}
177
178HookId HookList::add_conditional(NodeHook hook, NodeCondition condition, const std::type_info& key, uint8_t tag)
179{
180 const HookId id = next_id();
181 mutate([&](std::vector<HookEntry>& entries) {
182 entries.push_back(HookEntry { id, &key, tag, {}, hook, condition });
183 return true;
184 });
185 return id;
186}
187
188HookId HookList::add_conditional_unique(NodeHook hook, NodeCondition condition, const std::type_info& key, uint8_t tag)
189{
190 const HookId fresh = next_id();
191 HookId result = fresh;
192 mutate([&](std::vector<HookEntry>& entries) {
193 const auto it = std::ranges::find_if(entries, [&](const HookEntry& entry) {
194 return *entry.key == key && entry.condition && entry.condition->target_type() == condition.target_type();
195 });
196 if (it != entries.end()) {
197 result = it->id;
198 return false;
199 }
200 result = fresh;
201 entries.push_back(HookEntry { fresh, &key, tag, {}, hook, condition });
202 return true;
203 });
204 return result;
205}
206
208{
209 return mutate([id](std::vector<HookEntry>& entries) {
210 return std::erase_if(entries, [id](const HookEntry& entry) { return entry.id == id; }) > 0;
211 });
212}
213
214bool HookList::remove_by_key(const std::type_info& key)
215{
216 return mutate([&](std::vector<HookEntry>& entries) {
217 return std::erase_if(entries, [&](const HookEntry& entry) { return *entry.key == key; }) > 0;
218 });
219}
220
221bool HookList::remove_first_by_key(const std::type_info& key)
222{
223 return mutate([&](std::vector<HookEntry>& entries) {
224 const auto it = std::ranges::find_if(entries, [&](const HookEntry& entry) { return *entry.key == key; });
225 if (it == entries.end()) {
226 return false;
227 }
228 entries.erase(it);
229 return true;
230 });
231}
232
233bool HookList::remove_by_condition_key(const std::type_info& key)
234{
235 return mutate([&](std::vector<HookEntry>& entries) {
236 return std::erase_if(entries, [&](const HookEntry& entry) {
237 return entry.condition && entry.condition->target_type() == key;
238 }) > 0;
239 });
240}
241
242bool HookList::remove_by_tag(uint8_t tag)
243{
244 return mutate([tag](std::vector<HookEntry>& entries) {
245 return std::erase_if(entries, [tag](const HookEntry& entry) { return entry.tag == tag; }) > 0;
246 });
247}
248
250{
251 mutate([](std::vector<HookEntry>& entries) {
252 if (entries.empty()) {
253 return false;
254 }
255 entries.clear();
256 return true;
257 });
258}
259
260void atomic_set_strong(std::atomic<NodeState>& flag, NodeState& expected, const NodeState& desired)
261{
262 flag.compare_exchange_strong(expected, desired);
263};
264
265void atomic_set_flag_strong(std::atomic<NodeState>& flag, const NodeState& desired)
266{
267 auto expected = flag.load();
268 flag.compare_exchange_strong(expected, desired);
269};
270
271void atomic_add_flag(std::atomic<NodeState>& state, NodeState flag)
272{
273 auto current = state.load();
274 NodeState desired;
275 do {
276 desired = static_cast<NodeState>(current | flag);
277 } while (!state.compare_exchange_weak(current, desired,
278 std::memory_order_acq_rel,
279 std::memory_order_acquire));
280}
281
282void atomic_remove_flag(std::atomic<NodeState>& state, NodeState flag)
283{
284 auto current = state.load();
285 NodeState desired;
286 do {
287 desired = static_cast<NodeState>(current & ~flag);
288 } while (!state.compare_exchange_weak(current, desired,
289 std::memory_order_acq_rel,
290 std::memory_order_acquire));
291}
292
293void atomic_set_flag_weak(std::atomic<NodeState>& flag, NodeState& expected, const NodeState& desired)
294{
295 flag.compare_exchange_weak(expected, desired);
296};
297
298void atomic_inc_modulator_count(std::atomic<uint32_t>& count, int amount)
299{
300 count.fetch_add(amount, std::memory_order_relaxed);
301}
302
303void atomic_dec_modulator_count(std::atomic<uint32_t>& count, int amount)
304{
305 count.fetch_sub(amount, std::memory_order_relaxed);
306}
307
308void try_reset_processed_state(std::shared_ptr<Node> node)
309{
310 if (node && node->m_modulator_count.load(std::memory_order_relaxed) == 0) {
311 node->reset_processed_state();
312 }
313}
314
315std::vector<uint32_t> get_active_channels(const std::shared_ptr<Nodes::Node>& node, uint32_t fallback_channel)
316{
317 uint32_t channel_mask = node ? node->get_channel_mask().load() : 0;
318 return get_active_channels(channel_mask, fallback_channel);
319}
320
321std::vector<uint32_t> get_active_channels(uint32_t channel_mask, uint32_t fallback_channel)
322{
323 std::vector<uint32_t> channels;
324
325 if (channel_mask == 0) {
326 channels.push_back(fallback_channel);
327 } else {
328 for (uint32_t channel = 0; channel < MAX_CHANNEL_COUNT; ++channel) {
329 if (channel_mask & (1ULL << channel)) {
330 channels.push_back(channel);
331 }
332 }
333 }
334
335 return channels;
336}
337
339{
340 state.cycles_elapsed++;
341
342 double progress = 0.;
343 if (state.fade_cycles > 0) {
344 progress = std::min<double>(1.0, static_cast<double>(state.cycles_elapsed) / state.fade_cycles);
345 } else {
346 for (uint32_t ch = 0; ch < 32; ++ch) {
347 bool will_be_in = state.to_channels & (1 << ch);
348 state.amount[ch] = will_be_in ? 1.0 : 0.0;
349 }
350 state.phase = RoutingState::NONE;
351 return;
352 }
353
354 for (uint32_t ch = 0; ch < 32; ch++) {
355 bool was_in = state.from_channels & (1 << ch);
356 bool will_be_in = state.to_channels & (1 << ch);
357
358 if (was_in && will_be_in) {
359 state.amount[ch] = 1.0;
360 } else if (was_in && !will_be_in) {
361 state.amount[ch] = 1.0 - progress;
362 } else if (!was_in && will_be_in) {
363 state.amount[ch] = progress;
364 } else {
365 state.amount[ch] = 0.0;
366 }
367 }
368
369 if (state.cycles_elapsed >= state.fade_cycles) {
370 state.phase = RoutingState::COMPLETED;
371 }
372}
373
374}
glm::vec2 current
size_t count
uint32_t channels
The entries of a list at one instant, kept alive while this object exists.
Definition NodeUtils.hpp:99
HookId add(NodeHook hook, const std::type_info &key, uint8_t tag=0, std::array< double, 2 > params={})
Appends a hook with no duplicate check and returns its id.
Snapshot snapshot() const
The current entries, held stable for the lifetime of the returned object.
Definition NodeUtils.cpp:43
bool remove_by_tag(uint8_t tag)
Removes every entry carrying this tag.
std::atomic< const std::vector< HookEntry > * > m_head
HookId add_conditional(NodeHook hook, NodeCondition condition, const std::type_info &key, uint8_t tag=0)
Appends a hook that fires only while condition holds.
bool remove_by_key(const std::type_info &key)
Removes every entry registered with this callable type.
bool mutate(const std::function< bool(std::vector< HookEntry > &)> &edit)
bool remove(HookId id)
Removes the registration with this id.
void retire(const std::vector< HookEntry > *old)
Definition NodeUtils.cpp:78
HookId add_conditional_unique(NodeHook hook, NodeCondition condition, const std::type_info &key, uint8_t tag=0)
As add_conditional, returning the existing id when the same hook and condition types exist.
void fire(NodeContext &ctx) const
Calls every entry whose condition, if any, holds.
Definition NodeUtils.cpp:68
bool remove_first_by_key(const std::type_info &key)
Removes the first entry registered with this callable type.
bool remove_by_condition_key(const std::type_info &key)
Removes every entry whose condition has this callable type.
std::atomic< Retired * > m_retired
HookId add_unique(NodeHook hook, const std::type_info &key, uint8_t tag=0, std::array< double, 2 > params={})
Appends unless an entry with the same key and params exists, in which case that entry's id is returne...
std::atomic< uint32_t > m_readers
Base context class for node callbacks.
Definition Node.hpp:53
HookId next_hook_id()
Definition Node.hpp:467
void register_hook_list(HookList &list)
Definition Node.hpp:465
Base interface for all computational processing nodes.
Definition Node.hpp:126
TypedHook<> NodeHook
Alias for TypedHook<NodeContext>.
Definition NodeUtils.hpp:38
NodeState
Represents the processing state of a node in the audio graph.
Definition NodeSpec.hpp:43
void try_reset_processed_state(std::shared_ptr< Node > node)
Attempts to reset the processed state of a node.
void atomic_set_flag_weak(std::atomic< NodeState > &flag, NodeState &expected, const NodeState &desired)
Atomically sets a node state flag with weak memory ordering.
void atomic_inc_modulator_count(std::atomic< uint32_t > &count, int amount)
Atomically increments the modulator count by a specified amount.
constexpr uint32_t MAX_CHANNEL_COUNT
Hard limit imposed by uint32_t channel mask width.
Definition NodeSpec.hpp:5
uint64_t HookId
Identifies one registration inside a HookList.
Definition NodeUtils.hpp:60
void atomic_dec_modulator_count(std::atomic< uint32_t > &count, int amount)
Atomically decrements the modulator count by a specified amount.
void update_routing_state(RoutingState &state)
Updates the routing state for a node based on its current channel usage.
void atomic_set_strong(std::atomic< NodeState > &flag, NodeState &expected, const NodeState &desired)
Atomically sets a node state flag with strong memory ordering.
void atomic_remove_flag(std::atomic< NodeState > &state, NodeState flag)
Atomically removes a flag from a node state.
std::function< bool(NodeContext &)> NodeCondition
Predicate function type for conditional callbacks.
Definition NodeUtils.hpp:54
void atomic_set_flag_strong(std::atomic< NodeState > &flag, const NodeState &desired)
Atomically sets a node state flag to a specific value.
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 atomic_add_flag(std::atomic< NodeState > &state, NodeState flag)
Atomically adds a flag to a node state.
Contains the node-based computational processing system components.
Definition Chronie.hpp:14
std::array< double, 2 > params
Definition NodeUtils.hpp:74
const std::type_info * key
Definition NodeUtils.hpp:72
std::optional< NodeCondition > condition
Definition NodeUtils.hpp:76
One registration held by a HookList.
Definition NodeUtils.hpp:70
const std::vector< HookEntry > * entries
Definition NodeUtils.cpp:8
enum MayaFlux::Nodes::RoutingState::Phase NONE
@ COMPLETED
Routing transition has completed.
Definition NodeSpec.hpp:87
Represents the state of routing transitions for a node.
Definition NodeSpec.hpp:69