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