32{
33 if (!
signal.is_uniformly_sampled())
34 throw std::runtime_error(
std::format(
"Attempted to transform variably sampled {}.",
signal.get_name()));
35
37 "{} ({} DFT @ {})",
39 WindowFunctions::get_window_name<WindowFunctions::Constant>(),
40 transform_size
41 );
42
43 if (transform_size == 0)
44 return std::unique_ptr<FrequencySpectrum>(new FrequencySpectrum(window_function, display_name));
45
46
47 const FFTWBuffers context(
48 transform_size,
50 : std::nullopt
51 );
52
53
55
56
57 const fftwf_plan plan =
58 fftwf_plan_dft_r2c_1d(static_cast<int>(transform_size), context.input, context.coefficients, FFTW_ESTIMATE);
59
60 if (plan == nullptr)
61 throw std::runtime_error("Failed to create an FFTW plan.");
62
63 fftwf_execute(plan);
64 fftwf_destroy_plan(plan);
65
66
67 const auto bin_count = transform_size / 2 + 1;
68 auto spectrum = std::unique_ptr<FrequencySpectrum>(new FrequencySpectrum(window_function, display_name));
69 spectrum->reserve_bins(bin_count);
70
71 for (std::size_t bin_idx = 0; bin_idx < bin_count; ++bin_idx) {
72 const auto is_dc = bin_idx == 0;
73 const auto is_nyquist = transform_size % 2 == 0 && bin_idx == transform_size / 2;
74 const auto scale = (is_dc || is_nyquist ? 1.0f : 2.0f) / scale_divisor;
75
76 const auto real = context.coefficients[bin_idx][0];
77 const auto imag = context.coefficients[bin_idx][1];
78 const auto linear_amplitude =
std::sqrt(real * real + imag * imag) * scale;
79
80 spectrum->emplace_bin(
81 static_cast<float>(bin_idx) *
static_cast<float>(
signal.get_sample_rate()) /
82 static_cast<float>(transform_size),
85 );
86 }
87
88 return spectrum;
89}
static Signal::Sample::AmplitudeT amplitude_to_dbfs(Signal::Sample::AmplitudeT amplitude) noexcept
Convert an amplitude to a dBfs (decibels relative to full-scale) quantity.
static float prepare_input(const FFTWBuffers &buffers, WindowFunctions::AllFunctions window_function, std::span< const Signal::Sample::AmplitudeT > input)
Copies and prepare the input amplitude time-series for FFT with the given window function preference.
T holds_alternative(T... args)
T make_optional(T... args)