MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
RegionProcessors.cpp
Go to the documentation of this file.
2
6
8
9namespace MayaFlux::Kakshya {
10
11bool is_segment_complete(const OrganizedRegion& region, size_t segment_index)
12{
13 if (segment_index >= region.segments.size())
14 return true;
15
16 auto& segment = region.segments[segment_index];
17 return (region.current_position[0] >= segment.source_region.end_coordinates[0]);
18}
19
20RegionOrganizationProcessor::RegionOrganizationProcessor(const std::shared_ptr<SignalSourceContainer>& container)
21{
22 on_attach(container);
23}
24
25void RegionOrganizationProcessor::organize_container_data(const std::shared_ptr<SignalSourceContainer>& container)
26{
27 m_organized_regions.clear();
28
29 auto region_groups = container->get_all_region_groups();
30
31 std::ranges::for_each(region_groups | std::views::values,
32 [this, &container](const auto& group) {
33 organize_group(container, group);
34 });
35
36 std::ranges::sort(m_organized_regions, [](const OrganizedRegion& a, const OrganizedRegion& b) {
37 if (a.segments.empty() || b.segments.empty())
38 return false;
39 if (a.segments[0].source_region.start_coordinates.empty() || b.segments[0].source_region.start_coordinates.empty())
40 return false;
41 return a.segments[0].source_region.start_coordinates[0] < b.segments[0].source_region.start_coordinates[0];
42 });
43}
44
45void RegionOrganizationProcessor::process(const std::shared_ptr<SignalSourceContainer>& container)
46{
47 if (!container || m_organized_regions.empty()) {
48 return;
49 }
50
51 m_is_processing.store(true);
52
53 try {
54 std::vector<uint64_t> output_shape;
55 uint64_t current_region_frames = 0;
57 auto& current_region = m_organized_regions[m_current_region_index];
58 for (const auto& segment : current_region.segments) {
59 current_region_frames += segment.segment_size[0];
60 }
61 }
62
63 output_shape.push_back(current_region_frames);
64 output_shape.push_back(m_structure.get_frame_size());
65
66 auto& output_data = container->get_processed_data();
67 ensure_output_dimensioning(output_data, output_shape);
68
69 process_organized_regions(container, output_data);
70
71 container->update_processing_state(ProcessingState::PROCESSED);
72
73 } catch (const std::exception& e) {
74 container->update_processing_state(ProcessingState::ERROR);
75 error_rethrow(
78 std::source_location::current(),
79 "Error during region organization processing: {}",
80 e.what());
81 }
82
83 m_is_processing.store(false);
84}
85
86void RegionOrganizationProcessor::add_region_group(const std::string& group_name)
87{
88 if (auto container = m_container_weak.lock()) {
89 RegionGroup new_group(group_name);
90 container->add_region_group(new_group);
92 }
93}
94
95void RegionOrganizationProcessor::add_segment_to_region(const std::string& group_name,
96 size_t region_index,
97 const std::vector<uint64_t>& start_coords,
98 const std::vector<uint64_t>& end_coords,
99 const std::unordered_map<std::string, std::any>& attributes)
100{
101 auto region_it = std::ranges::find_if(m_organized_regions, [&](const OrganizedRegion& region) {
102 return region.group_name == group_name && region.region_index == region_index;
103 });
104
105 if (region_it != m_organized_regions.end()) {
106 Region region(start_coords, end_coords, attributes);
107 RegionSegment segment(region);
108
109 region_it->segments.push_back(std::move(segment));
110
111 if (auto container = m_container_weak.lock()) {
112 cache_region_if_needed(region_it->segments.back(), container);
113 }
114 }
115}
116
117void RegionOrganizationProcessor::set_region_transition(const std::string& group_name,
118 size_t region_index,
119 RegionTransition type,
120 double duration_ms)
121{
122 auto region_it = std::ranges::find_if(m_organized_regions, [&](OrganizedRegion& region) {
123 return region.group_name == group_name && region.region_index == region_index;
124 });
125
126 if (region_it != m_organized_regions.end()) {
127 region_it->transition_type = type;
128 region_it->transition_duration_ms = duration_ms;
129 }
130}
131
132void RegionOrganizationProcessor::set_selection_pattern(const std::string& group_name,
133 size_t region_index,
135{
136 auto region_it = std::ranges::find_if(m_organized_regions, [&](OrganizedRegion& region) {
137 return region.group_name == group_name && region.region_index == region_index;
138 });
139
140 if (region_it != m_organized_regions.end()) {
141 region_it->selection_pattern = pattern;
142 }
143}
144
145void RegionOrganizationProcessor::set_region_looping(const std::string& group_name,
146 size_t region_index,
147 bool enabled,
148 const std::vector<uint64_t>& loop_start,
149 const std::vector<uint64_t>& loop_end)
150{
151 auto region_it = std::ranges::find_if(m_organized_regions, [&](OrganizedRegion& region) {
152 return region.group_name == group_name && region.region_index == region_index;
153 });
154
155 if (region_it != m_organized_regions.end()) {
156 region_it->looping_enabled = enabled;
157 if (!loop_start.empty())
158 region_it->loop_start = loop_start;
159 if (!loop_end.empty())
160 region_it->loop_end = loop_end;
161 }
162}
163
164void RegionOrganizationProcessor::jump_to_region(const std::string& group_name, size_t region_index)
165{
166 auto region_it = std::ranges::find_if(m_organized_regions, [&](const OrganizedRegion& region) {
167 return region.group_name == group_name && region.region_index == region_index;
168 });
169
170 if (region_it != m_organized_regions.end()) {
171 m_current_region_index = std::distance(m_organized_regions.begin(), region_it);
172 if (!region_it->segments.empty()) {
173 m_current_position = region_it->segments[0].source_region.start_coordinates;
174 }
175 }
176}
177
178void RegionOrganizationProcessor::jump_to_position(const std::vector<uint64_t>& position)
179{
180 m_current_position = position;
181
182 auto region_index = find_region_for_position(position, m_organized_regions);
183 if (region_index) {
184 m_current_region_index = *region_index;
185 }
186}
187
188void RegionOrganizationProcessor::process_organized_regions(const std::shared_ptr<SignalSourceContainer>& container,
189 std::vector<DataVariant>& output_data)
190{
191 if (m_organized_regions.empty())
192 return;
193
194 auto& current_region = m_organized_regions[m_current_region_index];
195 current_region.state = RegionState::ACTIVE;
196
197 size_t selected_segment = select_next_segment(current_region);
198
199 bool segment_changed = (selected_segment != current_region.active_segment_index);
200 bool segment_completed = is_segment_complete(current_region, selected_segment);
201 bool region_completed = (segment_completed && selected_segment == current_region.segments.size() - 1);
202
203 if (region_completed && m_current_region_index < m_organized_regions.size() - 1) {
204 auto& next_region = m_organized_regions[m_current_region_index + 1];
205
206 bool should_transition = false;
207 switch (current_region.transition_type) {
209 should_transition = false;
210 break;
213 should_transition = (current_region.transition_duration_ms > 0);
214 break;
215 default:
216 should_transition = false;
217 break;
218 }
219
220 if (should_transition) {
221 apply_region_transition(current_region, next_region, container, output_data);
222 } else {
223 process_region_segment(current_region, current_region.segments[selected_segment],
224 container, output_data);
225 }
226
228 auto& new_current = m_organized_regions[m_current_region_index];
229 new_current.state = RegionState::READY;
230 new_current.active_segment_index = 0;
231
232 } else {
233 process_region_segment(current_region, current_region.segments[selected_segment],
234 container, output_data);
235 current_region.active_segment_index = selected_segment;
236 }
237}
238
240 const RegionSegment& segment,
241 const std::shared_ptr<SignalSourceContainer>& container,
242 std::vector<DataVariant>& output_data)
243{
244 if (m_cache_manager) {
245 if (auto cached_data = m_cache_manager->get_cached_segment(segment)) {
246 output_data.resize(cached_data->data.size());
247
248 std::ranges::for_each(std::views::zip(cached_data->data, output_data),
249 [](auto&& pair) {
250 auto&& [source, dest] = pair;
251 safe_copy_data_variant(source, dest);
252 });
253 return;
254 }
255 }
256
257 auto region_data = container->get_region_data(segment.source_region);
258
259 output_data.resize(region_data.size());
260
261 std::ranges::for_each(std::views::zip(region_data, output_data),
262 [](auto&& pair) {
263 auto&& [source, dest] = pair;
264 safe_copy_data_variant(source, dest);
265 });
266}
267
268/* void RegionOrganizationProcessor::apply_region_transition(const OrganizedRegion& current_region,
269 const OrganizedRegion& next_region,
270 const std::shared_ptr<SignalSourceContainer>& container,
271 std::vector<DataVariant>& output_data)
272{
273 if (next_region.segments.empty()) {
274 return;
275 }
276
277
278} */
279
281 const OrganizedRegion& next_region,
282 const std::shared_ptr<SignalSourceContainer>& container,
283 std::vector<DataVariant>& output_data)
284{
285 if (next_region.segments.empty()) {
286 return;
287 }
288
289 auto next_data = container->get_region_data(next_region.segments[0].source_region);
290
291 // TODO:: Reenable when C++23 is more widely supported
292 /* for (auto&& [current_var, next_var] : std::views::zip(output_data, next_data)) {
293 auto current_span = convert_variant<double>(current_var);
294 auto next_span = convert_variant<double>(const_cast<DataVariant&>(next_var));
295 auto paired_samples = std::views::zip(current_span, next_span)
296 | std::views::enumerate;
297
298 switch (current_region.transition_type) {
299 case RegionTransition::CROSSFADE:
300 std::ranges::for_each(paired_samples, [size = current_span.size()](auto&& item) {
301 auto [i, sample_pair] = item;
302 auto [current, next] = sample_pair;
303 double fade_factor = static_cast<double>(i) / static_cast<double>(size);
304 current = current * (1.0 - fade_factor) + next * fade_factor;
305 });
306 break;
307
308 case RegionTransition::OVERLAP:
309 std::ranges::for_each(paired_samples, [](auto&& item) {
310 auto [i, sample_pair] = item;
311 auto [current, next] = sample_pair;
312 current = current * 0.5 + next * 0.5;
313 });
314 break;
315
316 default:
317 break;
318 }
319 } */
320
321 const size_t data_count = std::min<size_t>(output_data.size(), next_data.size());
322 for (size_t variant_idx = 0; variant_idx < data_count; ++variant_idx) {
323 auto current_span = convert_variant<double>(output_data[variant_idx]);
324 auto next_span = convert_variant<double>(const_cast<DataVariant&>(next_data[variant_idx]));
325
326 const size_t sample_count = std::min<size_t>(current_span.size(), next_span.size());
327
328 switch (current_region.transition_type) {
330 for (size_t i = 0; i < sample_count; ++i) {
331 double fade_factor = static_cast<double>(i) / static_cast<double>(sample_count);
332 current_span[i] = current_span[i] * (1.0 - fade_factor) + next_span[i] * fade_factor;
333 }
334 break;
335
337 for (size_t i = 0; i < sample_count; ++i) {
338 current_span[i] = current_span[i] * 0.5 + next_span[i] * 0.5;
339 }
340 break;
341
342 default:
343 break;
344 }
345 }
346}
347
349{
350 if (region.segments.empty())
351 return 0;
352
353 switch (region.selection_pattern) {
355 return (region.active_segment_index + 1) % region.segments.size();
356
358 if (std::uniform_int_distribution<size_t> dist(0, region.segments.size() - 1); true) {
359 return dist(m_random_engine);
360 }
361
363 if (m_segment_weights.empty() || m_segment_weights.size() != region.segments.size()) {
364 return region.active_segment_index % region.segments.size();
365 }
366
367 if (std::discrete_distribution<size_t> dist(m_segment_weights.begin(), m_segment_weights.end()); true) {
368 return dist(m_random_engine);
369 }
370
371 default:
372 return 0;
373 }
374}
375
377 const std::vector<uint64_t>& position,
378 const std::vector<OrganizedRegion>& regions) const
379{
380 if (auto it = std::ranges::find_if(regions,
381 [&position](const auto& region) { return region.contains_position(position); });
382 it != regions.end()) {
383 return std::distance(regions.begin(), it);
384 }
385 return std::nullopt;
386}
387
388void RegionOrganizationProcessor::organize_group(const std::shared_ptr<SignalSourceContainer>& container,
389 const RegionGroup& group)
390{
391 // TODO:: Reenable when C++23 is more widely supported
392 /* for (auto&& [i, region] : std::views::enumerate(group.regions)) {
393 OrganizedRegion organized_region(group.name, i);
394
395 RegionSegment segment(region);
396
397 cache_region_if_needed(segment, container);
398
399 organized_region.segments.push_back(std::move(segment));
400
401 std::ranges::copy(group.attributes,
402 std::inserter(organized_region.attributes, organized_region.attributes.end()));
403 std::ranges::copy(region.attributes,
404 std::inserter(organized_region.attributes, organized_region.attributes.end()));
405
406 organized_region.current_position = region.start_coordinates;
407 organized_region.state = RegionState::READY;
408
409 m_organized_regions.push_back(std::move(organized_region));
410 } */
411 for (size_t i = 0; const auto& region : group.regions) {
412 OrganizedRegion organized_region(group.name, i);
413 RegionSegment segment(region);
414 cache_region_if_needed(segment, container);
415 organized_region.segments.push_back(std::move(segment));
416 std::ranges::copy(group.attributes,
417 std::inserter(organized_region.attributes, organized_region.attributes.end()));
418 std::ranges::copy(region.attributes,
419 std::inserter(organized_region.attributes, organized_region.attributes.end()));
420 organized_region.current_position = region.start_coordinates;
421 organized_region.state = RegionState::READY;
422 m_organized_regions.push_back(std::move(organized_region));
423 ++i;
424 }
425}
426
428{
429 if (auto container = m_container_weak.lock()) {
430 organize_container_data(container);
431 }
432}
433
434// =============================================================================
435// DynamicRegionProcessor Implementation
436// =============================================================================
437
438DynamicRegionProcessor::DynamicRegionProcessor(const std::shared_ptr<SignalSourceContainer>& container)
439 : RegionOrganizationProcessor(container)
440{
441}
442
443void DynamicRegionProcessor::process(const std::shared_ptr<SignalSourceContainer>& container)
444{
445 if (!container)
446 return;
447
448 if (should_reorganize(container)) {
451 }
452 m_needs_reorganization.store(false);
453 }
454
456}
457
458bool DynamicRegionProcessor::should_reorganize(const std::shared_ptr<SignalSourceContainer>& container)
459{
460 if (m_needs_reorganization.load()) {
461 return true;
462 }
463
466 }
467
468 return false;
469}
470
475
480
481}
void process(const std::shared_ptr< SignalSourceContainer > &container) override
Processes audio, performing reorganization if needed.
std::function< bool(const std::vector< OrganizedRegion > &, const std::shared_ptr< SignalSourceContainer > &)> m_auto_reorganization_criteria
Criteria function for automatic reorganization.
void set_reorganization_callback(RegionOrganizer callback)
Sets the callback for region reorganization.
RegionOrganizer m_reorganizer_callback
Callback for reorganization.
DynamicRegionProcessor(const std::shared_ptr< SignalSourceContainer > &container)
Construct a dynamic region processor for a given container.
std::atomic< bool > m_needs_reorganization
Flag for pending reorganization.
bool should_reorganize(const std::shared_ptr< SignalSourceContainer > &container)
Checks if reorganization should occur for the current container.
void trigger_reorganization()
Triggers a reorganization on the next processing cycle.
void set_region_transition(const std::string &group_name, size_t region_index, RegionTransition type, double duration_ms=0.0)
Configures the transition between regions.
void add_region_group(const std::string &group_name)
Creates a new region group for organizing related regions.
void jump_to_region(const std::string &group_name, size_t region_index)
Jump to a specific region for processing or playback.
void process(const std::shared_ptr< SignalSourceContainer > &container) override
Processes audio data according to the current region organization.
void jump_to_position(const std::vector< uint64_t > &position)
Jump to a specific position in the data.
virtual void apply_region_transition(const OrganizedRegion &current_region, const OrganizedRegion &next_region, const std::shared_ptr< SignalSourceContainer > &container, std::vector< DataVariant > &output_data)
Apply a transition between two regions.
void refresh_organized_data()
Refresh the internal organization of regions.
void organize_group(const std::shared_ptr< SignalSourceContainer > &container, const RegionGroup &group)
Organize a group of regions within the container.
virtual void process_organized_regions(const std::shared_ptr< SignalSourceContainer > &container, std::vector< DataVariant > &output_data)
Process regions according to their selection pattern.
void add_segment_to_region(const std::string &group_name, size_t region_index, const std::vector< uint64_t > &start_coords, const std::vector< uint64_t > &end_coords, const std::unordered_map< std::string, std::any > &attributes)
Adds a segment to an existing region.
void set_selection_pattern(const std::string &group_name, size_t region_index, RegionSelectionPattern pattern)
Set the selection pattern for a region (e.g., sequential, random).
void set_region_looping(const std::string &group_name, size_t region_index, bool enabled, const std::vector< uint64_t > &loop_start={}, const std::vector< uint64_t > &loop_end={})
Enable or disable looping for a region.
RegionOrganizationProcessor(const std::shared_ptr< SignalSourceContainer > &container)
Construct a region organization processor for a given container.
void organize_container_data(const std::shared_ptr< SignalSourceContainer > &container) override
Organize the container's data into regions and segments.
std::optional< size_t > find_region_for_position(const std::vector< uint64_t > &position, const std::vector< OrganizedRegion > &regions) const
virtual size_t select_next_segment(const OrganizedRegion &region) const
Select the next segment to process according to the region's pattern.
virtual void process_region_segment(const OrganizedRegion &region, const RegionSegment &segment, const std::shared_ptr< SignalSourceContainer > &container, std::vector< DataVariant > &output_data)
Process a single region segment.
Data-driven processor for organizing and processing non-linear audio regions.
virtual void cache_region_if_needed(const RegionSegment &segment, const std::shared_ptr< SignalSourceContainer > &container)
Cache a region's data if beneficial and not already cached.
std::unique_ptr< RegionCacheManager > m_cache_manager
virtual void ensure_output_dimensioning(std::vector< DataVariant > &output_data, const std::vector< uint64_t > &required_shape)
Ensure output data is properly dimensioned for region extraction.
std::vector< OrganizedRegion > m_organized_regions
void on_attach(const std::shared_ptr< SignalSourceContainer > &container) override
Attach this processor to a signal source container.
std::weak_ptr< SignalSourceContainer > m_container_weak
@ Runtime
General runtime operations (default fallback)
@ Kakshya
Containers[Signalsource, Stream, File], Regions, DataProcessors.
@ ERROR
Container is in an error state and cannot proceed.
@ PROCESSED
Container has completed processing and results are available.
std::function< void(std::vector< OrganizedRegion > &, std::shared_ptr< SignalSourceContainer >)> RegionOrganizer
Function type for dynamic region reorganization.
bool is_segment_complete(const OrganizedRegion &region, size_t segment_index)
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
Definition NDData.hpp:73
RegionSelectionPattern
Describes how regions are selected for processing or playback.
Definition Region.hpp:11
@ SEQUENTIAL
Process regions in order.
@ WEIGHTED
Weighted random selection.
RegionTransition
Describes how transitions between regions are handled.
Definition Region.hpp:26
@ OVERLAP
Overlap regions during transition.
@ CROSSFADE
Crossfade between regions.
@ IMMEDIATE
No transition, jump directly.
void safe_copy_data_variant(const DataVariant &input, DataVariant &output)
Safely copy data from a DataVariant to another DataVariant, handling type conversion.
Definition DataUtils.cpp:34
@ ACTIVE
Currently being processed.
@ READY
Ready for processing.
static size_t get_frame_size(const std::vector< DataDimension > &dimensions)
Extract the size of non time dimensions (channel, spatial, frequency)
std::vector< uint64_t > current_position
Current read position.
RegionTransition transition_type
Transition to next region.
size_t active_segment_index
Currently active segment.
std::unordered_map< std::string, std::any > attributes
Extensible metadata.
std::vector< RegionSegment > segments
Audio segments in this region.
size_t region_index
Index within the group.
RegionSelectionPattern selection_pattern
std::string group_name
Name of the region group.
A structured audio region with metadata and transition information.
std::unordered_map< std::string, std::any > attributes
Flexible key-value store for group-specific attributes.
std::vector< Region > regions
Collection of regions belonging to this group.
std::string name
Descriptive name of the group.
Organizes related signal regions into a categorized collection.
Region source_region
Associated region.
Represents a discrete segment of audio data with caching capabilities.
Represents a point or span in N-dimensional space.
Definition Region.hpp:67