315{
316
317 auto extraction_result = OperationHelper::extract_structured_double(input);
318 auto& target_data = std::get<0>(extraction_result);
319 auto& structure_info = std::get<1>(extraction_result);
320
321 std::ranges::for_each(fade_regions, [&target_data, fade_duration](const auto& fade_pair) {
322 const auto& [region_a, region_b] = fade_pair;
323
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]);
327
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()));
330
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);
334
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);
340 });
341 }
342 }
343 });
344
345 auto reconstructed_data = target_data
346 | std::views::transform([](const auto& span) {
347 return std::vector<double>(span.begin(), span.end());
348 })
349 | std::ranges::to<std::vector>();
350
351 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
352}