MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
MatrixHelper.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "OperationHelper.hpp"
4
8
9#include <Eigen/Dense>
10
11/**
12 * @file MatrixHelper.hpp
13 * @brief Region-aware and matrix transformation functions leveraging existing ecosystem
14 *
15 * - Uses EnergyAnalyzer for region detection instead of manual implementation
16 * - Leverages StatisticalAnalyzer for outlier detection instead of manual stats
17 * - Uses ExtractionHelper functions for data extraction
18 * - Uses normalization from MathematicalTransform where appropriate
19 */
20
21namespace MayaFlux::Yantra {
22
23/**
24 * @brief Region-selective transformation using container-based extraction (IN-PLACE)
25 * @tparam DataType OperationReadyData type
26 * @tparam TransformFunc Function type for transformation
27 * @param input Input data - WILL BE MODIFIED (serves as the target)
28 * @param container Signal source container
29 * @param regions Regions to transform and place back into input
30 * @param transform_func Transformation function
31 * @return Transformed data
32 */
33template <OperationReadyData DataType, typename TransformFunc>
34 requires std::invocable<TransformFunc, DataType>
35DataType transform_regions(DataType& input,
36 const std::shared_ptr<Kakshya::SignalSourceContainer>& container,
37 const std::vector<Kakshya::Region>& regions,
38 TransformFunc transform_func)
39{
40 if (!container) {
41 throw std::invalid_argument("Container is required for region-based transformations");
42 }
43
44 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
45
46 for (const auto& region : regions) {
47 auto region_data = container->get_region_data(region);
48 auto transformed_region = transform_func(region_data);
49 auto transformed_doubles = OperationHelper::extract_numeric_data(transformed_region);
50
51 auto start_sample = static_cast<uint64_t>(region.start_coordinates[0]);
52 auto end_sample = static_cast<uint64_t>(region.end_coordinates[0]);
53
54 for (size_t channel_idx = 0; channel_idx < target_data.size() && channel_idx < transformed_doubles.size(); ++channel_idx) {
55 auto& target_channel = target_data[channel_idx];
56 const auto& transformed_channel = transformed_doubles[channel_idx];
57
58 if (start_sample < target_channel.size() && end_sample <= target_channel.size()) {
59 auto target_span = target_channel.subspan(start_sample, end_sample - start_sample);
60 auto copy_size = std::min(transformed_channel.size(), target_span.size());
61
62 std::ranges::copy(transformed_channel | std::views::take(copy_size),
63 target_span.begin());
64 }
65 }
66 }
67
68 auto reconstructed_data = target_data
69 | std::views::transform([](const auto& span) {
70 return std::vector<double>(span.begin(), span.end());
71 })
72 | std::ranges::to<std::vector>();
73
74 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
75}
76
77/**
78 * @brief Region-selective transformation using container-based extraction (OUT-OF-PLACE)
79 * @tparam DataType OperationReadyData type
80 * @tparam TransformFunc Function type for transformation
81 * @param input Input data - will NOT be modified
82 * @param container Signal source container
83 * @param regions Regions to transform and place back into copy
84 * @param transform_func Transformation function
85 * @param working_buffer Buffer for operations (will be resized if needed)
86 * @return Transformed data
87 */
88template <OperationReadyData DataType, typename TransformFunc>
89 requires std::invocable<TransformFunc, DataType>
90DataType transform_regions(DataType& input,
91 const std::shared_ptr<Kakshya::SignalSourceContainer>& container,
92 const std::vector<Kakshya::Region>& regions,
93 TransformFunc transform_func,
94 std::vector<std::vector<double>>& working_buffer)
95{
96 if (!container) {
97 throw std::invalid_argument("Container is required for region-based transformations");
98 }
99
100 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
101
102 for (const auto& region : regions) {
103 auto region_data = container->get_region_data(region);
104 auto transformed_region = transform_func(region_data);
105 auto transformed_doubles = OperationHelper::extract_numeric_data(transformed_region);
106
107 auto start_sample = static_cast<uint64_t>(region.start_coordinates[0]);
108 auto end_sample = static_cast<uint64_t>(region.end_coordinates[0]);
109
110 for (size_t channel_idx = 0; channel_idx < target_data.size() && channel_idx < transformed_doubles.size(); ++channel_idx) {
111 auto& target_channel = target_data[channel_idx];
112 const auto& transformed_channel = transformed_doubles[channel_idx];
113
114 if (start_sample < target_channel.size() && end_sample <= target_channel.size()) {
115 auto target_span = target_channel.subspan(start_sample, end_sample - start_sample);
116 auto copy_size = std::min(transformed_channel.size(), target_span.size());
117
118 std::ranges::copy(transformed_channel | std::views::take(copy_size),
119 target_span.begin());
120 }
121 }
122 }
123
124 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
125}
126
127/**
128 * @brief Energy-based transformation using existing EnergyAnalyzer (IN-PLACE)
129 * @tparam DataType OperationReadyData type
130 * @tparam TransformFunc Function type for transformation
131 * @param input Input data - WILL BE MODIFIED
132 * @param energy_threshold Energy threshold for transformation
133 * @param transform_func Transformation function
134 * @param window_size Analysis window size
135 * @param hop_size Hop size between windows
136 * @return Transformed data
137 */
138template <OperationReadyData DataType, typename TransformFunc>
139 requires std::invocable<TransformFunc, double>
140DataType transform_by_energy(DataType& input,
141 double energy_threshold,
142 TransformFunc transform_func,
143 uint32_t window_size = 1024,
144 uint32_t hop_size = 512)
145{
146 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
147
148 auto energy_analyzer = std::make_shared<StandardEnergyAnalyzer>(window_size, hop_size);
149 energy_analyzer->set_energy_method(EnergyMethod::RMS);
150 auto energy_result = energy_analyzer->analyze_energy(input);
151
152 for (size_t ch = 0; ch < energy_result.channels.size() && ch < target_data.size(); ++ch) {
153 const auto& channel_energy = energy_result.channels[ch];
154 auto& target_channel = target_data[ch];
155
156 for (size_t i = 0; i < channel_energy.energy_values.size(); ++i) {
157 if (channel_energy.energy_values[i] > energy_threshold && i < channel_energy.window_positions.size()) {
158 auto [start_idx, end_idx] = channel_energy.window_positions[i];
159
160 if (start_idx < target_channel.size() && end_idx <= target_channel.size()) {
161 auto window_span = target_channel.subspan(start_idx, end_idx - start_idx);
162 std::ranges::transform(window_span, window_span.begin(), transform_func);
163 }
164 }
165 }
166 }
167
168 auto reconstructed_data = target_data
169 | std::views::transform([](const auto& span) {
170 return std::vector<double>(span.begin(), span.end());
171 })
172 | std::ranges::to<std::vector>();
173
174 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
175}
176
177/**
178 * @brief Energy-based transformation using existing EnergyAnalyzer (OUT-OF-PLACE)
179 * @tparam DataType OperationReadyData type
180 * @tparam TransformFunc Function type for transformation
181 * @param input Input data - will NOT be modified
182 * @param energy_threshold Energy threshold for transformation
183 * @param transform_func Transformation function
184 * @param window_size Analysis window size
185 * @param hop_size Hop size between windows
186 * @param working_buffer Buffer for operations (will be resized if needed)
187 * @return Transformed data
188 */
189template <OperationReadyData DataType, typename TransformFunc>
190 requires std::invocable<TransformFunc, double>
191DataType transform_by_energy(DataType& input,
192 double energy_threshold,
193 TransformFunc transform_func,
194 uint32_t window_size,
195 uint32_t hop_size,
196 std::vector<std::vector<double>>& working_buffer)
197{
198 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
199
200 auto energy_analyzer = std::make_shared<StandardEnergyAnalyzer>(window_size, hop_size);
201 energy_analyzer->set_energy_method(EnergyMethod::RMS);
202 auto energy_result = energy_analyzer->analyze_energy(input);
203
204 for (size_t ch = 0; ch < energy_result.channels.size() && ch < target_data.size(); ++ch) {
205 const auto& channel_energy = energy_result.channels[ch];
206 auto& target_channel = target_data[ch];
207
208 for (size_t i = 0; i < channel_energy.energy_values.size(); ++i) {
209 if (channel_energy.energy_values[i] > energy_threshold && i < channel_energy.window_positions.size()) {
210 auto [start_idx, end_idx] = channel_energy.window_positions[i];
211
212 if (start_idx < target_channel.size() && end_idx <= target_channel.size()) {
213 auto window_span = target_channel.subspan(start_idx, end_idx - start_idx);
214 std::ranges::transform(window_span, window_span.begin(), transform_func);
215 }
216 }
217 }
218 }
219
220 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
221}
222
223/**
224 * @brief Statistical outlier transformation using existing StatisticalAnalyzer (IN-PLACE)
225 * @tparam DataType OperationReadyData type
226 * @tparam TransformFunc Function type for transformation
227 * @param input Input data - WILL BE MODIFIED
228 * @param std_dev_threshold Standard deviation threshold for outliers
229 * @param transform_func Transformation function
230 * @return Transformed data
231 */
232template <OperationReadyData DataType, typename TransformFunc>
233 requires std::invocable<TransformFunc, double>
234DataType transform_outliers(DataType& input,
235 double std_dev_threshold,
236 TransformFunc transform_func)
237{
238 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
239
240 auto stat_analyzer = std::make_shared<StandardStatisticalAnalyzer>();
241 auto stats = stat_analyzer->analyze_statistics(input);
242
243 if (stats.channel_statistics.empty()) {
244 throw std::runtime_error("No channel statistics available for outlier detection");
245 }
246 const auto& first_channel_stats = stats.channel_statistics[0];
247 double threshold_low = first_channel_stats.mean_stat - std_dev_threshold * first_channel_stats.stat_std_dev;
248 double threshold_high = first_channel_stats.mean_stat + std_dev_threshold * first_channel_stats.stat_std_dev;
249
250 for (auto& channel_span : target_data) {
251 std::ranges::transform(channel_span, channel_span.begin(),
252 [&](double x) {
253 return (x < threshold_low || x > threshold_high) ? transform_func(x) : x;
254 });
255 }
256
257 auto reconstructed_data = target_data
258 | std::views::transform([](const auto& span) {
259 return std::vector<double>(span.begin(), span.end());
260 })
261 | std::ranges::to<std::vector>();
262
263 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
264}
265
266/**
267 * @brief Statistical outlier transformation using existing StatisticalAnalyzer (OUT-OF-PLACE)
268 * @tparam DataType OperationReadyData type
269 * @tparam TransformFunc Function type for transformation
270 * @param input Input data - will NOT be modified
271 * @param std_dev_threshold Standard deviation threshold for outliers
272 * @param transform_func Transformation function
273 * @param working_buffer Buffer for operations (will be resized if needed)
274 * @return Transformed data
275 */
276template <OperationReadyData DataType, typename TransformFunc>
277 requires std::invocable<TransformFunc, double>
278DataType transform_outliers(DataType& input,
279 double std_dev_threshold,
280 TransformFunc transform_func,
281 std::vector<std::vector<double>>& working_buffer)
282{
283 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
284
285 auto stat_analyzer = std::make_shared<StandardStatisticalAnalyzer>();
286 auto stats = stat_analyzer->analyze_statistics(input);
287
288 if (stats.channel_statistics.empty()) {
289 throw std::runtime_error("No channel statistics available for outlier detection");
290 }
291 const auto& first_channel_stats = stats.channel_statistics[0];
292 double threshold_low = first_channel_stats.mean_stat - std_dev_threshold * first_channel_stats.stat_std_dev;
293 double threshold_high = first_channel_stats.mean_stat + std_dev_threshold * first_channel_stats.stat_std_dev;
294
295 for (auto& channel_span : target_data) {
296 std::ranges::transform(channel_span, channel_span.begin(),
297 [&](double x) {
298 return (x < threshold_low || x > threshold_high) ? transform_func(x) : x;
299 });
300 }
301
302 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
303}
304
305/**
306 * @brief Cross-fade between regions with smooth transitions (IN-PLACE)
307 * @tparam DataType OperationReadyData type
308 * @param input Input data - WILL BE MODIFIED
309 * @param fade_regions Pairs of regions to crossfade
310 * @param fade_duration Duration of fade in samples
311 * @return Crossfaded data
312 */
313template <OperationReadyData DataType>
314DataType transform_crossfade_regions(DataType& input,
315 const std::vector<std::pair<Kakshya::Region, Kakshya::Region>>& fade_regions,
316 uint32_t fade_duration)
317{
318 // OLD: auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
319 auto extraction_result = OperationHelper::extract_structured_double(input);
320 auto& target_data = std::get<0>(extraction_result);
321 auto& structure_info = std::get<1>(extraction_result);
322
323 std::ranges::for_each(fade_regions, [&target_data, fade_duration](const auto& fade_pair) {
324 const auto& [region_a, region_b] = fade_pair;
325
326 auto start_a = static_cast<uint64_t>(region_a.start_coordinates[0]);
327 auto end_a = static_cast<uint64_t>(region_a.end_coordinates[0]);
328 auto start_b = static_cast<uint64_t>(region_b.start_coordinates[0]);
329
330 uint64_t fade_start = (end_a > fade_duration) ? end_a - fade_duration : 0;
331 uint64_t fade_end = std::min(start_b + fade_duration, static_cast<uint64_t>(target_data[0].size()));
332
333 for (auto& channel_span : target_data) {
334 if (fade_start < fade_end && fade_start < channel_span.size()) {
335 auto fade_span = channel_span.subspan(fade_start, fade_end - fade_start);
336
337 auto fade_indices = std::views::iota(size_t { 0 }, fade_span.size());
338 std::ranges::for_each(fade_indices, [&fade_span](size_t i) {
339 double ratio = static_cast<double>(i) / (fade_span.size() - 1);
340 double smooth_ratio = 0.5 * (1.0 - std::cos(ratio * M_PI));
341 fade_span[i] *= (1.0 - smooth_ratio);
342 });
343 }
344 }
345 });
346
347 auto reconstructed_data = target_data
348 | std::views::transform([](const auto& span) {
349 return std::vector<double>(span.begin(), span.end());
350 })
351 | std::ranges::to<std::vector>();
352
353 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
354}
355
356/**
357 * @brief Cross-fade between regions with smooth transitions (OUT-OF-PLACE)
358 * @tparam DataType OperationReadyData type
359 * @param input Input data - will NOT be modified
360 * @param fade_regions Pairs of regions to crossfade
361 * @param fade_duration Duration of fade in samples
362 * @param working_buffer Buffer for operations (will be resized if needed)
363 * @return Crossfaded data
364 */
365template <OperationReadyData DataType>
366DataType transform_crossfade_regions(DataType& input,
367 const std::vector<std::pair<Kakshya::Region, Kakshya::Region>>& fade_regions,
368 uint32_t fade_duration,
369 std::vector<std::vector<double>>& working_buffer)
370{
371 // OLD: auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
372 auto setup_result = OperationHelper::setup_operation_buffer(input, working_buffer);
373 auto& target_data = std::get<0>(setup_result);
374 auto& structure_info = std::get<1>(setup_result);
375
376 std::ranges::for_each(fade_regions, [&target_data, fade_duration](const auto& fade_pair) {
377 const auto& [region_a, region_b] = fade_pair;
378
379 auto start_a = static_cast<uint64_t>(region_a.start_coordinates[0]);
380 auto end_a = static_cast<uint64_t>(region_a.end_coordinates[0]);
381 auto start_b = static_cast<uint64_t>(region_b.start_coordinates[0]);
382
383 uint64_t fade_start = (end_a > fade_duration) ? end_a - fade_duration : 0;
384 uint64_t fade_end = std::min(start_b + fade_duration, static_cast<uint64_t>(target_data[0].size()));
385
386 for (auto& channel_span : target_data) {
387 if (fade_start < fade_end && fade_start < channel_span.size()) {
388 auto fade_span = channel_span.subspan(fade_start, fade_end - fade_start);
389
390 auto fade_indices = std::views::iota(size_t { 0 }, fade_span.size());
391 std::ranges::for_each(fade_indices, [&fade_span](size_t i) {
392 double ratio = static_cast<double>(i) / (fade_span.size() - 1);
393 double smooth_ratio = 0.5 * (1.0 - std::cos(ratio * M_PI));
394 fade_span[i] *= (1.0 - smooth_ratio);
395 });
396 }
397 }
398 });
399
400 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
401}
402
403/**
404 * @brief Matrix transformation using existing infrastructure (IN-PLACE)
405 * @tparam DataType OperationReadyData type
406 * @param input Input data - WILL BE MODIFIED
407 * @param transformation_matrix Matrix to apply
408 * @return Transformed data
409 */
410template <OperationReadyData DataType>
411DataType transform_matrix(DataType& input, const Eigen::MatrixXd& transformation_matrix)
412{
413 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
414
415 for (auto& channel_span : target_data) {
416 if (transformation_matrix.cols() == static_cast<long>(channel_span.size())) {
417 Eigen::Map<Eigen::VectorXd> data_vector(channel_span.data(), channel_span.size());
418 Eigen::VectorXd result = transformation_matrix * data_vector;
419
420 auto copy_size = std::min<long>(result.size(), static_cast<long>(channel_span.size()));
421 std::ranges::copy(result.head(copy_size), channel_span.begin());
422 }
423 }
424
425 auto reconstructed_data = target_data
426 | std::views::transform([](const auto& span) {
427 return std::vector<double>(span.begin(), span.end());
428 })
429 | std::ranges::to<std::vector>();
430
431 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
432}
433
434/**
435 * @brief Matrix transformation using existing infrastructure (OUT-OF-PLACE)
436 * @tparam DataType OperationReadyData type
437 * @param input Input data - will NOT be modified
438 * @param transformation_matrix Matrix to apply
439 * @param working_buffer Buffer for operations (will be resized if needed)
440 * @return Transformed data
441 */
442template <OperationReadyData DataType>
443DataType transform_matrix(DataType& input, const Eigen::MatrixXd& transformation_matrix, std::vector<std::vector<double>>& working_buffer)
444{
445 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
446
447 for (size_t channel_idx = 0; channel_idx < target_data.size(); ++channel_idx) {
448 auto& channel_span = target_data[channel_idx];
449 if (transformation_matrix.cols() == static_cast<long>(channel_span.size())) {
450 Eigen::Map<Eigen::VectorXd> data_vector(channel_span.data(), channel_span.size());
451 Eigen::VectorXd result = transformation_matrix * data_vector;
452
453 working_buffer[channel_idx].resize(result.size());
454 std::ranges::copy(result, working_buffer[channel_idx].begin());
455 }
456 }
457
458 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
459}
460
461/**
462 * @brief Multi-channel matrix transformation with error handling (IN-PLACE)
463 * @tparam DataType OperationReadyData type
464 * @param input Input data - WILL BE MODIFIED
465 * @param transformation_matrix Matrix to apply per channel
466 * @param num_channels Number of channels
467 * @return Transformed data
468 */
469template <OperationReadyData DataType>
470DataType transform_matrix_multichannel(DataType& input,
471 const Eigen::MatrixXd& transformation_matrix,
472 uint32_t num_channels)
473{
474 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
475
476 if (transformation_matrix.rows() != num_channels || transformation_matrix.cols() != num_channels) {
477 throw std::invalid_argument("Transformation matrix dimensions must match number of channels");
478 }
479
480 if (target_data.size() != num_channels) {
481 throw std::invalid_argument("Data channel count must match specified number of channels");
482 }
483
484 size_t min_frames = std::ranges::min(target_data | std::views::transform([](const auto& span) { return span.size(); }));
485
486 auto frame_indices = std::views::iota(size_t { 0 }, min_frames);
487
488 std::ranges::for_each(frame_indices, [&](size_t frame) {
489 Eigen::VectorXd frame_vector(num_channels);
490
491 for (size_t channel = 0; channel < num_channels; ++channel) {
492 frame_vector[channel] = target_data[channel][frame];
493 }
494
495 Eigen::VectorXd transformed = transformation_matrix * frame_vector;
496
497 for (size_t channel = 0; channel < num_channels; ++channel) {
498 target_data[channel][frame] = transformed[channel];
499 }
500 });
501
502 auto reconstructed_data = target_data
503 | std::views::transform([](const auto& span) {
504 return std::vector<double>(span.begin(), span.end());
505 })
506 | std::ranges::to<std::vector>();
507
508 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
509}
510
511/**
512 * @brief Multi-channel matrix transformation with error handling (OUT-OF-PLACE)
513 * @tparam DataType OperationReadyData type
514 * @param input Input data - will NOT be modified
515 * @param transformation_matrix Matrix to apply per channel
516 * @param num_channels Number of channels
517 * @param working_buffer Buffer for operations (will be resized if needed)
518 * @return Transformed data
519 */
520template <OperationReadyData DataType>
521DataType transform_matrix_multichannel(DataType& input,
522 const Eigen::MatrixXd& transformation_matrix,
523 uint32_t num_channels,
524 std::vector<std::vector<double>>& working_buffer)
525{
526 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
527
528 if (transformation_matrix.rows() != num_channels || transformation_matrix.cols() != num_channels) {
529 throw std::invalid_argument("Transformation matrix dimensions must match number of channels");
530 }
531
532 if (target_data.size() != num_channels) {
533 throw std::invalid_argument("Data channel count must match specified number of channels");
534 }
535
536 size_t min_frames = std::ranges::min(target_data | std::views::transform([](const auto& span) { return span.size(); }));
537
538 auto frame_indices = std::views::iota(size_t { 0 }, min_frames);
539
540 std::ranges::for_each(frame_indices, [&](size_t frame) {
541 Eigen::VectorXd frame_vector(num_channels);
542
543 for (size_t channel = 0; channel < num_channels; ++channel) {
544 frame_vector[channel] = target_data[channel][frame];
545 }
546
547 Eigen::VectorXd transformed = transformation_matrix * frame_vector;
548
549 for (size_t channel = 0; channel < num_channels; ++channel) {
550 working_buffer[channel][frame] = transformed[channel];
551 }
552 });
553
554 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
555}
556
557/**
558 * @brief Channel operations using C++20 ranges (IN-PLACE)
559 * @tparam DataType OperationReadyData type
560 * @param input Input data - WILL BE MODIFIED
561 * @param num_channels Number of channels
562 * @param interleave Whether to interleave or deinterleave
563 * @return Transformed data
564 */
565template <OperationReadyData DataType>
566DataType transform_channel_operation(DataType& input,
567 uint32_t num_channels,
568 bool interleave)
569{
570 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
571
572 if (target_data.size() != num_channels) {
573 throw std::invalid_argument("Data channel count must match specified number of channels");
574 }
575
576 auto reconstructed_data = target_data
577 | std::views::transform([](const auto& span) {
578 return std::vector<double>(span.begin(), span.end());
579 })
580 | std::ranges::to<std::vector>();
581
582 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
583}
584
585/**
586 * @brief Channel operations using C++20 ranges (OUT-OF-PLACE)
587 * @tparam DataType OperationReadyData type
588 * @param input Input data - will NOT be modified
589 * @param num_channels Number of channels
590 * @param interleave Whether to interleave or deinterleave
591 * @param working_buffer Buffer for operations (will be resized if needed)
592 * @return Transformed data
593 */
594template <OperationReadyData DataType>
595DataType transform_channel_operation(DataType& input,
596 uint32_t num_channels,
597 bool interleave,
598 std::vector<std::vector<double>>& working_buffer)
599{
600 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
601
602 if (target_data.size() != num_channels) {
603 throw std::invalid_argument("Data channel count must match specified number of channels");
604 }
605
606 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
607}
608
609/**
610 * @brief Detect regions based on energy threshold using existing EnergyAnalyzer
611 * @tparam DataType OperationReadyData type
612 * @param input Input data
613 * @param energy_threshold Energy threshold for region detection
614 * @param min_region_size Minimum size of detected regions in samples
615 * @param window_size Analysis window size
616 * @param hop_size Hop size between windows
617 * @return Detected regions
618 */
619template <OperationReadyData DataType>
620std::vector<Kakshya::Region> detect_regions_by_energy(const DataType& input,
621 double energy_threshold,
622 uint32_t min_region_size,
623 uint32_t window_size,
624 uint32_t hop_size)
625{
626 auto energy_analyzer = std::make_shared<StandardEnergyAnalyzer>(window_size, hop_size);
627 energy_analyzer->set_energy_method(EnergyMethod::RMS);
628 auto energy_result = energy_analyzer->analyze_energy(input);
629
630 std::vector<Kakshya::Region> regions;
631
632 if (energy_result.channels.empty()) {
633 return regions;
634 }
635
636 const auto& first_channel = energy_result.channels[0];
637 std::optional<uint64_t> region_start;
638
639 for (size_t i = 0; i < first_channel.energy_values.size(); ++i) {
640 bool above_threshold = first_channel.energy_values[i] > energy_threshold;
641
642 if (above_threshold && !region_start.has_value()) {
643 if (i < first_channel.window_positions.size()) {
644 region_start = first_channel.window_positions[i].first;
645 }
646 } else if (!above_threshold && region_start.has_value()) {
647 if (i < first_channel.window_positions.size()) {
648 uint64_t region_end = first_channel.window_positions[i].second;
649
650 if (region_end - region_start.value() >= min_region_size) {
651 Kakshya::Region region;
652 region.start_coordinates = { region_start.value() };
653 region.end_coordinates = { region_end };
654 regions.push_back(region);
655 }
656 region_start.reset();
657 }
658 }
659 }
660
661 if (region_start.has_value() && !first_channel.window_positions.empty()) {
662 uint64_t region_end = first_channel.window_positions.back().second;
663 if (region_end - region_start.value() >= min_region_size) {
664 Kakshya::Region region;
665
666 region.start_coordinates = { region_start.value() };
667 region.end_coordinates = { region_end };
668 regions.push_back(region);
669 }
670 }
671
672 return regions;
673}
674
675} // namespace MayaFlux::Yantra
Span-based energy analysis for digital signals in Maya Flux.
Range size
uint32_t channel
static std::tuple< std::vector< std::span< double > >, DataStructureInfo > extract_structured_double(T &compute_data)
Extract structured double data from Datum container or direct ComputeData with automatic container ha...
static auto setup_operation_buffer(T &input, std::vector< std::vector< double > > &working_buffer)
Setup operation buffer from Datum or ComputeData type.
static std::span< double > extract_numeric_data(const T &compute_data)
extract numeric data from single-variant types
DataType transform_regions(DataType &input, const std::shared_ptr< Kakshya::SignalSourceContainer > &container, const std::vector< Kakshya::Region > &regions, TransformFunc transform_func)
Region-selective transformation using container-based extraction (IN-PLACE)
@ RMS
Root Mean Square energy.
DataType transform_matrix(DataType &input, const Eigen::MatrixXd &transformation_matrix)
Matrix transformation using existing infrastructure (IN-PLACE)
DataType transform_matrix_multichannel(DataType &input, const Eigen::MatrixXd &transformation_matrix, uint32_t num_channels)
Multi-channel matrix transformation with error handling (IN-PLACE)
DataType transform_by_energy(DataType &input, double energy_threshold, TransformFunc transform_func, uint32_t window_size=1024, uint32_t hop_size=512)
Energy-based transformation using existing EnergyAnalyzer (IN-PLACE)
DataType transform_channel_operation(DataType &input, uint32_t num_channels, bool interleave)
Channel operations using C++20 ranges (IN-PLACE)
std::vector< Kakshya::Region > detect_regions_by_energy(const DataType &input, double energy_threshold, uint32_t min_region_size, uint32_t window_size, uint32_t hop_size)
Detect regions based on energy threshold using existing EnergyAnalyzer.
DataType transform_crossfade_regions(DataType &input, const std::vector< std::pair< Kakshya::Region, Kakshya::Region > > &fade_regions, uint32_t fade_duration)
Cross-fade between regions with smooth transitions (IN-PLACE)
DataType transform_outliers(DataType &input, double std_dev_threshold, TransformFunc transform_func)
Statistical outlier transformation using existing StatisticalAnalyzer (IN-PLACE)
std::vector< uint64_t > end_coordinates
Ending frame index (inclusive)
Definition Region.hpp:72
std::vector< uint64_t > start_coordinates
Starting frame index (inclusive)
Definition Region.hpp:69
Represents a point or span in N-dimensional space.
Definition Region.hpp:67