31template <OperationReadyData DataType,
typename TransformFunc>
32 requires std::invocable<TransformFunc, DataType>
34 const std::shared_ptr<Kakshya::SignalSourceContainer>& container,
35 const std::vector<Kakshya::Region>& regions,
36 TransformFunc transform_func)
39 throw std::invalid_argument(
"Container is required for region-based transformations");
44 for (
const auto& region : regions) {
45 auto region_data = container->get_region_data(region);
46 auto transformed_region = transform_func(region_data);
49 auto start_sample =
static_cast<uint64_t
>(region.start_coordinates[0]);
50 auto end_sample =
static_cast<uint64_t
>(region.end_coordinates[0]);
52 for (
size_t channel_idx = 0; channel_idx < target_data.size() && channel_idx < transformed_doubles.size(); ++channel_idx) {
53 auto& target_channel = target_data[channel_idx];
54 const auto& transformed_channel = transformed_doubles[channel_idx];
56 if (start_sample < target_channel.size() && end_sample <= target_channel.size()) {
57 auto target_span = target_channel.subspan(start_sample, end_sample - start_sample);
58 auto copy_size = std::min(transformed_channel.size(), target_span.size());
60 std::ranges::copy(transformed_channel | std::views::take(copy_size),
66 auto reconstructed_data = target_data
67 | std::views::transform([](
const auto& span) {
68 return std::vector<double>(span.begin(), span.end());
70 | std::ranges::to<std::vector>();
72 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
86template <OperationReadyData DataType,
typename TransformFunc>
87 requires std::invocable<TransformFunc, DataType>
89 const std::shared_ptr<Kakshya::SignalSourceContainer>& container,
90 const std::vector<Kakshya::Region>& regions,
91 TransformFunc transform_func,
92 std::vector<std::vector<double>>& working_buffer)
95 throw std::invalid_argument(
"Container is required for region-based transformations");
100 for (
const auto& region : regions) {
101 auto region_data = container->get_region_data(region);
102 auto transformed_region = transform_func(region_data);
105 auto start_sample =
static_cast<uint64_t
>(region.start_coordinates[0]);
106 auto end_sample =
static_cast<uint64_t
>(region.end_coordinates[0]);
108 for (
size_t channel_idx = 0; channel_idx < target_data.size() && channel_idx < transformed_doubles.size(); ++channel_idx) {
109 auto& target_channel = target_data[channel_idx];
110 const auto& transformed_channel = transformed_doubles[channel_idx];
112 if (start_sample < target_channel.size() && end_sample <= target_channel.size()) {
113 auto target_span = target_channel.subspan(start_sample, end_sample - start_sample);
114 auto copy_size = std::min(transformed_channel.size(), target_span.size());
116 std::ranges::copy(transformed_channel | std::views::take(copy_size),
117 target_span.begin());
122 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
136template <OperationReadyData DataType,
typename TransformFunc>
137 requires std::invocable<TransformFunc, double>
139 double energy_threshold,
140 TransformFunc transform_func,
141 uint32_t window_size = 1024,
142 uint32_t hop_size = 512)
146 auto energy_analyzer = std::make_shared<StandardEnergyAnalyzer>(window_size, hop_size);
148 auto energy_result = energy_analyzer->analyze_energy(input);
150 for (
size_t ch = 0; ch < energy_result.channels.size() && ch < target_data.size(); ++ch) {
151 const auto& channel_energy = energy_result.channels[ch];
152 auto& target_channel = target_data[ch];
154 for (
size_t i = 0; i < channel_energy.energy_values.size(); ++i) {
155 if (channel_energy.energy_values[i] > energy_threshold && i < channel_energy.window_positions.size()) {
156 auto [start_idx, end_idx] = channel_energy.window_positions[i];
158 if (start_idx < target_channel.size() && end_idx <= target_channel.size()) {
159 auto window_span = target_channel.subspan(start_idx, end_idx - start_idx);
160 std::ranges::transform(window_span, window_span.begin(), transform_func);
166 auto reconstructed_data = target_data
167 | std::views::transform([](
const auto& span) {
168 return std::vector<double>(span.begin(), span.end());
170 | std::ranges::to<std::vector>();
172 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
187template <OperationReadyData DataType,
typename TransformFunc>
188 requires std::invocable<TransformFunc, double>
190 double energy_threshold,
191 TransformFunc transform_func,
192 uint32_t window_size,
194 std::vector<std::vector<double>>& working_buffer)
198 auto energy_analyzer = std::make_shared<StandardEnergyAnalyzer>(window_size, hop_size);
200 auto energy_result = energy_analyzer->analyze_energy(input);
202 for (
size_t ch = 0; ch < energy_result.channels.size() && ch < target_data.size(); ++ch) {
203 const auto& channel_energy = energy_result.channels[ch];
204 auto& target_channel = target_data[ch];
206 for (
size_t i = 0; i < channel_energy.energy_values.size(); ++i) {
207 if (channel_energy.energy_values[i] > energy_threshold && i < channel_energy.window_positions.size()) {
208 auto [start_idx, end_idx] = channel_energy.window_positions[i];
210 if (start_idx < target_channel.size() && end_idx <= target_channel.size()) {
211 auto window_span = target_channel.subspan(start_idx, end_idx - start_idx);
212 std::ranges::transform(window_span, window_span.begin(), transform_func);
218 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
230template <OperationReadyData DataType,
typename TransformFunc>
231 requires std::invocable<TransformFunc, double>
233 double std_dev_threshold,
234 TransformFunc transform_func)
238 auto stat_analyzer = std::make_shared<StandardStatisticalAnalyzer>();
239 auto stats = stat_analyzer->analyze_statistics(input);
241 if (stats.channel_statistics.empty()) {
242 throw std::runtime_error(
"No channel statistics available for outlier detection");
244 const auto& first_channel_stats = stats.channel_statistics[0];
245 double threshold_low = first_channel_stats.mean_stat - std_dev_threshold * first_channel_stats.stat_std_dev;
246 double threshold_high = first_channel_stats.mean_stat + std_dev_threshold * first_channel_stats.stat_std_dev;
248 for (
auto& channel_span : target_data) {
249 std::ranges::transform(channel_span, channel_span.begin(),
251 return (x < threshold_low || x > threshold_high) ? transform_func(x) : x;
255 auto reconstructed_data = target_data
256 | std::views::transform([](
const auto& span) {
257 return std::vector<double>(span.begin(), span.end());
259 | std::ranges::to<std::vector>();
261 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
274template <OperationReadyData DataType,
typename TransformFunc>
275 requires std::invocable<TransformFunc, double>
277 double std_dev_threshold,
278 TransformFunc transform_func,
279 std::vector<std::vector<double>>& working_buffer)
283 auto stat_analyzer = std::make_shared<StandardStatisticalAnalyzer>();
284 auto stats = stat_analyzer->analyze_statistics(input);
286 if (stats.channel_statistics.empty()) {
287 throw std::runtime_error(
"No channel statistics available for outlier detection");
289 const auto& first_channel_stats = stats.channel_statistics[0];
290 double threshold_low = first_channel_stats.mean_stat - std_dev_threshold * first_channel_stats.stat_std_dev;
291 double threshold_high = first_channel_stats.mean_stat + std_dev_threshold * first_channel_stats.stat_std_dev;
293 for (
auto& channel_span : target_data) {
294 std::ranges::transform(channel_span, channel_span.begin(),
296 return (x < threshold_low || x > threshold_high) ? transform_func(x) : x;
300 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
311template <OperationReadyData DataType>
313 const std::vector<std::pair<Kakshya::Region, Kakshya::Region>>& fade_regions,
314 uint32_t fade_duration)
318 auto& target_data = std::get<0>(extraction_result);
319 auto& structure_info = std::get<1>(extraction_result);
321 std::ranges::for_each(fade_regions, [&target_data, fade_duration](
const auto& fade_pair) {
322 const auto& [region_a, region_b] = fade_pair;
324 auto start_a =
static_cast<uint64_t
>(region_a.start_coordinates[0]);
325 auto end_a =
static_cast<uint64_t
>(region_a.end_coordinates[0]);
326 auto start_b =
static_cast<uint64_t
>(region_b.start_coordinates[0]);
328 uint64_t fade_start = (end_a > fade_duration) ? end_a - fade_duration : 0;
329 uint64_t fade_end = std::min(start_b + fade_duration,
static_cast<uint64_t
>(target_data[0].size()));
331 for (
auto& channel_span : target_data) {
332 if (fade_start < fade_end && fade_start < channel_span.size()) {
333 auto fade_span = channel_span.subspan(fade_start, fade_end - fade_start);
335 auto fade_indices = std::views::iota(
size_t { 0 }, fade_span.size());
336 std::ranges::for_each(fade_indices, [&fade_span](
size_t i) {
337 double ratio =
static_cast<double>(i) / (fade_span.size() - 1);
338 double smooth_ratio = 0.5 * (1.0 - std::cos(ratio * M_PI));
339 fade_span[i] *= (1.0 - smooth_ratio);
345 auto reconstructed_data = target_data
346 | std::views::transform([](
const auto& span) {
347 return std::vector<double>(span.begin(), span.end());
349 | std::ranges::to<std::vector>();
351 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
363template <OperationReadyData DataType>
365 const std::vector<std::pair<Kakshya::Region, Kakshya::Region>>& fade_regions,
366 uint32_t fade_duration,
367 std::vector<std::vector<double>>& working_buffer)
371 auto& target_data = std::get<0>(setup_result);
372 auto& structure_info = std::get<1>(setup_result);
374 std::ranges::for_each(fade_regions, [&target_data, fade_duration](
const auto& fade_pair) {
375 const auto& [region_a, region_b] = fade_pair;
377 auto start_a =
static_cast<uint64_t
>(region_a.start_coordinates[0]);
378 auto end_a =
static_cast<uint64_t
>(region_a.end_coordinates[0]);
379 auto start_b =
static_cast<uint64_t
>(region_b.start_coordinates[0]);
381 uint64_t fade_start = (end_a > fade_duration) ? end_a - fade_duration : 0;
382 uint64_t fade_end = std::min(start_b + fade_duration,
static_cast<uint64_t
>(target_data[0].size()));
384 for (
auto& channel_span : target_data) {
385 if (fade_start < fade_end && fade_start < channel_span.size()) {
386 auto fade_span = channel_span.subspan(fade_start, fade_end - fade_start);
388 auto fade_indices = std::views::iota(
size_t { 0 }, fade_span.size());
389 std::ranges::for_each(fade_indices, [&fade_span](
size_t i) {
390 double ratio =
static_cast<double>(i) / (fade_span.size() - 1);
391 double smooth_ratio = 0.5 * (1.0 - std::cos(ratio * M_PI));
392 fade_span[i] *= (1.0 - smooth_ratio);
398 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
408template <OperationReadyData DataType>
413 for (
auto& channel_span : target_data) {
414 if (transformation_matrix.cols() ==
static_cast<long>(channel_span.size())) {
415 Eigen::Map<Eigen::VectorXd> data_vector(channel_span.data(), channel_span.size());
416 Eigen::VectorXd result = transformation_matrix * data_vector;
418 auto copy_size = std::min<long>(result.size(),
static_cast<long>(channel_span.size()));
419 std::ranges::copy(result.head(copy_size), channel_span.begin());
423 auto reconstructed_data = target_data
424 | std::views::transform([](
const auto& span) {
425 return std::vector<double>(span.begin(), span.end());
427 | std::ranges::to<std::vector>();
429 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
440template <OperationReadyData DataType>
441DataType
transform_matrix(DataType& input,
const Eigen::MatrixXd& transformation_matrix, std::vector<std::vector<double>>& working_buffer)
445 for (
size_t channel_idx = 0; channel_idx < target_data.size(); ++channel_idx) {
446 auto& channel_span = target_data[channel_idx];
447 if (transformation_matrix.cols() ==
static_cast<long>(channel_span.size())) {
448 Eigen::Map<Eigen::VectorXd> data_vector(channel_span.data(), channel_span.size());
449 Eigen::VectorXd result = transformation_matrix * data_vector;
451 working_buffer[channel_idx].resize(result.size());
452 std::ranges::copy(result, working_buffer[channel_idx].begin());
456 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
467template <OperationReadyData DataType>
469 const Eigen::MatrixXd& transformation_matrix,
470 uint32_t num_channels)
474 if (transformation_matrix.rows() != num_channels || transformation_matrix.cols() != num_channels) {
475 throw std::invalid_argument(
"Transformation matrix dimensions must match number of channels");
478 if (target_data.size() != num_channels) {
479 throw std::invalid_argument(
"Data channel count must match specified number of channels");
482 size_t min_frames = std::ranges::min(target_data | std::views::transform([](
const auto& span) {
return span.size(); }));
484 auto frame_indices = std::views::iota(
size_t { 0 }, min_frames);
486 std::ranges::for_each(frame_indices, [&](
size_t frame) {
487 Eigen::VectorXd frame_vector(num_channels);
489 for (
size_t channel = 0; channel < num_channels; ++channel) {
490 frame_vector[channel] = target_data[channel][frame];
493 Eigen::VectorXd transformed = transformation_matrix * frame_vector;
495 for (
size_t channel = 0; channel < num_channels; ++channel) {
496 target_data[channel][frame] = transformed[channel];
500 auto reconstructed_data = target_data
501 | std::views::transform([](
const auto& span) {
502 return std::vector<double>(span.begin(), span.end());
504 | std::ranges::to<std::vector>();
506 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
518template <OperationReadyData DataType>
520 const Eigen::MatrixXd& transformation_matrix,
521 uint32_t num_channels,
522 std::vector<std::vector<double>>& working_buffer)
526 if (transformation_matrix.rows() != num_channels || transformation_matrix.cols() != num_channels) {
527 throw std::invalid_argument(
"Transformation matrix dimensions must match number of channels");
530 if (target_data.size() != num_channels) {
531 throw std::invalid_argument(
"Data channel count must match specified number of channels");
534 size_t min_frames = std::ranges::min(target_data | std::views::transform([](
const auto& span) {
return span.size(); }));
536 auto frame_indices = std::views::iota(
size_t { 0 }, min_frames);
538 std::ranges::for_each(frame_indices, [&](
size_t frame) {
539 Eigen::VectorXd frame_vector(num_channels);
541 for (
size_t channel = 0; channel < num_channels; ++channel) {
542 frame_vector[channel] = target_data[channel][frame];
545 Eigen::VectorXd transformed = transformation_matrix * frame_vector;
547 for (
size_t channel = 0; channel < num_channels; ++channel) {
548 working_buffer[channel][frame] = transformed[channel];
552 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
563template <OperationReadyData DataType>
565 uint32_t num_channels,
570 if (target_data.size() != num_channels) {
571 throw std::invalid_argument(
"Data channel count must match specified number of channels");
574 auto reconstructed_data = target_data
575 | std::views::transform([](
const auto& span) {
576 return std::vector<double>(span.begin(), span.end());
578 | std::ranges::to<std::vector>();
580 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
592template <OperationReadyData DataType>
594 uint32_t num_channels,
596 std::vector<std::vector<double>>& working_buffer)
600 if (target_data.size() != num_channels) {
601 throw std::invalid_argument(
"Data channel count must match specified number of channels");
604 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
617template <OperationReadyData DataType>
619 double energy_threshold,
620 uint32_t min_region_size,
621 uint32_t window_size,
624 auto energy_analyzer = std::make_shared<StandardEnergyAnalyzer>(window_size, hop_size);
626 auto energy_result = energy_analyzer->analyze_energy(input);
628 std::vector<Kakshya::Region> regions;
630 if (energy_result.channels.empty()) {
634 const auto& first_channel = energy_result.channels[0];
635 std::optional<uint64_t> region_start;
637 for (
size_t i = 0; i < first_channel.energy_values.size(); ++i) {
638 bool above_threshold = first_channel.energy_values[i] > energy_threshold;
640 if (above_threshold && !region_start.has_value()) {
641 if (i < first_channel.window_positions.size()) {
642 region_start = first_channel.window_positions[i].first;
644 }
else if (!above_threshold && region_start.has_value()) {
645 if (i < first_channel.window_positions.size()) {
646 uint64_t region_end = first_channel.window_positions[i].second;
648 if (region_end - region_start.value() >= min_region_size) {
652 regions.push_back(region);
654 region_start.reset();
659 if (region_start.has_value() && !first_channel.window_positions.empty()) {
660 uint64_t region_end = first_channel.window_positions.back().second;
661 if (region_end - region_start.value() >= min_region_size) {
666 regions.push_back(region);
Span-based energy analysis for digital signals in Maya Flux.
static std::tuple< std::vector< std::span< double > >, DataStructureInfo > extract_structured_double(T &compute_data)
Extract structured double data from IO container or direct ComputeData with automatic container handl...
static auto setup_operation_buffer(T &input, std::vector< std::vector< double > > &working_buffer)
Setup operation buffer from IO 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 > ®ions, 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)
Eigen::MatrixXd create_rotation_matrix(double angle, uint32_t axis, uint32_t dimensions)
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)
Eigen::MatrixXd create_scaling_matrix(const std::vector< double > &scale_factors)
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)
std::vector< uint64_t > start_coordinates
Starting frame index (inclusive)
Represents a point or span in N-dimensional space.