22 static_assert(std::is_floating_point_v<SampleType>);
23 using InternalType = std::conditional_t<std::is_same_v<SampleType, float>, double, SampleType>;
27 static constexpr SampleType
min_q = SampleType{0.1};
28 static constexpr SampleType
max_q = SampleType{20};
34 SampleType
q = SampleType{0.7071067811865476};
54 endpoints_[0] =
first;
63 if (!std::isfinite(
static_cast<double>(amount)) || amount < SampleType{0} ||
64 amount > SampleType{1})
70 SampleType
process(SampleType input)
noexcept {
71 if (!std::isfinite(
static_cast<double>(input))) {
80 const InternalType
first = first_filter_.process(
static_cast<InternalType
>(input));
81 const InternalType
second = second_filter_.process(
static_cast<InternalType
>(input));
82 const bool first_finite = std::isfinite(
first);
83 const bool second_finite = std::isfinite(
second);
84 if (morph_ == SampleType{0}) {
86 second_filter_.reset();
87 if (!first_finite || !representable_(
first)) {
88 first_filter_.reset();
91 return static_cast<SampleType
>(
first);
93 if (morph_ == SampleType{1}) {
95 first_filter_.reset();
96 if (!second_finite || !representable_(
second)) {
97 second_filter_.reset();
100 return static_cast<SampleType
>(
second);
102 if (!first_finite || !second_finite) {
104 first_filter_.reset();
106 second_filter_.reset();
107 return SampleType{0};
109 const InternalType output = std::lerp(
first,
second,
static_cast<InternalType
>(morph_));
110 if (!representable_(output)) {
112 return SampleType{0};
114 return static_cast<SampleType
>(output);
117 bool process_block(
const SampleType* input, SampleType* output, std::size_t frames)
noexcept {
118 if ((input ==
nullptr || output ==
nullptr) && frames != 0)
120 for (std::size_t
i = 0;
i < frames; ++
i)
130 first_filter_.reset();
131 second_filter_.reset();
135 return endpoints_[0];
138 return endpoints_[1];
140 [[nodiscard]] SampleType
morph() const noexcept {
150 return narrow_coefficients_(first_filter_.coefficients());
153 return narrow_coefficients_(second_filter_.coefficients());
157 [[nodiscard]]
double magnitude(
double frequency_hz)
const noexcept {
160 const double omega =
angular_frequency(frequency_hz,
static_cast<double>(sample_rate_));
161 const auto first = complex_response_(first_filter_.coefficients(), omega);
162 const auto second = complex_response_(second_filter_.coefficients(), omega);
171 for (std::size_t
i = 0;
i < output.size(); ++
i)
184 static bool valid_sample_rate_(SampleType
sample_rate)
noexcept {
185 return std::isfinite(
static_cast<double>(
sample_rate)) &&
189 static bool valid_endpoint_(
const Endpoint& endpoint, SampleType
sample_rate)
noexcept {
190 if (!std::isfinite(
static_cast<double>(endpoint.frequency_hz)) ||
191 !std::isfinite(
static_cast<double>(endpoint.q)) ||
193 endpoint.frequency_hz >
sample_rate * SampleType{0.45} || endpoint.q <
min_q ||
196 switch (endpoint.type) {
206 static BiquadCoefficientsT<SampleType> design_(
Endpoint endpoint,
211 BiquadT<double> designer;
212 designer.set_coefficients(
213 to_biquad_type_double_(endpoint.type),
static_cast<double>(endpoint.frequency_hz),
214 static_cast<double>(endpoint.q),
static_cast<double>(
sample_rate));
215 const auto c = designer.coefficients();
216 return {
static_cast<SampleType
>(
c.b0),
static_cast<SampleType
>(
c.b1),
217 static_cast<SampleType
>(
c.b2),
static_cast<SampleType
>(
c.a1),
218 static_cast<SampleType
>(
c.a2)};
235 static bool finite_coefficients_(
const BiquadCoefficientsT<SampleType>&
c)
noexcept {
236 return std::isfinite(
static_cast<double>(
c.b0)) &&
237 std::isfinite(
static_cast<double>(
c.b1)) &&
238 std::isfinite(
static_cast<double>(
c.b2)) &&
239 std::isfinite(
static_cast<double>(
c.a1)) && std::isfinite(
static_cast<double>(
c.a2));
242 template <
typename CoefficientType>
243 static std::complex<double> complex_response_(
const BiquadCoefficientsT<CoefficientType>&
c,
244 double omega)
noexcept {
245 const auto z1 = std::polar(1.0, -omega);
246 const auto z2 = z1 * z1;
247 const std::complex<double> numerator =
static_cast<double>(
c.b0) +
248 static_cast<double>(
c.b1) * z1 +
249 static_cast<double>(
c.b2) * z2;
250 const std::complex<double> denominator =
251 1.0 +
static_cast<double>(
c.a1) * z1 +
static_cast<double>(
c.a2) * z2;
252 if (!(std::abs(denominator) > 0.0))
254 return numerator / denominator;
257 static bool representable_(InternalType value)
noexcept {
258 return std::isfinite(value) &&
259 std::abs(value) <=
static_cast<InternalType
>(std::numeric_limits<SampleType>::max());
262 static BiquadCoefficientsT<SampleType>
263 narrow_coefficients_(
const BiquadCoefficientsT<InternalType>&
c)
noexcept {
264 return {
static_cast<SampleType
>(
c.b0),
static_cast<SampleType
>(
c.b1),
265 static_cast<SampleType
>(
c.b2),
static_cast<SampleType
>(
c.a1),
266 static_cast<SampleType
>(
c.a2)};
269 BiquadT<InternalType> first_filter_{};
270 BiquadT<InternalType> second_filter_{};
272 SampleType sample_rate_ = SampleType{48000};
273 SampleType morph_ = SampleType{0};
274 bool configured_ =
false;
Type
Definition biquad.hpp:58
@ notch
Definition biquad.hpp:58
@ highpass
Definition biquad.hpp:58
@ bandpass
Definition biquad.hpp:58
@ lowpass
Definition biquad.hpp:58
Definition filter_morph.hpp:21
bool configured() const noexcept
Definition filter_morph.hpp:146
BiquadCoefficientsT< float > first_coefficients() const noexcept
Definition filter_morph.hpp:149
bool configure(SampleType sample_rate, Endpoint first, Endpoint second) noexcept
Transactionally design both endpoints.
Definition filter_morph.hpp:39
BiquadCoefficientsT< float > second_coefficients() const noexcept
Definition filter_morph.hpp:152
void reset() noexcept
Definition filter_morph.hpp:129
void response_curve_db(double min_hz, double max_hz, std::span< float > output) const noexcept
Definition filter_morph.hpp:170
static constexpr float max_q
Definition filter_morph.hpp:28
static constexpr float max_sample_rate
Definition filter_morph.hpp:29
SampleType morph() const noexcept
Definition filter_morph.hpp:140
bool process_block(const SampleType *input, SampleType *output, std::size_t frames) noexcept
Definition filter_morph.hpp:117
static constexpr int tail_samples() noexcept
Recursive biquads have an asymptotic (formally unbounded) tail.
Definition filter_morph.hpp:179
static constexpr int latency_samples() noexcept
Definition filter_morph.hpp:175
Endpoint second_endpoint() const noexcept
Definition filter_morph.hpp:137
SampleType process(SampleType input) noexcept
Definition filter_morph.hpp:70
bool process_block(SampleType *samples, std::size_t frames) noexcept
Definition filter_morph.hpp:125
Endpoint first_endpoint() const noexcept
Definition filter_morph.hpp:134
float sample_rate() const noexcept
Definition filter_morph.hpp:143
double magnitude(double frequency_hz) const noexcept
Exact complex parallel sum of the two live sections.
Definition filter_morph.hpp:157
bool set_morph(SampleType amount) noexcept
Set the linear-amplitude morph position.
Definition filter_morph.hpp:62
float magnitude_db(double frequency_hz) const noexcept
Definition filter_morph.hpp:166
static constexpr float min_q
Definition filter_morph.hpp:27
static constexpr float min_frequency_hz
Definition filter_morph.hpp:26
Evaluate what a biquad (or a cascade of them) does to the spectrum.
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
@ first
Definition dither.hpp:23
@ second
Definition dither.hpp:24
FilterMorphT< double > FilterMorph64
Definition filter_morph.hpp:278
@ c
Definition spectrum_trace.hpp:35
bool biquad_is_stable(const BiquadCoefficientsT< SampleType > &c) noexcept
Return whether a normalized real biquad's poles are strictly inside the unit circle.
Definition biquad.hpp:34
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
FilterMorphT< float > FilterMorph
Definition filter_morph.hpp:277
@ i
Definition formant_filter_bank.hpp:20
MorphFilterType
Definition filter_morph.hpp:19
@ notch
Definition filter_morph.hpp:19
@ highpass
Definition filter_morph.hpp:19
@ bandpass
Definition filter_morph.hpp:19
@ lowpass
Definition filter_morph.hpp:19
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
Definition filter_morph.hpp:31
MorphFilterType type
Definition filter_morph.hpp:32
SampleType frequency_hz
Definition filter_morph.hpp:33
SampleType q
Definition filter_morph.hpp:34