MayaFlux 0.1.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TemporalHelper.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4
6
7/**
8 * @file TemporalSpectralTransforms.hpp
9 * @brief Temporal and spectral transformation functions leveraging existing ecosystem
10 *
11 * Provides temporal transformation functions that can be used by any class.
12 * Leverages existing infrastructure:
13 * - Uses C++20 ranges/views/algorithms for efficient processing
14 * - Integrates with existing windowing and spectral analysis functions
15 *
16 * Philosophy: **Function-based helpers** that compose existing capabilities.
17 */
18
19namespace MayaFlux::Yantra {
20
21/**
22 * @brief Overlap-add processing for windowed transforms using C++20 ranges
23 * @tparam TransformFunc Function type for transformation
24 * @param data Input data
25 * @param window_size Size of analysis windows
26 * @param hop_size Hop size between windows
27 * @param transform_func Transformation function to apply per window
28 * @return Processed data
29 */
30template <typename TransformFunc>
31 requires std::invocable<TransformFunc, std::span<const double>>
32std::vector<double> process_overlap_add(const std::span<const double>& data,
33 uint32_t window_size,
34 uint32_t hop_size,
35 TransformFunc transform_func)
36{
37 const size_t num_windows = (data.size() - window_size) / hop_size + 1;
38 std::vector<double> output(data.size(), 0.0);
39
40 std::ranges::for_each(std::views::iota(size_t { 0 }, num_windows), [&](size_t win) {
41 size_t start_idx = win * hop_size;
42 auto window_data = data.subspan(start_idx,
43 std::min(static_cast<size_t>(window_size), data.size() - start_idx));
44
45 auto transformed = transform_func(window_data);
46
47 for (size_t i = 0; i < transformed.size() && start_idx + i < output.size(); ++i) {
48 output[start_idx + i] += transformed[i];
49 }
50 });
51
52 return output;
53}
54
55/**
56 * @brief Time reversal transformation using C++20 ranges (IN-PLACE)
57 * @tparam DataType OperationReadyData type
58 * @param input Input data - WILL BE MODIFIED
59 * @return Time-reversed data (same as input, modified in-place)
60 */
61template <OperationReadyData DataType>
62DataType transform_time_reverse(DataType& input)
63{
64 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
65
66 for (auto& span : target_data) {
67 std::ranges::reverse(span);
68 }
69
70 auto reconstructed_data = target_data
71 | std::views::transform([](const auto& span) {
72 return std::vector<double>(span.begin(), span.end());
73 })
74 | std::ranges::to<std::vector>();
75
76 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
77}
78
79/**
80 * @brief Time reversal transformation using C++20 ranges (OUT-OF-PLACE)
81 * @tparam DataType OperationReadyData type
82 * @param input Input data - will NOT be modified
83 * @param working_buffer Buffer for operations (will be resized if needed)
84 * @return Time-reversed data
85 */
86template <OperationReadyData DataType>
87DataType transform_time_reverse(DataType& input, std::vector<std::vector<double>>& working_buffer)
88{
89 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
90
91 for (auto& span : target_data) {
92 std::ranges::reverse(span);
93 }
94
95 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
96}
97
98/**
99 * @brief Simple time stretching via resampling using C++20 ranges (IN-PLACE)
100 * @tparam DataType OperationReadyData type
101 * @param input Input data - WILL BE MODIFIED
102 * @param stretch_factor Stretch ratio (>1.0 = slower, <1.0 = faster)
103 * @return Time-stretched data
104 */
105template <OperationReadyData DataType>
106DataType transform_time_stretch(DataType& input, double stretch_factor)
107{
108 if (stretch_factor == 1.0) {
109 return input;
110 }
111
112 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
113
114 std::vector<std::vector<double>> result(target_data.size());
115
116 for (size_t i = 0; i < target_data.size(); ++i) {
117 auto new_size = static_cast<size_t>(target_data[i].size() * stretch_factor);
118 result[i].resize(new_size);
119 interpolate(target_data[i], result[i], new_size);
120 }
121
122 return OperationHelper::reconstruct_from_double<DataType>(result, structure_info);
123}
124
125/**
126 * @brief Simple time stretching via resampling using C++20 ranges (OUT-OF-PLACE)
127 * @tparam DataType OperationReadyData type
128 * @param input Input data - will NOT be modified
129 * @param stretch_factor Stretch ratio (>1.0 = slower, <1.0 = faster)
130 * @param working_buffer Buffer for operations (will be resized if needed)
131 * @return Time-stretched data
132 */
133template <OperationReadyData DataType>
134DataType transform_time_stretch(DataType& input, double stretch_factor, std::vector<std::vector<double>>& working_buffer)
135{
136 if (stretch_factor == 1.0) {
137 return input;
138 }
139
140 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
141
142 for (size_t i = 0; i < target_data.size(); ++i) {
143 auto new_size = static_cast<size_t>(target_data[i].size() * stretch_factor);
144 working_buffer[i].resize(new_size);
145 interpolate(target_data[i], working_buffer[i], new_size);
146 }
147
148 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
149}
150
151/**
152 * @brief Delay transformation that extends buffer size (IN-PLACE)
153 * @tparam DataType OperationReadyData type
154 * @param input Input data - WILL BE MODIFIED/EXTENDED
155 * @param delay_samples Number of samples to delay (extends output size)
156 * @param fill_value Value to use for padding
157 * @return Delayed data with extended size
158 */
159template <OperationReadyData DataType>
160DataType transform_delay(DataType& input, uint32_t delay_samples, double fill_value = 0.0)
161{
162 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
163
164 std::vector<std::vector<double>> result(target_data.size());
165 for (size_t i = 0; i < target_data.size(); ++i) {
166 result[i].resize(target_data[i].size() + delay_samples);
167 std::fill_n(result[i].begin(), delay_samples, fill_value);
168 std::copy(target_data[i].begin(), target_data[i].end(), result[i].begin() + delay_samples);
169 }
170
171 return OperationHelper::reconstruct_from_double<DataType>(result, structure_info);
172}
173
174/**
175 * @brief Delay transformation that extends buffer size (OUT-OF-PLACE) - CORRECTED
176 * @tparam DataType OperationReadyData type
177 * @param input Input data - will NOT be modified
178 * @param delay_samples Number of samples to delay (extends output size)
179 * @param fill_value Value to use for padding
180 * @param working_buffer Buffer for operations (will be resized if needed)
181 * @return Delayed data with extended size
182 */
183template <OperationReadyData DataType>
184DataType transform_delay(DataType& input, uint32_t delay_samples, double fill_value, std::vector<std::vector<double>>& working_buffer)
185{
186 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
187
188 for (size_t i = 0; i < target_data.size(); ++i) {
189 std::vector<double> original_data(target_data[i].begin(), target_data[i].end());
190
191 working_buffer[i].resize(original_data.size() + delay_samples);
192 std::fill_n(working_buffer[i].begin(), delay_samples, fill_value);
193 std::ranges::copy(original_data, working_buffer[i].begin() + delay_samples);
194 }
195
196 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
197}
198
199/**
200 * @brief Fade transformation (linear fade-in and fade-out) using C++20 ranges (IN-PLACE)
201 * @tparam DataType OperationReadyData type
202 * @param input Input data - WILL BE MODIFIED
203 * @param fade_in_duration_ratio Fade-in duration as a ratio of total length (0.0 to 1.0)
204 * @param fade_out_duration_ratio Fade-out duration as a ratio of total length (0.0 to 1.0)
205 * @return Faded data
206 */
207template <OperationReadyData DataType>
208DataType transform_fade(DataType& input, double fade_in_duration_ratio = 0.0, double fade_out_duration_ratio = 0.0)
209{
210 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
211
212 for (auto& span : target_data) {
213 auto fade_in_samples = static_cast<size_t>(span.size() * fade_in_duration_ratio);
214 auto fade_out_samples = static_cast<size_t>(span.size() * fade_out_duration_ratio);
215 size_t fade_out_start = span.size() - fade_out_samples;
216
217 for (size_t j = 0; j < fade_in_samples && fade_in_samples > 1; ++j) {
218 double fade_factor = static_cast<double>(j) / static_cast<double>(fade_in_samples - 1);
219 span[j] *= fade_factor;
220 }
221
222 for (size_t j = 0; j < fade_out_samples && fade_out_samples > 1 && fade_out_start + j < span.size(); ++j) {
223 double fade_factor = 1.0 - (static_cast<double>(j) / static_cast<double>(fade_out_samples - 1));
224 span[fade_out_start + j] *= fade_factor;
225 }
226 }
227
228 auto reconstructed_data = target_data
229 | std::views::transform([](const auto& span) {
230 return std::vector<double>(span.begin(), span.end());
231 })
232 | std::ranges::to<std::vector>();
233
234 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
235}
236
237/**
238 * @brief Fade transformation (linear fade-in and fade-out) using C++20 ranges (OUT-OF-PLACE)
239 * @tparam DataType OperationReadyData type
240 * @param input Input data - will NOT be modified
241 * @param fade_in_duration_ratio Fade-in duration as a ratio of total length (0.0 to 1.0)
242 * @param fade_out_duration_ratio Fade-out duration as a ratio of total length (0.0 to 1.0)
243 * @param working_buffer Buffer for operations (will be resized if needed)
244 * @return Faded data
245 */
246template <OperationReadyData DataType>
247DataType transform_fade(DataType& input, double fade_in_duration_ratio, double fade_out_duration_ratio, std::vector<std::vector<double>>& working_buffer)
248{
249 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
250
251 for (size_t i = 0; i < target_data.size(); ++i) {
252 auto& span = target_data[i];
253 working_buffer[i].assign(span.begin(), span.end());
254
255 auto fade_in_samples = static_cast<size_t>(span.size() * fade_in_duration_ratio);
256 auto fade_out_samples = static_cast<size_t>(span.size() * fade_out_duration_ratio);
257 size_t fade_out_start = span.size() - fade_out_samples;
258
259 for (size_t j = 0; j < fade_in_samples && fade_in_samples > 1; ++j) {
260 double fade_factor = static_cast<double>(j) / static_cast<double>(fade_in_samples - 1);
261 working_buffer[i][j] *= fade_factor;
262 }
263
264 for (size_t j = 0; j < fade_out_samples && fade_out_samples > 1 && fade_out_start + j < span.size(); ++j) {
265 double fade_factor = 1.0 - (static_cast<double>(j) / static_cast<double>(fade_out_samples - 1));
266 working_buffer[i][fade_out_start + j] *= fade_factor;
267 }
268 }
269
270 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
271}
272
273/**
274 * @brief Slice transformation to extract a portion of the data based on ratios (IN-PLACE)
275 * @tparam DataType OperationReadyData type
276 * @param input Input data - WILL BE MODIFIED
277 * @param start_ratio Start ratio (0.0 to 1.0)
278 * @param end_ratio End ratio (0.0 to 1.0)
279 * @return Sliced data
280 */
281template <OperationReadyData DataType>
282DataType transform_slice(DataType& input, double start_ratio, double end_ratio)
283{
284 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
285
286 std::vector<std::vector<double>> result(target_data.size());
287 for (size_t i = 0; i < target_data.size(); ++i) {
288 size_t start_idx = static_cast<size_t>(target_data[i].size() * std::clamp(start_ratio, 0.0, 1.0));
289 size_t end_idx = static_cast<size_t>(target_data[i].size() * std::clamp(end_ratio, 0.0, 1.0));
290
291 if (start_idx >= end_idx || end_idx > target_data[i].size()) {
292 result[i] = { 0.0 };
293 } else {
294 result[i].assign(target_data[i].begin() + start_idx, target_data[i].begin() + end_idx);
295 }
296 }
297
298 return OperationHelper::reconstruct_from_double<DataType>(result, structure_info);
299}
300
301/**
302 * @brief Slice transformation to extract a portion of the data based on ratios (OUT-OF-PLACE)
303 * @tparam DataType OperationReadyData type
304 * @param input Input data - will NOT be modified
305 * @param start_ratio Start ratio (0.0 to 1.0)
306 * @param end_ratio End ratio (0.0 to 1.0)
307 * @param working_buffer Buffer for operations (will be resized if needed)
308 * @return Sliced data
309 */
310template <OperationReadyData DataType>
311DataType transform_slice(DataType& input, double start_ratio, double end_ratio, std::vector<std::vector<double>>& working_buffer)
312{
313 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
314
315 for (size_t i = 0; i < target_data.size(); ++i) {
316 size_t start_idx = static_cast<size_t>(target_data[i].size() * std::clamp(start_ratio, 0.0, 1.0));
317 size_t end_idx = static_cast<size_t>(target_data[i].size() * std::clamp(end_ratio, 0.0, 1.0));
318
319 if (start_idx >= end_idx || end_idx > target_data[i].size()) {
320 working_buffer[i] = { 0.0 };
321 } else {
322 working_buffer[i].assign(target_data[i].begin() + start_idx, target_data[i].begin() + end_idx);
323 }
324 }
325
326 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
327}
328
329} // namespace MayaFlux::Yantra
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.
void interpolate(std::span< double > input, std::vector< double > &output, uint32_t target_size)
DataType transform_time_reverse(DataType &input)
Time reversal transformation using C++20 ranges (IN-PLACE)
DataType transform_delay(DataType &input, uint32_t delay_samples, double fill_value=0.0)
Delay transformation that extends buffer size (IN-PLACE)
DataType transform_time_stretch(DataType &input, double stretch_factor)
Simple time stretching via resampling using C++20 ranges (IN-PLACE)
DataType transform_slice(DataType &input, double start_ratio, double end_ratio)
Slice transformation to extract a portion of the data based on ratios (IN-PLACE)
DataType transform_fade(DataType &input, double fade_in_duration_ratio=0.0, double fade_out_duration_ratio=0.0)
Fade transformation (linear fade-in and fade-out) using C++20 ranges (IN-PLACE)
std::vector< double > process_overlap_add(const std::span< const double > &data, uint32_t window_size, uint32_t hop_size, TransformFunc transform_func)
Overlap-add processing for windowed transforms using C++20 ranges.