EchoMap
2026-07-25 6d3977c
An experimental cross-platform digital signal processing application for sound-source localisation.
Toggle main menu visibility
Loading...
Searching...
No Matches
FrequencySpectrumFactory.cpp
Go to the documentation of this file.
1
9
10
#include "
FrequencySpectrumFactory.hpp
"
11
12
#include <fftw3.h>
13
14
#ifdef __EMSCRIPTEN__
15
#include "
../../web/EmscriptenExtra.hpp
"
16
#else
17
#include <
cmath
>
18
#endif
19
20
#include "
../FrequencySpectrum.hpp
"
21
#include "
../Signal.hpp
"
22
#include "
FFTWBuffers.hpp
"
23
24
namespace
echomap
25
{
26
27
std::unique_ptr<FrequencySpectrum>
FrequencySpectrumFactory::create_frequency_spectrum
(
28
const
Signal
& signal,
29
const
WindowFunctions::AllFunctions window_function,
30
const
std::size_t
transform_size
31
)
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,
49
std::holds_alternative<WindowFunctions::Constant>
(window_function) ?
std::make_optional
(signal.amplitudes())
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
}
90
91
float
FrequencySpectrumFactory::prepare_input
(
92
const
FFTWBuffers
& buffers,
93
const
WindowFunctions::AllFunctions window_function,
94
const
std::span<const Signal::Sample::AmplitudeT>
input
95
)
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
}
106
107
Signal::Sample::AmplitudeT
FrequencySpectrumFactory::amplitude_to_dbfs
(
108
const
Signal::Sample::AmplitudeT
amplitude
109
)
noexcept
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
}
121
122
}
// namespace echomap
EmscriptenExtra.hpp
EmscriptenExtra specification.
echomap::web::abs
static constexpr auto abs(T const &x) noexcept
Constexpr absolute value function for pre-C++23 Emscripten compilers.
Definition
EmscriptenExtra.hpp:31
FFTWBuffers.hpp
FFTWBuffers specification.
FrequencySpectrumFactory.hpp
FrequencySpectrumFactory specification.
FrequencySpectrum.hpp
FrequencySpectrum specification.
Signal.hpp
Audio signal class specification.
std::atan2
T atan2(T... args)
echomap::FFTWBuffers
Simple RAII wrapper for commonly used C types from FFTW(f).
Definition
FFTWBuffers.hpp:27
echomap::FFTWBuffers::input_size
std::size_t input_size
Size of the input vector.
Definition
FFTWBuffers.hpp:33
echomap::FFTWBuffers::coefficients
fftwf_complex_fwd *const coefficients
Complex coefficients (output of real-to-complex FFT).
Definition
FFTWBuffers.hpp:31
echomap::FFTWBuffers::input
float * input
Real-valued inputs, either managed by us or aliased by the user.
Definition
FFTWBuffers.hpp:32
echomap::FrequencySpectrumFactory::amplitude_to_dbfs
static Signal::Sample::AmplitudeT amplitude_to_dbfs(Signal::Sample::AmplitudeT amplitude) noexcept
Convert an amplitude to a dBfs (decibels relative to full-scale) quantity.
Definition
FrequencySpectrumFactory.cpp:107
echomap::FrequencySpectrumFactory::prepare_input
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.
Definition
FrequencySpectrumFactory.cpp:91
echomap::FrequencySpectrumFactory::create_frequency_spectrum
static std::unique_ptr< FrequencySpectrum > create_frequency_spectrum(const Signal &signal, WindowFunctions::AllFunctions window_function, std::size_t transform_size)
Construct a FrequencySpectrum of the given uniformly sampled Signal.
Definition
FrequencySpectrumFactory.cpp:27
echomap::FrequencySpectrum
A Signal sampled in the frequency domain.
Definition
FrequencySpectrum.hpp:27
echomap::Signal
A single channel of discretely sampled audio data.
Definition
Signal.hpp:40
echomap::Signal::normalised_range
static constexpr std::pair< Sample::AmplitudeT, Sample::AmplitudeT > normalised_range
The range within which the amplitude values are normalised.
Definition
Signal.hpp:74
echomap::WindowFunctions::get_window_name
static constexpr std::string_view get_window_name()
Retrieve a human-readable name for the templated window function.
Definition
WindowFunctions.hpp:292
cmath
echomap::WindowFunction
Definition
WindowFunctions.hpp:22
std::format
T format(T... args)
std::holds_alternative
T holds_alternative(T... args)
std::log10
T log10(T... args)
std::make_optional
T make_optional(T... args)
std::max
T max(T... args)
echomap
The main EchoMap outermost namespace for all non-exported symbols.
Definition
ActionController.hpp:20
std::runtime_error
std::span::size
T size(T... args)
std::size_t
std::span
std::sqrt
T sqrt(T... args)
echomap::Signal::Sample::AmplitudeT
float AmplitudeT
Type for sample amplitudes.
Definition
Signal.hpp:48
std::unique_ptr
std::visit
T visit(T... args)
src
objects
factories
FrequencySpectrumFactory.cpp
Generated by
1.17.0