EchoMap 2026-07-25 6d3977c
An experimental cross-platform digital signal processing application for sound-source localisation.
Loading...
Searching...
No Matches
echomap::FrequencySpectrumFactory Class Reference

Provides a static helper to perform discrete Fourier transforms on Signal objects to produce frequency spectra. More...

#include <FrequencySpectrumFactory.hpp>

Static Public Member Functions

static std::unique_ptr< FrequencySpectrumcreate_frequency_spectrum (const Signal &signal, WindowFunctions::AllFunctions window_function, std::size_t transform_size)
 Construct a FrequencySpectrum of the given uniformly sampled Signal.

Static Private Member Functions

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.
static Signal::Sample::AmplitudeT amplitude_to_dbfs (Signal::Sample::AmplitudeT amplitude) noexcept
 Convert an amplitude to a dBfs (decibels relative to full-scale) quantity.

Detailed Description

Provides a static helper to perform discrete Fourier transforms on Signal objects to produce frequency spectra.

Definition at line 27 of file FrequencySpectrumFactory.hpp.

Member Function Documentation

◆ amplitude_to_dbfs()

Signal::Sample::AmplitudeT echomap::FrequencySpectrumFactory::amplitude_to_dbfs ( Signal::Sample::AmplitudeT amplitude)
staticnodiscardprivatenoexcept

Convert an amplitude to a dBfs (decibels relative to full-scale) quantity.

Parameters
amplitudeThe linear amplitude.
Returns
The dBfs quantisation of the linear amplitude.

Definition at line 107 of file FrequencySpectrumFactory.cpp.

110{
111#if defined(__EMSCRIPTEN__) || defined(__DOXYGEN__)
112 constexpr auto full = std::max(web::abs(Signal::normalised_range.first), web::abs(Signal::normalised_range.second));
113#else
114 constexpr auto full = std::max(std::abs(Signal::normalised_range.first), std::abs(Signal::normalised_range.second));
115#endif
116 assert(full > 0.0f);
117
118 constexpr auto maximum_ratio = 1.0e-6f; // 20log_{10}(1e-6) = -120dB.
119 return 20.0f * std::log10(std::max(std::abs(amplitude) / full, maximum_ratio));
120}
static constexpr std::pair< Sample::AmplitudeT, Sample::AmplitudeT > normalised_range
The range within which the amplitude values are normalised.
Definition Signal.hpp:74
T log10(T... args)
T max(T... args)

◆ create_frequency_spectrum()

std::unique_ptr< FrequencySpectrum > echomap::FrequencySpectrumFactory::create_frequency_spectrum ( const Signal & signal,
WindowFunctions::AllFunctions window_function,
std::size_t transform_size )
staticnodiscard

Construct a FrequencySpectrum of the given uniformly sampled Signal.

Parameters
signalThe uniformly sampled Signal of which to take the DFT.
window_functionThe window function to apply onto the input time-series.
transform_sizeThe number of samples in the transform window.
Returns
An owning container of the created FrequencySpectrum.
Exceptions
std::runtime_errorThe given Signal was not uniformly sampled; hence, a DFT is not applicable.
std::runtime_errorFFTW failed to initialise.

Definition at line 27 of file FrequencySpectrumFactory.cpp.

32{
33 if (!signal.is_uniformly_sampled())
34 throw std::runtime_error(std::format("Attempted to transform variably sampled {}.", signal.get_name()));
35
36 const auto display_name = std::format(
37 "{} ({} DFT @ {})",
38 signal.get_name(),
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 // Create FFTW buffers, making a separate input buffer if and only if we're using a non-constant input transform.
47 const FFTWBuffers context(
48 transform_size,
50 : std::nullopt
51 );
52
53 // Pre-process input as advised by the window function.
54 const auto scale_divisor = prepare_input(context, window_function, signal.amplitudes());
55
56 // Create a plan, do the FFT, and clean up. (Buffers are RAII scoped to this function.)
57 const fftwf_plan plan = // NOLINT(*-misplaced-const) - Declaration is correct.
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 // Construct the FrequencySpectrum from the coefficients.
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),
83 amplitude_to_dbfs(linear_amplitude),
84 std::atan2(imag, real)
85 );
86 }
87
88 return spectrum;
89}
T atan2(T... args)
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 format(T... args)
T holds_alternative(T... args)
T make_optional(T... args)
T signal(T... args)
T sqrt(T... args)

◆ prepare_input()

float echomap::FrequencySpectrumFactory::prepare_input ( const FFTWBuffers & buffers,
WindowFunctions::AllFunctions window_function,
std::span< const Signal::Sample::AmplitudeT > input )
staticprivate

Copies and prepare the input amplitude time-series for FFT with the given window function preference.

Parameters
buffersInitialised FFTWBuffers for the input.
window_functionSelector for the window function to use.
inputThe amplitude series.
Returns
The scaling divisor constant used for computing the magnitude.
Precondition
The size of the given input range is at least the transform size specified by the FFTWBuffers object.

Definition at line 91 of file FrequencySpectrumFactory.cpp.

96{
97 assert(input.size() >= buffers.input_size);
98
99 return std::visit(
100 [&buffers, &input]<WindowFunction WindowT>(WindowT) {
101 return WindowFunctions::apply_window<WindowT>(buffers, input);
102 },
103 window_function
104 );
105}
T size(T... args)
T visit(T... args)

The documentation for this class was generated from the following files: