33template <OperationReadyData DataType,
typename TransformFunc>
34 requires std::invocable<TransformFunc, DataType>
36 const std::shared_ptr<Kakshya::SignalSourceContainer>& container,
37 const std::vector<Kakshya::Region>& regions,
38 TransformFunc transform_func)
41 throw std::invalid_argument(
"Container is required for region-based transformations");
46 for (
const auto& region : regions) {
47 auto region_data = container->get_region_data(region);
48 auto transformed_region = transform_func(region_data);
51 auto start_sample =
static_cast<uint64_t
>(region.start_coordinates[0]);
52 auto end_sample =
static_cast<uint64_t
>(region.end_coordinates[0]);
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];
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());
62 std::ranges::copy(transformed_channel | std::views::take(copy_size),
68 auto reconstructed_data = target_data
69 | std::views::transform([](
const auto& span) {
70 return std::vector<double>(span.begin(), span.end());
72 | std::ranges::to<std::vector>();
74 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
88template <OperationReadyData DataType,
typename TransformFunc>
89 requires std::invocable<TransformFunc, DataType>
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)
97 throw std::invalid_argument(
"Container is required for region-based transformations");
102 for (
const auto& region : regions) {
103 auto region_data = container->get_region_data(region);
104 auto transformed_region = transform_func(region_data);
107 auto start_sample =
static_cast<uint64_t
>(region.start_coordinates[0]);
108 auto end_sample =
static_cast<uint64_t
>(region.end_coordinates[0]);
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];
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());
118 std::ranges::copy(transformed_channel | std::views::take(copy_size),
119 target_span.begin());
124 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
138template <OperationReadyData DataType,
typename TransformFunc>
139 requires std::invocable<TransformFunc, double>
141 double energy_threshold,
142 TransformFunc transform_func,
143 uint32_t window_size = 1024,
144 uint32_t hop_size = 512)
148 auto energy_analyzer = std::make_shared<StandardEnergyAnalyzer>(window_size, hop_size);
150 auto energy_result = energy_analyzer->analyze_energy(input);
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];
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];
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);
168 auto reconstructed_data = target_data
169 | std::views::transform([](
const auto& span) {
170 return std::vector<double>(span.begin(), span.end());
172 | std::ranges::to<std::vector>();
174 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
189template <OperationReadyData DataType,
typename TransformFunc>
190 requires std::invocable<TransformFunc, double>
192 double energy_threshold,
193 TransformFunc transform_func,
194 uint32_t window_size,
196 std::vector<std::vector<double>>& working_buffer)
200 auto energy_analyzer = std::make_shared<StandardEnergyAnalyzer>(window_size, hop_size);
202 auto energy_result = energy_analyzer->analyze_energy(input);
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];
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];
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);
220 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
232template <OperationReadyData DataType,
typename TransformFunc>
233 requires std::invocable<TransformFunc, double>
235 double std_dev_threshold,
236 TransformFunc transform_func)
240 auto stat_analyzer = std::make_shared<StandardStatisticalAnalyzer>();
241 auto stats = stat_analyzer->analyze_statistics(input);
243 if (stats.channel_statistics.empty()) {
244 throw std::runtime_error(
"No channel statistics available for outlier detection");
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;
250 for (
auto& channel_span : target_data) {
251 std::ranges::transform(channel_span, channel_span.begin(),
253 return (x < threshold_low || x > threshold_high) ? transform_func(x) : x;
257 auto reconstructed_data = target_data
258 | std::views::transform([](
const auto& span) {
259 return std::vector<double>(span.begin(), span.end());
261 | std::ranges::to<std::vector>();
263 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
276template <OperationReadyData DataType,
typename TransformFunc>
277 requires std::invocable<TransformFunc, double>
279 double std_dev_threshold,
280 TransformFunc transform_func,
281 std::vector<std::vector<double>>& working_buffer)
285 auto stat_analyzer = std::make_shared<StandardStatisticalAnalyzer>();
286 auto stats = stat_analyzer->analyze_statistics(input);
288 if (stats.channel_statistics.empty()) {
289 throw std::runtime_error(
"No channel statistics available for outlier detection");
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;
295 for (
auto& channel_span : target_data) {
296 std::ranges::transform(channel_span, channel_span.begin(),
298 return (x < threshold_low || x > threshold_high) ? transform_func(x) : x;
302 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
313template <OperationReadyData DataType>
315 const std::vector<std::pair<Kakshya::Region, Kakshya::Region>>& fade_regions,
316 uint32_t fade_duration)
320 auto& target_data = std::get<0>(extraction_result);
321 auto& structure_info = std::get<1>(extraction_result);
323 std::ranges::for_each(fade_regions, [&target_data, fade_duration](
const auto& fade_pair) {
324 const auto& [region_a, region_b] = fade_pair;
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]);
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()));
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);
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);
347 auto reconstructed_data = target_data
348 | std::views::transform([](
const auto& span) {
349 return std::vector<double>(span.begin(), span.end());
351 | std::ranges::to<std::vector>();
353 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
365template <OperationReadyData DataType>
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)
373 auto& target_data = std::get<0>(setup_result);
374 auto& structure_info = std::get<1>(setup_result);
376 std::ranges::for_each(fade_regions, [&target_data, fade_duration](
const auto& fade_pair) {
377 const auto& [region_a, region_b] = fade_pair;
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]);
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()));
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);
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);
400 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
410template <OperationReadyData DataType>
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;
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());
425 auto reconstructed_data = target_data
426 | std::views::transform([](
const auto& span) {
427 return std::vector<double>(span.begin(), span.end());
429 | std::ranges::to<std::vector>();
431 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
442template <OperationReadyData DataType>
443DataType
transform_matrix(DataType& input,
const Eigen::MatrixXd& transformation_matrix, std::vector<std::vector<double>>& working_buffer)
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;
453 working_buffer[channel_idx].resize(result.size());
454 std::ranges::copy(result, working_buffer[channel_idx].begin());
458 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
469template <OperationReadyData DataType>
471 const Eigen::MatrixXd& transformation_matrix,
472 uint32_t num_channels)
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");
480 if (target_data.size() != num_channels) {
481 throw std::invalid_argument(
"Data channel count must match specified number of channels");
484 size_t min_frames = std::ranges::min(target_data | std::views::transform([](
const auto& span) {
return span.size(); }));
486 auto frame_indices = std::views::iota(
size_t { 0 }, min_frames);
488 std::ranges::for_each(frame_indices, [&](
size_t frame) {
489 Eigen::VectorXd frame_vector(num_channels);
495 Eigen::VectorXd transformed = transformation_matrix * frame_vector;
502 auto reconstructed_data = target_data
503 | std::views::transform([](
const auto& span) {
504 return std::vector<double>(span.begin(), span.end());
506 | std::ranges::to<std::vector>();
508 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
520template <OperationReadyData DataType>
522 const Eigen::MatrixXd& transformation_matrix,
523 uint32_t num_channels,
524 std::vector<std::vector<double>>& working_buffer)
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");
532 if (target_data.size() != num_channels) {
533 throw std::invalid_argument(
"Data channel count must match specified number of channels");
536 size_t min_frames = std::ranges::min(target_data | std::views::transform([](
const auto& span) {
return span.size(); }));
538 auto frame_indices = std::views::iota(
size_t { 0 }, min_frames);
540 std::ranges::for_each(frame_indices, [&](
size_t frame) {
541 Eigen::VectorXd frame_vector(num_channels);
547 Eigen::VectorXd transformed = transformation_matrix * frame_vector;
554 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
565template <OperationReadyData DataType>
567 uint32_t num_channels,
572 if (target_data.size() != num_channels) {
573 throw std::invalid_argument(
"Data channel count must match specified number of channels");
576 auto reconstructed_data = target_data
577 | std::views::transform([](
const auto& span) {
578 return std::vector<double>(span.begin(), span.end());
580 | std::ranges::to<std::vector>();
582 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
594template <OperationReadyData DataType>
596 uint32_t num_channels,
598 std::vector<std::vector<double>>& working_buffer)
602 if (target_data.size() != num_channels) {
603 throw std::invalid_argument(
"Data channel count must match specified number of channels");
606 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
619template <OperationReadyData DataType>
621 double energy_threshold,
622 uint32_t min_region_size,
623 uint32_t window_size,
626 auto energy_analyzer = std::make_shared<StandardEnergyAnalyzer>(window_size, hop_size);
628 auto energy_result = energy_analyzer->analyze_energy(input);
630 std::vector<Kakshya::Region> regions;
632 if (energy_result.channels.empty()) {
636 const auto& first_channel = energy_result.channels[0];
637 std::optional<uint64_t> region_start;
639 for (
size_t i = 0; i < first_channel.energy_values.size(); ++i) {
640 bool above_threshold = first_channel.energy_values[i] > energy_threshold;
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;
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;
650 if (region_end - region_start.value() >= min_region_size) {
654 regions.push_back(region);
656 region_start.reset();
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) {
668 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 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 > ®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)
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)
std::vector< uint64_t > start_coordinates
Starting frame index (inclusive)
Represents a point or span in N-dimensional space.