Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
frequency_response.hpp
Go to the documentation of this file.
1#pragma once
2
19
21
22#include <algorithm>
23#include <cmath>
24#include <complex>
25#include <cstddef>
26#include <span>
27
28namespace pulp::signal {
29
32inline constexpr float min_response_db = -200.0f;
33
35inline float magnitude_to_db(double magnitude) {
36 if (!(magnitude > 0.0)) return min_response_db;
37 double db = 20.0 * std::log10(magnitude);
38 if (!std::isfinite(db) || db < min_response_db) return min_response_db;
39 return static_cast<float>(db);
40}
41
44template <typename SampleType>
45inline double section_magnitude(const BiquadCoefficientsT<SampleType>& c, double omega) {
46 const std::complex<double> z_inv = std::polar(1.0, -omega);
47 const std::complex<double> z_inv2 = z_inv * z_inv;
48 const std::complex<double> num =
49 static_cast<double>(c.b0) + static_cast<double>(c.b1) * z_inv + static_cast<double>(c.b2) * z_inv2;
50 const std::complex<double> den =
51 1.0 + static_cast<double>(c.a1) * z_inv + static_cast<double>(c.a2) * z_inv2;
52 const double d = std::abs(den);
53 if (!(d > 0.0)) return 0.0; // pole on the unit circle — treat as no output
54 return std::abs(num) / d;
55}
56
57// Preserve the original concrete float entry point so aggregate calls such as
58// section_magnitude({1, 0, 0, 0, 0}, omega) remain source-compatible.
59inline double section_magnitude(const BiquadCoefficients& c, double omega) {
60 return section_magnitude<float>(c, omega);
61}
62
67template <typename SampleType>
68inline double section_group_delay(const BiquadCoefficientsT<SampleType>& c, double omega) {
69 const std::complex<double> z1(std::cos(omega), -std::sin(omega));
70 const std::complex<double> z2 = z1 * z1;
71 const auto delay = [&](double p0, double p1, double p2) {
72 return std::real((p1 * z1 + 2.0 * p2 * z2) / (p0 + p1 * z1 + p2 * z2));
73 };
74 return delay(c.b0, c.b1, c.b2) - delay(1.0, c.a1, c.a2);
75}
76
79template <typename SampleType, std::size_t Extent>
80inline double cascade_magnitude(std::span<const BiquadCoefficientsT<SampleType>, Extent> sos,
81 double omega) {
82 double mag = 1.0;
83 for (const auto& c : sos) mag *= section_magnitude(c, omega);
84 return mag;
85}
86
87// Preserve the original float-span entry point so containers such as
88// std::vector<BiquadCoefficients> retain their implicit span conversion. The
89// templated overload above adds precision without narrowing that source API.
90inline double cascade_magnitude(std::span<const BiquadCoefficients> sos, double omega) {
92}
93
97inline double angular_frequency(double freq_hz, double sample_rate) {
98 if (!(sample_rate > 0.0)) return 0.0;
99 const double omega = 2.0 * std::acos(-1.0) * freq_hz / sample_rate;
100 return std::clamp(omega, 0.0, std::acos(-1.0));
101}
102
104template <typename SampleType>
105inline float section_magnitude_db(const BiquadCoefficientsT<SampleType>& c, double freq_hz,
106 double sample_rate) {
107 return magnitude_to_db(section_magnitude(c, angular_frequency(freq_hz, sample_rate)));
108}
109
110inline float section_magnitude_db(const BiquadCoefficients& c, double freq_hz,
111 double sample_rate) {
112 return section_magnitude_db<float>(c, freq_hz, sample_rate);
113}
114
116template <typename SampleType, std::size_t Extent>
117inline float cascade_magnitude_db(std::span<const BiquadCoefficientsT<SampleType>, Extent> sos,
118 double freq_hz, double sample_rate) {
119 return magnitude_to_db(cascade_magnitude(sos, angular_frequency(freq_hz, sample_rate)));
120}
121
122inline float cascade_magnitude_db(std::span<const BiquadCoefficients> sos, double freq_hz,
123 double sample_rate) {
124 return cascade_magnitude_db<float, std::dynamic_extent>(sos, freq_hz, sample_rate);
125}
126
129template <typename SampleType>
130inline float magnitude_db(const BiquadT<SampleType>& filter, double freq_hz, double sample_rate) {
131 const auto c = filter.coefficients();
132 return section_magnitude_db(c, freq_hz, sample_rate);
133}
134
142inline double fft_bin_frequency(std::size_t index, std::size_t bin_count, double sample_rate) {
143 if (bin_count <= 1) return 0.0;
144 const double nyquist = sample_rate * 0.5;
145 return nyquist * static_cast<double>(index) / static_cast<double>(bin_count - 1);
146}
147
152inline double log_frequency_at(std::size_t index, std::size_t count, double min_hz, double max_hz) {
153 if (count <= 1) return min_hz;
154 const double log_min = std::log10(min_hz);
155 const double log_max = std::log10(max_hz);
156 const double t = static_cast<double>(index) / static_cast<double>(count - 1);
157 return std::pow(10.0, log_min + t * (log_max - log_min));
158}
159
166template <typename SampleType, std::size_t Extent>
167inline void response_curve_db(std::span<const BiquadCoefficientsT<SampleType>, Extent> sos,
168 double min_hz, double max_hz, double sample_rate,
169 std::span<float> out) {
170 const std::size_t n = out.size();
171 for (std::size_t i = 0; i < n; ++i) {
172 const double hz = log_frequency_at(i, n, min_hz, max_hz);
173 out[i] = cascade_magnitude_db(sos, hz, sample_rate);
174 }
175}
176
177inline void response_curve_db(std::span<const BiquadCoefficients> sos, double min_hz,
178 double max_hz, double sample_rate, std::span<float> out) {
179 response_curve_db<float, std::dynamic_extent>(sos, min_hz, max_hz, sample_rate, out);
180}
181
182} // namespace pulp::signal
Definition biquad.hpp:53
Coefficients coefficients() const
Definition biquad.hpp:81
Definition additive_bank.hpp:218
float magnitude_to_db(double magnitude)
Convert a linear magnitude to dB, floored at min_response_db.
Definition frequency_response.hpp:35
void response_curve_db(std::span< const BiquadCoefficientsT< SampleType >, Extent > sos, double min_hz, double max_hz, double sample_rate, std::span< float > out)
Sample a cascade's magnitude response into out, at out.size() log-spaced frequencies spanning [min_hz...
Definition frequency_response.hpp:167
double cascade_magnitude(std::span< const BiquadCoefficientsT< SampleType >, Extent > sos, double omega)
Linear magnitude |H| of a cascade at angular frequency ω.
Definition frequency_response.hpp:80
constexpr float min_response_db
Floor applied to dB conversions.
Definition frequency_response.hpp:32
double fft_bin_frequency(std::size_t index, std::size_t bin_count, double sample_rate)
Center frequency of bin index in a real-FFT magnitude array.
Definition frequency_response.hpp:142
float magnitude_db(const BiquadT< SampleType > &filter, double freq_hz, double sample_rate)
Ask a live filter what it looks like: the magnitude, in dB, that this BiquadT is applying at freq_hz ...
Definition frequency_response.hpp:130
double section_magnitude(const BiquadCoefficientsT< SampleType > &c, double omega)
Linear magnitude |H(e^jω)| of one section at normalized angular frequency ω = 2π·f/fs (radians/sample...
Definition frequency_response.hpp:45
@ c
Definition spectrum_trace.hpp:35
@ delay
Definition envelope.hpp:57
double section_group_delay(const BiquadCoefficientsT< SampleType > &c, double omega)
Group delay of one biquad section at omega (radians per sample), in samples: -d(arg H)/d(omega) for H...
Definition frequency_response.hpp:68
float cascade_magnitude_db(std::span< const BiquadCoefficientsT< SampleType >, Extent > sos, double freq_hz, double sample_rate)
Magnitude of a cascade at a frequency in Hz, in dB.
Definition frequency_response.hpp:117
float section_magnitude_db(const BiquadCoefficientsT< SampleType > &c, double freq_hz, double sample_rate)
Magnitude of one section at a frequency in Hz, in dB.
Definition frequency_response.hpp:105
double log_frequency_at(std::size_t index, std::size_t count, double min_hz, double max_hz)
Frequency of sample index of count, log-spaced across [min_hz, max_hz].
Definition frequency_response.hpp:152
@ i
Definition formant_filter_bank.hpp:20
double angular_frequency(double freq_hz, double sample_rate)
Angular frequency (radians/sample) of freq_hz at sample_rate, clamped to [0, π].
Definition frequency_response.hpp:97
BiquadCoefficientsT< float > BiquadCoefficients
Definition biquad.hpp:29
Definition biquad.hpp:24