MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
PlotProcessor.cpp
Go to the documentation of this file.
1#include "PlotProcessor.hpp"
2
5
7
11
12namespace MayaFlux::Kakshya {
13
14namespace {
15 constexpr auto C = Journal::Component::Kakshya;
17
18 void release_node(PlotProcessor::SeriesBinding& binding)
19 {
20 if (!binding.node)
21 return;
22 binding.node->remove_buffer_reference();
23 binding.node.reset();
24 }
25}
26
27// =========================================================================
28// Binding
29// =========================================================================
30
31void PlotProcessor::bind_node(uint32_t series_index,
32 std::shared_ptr<Nodes::Node> node)
33{
34 if (!node) {
35 MF_ERROR(C, X, "PlotProcessor::bind_node: null node for series {}", series_index);
36 return;
37 }
38 auto& b = m_bindings[series_index];
39 release_node(b);
40 b.source_type = SourceType::NODE;
41 b.node = std::move(node);
42 b.node->add_buffer_reference();
43 b.audio_buffer.reset();
44 b.network.reset();
45 b.callable = {};
46}
47
48void PlotProcessor::bind_audio_buffer(uint32_t series_index,
49 std::shared_ptr<Buffers::AudioBuffer> buffer)
50{
51 if (!buffer) {
52 MF_ERROR(C, X, "PlotProcessor::bind_audio_buffer: null buffer for series {}", series_index);
53 return;
54 }
55 auto& b = m_bindings[series_index];
56 b.source_type = SourceType::AUDIO_BUFFER;
57 b.audio_buffer = std::move(buffer);
58 release_node(b);
59 b.network.reset();
60 b.callable = {};
61}
62
63void PlotProcessor::bind_network(uint32_t series_index,
64 std::shared_ptr<Nodes::Network::NodeNetwork> network)
65{
66 if (!network) {
67 MF_ERROR(C, X, "PlotProcessor::bind_network: null network for series {}", series_index);
68 return;
69 }
70
71 const auto mode = network->get_output_mode();
74 MF_ERROR(C, X,
75 "PlotProcessor::bind_network: network for series {} has no audio output mode", series_index);
76 return;
77 }
78
79 auto& b = m_bindings[series_index];
80 b.source_type = SourceType::NETWORK;
81 b.network = std::move(network);
82 release_node(b);
83 b.audio_buffer.reset();
84 b.callable = {};
85}
86
87void PlotProcessor::bind_callable(uint32_t series_index,
88 std::function<void(std::vector<double>&)> fn)
89{
90 if (!fn) {
91 MF_ERROR(C, X, "PlotProcessor::bind_callable: null callable for series {}", series_index);
92 return;
93 }
94 auto& b = m_bindings[series_index];
95 b.source_type = SourceType::CALLABLE;
96 b.callable = std::move(fn);
97 release_node(b);
98 b.audio_buffer.reset();
99 b.network.reset();
100}
101
102void PlotProcessor::set_raw(uint32_t series_index, std::vector<double> data)
103{
104 auto& b = m_bindings[series_index];
105 release_node(b);
106 b.source_type = SourceType::RAW;
107 b.pending_raw = std::move(data);
108 b.raw_dirty.test_and_set(std::memory_order_release);
109}
110
111void PlotProcessor::set_series_semantics(uint32_t series_index,
113 DataModality modality)
114{
115 auto it = m_bindings.find(series_index);
116 if (it == m_bindings.end())
117 return;
118 it->second.role = role;
119 it->second.modality = modality;
120}
121
122void PlotProcessor::unbind(uint32_t series_index)
123{
124 auto it = m_bindings.find(series_index);
125 if (it == m_bindings.end())
126 return;
127 if (it->second.source_type == SourceType::NODE && it->second.node)
128 it->second.node->remove_buffer_reference();
129 m_bindings.erase(it);
130}
131
132bool PlotProcessor::has_binding(uint32_t series_index) const
133{
134 return m_bindings.contains(series_index);
135}
136
137// =========================================================================
138// DataProcessor
139// =========================================================================
140
141void PlotProcessor::on_attach(const std::shared_ptr<SignalSourceContainer>& container)
142{
143 if (!std::dynamic_pointer_cast<PlotContainer>(container))
144 MF_ERROR(C, X, "PlotProcessor requires a PlotContainer");
145}
146
147void PlotProcessor::on_detach(const std::shared_ptr<SignalSourceContainer>&)
148{
149 for (auto& [idx, b] : m_bindings) {
150 if (b.source_type == SourceType::NODE && b.node)
151 b.node->remove_buffer_reference();
152 }
153 m_bindings.clear();
154}
155
156void PlotProcessor::process(const std::shared_ptr<SignalSourceContainer>& container)
157{
158 auto plot = std::dynamic_pointer_cast<PlotContainer>(container);
159 if (!plot) {
160 MF_ERROR(C, X, "PlotProcessor::process: container is not a PlotContainer");
161 return;
162 }
163
164 m_processing.store(true, std::memory_order_release);
165
166 for (auto& [idx, b] : m_bindings) {
167 if (idx >= plot->series_count())
168 continue;
169
170 thread_local std::vector<double> staging;
171 staging.resize(plot->series_size(idx));
172
173 {
174 auto frame = plot->get_frame(idx).as<double>();
175 if (frame.size() == staging.size()) {
176 std::ranges::copy(frame, staging.begin());
177 } else {
178 std::ranges::fill(staging, 0.0);
179 }
180 }
181
182 switch (b.source_type) {
183 case SourceType::NODE:
184 acquire_from_node(b, staging);
185 break;
188 break;
190 acquire_from_network(b, staging);
191 break;
193 acquire_from_callable(b, staging);
194 break;
195 case SourceType::RAW:
196 acquire_from_raw(b, staging);
197 break;
198 }
199
200 plot->write_series(idx, staging);
201 }
202
203 auto& src = plot->get_data();
204 auto& dst = plot->get_processed_data();
205 dst.resize(src.size());
206 for (size_t i = 0; i < src.size(); ++i)
207 dst[i] = src[i];
208
209 m_processing.store(false, std::memory_order_release);
210}
211
212// =========================================================================
213// Private acquisition
214// =========================================================================
215
216void PlotProcessor::acquire_from_node(SeriesBinding& b, std::vector<double>& series)
217{
218 if (!b.node)
219 return;
220
221 auto samples = Buffers::extract_multiple_samples(b.node, series.size());
222 const size_t n = std::min(series.size(), samples.size());
223 std::copy_n(samples.begin(), n, series.begin());
224 if (n < series.size())
225 std::fill(series.begin() + static_cast<ptrdiff_t>(n), series.end(), 0.0);
226}
227
229{
230 if (!b.audio_buffer)
231 return;
232
233 const auto& data = b.audio_buffer->get_data();
234 const size_t n = std::min(series.size(), data.size());
235 std::copy_n(data.begin(), n, series.begin());
236 if (n < series.size())
237 std::fill(series.begin() + static_cast<ptrdiff_t>(n), series.end(), 0.0);
238}
239
240void PlotProcessor::acquire_from_network(SeriesBinding& b, std::vector<double>& series)
241{
242 if (!b.network)
243 return;
244
245 auto buf = b.network->get_audio_buffer();
246 if (!buf)
247 return;
248
249 const size_t n = std::min(series.size(), buf->size());
250 std::copy_n(buf->begin(), n, series.begin());
251 if (n < series.size())
252 std::fill(series.begin() + static_cast<ptrdiff_t>(n), series.end(), 0.0);
253}
254
255void PlotProcessor::acquire_from_callable(SeriesBinding& b, std::vector<double>& series)
256{
257 if (b.callable)
258 b.callable(series);
259}
260
261void PlotProcessor::acquire_from_raw(SeriesBinding& b, std::vector<double>& series)
262{
263 if (!b.raw_dirty.test(std::memory_order_acquire))
264 return;
265 b.raw_dirty.clear(std::memory_order_release);
266 const size_t n = std::min(series.size(), b.pending_raw.size());
267 std::copy_n(b.pending_raw.begin(), n, series.begin());
268 if (n < series.size())
269 std::fill(series.begin() + static_cast<ptrdiff_t>(n), series.end(), 0.0);
270}
271
272} // namespace MayaFlux::Kakshya
#define MF_ERROR(comp, ctx,...)
Core::GlobalNetworkConfig network
Definition Config.cpp:43
std::shared_ptr< NetworkGeometryBuffer > buffer
uint32_t * dst
const uint32_t * src
uint32_t mode
size_t b
void on_detach(const std::shared_ptr< SignalSourceContainer > &container) override
Called when this processor is detached from a container.
void unbind(uint32_t series_index)
Remove a binding from a series slot.
bool has_binding(uint32_t series_index) const
void acquire_from_raw(SeriesBinding &b, std::vector< double > &series)
void bind_network(uint32_t series_index, std::shared_ptr< Nodes::Network::NodeNetwork > network)
Bind a series slot to a NodeNetwork with audio output.
void bind_audio_buffer(uint32_t series_index, std::shared_ptr< Buffers::AudioBuffer > buffer)
Bind a series slot to an AudioBuffer.
void bind_callable(uint32_t series_index, std::function< void(std::vector< double > &)> fn)
Bind a series slot to a callable.
void on_attach(const std::shared_ptr< SignalSourceContainer > &container) override
Called when this processor is attached to a container.
void process(const std::shared_ptr< SignalSourceContainer > &container) override
Acquire data from all bound sources and write into the container.
void bind_node(uint32_t series_index, std::shared_ptr< Nodes::Node > node)
Bind a series slot to a Node.
void set_raw(uint32_t series_index, std::vector< double > data)
Push raw sample data for a series.
std::unordered_map< uint32_t, SeriesBinding > m_bindings
void acquire_from_audio_buffer(SeriesBinding &b, std::vector< double > &series)
void acquire_from_callable(SeriesBinding &b, std::vector< double > &series)
void acquire_from_node(SeriesBinding &b, std::vector< double > &series)
void set_series_semantics(uint32_t series_index, DataDimension::Role role, DataModality modality)
Set the role and modality for a series binding.
void acquire_from_network(SeriesBinding &b, std::vector< double > &series)
std::vector< double > extract_multiple_samples(const std::shared_ptr< Nodes::Node > &node, size_t num_samples)
Extract multiple samples from a node into a vector.
@ ContainerProcessing
Container operations (Kakshya - file/stream/region processing)
@ Kakshya
Containers[Signalsource, Stream, File], Regions, DataProcessors.
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:132
@ AUDIO_COMPUTE
processed each cycle but not sent to output
@ AUDIO_SINK
Aggregated audio samples sent to output.
Role
Semantic role of the dimension.
Definition NDData.hpp:204