MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
RegionProcessors.cpp
Go to the documentation of this file.
2
5
7
8namespace MayaFlux::Kakshya {
9
10bool is_segment_complete(const OrganizedRegion& region, size_t segment_index)
11{
12 if (segment_index >= region.segments.size())
13 return true;
14
15 auto& segment = region.segments[segment_index];
16 return (region.current_position[0] >= segment.source_region.end_coordinates[0]);
17}
18
19RegionOrganizationProcessor::RegionOrganizationProcessor(const std::shared_ptr<SignalSourceContainer>& container)
20{
21 on_attach(container);
22}
23
24void RegionOrganizationProcessor::organize_container_data(const std::shared_ptr<SignalSourceContainer>& container)
25{
26 m_organized_regions.clear();
27
28 auto region_groups = container->get_all_region_groups();
29
30 std::ranges::for_each(region_groups | std::views::values,
31 [this, &container](const auto& group) {
32 organize_group(container, group);
33 });
34
35 std::ranges::sort(m_organized_regions, [](const OrganizedRegion& a, const OrganizedRegion& b) {
36 if (a.segments.empty() || b.segments.empty())
37 return false;
38 if (a.segments[0].source_region.start_coordinates.empty() || b.segments[0].source_region.start_coordinates.empty())
39 return false;
40 return a.segments[0].source_region.start_coordinates[0] < b.segments[0].source_region.start_coordinates[0];
41 });
42}
43
44void RegionOrganizationProcessor::process(const std::shared_ptr<SignalSourceContainer>& container)
45{
46 if (!container || m_organized_regions.empty()) {
47 return;
48 }
49
50 m_is_processing.store(true);
51
52 try {
53 std::vector<uint64_t> output_shape;
54 uint64_t current_region_frames = 0;
56 auto& current_region = m_organized_regions[m_current_region_index];
57 for (const auto& segment : current_region.segments) {
58 current_region_frames += segment.segment_size[0];
59 }
60 }
61
62 output_shape.push_back(current_region_frames);
63 output_shape.push_back(m_structure.get_frame_size());
64
65 auto& output_data = container->get_processed_data();
66 ensure_output_dimensioning(output_data, output_shape);
67
68 process_organized_regions(container, output_data);
69
70 container->update_processing_state(ProcessingState::PROCESSED);
71
72 } catch (const std::exception& e) {
73 container->update_processing_state(ProcessingState::ERROR);
74 error_rethrow(
77 std::source_location::current(),
78 "Error during region organization processing: {}",
79 e.what());
80 }
81
82 m_is_processing.store(false);
83}
84
85void RegionOrganizationProcessor::add_region_group(const std::string& group_name)
86{
87 if (auto container = m_container_weak.lock()) {
88 RegionGroup new_group(group_name);
89 container->add_region_group(new_group);
91 }
92}
93
94void RegionOrganizationProcessor::add_segment_to_region(const std::string& group_name,
95 size_t region_index,
96 const std::vector<uint64_t>& start_coords,
97 const std::vector<uint64_t>& end_coords,
98 const std::unordered_map<std::string, std::any>& attributes)
99{
100 auto region_it = std::ranges::find_if(m_organized_regions, [&](const OrganizedRegion& region) {
101 return region.group_name == group_name && region.region_index == region_index;
102 });
103
104 if (region_it != m_organized_regions.end()) {
105 Region region(start_coords, end_coords, attributes);
106 RegionSegment segment(region);
107
108 region_it->segments.push_back(std::move(segment));
109
110 if (auto container = m_container_weak.lock()) {
111 cache_region_if_needed(region_it->segments.back(), container);
112 }
113 }
114}
115
116void RegionOrganizationProcessor::set_region_transition(const std::string& group_name,
117 size_t region_index,
118 RegionTransition type,
119 double duration_ms)
120{
121 auto region_it = std::ranges::find_if(m_organized_regions, [&](OrganizedRegion& region) {
122 return region.group_name == group_name && region.region_index == region_index;
123 });
124
125 if (region_it != m_organized_regions.end()) {
126 region_it->transition_type = type;
127 region_it->transition_duration_ms = duration_ms;
128 }
129}
130
131void RegionOrganizationProcessor::set_selection_pattern(const std::string& group_name,
132 size_t region_index,
134{
135 auto region_it = std::ranges::find_if(m_organized_regions, [&](OrganizedRegion& region) {
136 return region.group_name == group_name && region.region_index == region_index;
137 });
138
139 if (region_it != m_organized_regions.end()) {
140 region_it->selection_pattern = pattern;
141 }
142}
143
144void RegionOrganizationProcessor::set_region_looping(const std::string& group_name,
145 size_t region_index,
146 bool enabled,
147 const std::vector<uint64_t>& loop_start,
148 const std::vector<uint64_t>& loop_end)
149{
150 auto region_it = std::ranges::find_if(m_organized_regions, [&](OrganizedRegion& region) {
151 return region.group_name == group_name && region.region_index == region_index;
152 });
153
154 if (region_it != m_organized_regions.end()) {
155 region_it->looping_enabled = enabled;
156 if (!loop_start.empty())
157 region_it->loop_start = loop_start;
158 if (!loop_end.empty())
159 region_it->loop_end = loop_end;
160 }
161}
162
163void RegionOrganizationProcessor::jump_to_region(const std::string& group_name, size_t region_index)
164{
165 auto region_it = std::ranges::find_if(m_organized_regions, [&](const OrganizedRegion& region) {
166 return region.group_name == group_name && region.region_index == region_index;
167 });
168
169 if (region_it != m_organized_regions.end()) {
170 m_current_region_index = std::distance(m_organized_regions.begin(), region_it);
171 if (!region_it->segments.empty()) {
172 m_current_position = region_it->segments[0].source_region.start_coordinates;
173 }
174 }
175}
176
177void RegionOrganizationProcessor::jump_to_position(const std::vector<uint64_t>& position)
178{
179 m_current_position = position;
180
181 auto region_index = find_region_for_position(position, m_organized_regions);
182 if (region_index) {
183 m_current_region_index = *region_index;
184 }
185}
186
187void RegionOrganizationProcessor::process_organized_regions(const std::shared_ptr<SignalSourceContainer>& container,
188 std::vector<DataVariant>& output_data)
189{
190 if (m_organized_regions.empty())
191 return;
192
193 auto& current_region = m_organized_regions[m_current_region_index];
194 current_region.state = RegionState::ACTIVE;
195
196 size_t selected_segment = select_next_segment(current_region);
197
198 bool segment_changed = (selected_segment != current_region.active_segment_index);
199 bool segment_completed = is_segment_complete(current_region, selected_segment);
200 bool region_completed = (segment_completed && selected_segment == current_region.segments.size() - 1);
201
202 if (region_completed && m_current_region_index < m_organized_regions.size() - 1) {
203 auto& next_region = m_organized_regions[m_current_region_index + 1];
204
205 bool should_transition = false;
206 switch (current_region.transition_type) {
208 should_transition = false;
209 break;
212 should_transition = (current_region.transition_duration_ms > 0);
213 break;
214 default:
215 should_transition = false;
216 break;
217 }
218
219 if (should_transition) {
220 apply_region_transition(current_region, next_region, container, output_data);
221 } else {
222 process_region_segment(current_region, current_region.segments[selected_segment],
223 container, output_data);
224 }
225
227 auto& new_current = m_organized_regions[m_current_region_index];
228 new_current.state = RegionState::READY;
229 new_current.active_segment_index = 0;
230
231 } else {
232 process_region_segment(current_region, current_region.segments[selected_segment],
233 container, output_data);
234 current_region.active_segment_index = selected_segment;
235 }
236}
237
239 const RegionSegment& segment,
240 const std::shared_ptr<SignalSourceContainer>& container,
241 std::vector<DataVariant>& output_data)
242{
243 if (m_cache_manager) {
244 if (auto cached_data = m_cache_manager->get_cached_segment(segment)) {
245 output_data.resize(cached_data->data.size());
246
247 std::ranges::for_each(std::views::zip(cached_data->data, output_data),
248 [](auto&& pair) {
249 auto&& [source, dest] = pair;
250 safe_copy_data_variant(source, dest);
251 });
252 return;
253 }
254 }
255
256 auto region_data = container->get_region_data(segment.source_region);
257
258 output_data.resize(region_data.size());
259
260 std::ranges::for_each(std::views::zip(region_data, output_data),
261 [](auto&& pair) {
262 auto&& [source, dest] = pair;
263 safe_copy_data_variant(source, dest);
264 });
265}
266
267/* void RegionOrganizationProcessor::apply_region_transition(const OrganizedRegion& current_region,
268 const OrganizedRegion& next_region,
269 const std::shared_ptr<SignalSourceContainer>& container,
270 std::vector<DataVariant>& output_data)
271{
272 if (next_region.segments.empty()) {
273 return;
274 }
275
276
277} */
278
280 const OrganizedRegion& next_region,
281 const std::shared_ptr<SignalSourceContainer>& container,
282 std::vector<DataVariant>& output_data)
283{
284 if (next_region.segments.empty()) {
285 return;
286 }
287
288 auto next_data = container->get_region_data(next_region.segments[0].source_region);
289
290 // TODO:: Reenable when C++23 is more widely supported
291 /* for (auto&& [current_var, next_var] : std::views::zip(output_data, next_data)) {
292 auto current_span = convert_variant<double>(current_var);
293 auto next_span = convert_variant<double>(const_cast<DataVariant&>(next_var));
294 auto paired_samples = std::views::zip(current_span, next_span)
295 | std::views::enumerate;
296
297 switch (current_region.transition_type) {
298 case RegionTransition::CROSSFADE:
299 std::ranges::for_each(paired_samples, [size = current_span.size()](auto&& item) {
300 auto [i, sample_pair] = item;
301 auto [current, next] = sample_pair;
302 double fade_factor = static_cast<double>(i) / static_cast<double>(size);
303 current = current * (1.0 - fade_factor) + next * fade_factor;
304 });
305 break;
306
307 case RegionTransition::OVERLAP:
308 std::ranges::for_each(paired_samples, [](auto&& item) {
309 auto [i, sample_pair] = item;
310 auto [current, next] = sample_pair;
311 current = current * 0.5 + next * 0.5;
312 });
313 break;
314
315 default:
316 break;
317 }
318 } */
319
320 const size_t data_count = std::min<size_t>(output_data.size(), next_data.size());
321 for (size_t variant_idx = 0; variant_idx < data_count; ++variant_idx) {
322 auto current_span = convert_variant<double>(output_data[variant_idx]);
323 auto next_span = convert_variant<double>(const_cast<DataVariant&>(next_data[variant_idx]));
324
325 const size_t sample_count = std::min<size_t>(current_span.size(), next_span.size());
326
327 switch (current_region.transition_type) {
329 for (size_t i = 0; i < sample_count; ++i) {
330 double fade_factor = static_cast<double>(i) / static_cast<double>(sample_count);
331 current_span[i] = current_span[i] * (1.0 - fade_factor) + next_span[i] * fade_factor;
332 }
333 break;
334
336 for (size_t i = 0; i < sample_count; ++i) {
337 current_span[i] = current_span[i] * 0.5 + next_span[i] * 0.5;
338 }
339 break;
340
341 default:
342 break;
343 }
344 }
345}
346
348{
349 if (region.segments.empty())
350 return 0;
351
352 switch (region.selection_pattern) {
354 return (region.active_segment_index + 1) % region.segments.size();
355
357 if (std::uniform_int_distribution<size_t> dist(0, region.segments.size() - 1); true) {
358 return dist(m_random_engine);
359 }
360
362 if (m_segment_weights.empty() || m_segment_weights.size() != region.segments.size()) {
363 return region.active_segment_index % region.segments.size();
364 }
365
366 if (std::discrete_distribution<size_t> dist(m_segment_weights.begin(), m_segment_weights.end()); true) {
367 return dist(m_random_engine);
368 }
369
370 default:
371 return 0;
372 }
373}
374
376 const std::vector<uint64_t>& position,
377 const std::vector<OrganizedRegion>& regions) const
378{
379 if (auto it = std::ranges::find_if(regions,
380 [&position](const auto& region) { return region.contains_position(position); });
381 it != regions.end()) {
382 return std::distance(regions.begin(), it);
383 }
384 return std::nullopt;
385}
386
387void RegionOrganizationProcessor::organize_group(const std::shared_ptr<SignalSourceContainer>& container,
388 const RegionGroup& group)
389{
390 // TODO:: Reenable when C++23 is more widely supported
391 /* for (auto&& [i, region] : std::views::enumerate(group.regions)) {
392 OrganizedRegion organized_region(group.name, i);
393
394 RegionSegment segment(region);
395
396 cache_region_if_needed(segment, container);
397
398 organized_region.segments.push_back(std::move(segment));
399
400 std::ranges::copy(group.attributes,
401 std::inserter(organized_region.attributes, organized_region.attributes.end()));
402 std::ranges::copy(region.attributes,
403 std::inserter(organized_region.attributes, organized_region.attributes.end()));
404
405 organized_region.current_position = region.start_coordinates;
406 organized_region.state = RegionState::READY;
407
408 m_organized_regions.push_back(std::move(organized_region));
409 } */
410 for (size_t i = 0; const auto& region : group.regions) {
411 OrganizedRegion organized_region(group.name, i);
412 RegionSegment segment(region);
413 cache_region_if_needed(segment, container);
414 organized_region.segments.push_back(std::move(segment));
415 std::ranges::copy(group.attributes,
416 std::inserter(organized_region.attributes, organized_region.attributes.end()));
417 std::ranges::copy(region.attributes,
418 std::inserter(organized_region.attributes, organized_region.attributes.end()));
419 organized_region.current_position = region.start_coordinates;
420 organized_region.state = RegionState::READY;
421 m_organized_regions.push_back(std::move(organized_region));
422 ++i;
423 }
424}
425
427{
428 if (auto container = m_container_weak.lock()) {
429 organize_container_data(container);
430 }
431}
432
433// =============================================================================
434// DynamicRegionProcessor Implementation
435// =============================================================================
436
437DynamicRegionProcessor::DynamicRegionProcessor(const std::shared_ptr<SignalSourceContainer>& container)
438 : RegionOrganizationProcessor(container)
439{
440}
441
442void DynamicRegionProcessor::process(const std::shared_ptr<SignalSourceContainer>& container)
443{
444 if (!container)
445 return;
446
447 if (should_reorganize(container)) {
450 }
451 m_needs_reorganization.store(false);
452 }
453
455}
456
457bool DynamicRegionProcessor::should_reorganize(const std::shared_ptr<SignalSourceContainer>& container)
458{
459 if (m_needs_reorganization.load()) {
460 return true;
461 }
462
465 }
466
467 return false;
468}
469
474
479
480}
size_t a
size_t b
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:76
RegionSelectionPattern
Describes how regions are selected for processing or playback.
Definition Region.hpp:17
@ SEQUENTIAL
Process regions in order.
@ WEIGHTED
Weighted random selection.
RegionTransition
Describes how transitions between regions are handled.
Definition Region.hpp:32
@ 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:73