MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
PlotContainer.hpp
Go to the documentation of this file.
1#pragma once
2
4
5namespace MayaFlux::Nodes {
6class Node;
7namespace Network {
8 class NodeNetwork;
9}
10}
11
12namespace MayaFlux::Buffers {
13class AudioBuffer;
14}
15
16namespace MayaFlux::Kakshya {
17
18class PlotProcessor;
19
20/**
21 * @class PlotContainer
22 * @brief SignalSourceContainer holding N named scalar series for plotting and signal use.
23 *
24 * Each series is a named std::vector<double> stored as a DataVariant in m_data,
25 * with a corresponding DataDimension (Role::CUSTOM, size = sample count, name = series name).
26 * processed_data mirrors m_data after PlotProcessor::process() — one DataVariant per series,
27 * index-stable, suitable for direct consumption by Forma geometry functions.
28 *
29 * Series are append-only by index. Resize via resize_series() when the sample count changes.
30 * Region semantics apply per-series: a Region with start/end coordinates on the TIME-equivalent
31 * axis (dim 0) selects a sample range within that series. get_region_data() returns the
32 * selected slice as vector<double>, enabling wavetable read-back and drag-to-select interaction.
33 *
34 * Dimension tracking, frame/stream navigation, and processing-token machinery inherited from
35 * SignalSourceContainer are no-op stubs here, grown into as needed.
36 */
37class MAYAFLUX_API PlotContainer : public SignalSourceContainer {
38public:
39 /**
40 * @brief Construct an empty container. Series are added via add_series().
41 */
43
44 ~PlotContainer() override = default;
45
46 PlotContainer(const PlotContainer&) = delete;
50
51 // =========================================================================
52 // Series management
53 // =========================================================================
54
55 /**
56 * @brief Add a named series with a given capacity, zero-initialised.
57 *
58 * @param name Series name. Used as the DataDimension name and for lookup.
59 * @param count Number of samples. Sets the DataDimension size.
60 * @param role Semantic role of this series. Used by DomainMapping to
61 * locate series by axis intent rather than by index.
62 * e.g. SPATIAL_X, SPATIAL_Y, SPATIAL_Z, TIME, FREQUENCY,
63 * COLOR, CHANNEL, CUSTOM.
64 * @param modality Data modality of this series. Describes the nature of the
65 * scalar sequence: AUDIO_1D for time-domain waveforms,
66 * SPECTRAL_2D for frequency bins, SCALAR_F32 for generic
67 * scalar data, TENSOR_ND when no closer modality applies.
68 * @return Index of the newly added series.
69 */
70 uint32_t add_series(std::string name,
71 uint64_t count,
72 DataDimension::Role role = DataDimension::Role::CUSTOM,
73 DataModality modality = DataModality::TENSOR_ND);
74
75 // =========================================================================
76 // Source binding — delegates to PlotProcessor, creating it if absent.
77 //
78 // Each bind_* call associates a data source with a series slot. The
79 // processor acquires from that source on every process() call and writes
80 // into m_data. Binding a slot that already has a source replaces it.
81 // =========================================================================
82
83 /**
84 * @brief Bind a series to a Node.
85 *
86 * Each process() fills the series by calling
87 * Buffers::extract_multiple_samples(node, series_size), which handles
88 * snapshot context and node lifecycle identically to NodeSourceProcessor.
89 *
90 * @param series_index Index returned by add_series().
91 * @param node Node to read from.
92 */
93 void bind(uint32_t series_index, std::shared_ptr<Nodes::Node> node);
94
95 /**
96 * @brief Bind a series to an AudioBuffer.
97 *
98 * Each process() copies get_data() span into the series.
99 * Series size is not automatically resized; allocate via add_series()
100 * to match the expected buffer frame count.
101 *
102 * @param series_index Index returned by add_series().
103 * @param buffer AudioBuffer to read from.
104 */
105 void bind(uint32_t series_index,
106 std::shared_ptr<Buffers::AudioBuffer> buffer);
107
108 /**
109 * @brief Bind a series to a NodeNetwork with audio output.
110 *
111 * Each process() reads get_audio_buffer() from the network.
112 * Fails at bind time if the network has no audio output mode.
113 *
114 * @param series_index Index returned by add_series().
115 * @param network NodeNetwork to read from.
116 */
117 void bind(uint32_t series_index,
118 std::shared_ptr<Nodes::Network::NodeNetwork> network);
119
120 /**
121 * @brief Bind a series to a callable.
122 *
123 * Each process() calls fn(series_vector) by reference. The callable
124 * fills or mutates it freely. Use for computed series, ring buffer
125 * views, or any source that does not fit the other patterns.
126 *
127 * @param series_index Index returned by add_series().
128 * @param fn Callable invoked with the series vector each process().
129 */
130 void bind(uint32_t series_index,
131 std::function<void(std::vector<double>&)> fn);
132
133 /**
134 * @brief Push raw sample data into a series.
135 *
136 * Lock-free pending swap committed on the next process() call.
137 * May be called from any thread.
138 *
139 * @param series_index Index returned by add_series().
140 * @param data Samples to write. Size should match series capacity.
141 */
142 void set_raw(uint32_t series_index, std::vector<double> data);
143
144 /**
145 * @brief Remove the source binding for a series.
146 * @param series_index Series to unbind.
147 */
148 void unbind(uint32_t series_index);
149
150 /**
151 * @brief Write the full sample buffer for a series.
152 * @param index Series index from add_series().
153 * @param samples Source data. Must match the series sample count.
154 */
155 void write_series(uint32_t index, std::span<const double> samples);
156
157 /**
158 * @brief Write a single sample within a series.
159 * @param index Series index.
160 * @param sample_index Sample position within the series.
161 * @param value Value to write.
162 */
163 void write_sample(uint32_t index, uint64_t sample_index, double value);
164
165 /**
166 * @brief Resize a series. Truncates or zero-extends. Updates the DataDimension.
167 * @param index Series index.
168 * @param count New sample count.
169 */
170 void resize_series(uint32_t index, uint64_t count);
171
172 /**
173 * @brief Return the number of series.
174 */
175 [[nodiscard]] uint32_t series_count() const;
176
177 /**
178 * @brief Return the name of a series.
179 */
180 [[nodiscard]] const std::string& series_name(uint32_t index) const;
181
182 /**
183 * @brief Return the sample count of a series.
184 */
185 [[nodiscard]] uint64_t series_size(uint32_t index) const;
186
187 /**
188 * @brief Return the role of a series.
189 */
190 [[nodiscard]] DataDimension::Role series_role(uint32_t index) const;
191
192 // =========================================================================
193 // NDDataContainer
194 // =========================================================================
195
196 [[nodiscard]] std::vector<DataDimension> get_dimensions() const override;
197 [[nodiscard]] uint64_t get_total_elements() const override;
198 [[nodiscard]] MemoryLayout get_memory_layout() const override { return MemoryLayout::ROW_MAJOR; }
200
201 [[nodiscard]] uint64_t get_frame_size() const override { return 1; }
202 [[nodiscard]] uint64_t get_num_frames() const override;
203
204 /**
205 * @brief Extract a sample range from a single series.
206 *
207 * region.start_coordinates[0] = series index.
208 * region.start_coordinates[1] = first sample (inclusive).
209 * region.end_coordinates[1] = last sample (inclusive).
210 *
211 * Returns a single DataVariant containing vector<double> of the slice.
212 * Returns empty if coordinates are out of range.
213 */
214 [[nodiscard]] std::vector<DataVariant> get_region_data(const Region& region) const override;
215
216 /**
217 * @brief Write a sample range back into a series.
218 *
219 * Same coordinate convention as get_region_data(). The first DataVariant
220 * in data must hold vector<double>. Used by Context drag handlers for
221 * wavetable editing and interactive signal manipulation.
222 */
223 void set_region_data(const Region& region, const std::vector<DataVariant>& data) override;
224
225 [[nodiscard]] std::vector<DataVariant> get_region_group_data(const RegionGroup& group) const override;
226 [[nodiscard]] std::vector<DataVariant> get_segments_data(const std::vector<RegionSegment>& segments) const override;
227
228 [[nodiscard]] std::span<const double> get_frame(uint64_t frame_index) const override;
229 void get_frames(std::span<double> output, uint64_t start_frame, uint64_t num_frames) const override;
230
231 [[nodiscard]] double get_value_at(const std::vector<uint64_t>& coordinates) const override;
232 void set_value_at(const std::vector<uint64_t>& coordinates, double value) override;
233
234 [[nodiscard]] uint64_t coordinates_to_linear_index(const std::vector<uint64_t>& coordinates) const override;
235 [[nodiscard]] std::vector<uint64_t> linear_index_to_coordinates(uint64_t linear_index) const override;
236
237 void clear() override;
238 void lock() override { }
239 void unlock() override { }
240 bool try_lock() override { return true; }
241
242 [[nodiscard]] const void* get_raw_data() const override;
243 [[nodiscard]] bool has_data() const override;
244
245 [[nodiscard]] ContainerDataStructure& get_structure() override { return m_structure; }
246 [[nodiscard]] const ContainerDataStructure& get_structure() const override { return m_structure; }
247 void set_structure(ContainerDataStructure s) override { m_structure = std::move(s); }
248
249 void add_region_group(const RegionGroup& group) override;
250 [[nodiscard]] const RegionGroup& get_region_group(const std::string& name) const override;
251 [[nodiscard]] std::unordered_map<std::string, RegionGroup> get_all_region_groups() const override;
252 void remove_region_group(const std::string& name) override;
253
254 [[nodiscard]] bool is_region_loaded(const Region&) const override { return true; }
255 void load_region(const Region&) override { }
256 void unload_region(const Region&) override { }
257
258 [[nodiscard]] DataAccess channel_data(size_t index) override;
259 [[nodiscard]] std::vector<DataAccess> all_channel_data() override;
260
261 // =========================================================================
262 // SignalSourceContainer
263 // =========================================================================
264
265 [[nodiscard]] ProcessingState get_processing_state() const override;
266 void update_processing_state(ProcessingState state) override;
267
268 void register_state_change_callback(
269 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> cb) override;
270 void unregister_state_change_callback() override;
271
272 [[nodiscard]] bool is_ready_for_processing() const override;
273 void mark_ready_for_processing(bool ready) override;
274
275 void create_default_processor() override;
276 void process_default() override;
277
278 void set_default_processor(const std::shared_ptr<DataProcessor>& processor) override;
279 [[nodiscard]] std::shared_ptr<DataProcessor> get_default_processor() const override;
280
281 [[nodiscard]] std::shared_ptr<DataProcessingChain> get_processing_chain() override { return m_chain; }
282 void set_processing_chain(const std::shared_ptr<DataProcessingChain>& chain) override { m_chain = chain; }
283
284 [[nodiscard]] std::vector<DataVariant>& get_processed_data() override { return m_processed_data; }
285 [[nodiscard]] const std::vector<DataVariant>& get_processed_data() const override { return m_processed_data; }
286 [[nodiscard]] const std::vector<DataVariant>& get_data() override { return m_data; }
287
288 void mark_buffers_for_processing(bool) override { }
289 void mark_buffers_for_removal() override { }
290
291 // ---- dimension reader stubs (no concurrent consumer tracking needed yet) ----
292 uint32_t register_dimension_reader(uint32_t) override { return 0; }
293 void unregister_dimension_reader(uint32_t) override { }
294 [[nodiscard]] bool has_active_readers() const override { return false; }
295 void mark_dimension_consumed(uint32_t, uint32_t) override { }
296 [[nodiscard]] bool all_dimensions_consumed() const override { return true; }
297
298private:
299 /**
300 * @brief Return the PlotProcessor, creating and attaching it if absent.
301 *
302 * Called by all bind_* methods. Guarantees the processor exists before
303 * delegating the bind call, without requiring the caller to manage it.
304 */
305 PlotProcessor& ensure_processor();
306
307 std::vector<DataVariant> m_data;
308 std::vector<DataVariant> m_processed_data;
309
311 std::shared_ptr<DataProcessor> m_processor;
312 std::shared_ptr<DataProcessingChain> m_chain;
313
314 std::unordered_map<std::string, RegionGroup> m_region_groups;
315
316 std::atomic<ProcessingState> m_processing_state { ProcessingState::IDLE };
317 std::atomic<bool> m_ready_for_processing { false };
318
319 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> m_state_cb;
320
321 mutable std::vector<double> m_frame_cache;
322};
323
324} // namespace MayaFlux::Kakshya
Core::GlobalNetworkConfig network
Definition Config.cpp:37
size_t count
Type-erased accessor for NDData with semantic view construction.
std::shared_ptr< DataProcessingChain > m_chain
std::vector< DataVariant > m_data
uint32_t register_dimension_reader(uint32_t) override
Register a reader for a specific dimension.
void set_processing_chain(const std::shared_ptr< DataProcessingChain > &chain) override
Set the processing chain for this container.
void unload_region(const Region &) override
Unload a region from memory.
void mark_dimension_consumed(uint32_t, uint32_t) override
Mark a dimension as consumed for the current processing cycle.
const std::vector< DataVariant > & get_processed_data() const override
Get a const reference to the processed data buffer.
MemoryLayout get_memory_layout() const override
Get the memory layout used by this container.
std::function< void(const std::shared_ptr< SignalSourceContainer > &, ProcessingState)> m_state_cb
PlotContainer(PlotContainer &&)=delete
void unregister_dimension_reader(uint32_t) override
Unregister a reader for a specific dimension.
std::unordered_map< std::string, RegionGroup > m_region_groups
std::vector< DataVariant > m_processed_data
ContainerDataStructure m_structure
bool all_dimensions_consumed() const override
Check if all active dimensions have been consumed in this cycle.
void mark_buffers_for_removal() override
Mark associated buffers for removal from the system.
bool is_region_loaded(const Region &) const override
Check if a region is loaded in memory.
void set_structure(ContainerDataStructure s) override
Set the data structure for this container.
PlotContainer(const PlotContainer &)=delete
void lock() override
Acquire a lock for thread-safe access.
std::vector< DataVariant > & get_processed_data() override
Get a mutable reference to the processed data buffer.
std::shared_ptr< DataProcessingChain > get_processing_chain() override
Get the current processing chain for this container.
std::vector< double > m_frame_cache
void load_region(const Region &) override
Load a region into memory.
void set_memory_layout(MemoryLayout) override
Set the memory layout for this container.
~PlotContainer() override=default
bool try_lock() override
Attempt to acquire a lock without blocking.
uint64_t get_frame_size() const override
Get the number of elements that constitute one "frame".
void unlock() override
Release a previously acquired lock.
PlotContainer & operator=(PlotContainer &&)=delete
ContainerDataStructure & get_structure() override
Get the data structure defining this container's layout.
std::shared_ptr< DataProcessor > m_processor
const ContainerDataStructure & get_structure() const override
const std::vector< DataVariant > & get_data() override
Get a reference to the raw data stored in the container.
void mark_buffers_for_processing(bool) override
Mark associated buffers for processing in the next cycle.
bool has_active_readers() const override
Check if any dimensions currently have active readers.
PlotContainer & operator=(const PlotContainer &)=delete
SignalSourceContainer holding N named scalar series for plotting and signal use.
DataProcessor that acquires per-series data from heterogeneous sources and writes into PlotContainer:...
Data-driven interface for managing arbitrary processable signal sources.
ProcessingState
Represents the current processing lifecycle state of a container.
std::optional< RegionGroup > get_region_group(const std::unordered_map< std::string, RegionGroup > &groups, const std::string &name)
Get a RegionGroup by name from a group map.
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:81
void add_region_group(std::unordered_map< std::string, RegionGroup > &groups, const RegionGroup &group)
Add a RegionGroup to a group map.
MemoryLayout
Memory layout for multi-dimensional data.
Definition NDData.hpp:39
void remove_region_group(std::unordered_map< std::string, RegionGroup > &groups, const std::string &name)
Remove a RegionGroup by name from a group map.
Contains the node-based computational processing system components.
Definition Chronie.hpp:14
Container structure for consistent dimension ordering.
Role
Semantic role of the dimension.
Definition NDData.hpp:150
Organizes related signal regions into a categorized collection.
Represents a point or span in N-dimensional space.
Definition Region.hpp:73