7#include <unsupported/Eigen/FFT>
26 auto smallest = *std::ranges::min_element(data,
27 [](
const auto& a,
const auto& b) {
28 return a.size() < b.size();
31 return static_cast<uint64_t
>(smallest.size());
43template <
typename ProcessorFunc>
45 std::span<double> data,
48 ProcessorFunc&& processor)
50 const size_t num_windows = (data.size() >= window_size) ? (data.size() - window_size) / hop_size + 1 : 0;
52 if (num_windows == 0) {
53 return { data.begin(), data.end() };
56 std::vector<double> output(data.size(), 0.0);
60 Eigen::FFT<double> fft;
62 auto window_indices = std::views::iota(
size_t { 0 }, num_windows);
64 std::ranges::for_each(window_indices, [&](
size_t win) {
65 size_t start_idx = win * hop_size;
66 auto window_data = data.subspan(start_idx,
67 std::min(
static_cast<size_t>(window_size), data.size() - start_idx));
69 Eigen::VectorXd windowed = Eigen::VectorXd::Zero(
static_cast<Eigen::Index
>(window_size));
70 const size_t actual_size = std::min(window_data.size(), hann_window.size());
72 for (
size_t j = 0; j < actual_size; ++j) {
73 windowed(
static_cast<Eigen::Index
>(j)) = window_data[j] * hann_window[j];
76 Eigen::VectorXcd spectrum;
77 fft.fwd(spectrum, windowed);
79 std::forward<ProcessorFunc>(processor)(spectrum, win);
81 Eigen::VectorXd result;
82 fft.inv(result, spectrum);
84 for (
size_t i = 0; i < static_cast<size_t>(result.size()) && start_idx + i < output.size(); ++i) {
85 output[start_idx + i] += result[
static_cast<Eigen::Index
>(i)];
100template <OperationReadyData DataType>
103 uint32_t window_size = 0)
107 uint32_t size = window_size > 0 ? window_size :
smallest_size(target_data);
111 for (
auto& span : target_data) {
112 auto data_view = span | std::views::take(std::min(size,
static_cast<uint32_t
>(span.size())));
113 auto window_view = window | std::views::take(data_view.size());
115 std::ranges::transform(data_view, window_view, data_view.begin(),
116 [](
double sample,
double win) { return sample * win; });
119 auto reconstructed_data = target_data
120 | std::views::transform([](
const auto& span) {
121 return std::vector<double>(span.begin(), span.end());
123 | std::ranges::to<std::vector>();
125 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
137template <OperationReadyData DataType>
140 uint32_t window_size,
141 std::vector<std::vector<double>>& working_buffer)
145 uint32_t size = window_size > 0 ? window_size :
smallest_size(working_buffer);
149 working_buffer.resize(target_data.size());
150 for (
size_t i = 0; i < target_data.size(); ++i) {
151 auto& span = target_data[i];
152 auto& buffer = working_buffer[i];
153 buffer.resize(span.size());
155 auto data_view = span | std::views::take(std::min(size,
static_cast<uint32_t
>(span.size())));
156 auto window_view = window | std::views::take(data_view.size());
158 std::ranges::transform(data_view, window_view, buffer.begin(),
159 [](
double sample,
double win) { return sample * win; });
161 if (span.size() > size) {
162 std::copy(span.begin() + size, span.end(), buffer.begin() + size);
166 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
180template <OperationReadyData DataType>
184 double sample_rate = 48000.0,
185 uint32_t window_size = 1024,
186 uint32_t hop_size = 256)
190 auto processor = [low_freq, high_freq, sample_rate](Eigen::VectorXcd& spectrum, size_t) {
191 auto bin_indices = std::views::iota(0,
static_cast<int>(spectrum.size()));
192 std::ranges::for_each(bin_indices, [&](
int bin) {
193 double freq = (bin * sample_rate) / (2.0 * (
double)spectrum.size());
194 if (freq < low_freq || freq > high_freq) {
200 for (
auto& span : target_data) {
202 std::ranges::copy(result, span.begin());
205 auto reconstructed_data = target_data
206 | std::views::transform([](
const auto& span) {
207 return std::vector<double>(span.begin(), span.end());
209 | std::ranges::to<std::vector>();
211 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
226template <OperationReadyData DataType>
231 uint32_t window_size,
233 std::vector<std::vector<double>>& working_buffer)
237 auto processor = [low_freq, high_freq, sample_rate](Eigen::VectorXcd& spectrum, size_t) {
238 auto bin_indices = std::views::iota(0,
static_cast<int>(spectrum.size()));
239 std::ranges::for_each(bin_indices, [&](
int bin) {
240 double freq = (bin * sample_rate) / (2.0 * (
double)spectrum.size());
241 if (freq < low_freq || freq > high_freq) {
247 working_buffer.resize(target_data.size());
248 for (
size_t i = 0; i < target_data.size(); ++i) {
252 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
264template <OperationReadyData DataType>
267 uint32_t window_size = 1024,
268 uint32_t hop_size = 256)
272 if (semitones == 0.0) {
276 double pitch_ratio = std::pow(2.0, semitones / 12.0);
278 auto processor = [pitch_ratio](Eigen::VectorXcd& spectrum, size_t) {
279 Eigen::VectorXcd shifted_spectrum = Eigen::VectorXcd::Zero(spectrum.size());
280 auto bin_indices = std::views::iota(0,
static_cast<int>(spectrum.size()));
282 std::ranges::for_each(bin_indices, [&](
int bin) {
283 int shifted_bin =
static_cast<int>(bin * pitch_ratio);
284 if (shifted_bin < shifted_spectrum.size()) {
285 shifted_spectrum[shifted_bin] = spectrum[bin];
289 spectrum = std::move(shifted_spectrum);
292 for (
auto& span : target_data) {
294 std::ranges::copy(result, span.begin());
297 auto reconstructed_data = target_data
298 | std::views::transform([](
const auto& span) {
299 return std::vector<double>(span.begin(), span.end());
301 | std::ranges::to<std::vector>();
303 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
316template <OperationReadyData DataType>
319 uint32_t window_size,
321 std::vector<std::vector<double>>& working_buffer)
325 if (semitones == 0.0) {
329 double pitch_ratio = std::pow(2.0, semitones / 12.0);
331 auto processor = [pitch_ratio](Eigen::VectorXcd& spectrum, size_t) {
332 Eigen::VectorXcd shifted_spectrum = Eigen::VectorXcd::Zero(spectrum.size());
333 auto bin_indices = std::views::iota(0,
static_cast<int>(spectrum.size()));
335 std::ranges::for_each(bin_indices, [&](
int bin) {
336 int shifted_bin =
static_cast<int>(bin * pitch_ratio);
337 if (shifted_bin < shifted_spectrum.size()) {
338 shifted_spectrum[shifted_bin] = spectrum[bin];
342 spectrum = std::move(shifted_spectrum);
345 working_buffer.resize(target_data.size());
346 for (
size_t i = 0; i < target_data.size(); ++i) {
350 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
361template <OperationReadyData DataType>
363 uint32_t window_size = 1024,
364 uint32_t hop_size = 256)
368 auto processor = [](Eigen::VectorXcd& spectrum, size_t) {
369 std::ranges::transform(spectrum, spectrum.begin(),
370 [](
const std::complex<double>& bin) { return std::conj(bin); });
373 for (
auto& span : target_data) {
375 std::ranges::copy(result, span.begin());
378 auto reconstructed_data = target_data
379 | std::views::transform([](
const auto& span) {
380 return std::vector<double>(span.begin(), span.end());
382 | std::ranges::to<std::vector>();
384 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
396template <OperationReadyData DataType>
398 uint32_t window_size,
400 std::vector<std::vector<double>>& working_buffer)
404 auto processor = [](Eigen::VectorXcd& spectrum, size_t) {
405 std::ranges::transform(spectrum, spectrum.begin(),
406 [](
const std::complex<double>& bin) { return std::conj(bin); });
409 working_buffer.resize(target_data.size());
410 for (
size_t i = 0; i < target_data.size(); ++i) {
414 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
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.
std::vector< double > generate_window(uint32_t size, WindowType window_type)
Generate window coefficients using C++20 ranges.
std::vector< double > process_spectral_windows(std::span< double > data, uint32_t window_size, uint32_t hop_size, ProcessorFunc &&processor)
Common spectral processing helper to eliminate code duplication.
DataType transform_spectral_filter(DataType &input, double low_freq, double high_freq, double sample_rate=48000.0, uint32_t window_size=1024, uint32_t hop_size=256)
Spectral filtering using existing FFT infrastructure with C++20 ranges (IN-PLACE)
DataType transform_spectral_invert(DataType &input, uint32_t window_size=1024, uint32_t hop_size=256)
Spectral inversion (phase inversion in frequency domain) using C++20 ranges (IN-PLACE)
uint64_t smallest_size(std::vector< std::vector< double > > &data)
DataType transform_window(DataType &input, Nodes::Generator::WindowType window_type, uint32_t window_size=0)
Windowing transformation using C++20 ranges (IN-PLACE)
DataType transform_pitch_shift(DataType &input, double semitones, uint32_t window_size=1024, uint32_t hop_size=256)
Pitch shifting using existing FFT from AnalysisHelper with C++20 ranges (IN-PLACE)