JPL Spatial
Sound spatialization and propagation library
Loading...
Searching...
No Matches
FilterComponents.h
Go to the documentation of this file.
1//
2// ██╗██████╗ ██╗ ██╗██████╗ ███████╗
3// ██║██╔══██╗ ██║ ██║██╔══██╗██╔════╝ ** JPL Spatial **
4// ██║██████╔╝ ██║ ██║██████╔╝███████╗
5// ██ ██║██╔═══╝ ██║ ██║██╔══██╗╚════██║ https://github.com/Jaytheway/JPLSpatial
6// ╚█████╔╝██║ ███████╗██║██████╔╝███████║
7// ╚════╝ ╚═╝ ╚══════╝╚═╝╚═════╝ ╚══════╝
8//
9// Copyright Jaroslav Pevno 2026, JPL Spatial is offered under the terms of the ISC license:
10//
11// Permission to use, copy, modify, and/or distribute this software for any purpose with or
12// without fee is hereby granted, provided that the above copyright notice and this permission
13// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
14// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
16// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
17// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#pragma once
21
23
24#include <cmath>
25#include <concepts>
26#include <complex>
27#include <numbers>
28#include <span>
29#include <vector>
30
31namespace JPL
32{
33 //==========================================================================
34 namespace FilterUtils
35 {
36 template<class FilterType>
37 void ComputeFilterResponse(const FilterType& filter, float sampleRate, std::vector<float>& outMagnitudes);
38
39 template<class FilterType>
40 void ComputeFilterResponse_dB(const FilterType& filter, float sampleRate, std::vector<float>& outMagnitudes);
41
42 //======================================================================
50 template<std::floating_point T>
51 class SemitoneGridView;
52 } // namespace FilterUtils
53
54 //==========================================================================
56 namespace Cache
57 {
58 struct Shelf
59 {
60 float t; // tan(PI * frequency / sampleRate)
61 float sqrtK; // sqrt(gain)
62 float gain;
63 };
64
66 {
67 double cosOmega; // cos(2.0 * PI * frequency / sampleRate)
68 double sqrt2SinOmega; // sqrt(2.0) * sin(2.0 * PI * frequency / sampleRate)
69
70 [[nodiscard]] static JPL_INLINE BiquadShelf From(double normalizedFrequency);
71
72 // Note: for now we don't cache gain term, since within the current use-case,
73 // when frequency changes, we need to recompute gain anyway.
74 };
75 } // namespace Cache
76
77 //==========================================================================
78 namespace Topology
79 {
80 struct OnePole
81 {
82 using Sample = float;
83
84 Sample b0 = 1.0f, b1 = 0.0f;
85 Sample /*a0 = 0.0f,*/ a1 = 0.0f;
86
87 // Calculate magnitude at the given normalized frequency
88 [[nodiscard]] inline Sample CalculateResponse(float normalizedFrequency) const;
89
90 [[nodiscard]] static JPL_INLINE OnePole MakeLowShelf(float frequency, float sampleRate, float gain);
91 [[nodiscard]] static JPL_INLINE OnePole MakeHighShelf(float frequency, float sampleRate, float gain);
92
93 [[nodiscard]] static JPL_INLINE OnePole MakeLowShelf(float normalizedFrequency, float gain);
94 [[nodiscard]] static JPL_INLINE OnePole MakeHighShelf(float normalizedFrequency, float gain);
95
96 [[nodiscard]] static inline OnePole MakeLowShelf(Cache::Shelf cache);
97 [[nodiscard]] static inline OnePole MakeHighShelf(Cache::Shelf cache);
98 };
99
100 struct Biquad
101 {
102 using Sample = float;
103
104 Sample b0 = 1.0f, b1 = 0.0f, b2 = 0.0f;
105 Sample a1 = 0.0f, a2 = 0.0f;
106
107 [[nodiscard]] inline std::complex<double> CalculateResponse(float normalizedFrequency) const;
108
109 [[nodiscard]] static inline Biquad Combine(const OnePole& low, const OnePole& high, float broadbandGain);
110
111 // RBJ with fixed slope=1
112 [[nodiscard]] static JPL_INLINE Biquad MakeHighShelf(double normalizedFrequency, double gainDb);
113 [[nodiscard]] static inline Biquad MakeHighShelf(const Cache::BiquadShelf& cache, double gainDb);
114
115 template<class StateType>
116 void ProcessInterpolating(std::span<Sample> samples, StateType& state, Biquad& previous) const;
117
118 template<class StateType>
119 void ProcessInterpolating(std::span<Sample> samples, uint32 stride, StateType& state, Biquad& previous) const;
120
121 JPL_INLINE void SetCoefficients(const Topology::Biquad& other);
122 };
123
124 template<std::size_t N>
125 struct Cascade
126 {
127 std::array<Biquad, N> Stages;
128
129 [[nodiscard]] JPL_INLINE std::complex<double> CalculateResponse(double normalizedFrequency) const;
130 };
131 } // namespace Topology
132
133 //==========================================================================
134 namespace State
135 {
137 {
138 using Sample = float;
139 Sample x1 = 0.0f, y1 = 0.0f;
140
141 JPL_INLINE void Reset() { x1 = y1 = 0.0f; }
142
143 [[nodiscard]] JPL_INLINE Sample Process(Sample x, const Topology::OnePole& onePole)
144 {
145 const Sample y =
146 (onePole.b0 * x) +
147 (onePole.b1 * x1) -
148 (onePole.a1 * y1);
149 x1 = x;
150 y1 = y;
151 return y;
152 }
153 };
154
156 {
157 using Sample = float;
158 Sample x1 = 0.0f, x2 = 0.0f, y1 = 0.0f, y2 = 0.0f;
159
160 JPL_INLINE void Reset() { x1 = x2 = y1 = y2 = 0.0f; }
161
162 [[nodiscard]] JPL_INLINE Sample Process(Sample x, const Topology::Biquad& biquad)
163 {
164 const Sample y =
165 biquad.b0 * x +
166 biquad.b1 * x1 +
167 biquad.b2 * x2 -
168 biquad.a1 * y1 -
169 biquad.a2 * y2;
170 x2 = x1;
171 y2 = y1;
172 x1 = x;
173 y1 = y;
174 return y;
175 }
176 };
177
179 {
180 using Sample = float;
181 Sample s1 = 0.0f, s2 = 0.0f;
182
183 JPL_INLINE void Reset() { s1 = s2 = 0.0f; }
184
185 [[nodiscard]] JPL_INLINE Sample Process(Sample x, const Topology::Biquad& biquad)
186 {
187 const Sample y = biquad.b0 * x + s1;
188 s1 = biquad.b1 * x - biquad.a1 * y + s2;
189 s2 = biquad.b2 * x - biquad.a2 * y;
190 return y;
191 }
192 };
193
194 template<std::size_t N>
195 struct Cascade
196 {
197 using Sample = float;
198
199 std::array<State::DirectFormI, N> States;
200
201 [[nodiscard]] JPL_INLINE Sample Process(float x, const Topology::Cascade<N>& topo)
202 {
203 Sample s = x;
204 for (uint32 i = 0; i < States.size(); ++i)
205 s = States[i].Process(s, topo.Stages[i]);
206 return s;
207 }
208 };
209 } // namespace State
210} // namespace JPL
211
212//==============================================================================
213//
214// Code beyond this point is implementation detail...
215//
216//==============================================================================
217
218namespace JPL
219{
220 namespace FilterUtils
221 {
222 //======================================================================
223 template<std::floating_point T>
224 class SemitoneGridView : public std::ranges::view_interface<SemitoneGridView<T>>
225 {
226 public:
227 static constexpr T cSemitoneRatio = T(1.0594630943592953); // pow(2, 1/12)
228 static constexpr T cMinFrequency = T(20.0);
229
230 public:
231 constexpr explicit SemitoneGridView(T minFrequency, T maxFrequency)
232 : mMin(std::max(cMinFrequency, minFrequency)), mMax(maxFrequency)
233 {
234 }
235 constexpr explicit SemitoneGridView(T maxFrequency)
236 : SemitoneGridView(cMinFrequency, maxFrequency)
237 {
238 }
239
240 class iterator;
241
242 constexpr iterator begin() const noexcept
243 {
244 return iterator(mMin, mMax, mMin > mMax);
245 }
246
247 constexpr iterator end() const noexcept
248 {
249 return iterator(mMax, mMax, /* end */ true);
250 }
251
252 [[nodiscard]] constexpr std::size_t size() const noexcept
253 {
254 if (mMax < mMin)
255 return 0;
256
257 if (mMax == mMin)
258 return 1;
259
260 if (std::is_constant_evaluated())
261 {
262 std::size_t s = 0;
263 T frequency = mMin;
264
265 while (frequency <= mMax)
266 {
267 ++s;
268 frequency *= cSemitoneRatio;
269 }
270
271 return s;
272 }
273 else
274 {
275 return static_cast<std::size_t>(std::floor(T(12) * std::log2(mMax / mMin))) + 1;
276 }
277 }
278
279 public:
281 {
282 public:
283 using iterator_concept = std::forward_iterator_tag;
284 using iterator_category = std::forward_iterator_tag;
285 using value_type = T;
286 using difference_type = std::ptrdiff_t;
287
288 public:
289 constexpr iterator() : mCurrent(0), mMaxValue(0), bIsEnd(true) {}
290 constexpr iterator(T start, T max, bool end = false)
291 : mCurrent(start), mMaxValue(max), bIsEnd(end)
292 {
293 }
294
295 constexpr T operator*() const noexcept { return mCurrent; }
296
297 constexpr iterator& operator++() noexcept
298 {
299 if (not bIsEnd)
300 {
301 mCurrent *= cSemitoneRatio;
302 bIsEnd = mCurrent - mMaxValue > T(1e-9);
303 }
304 return *this;
305 }
306
307 constexpr iterator operator++(int) noexcept
308 {
309 iterator tmp = *this;
310 ++(*this);
311 return tmp;
312 }
313
314 constexpr bool operator==(const iterator& other) const noexcept
315 {
316 if (bIsEnd and other.bIsEnd)
317 return true;
318 if (bIsEnd or other.bIsEnd)
319 return false;
320 return JPL::Math::IsNearlyEqual(mCurrent, other.mCurrent, T(1e-9));
321 }
322
323 private:
324 T mCurrent;
325 T mMaxValue;
326 bool bIsEnd;
327 };
328
329 private:
330 T mMin;
331 T mMax;
332 };
333
334 //======================================================================
335 namespace Impl
336 {
337 template<bool bDecibels, class FilterType>
338 void ComputeFilterResponse(const FilterType& filter, float sampleRate, std::vector<float>& outMagnitudes)
339 {
340 const auto semitoneGrid = SemitoneGridView(sampleRate * 0.5f);
341
342 outMagnitudes.clear();
343 outMagnitudes.reserve(semitoneGrid.size());
344
345 const float invSampleRate = 1.0f / sampleRate;
346
347 auto projection = [](auto&& v)
348 {
349 using Func = decltype(&FilterType::CalculateResponse);
350 static constexpr bool bIsComplex =
351 std::same_as<std::invoke_result_t<Func, FilterType, float>, std::complex<double>> or
352 std::same_as<std::invoke_result_t<Func, FilterType, double>, std::complex<double>>;
353
354 if constexpr (bIsComplex)
355 {
356 return std::abs(v);
357 }
358 else
359 {
360 return v;
361 }
362 };
363
364 for (float frequency : semitoneGrid)
365 outMagnitudes.push_back(projection(filter.CalculateResponse(frequency * invSampleRate)));
366
367 if constexpr (bDecibels)
368 {
369 for (float& magnitude : outMagnitudes)
370 magnitude = -GainTodB(magnitude);
371 }
372 }
373 } // namespace Impl
374
375 //======================================================================
376 template<class FilterType>
377 void ComputeFilterResponse(const FilterType& filter, float sampleRate, std::vector<float>& outMagnitudes)
378 {
379 Impl::ComputeFilterResponse<false>(filter, sampleRate, outMagnitudes);
380 }
381
382 template<class FilterType>
383 void ComputeFilterResponse_dB(const FilterType& filter, float sampleRate, std::vector<float>& outMagnitudes)
384 {
385 Impl::ComputeFilterResponse<true>(filter, sampleRate, outMagnitudes);
386 }
387 } // namespace FilterUtils
388
389 //==========================================================================
390 inline auto Topology::OnePole::CalculateResponse(float normalizedFrequency) const -> Sample
391 {
392 const Sample omega = JPL_TWO_PI * normalizedFrequency;
393 const Sample cosOmega = ::cosf(omega);
394 const Sample numSq = (b0 * b0) + (b1 * b1) + (2.0f * b0 * b1 * cosOmega);
395 const Sample denSq = 1.0f + (a1 * a1) + (2.0f * a1 * cosOmega);
396 return ::sqrtf(numSq / denSq);
397 }
398
399 JPL_INLINE Topology::OnePole Topology::OnePole::MakeLowShelf(float frequency, float sampleRate, float gain)
400 {
401 return MakeLowShelf(frequency / sampleRate, gain);
402 }
403
404 JPL_INLINE Topology::OnePole Topology::OnePole::MakeLowShelf(float normalizedFrequency, float gain)
405 {
406 const float t = ::tanf(JPL_PI * normalizedFrequency);
407 const float sqrtK = ::sqrtf(gain);
408 return MakeLowShelf(Cache::Shelf{ .t = t, .sqrtK = sqrtK, .gain = gain });
409 }
410
412 {
413 const Sample raw_b0 = (cache.t * cache.sqrtK) + 1.0f;
414 const Sample raw_b1 = (cache.t * cache.sqrtK) - 1.0f;
415 const Sample raw_a0 = (cache.t / cache.sqrtK) + 1.0f;
416 const Sample raw_a1 = (cache.t / cache.sqrtK) - 1.0f;
417
418 // Pre-normalize coefficients
419 const Sample norm = Sample(1.0f) / raw_a0;
420 return OnePole{
421 .b0 = raw_b0 * norm,
422 .b1 = raw_b1 * norm,
423 .a1 = raw_a1 * norm
424 };
425 }
426
427
428 JPL_INLINE Topology::OnePole Topology::OnePole::MakeHighShelf(float frequency, float sampleRate, float gain)
429 {
430 return MakeHighShelf(frequency / sampleRate, gain);
431 }
432
433 JPL_INLINE Topology::OnePole Topology::OnePole::MakeHighShelf(float normalizedFrequency, float gain)
434 {
435 const float t = ::tanf(JPL_PI * normalizedFrequency);
436 const float sqrtK = ::sqrtf(gain);
437 return MakeHighShelf(Cache::Shelf{ .t = t, .sqrtK = sqrtK, .gain = gain });
438 }
439
441 {
442 const Sample raw_b0 = (cache.t * cache.sqrtK) + cache.gain;
443 const Sample raw_b1 = (cache.t * cache.sqrtK) - cache.gain;
444 const Sample raw_a0 = (cache.t * cache.sqrtK) + 1.0f;
445 const Sample raw_a1 = (cache.t * cache.sqrtK) - 1.0f;
446
447 // Pre-normalize coefficients
448 const Sample norm = Sample(1.0f) / raw_a0;
449 return OnePole{
450 .b0 = raw_b0 * norm,
451 .b1 = raw_b1 * norm,
452 .a1 = raw_a1 * norm
453 };
454 }
455
456 //==========================================================================
457 inline std::complex<double> Topology::Biquad::CalculateResponse(float normalizedFrequency) const
458 {
459 // DSPFilters by Vinnie Falco (MIT)
460 using Complex = std::complex<double>;
461 const double omega = 2 * std::numbers::pi_v<double> *normalizedFrequency;
462 const Complex czn1 = std::polar(1.0, -omega);
463 const Complex czn2 = std::polar(1.0, -2.0 * omega);
464 Complex ch(1.0);
465 Complex cbot(1.0);
466
467 Complex ct(b0);
468 Complex cb(1.0);
469 ct = ct + static_cast<double>(b1) * czn1;
470 ct = ct + static_cast<double>(b2) * czn2;
471 cb = cb + static_cast<double>(a1) * czn1;
472 cb = cb + static_cast<double>(a2) * czn2;
473 ch *= ct;
474 cbot *= cb;
475
476 return ch / cbot;
477 }
478
479 inline Topology::Biquad Topology::Biquad::Combine(const OnePole& low, const OnePole& high, float broadbandGain)
480 {
481 return Biquad{
482 .b0 = broadbandGain * (low.b0 * high.b0),
483 .b1 = broadbandGain * (low.b0 * high.b1 + low.b1 * high.b0),
484 .b2 = broadbandGain * (low.b1 * high.b1),
485 .a1 = low.a1 + high.a1,
486 .a2 = low.a1 * high.a1
487 };
488 }
489
490 JPL_INLINE Topology::Biquad Topology::Biquad::MakeHighShelf(double normalizedFrequency, double gainDb)
491 {
492 return MakeHighShelf(Cache::BiquadShelf::From(normalizedFrequency), gainDb);
493 }
494
495 JPL_INLINE Cache::BiquadShelf Cache::BiquadShelf::From(double normalizedFrequency)
496 {
497 const double omega = 2.0 * std::numbers::pi_v<double> * normalizedFrequency;
498 const auto [sineOmega, cosOmega] = Math::SinCos(omega);
499 return Cache::BiquadShelf{ .cosOmega = cosOmega, .sqrt2SinOmega = Math::Sqrt(2.0) * sineOmega };
500 }
501
503 {
504 // log2(10) / 40
505 static constexpr double log2_10_over40 = std::numbers::ln10 * std::numbers::log2e / 40.0;
506 const double A = std::exp2(gainDb * log2_10_over40); // std::pow(10.0, gainDb / 40.0);
507
508 // Fixed term for Q = 0.7071 (S = 1)
509 const double beta = std::sqrt(A) * cache.sqrt2SinOmega;
510
511 const double A_plus_1 = A + 1.0;
512 const double A_minus_1 = A - 1.0;
513 const double A_minus_1_cos = A_minus_1 * cache.cosOmega;
514 const double A_plus_1_cos = A_plus_1 * cache.cosOmega;
515
516 const double b0 = A * (A_plus_1 + A_minus_1_cos + beta);
517 const double b1 = -2.0 * A * (A_minus_1 + A_plus_1_cos);
518 const double b2 = A * (A_plus_1 + A_minus_1_cos - beta);
519 const double a0 = A_plus_1 - A_minus_1_cos + beta;
520 const double a1 = 2.0 * (A_minus_1 - A_plus_1_cos);
521 const double a2 = A_plus_1 - A_minus_1_cos - beta;
522
523 const double inv_a0 = 1.0 / a0;
524 return Topology::Biquad{
525 .b0 = static_cast<float>(b0 * inv_a0),
526 .b1 = static_cast<float>(b1 * inv_a0),
527 .b2 = static_cast<float>(b2 * inv_a0),
528 .a1 = static_cast<float>(a1 * inv_a0),
529 .a2 = static_cast<float>(a2 * inv_a0)
530 };
531 }
532
533 template<class StateType>
534 inline void Topology::Biquad::ProcessInterpolating(std::span<Sample> samples, StateType& state, Topology::Biquad& previous) const
535 {
536 if (samples.empty())
537 return;
538
539 const Sample t = 1.0f / samples.size();
540 const Sample db0 = t * (b0 - previous.b0);
541 const Sample db1 = t * (b1 - previous.b1);
542 const Sample db2 = t * (b2 - previous.b2);
543 const Sample da1 = t * (a1 - previous.a1);
544 const Sample da2 = t * (a2 - previous.a2);
545
546 for (Sample& sample : samples)
547 {
548 previous.b0 += db0;
549 previous.b1 += db1;
550 previous.b2 += db2;
551 previous.a1 += da1;
552 previous.a2 += da2;
553 sample = state.Process(sample, previous);
554 }
555
556 previous = *this;
557 }
558
559 template<class StateType>
560 inline void Topology::Biquad::ProcessInterpolating(std::span<Sample> samples, uint32 stride, StateType& state, Biquad& previous) const
561 {
562 if (samples.empty())
563 return;
564
565 const uint32 numFrames = 1 + (samples.size() - 1) / stride;
566 const Sample t = 1.0f / static_cast<Sample>(numFrames);
567 const Sample db0 = t * (b0 - previous.b0);
568 const Sample db1 = t * (b1 - previous.b1);
569 const Sample db2 = t * (b2 - previous.b2);
570 const Sample da1 = t * (a1 - previous.a1);
571 const Sample da2 = t * (a2 - previous.a2);
572
573 for (uint32 s = 0; s < samples.size(); s += stride)
574 {
575 previous.b0 += db0;
576 previous.b1 += db1;
577 previous.b2 += db2;
578 previous.a1 += da1;
579 previous.a2 += da2;
580 samples[s] = state.Process(samples[s], previous);
581 }
582
583 previous = *this;
584 }
585
587 {
588 *this = other;
589 }
590
591 //==========================================================================
592 template<std::size_t N>
593 JPL_INLINE std::complex<double> Topology::Cascade<N>::CalculateResponse(double normalizedFrequency) const
594 {
595 // From DSPFilters by Vinnie Falco (MIT)
596 using Complex = std::complex<double>;
597 const double omega = 2.0f * std::numbers::pi_v<double> *normalizedFrequency;
598 const Complex czn1 = std::polar(1.0, -omega);
599 const Complex czn2 = std::polar(1.0, -2.0 * omega);
600 Complex ch(1);
601 Complex cbot(1);
602
603 for (const Biquad& stage : Stages)
604 {
605 Complex cb(1);
606 Complex ct(stage.b0);
607 ct = ct + static_cast<double>(stage.b1) * czn1;
608 ct = ct + static_cast<double>(stage.b2) * czn2;
609 cb = cb + static_cast<double>(stage.a1) * czn1;
610 cb = cb + static_cast<double>(stage.a2) * czn2;
611 ch *= ct;
612 cbot *= cb;
613 }
614
615 return ch / cbot;
616 }
617
618} // namespace JPL
Definition FilterComponents.h:281
std::ptrdiff_t difference_type
Definition FilterComponents.h:286
constexpr iterator()
Definition FilterComponents.h:289
constexpr T operator*() const noexcept
Definition FilterComponents.h:295
T value_type
Definition FilterComponents.h:285
constexpr bool operator==(const iterator &other) const noexcept
Definition FilterComponents.h:314
constexpr iterator(T start, T max, bool end=false)
Definition FilterComponents.h:290
std::forward_iterator_tag iterator_concept
Definition FilterComponents.h:283
std::forward_iterator_tag iterator_category
Definition FilterComponents.h:284
constexpr iterator operator++(int) noexcept
Definition FilterComponents.h:307
constexpr iterator & operator++() noexcept
Definition FilterComponents.h:297
Definition FilterComponents.h:225
constexpr SemitoneGridView(T minFrequency, T maxFrequency)
Definition FilterComponents.h:231
static constexpr T cMinFrequency
Definition FilterComponents.h:228
constexpr iterator begin() const noexcept
Definition FilterComponents.h:242
constexpr SemitoneGridView(T maxFrequency)
Definition FilterComponents.h:235
constexpr std::size_t size() const noexcept
Definition FilterComponents.h:252
constexpr iterator end() const noexcept
Definition FilterComponents.h:247
static constexpr T cSemitoneRatio
Definition FilterComponents.h:227
void ComputeFilterResponse(const FilterType &filter, float sampleRate, std::vector< float > &outMagnitudes)
Definition FilterComponents.h:338
void ComputeFilterResponse_dB(const FilterType &filter, float sampleRate, std::vector< float > &outMagnitudes)
Definition FilterComponents.h:383
void ComputeFilterResponse(const FilterType &filter, float sampleRate, std::vector< float > &outMagnitudes)
Definition FilterComponents.h:377
JPL_INLINE constexpr T Sqrt(T x) noexcept
Definition Math.h:269
JPL_INLINE constexpr bool IsNearlyEqual(T a, T b, T tolerance=JPL_FLOAT_EPS_V< T >) noexcept
Definition Math.h:152
JPL_INLINE std::pair< T, T > SinCos(T value) noexcept
Definition Math.h:164
Definition AcousticMaterial.h:36
std::uint32_t uint32
Definition Core.h:311
JPL_INLINE T GainTodB(const T &gainFactor) noexcept
Definition DecibelsAndGain.h:75
JPL_INLINE simd max(const simd &a, const simd &b) noexcept
Element-wise max.
Definition SIMD.h:1799
Definition ChannelMap.h:281
Definition FilterComponents.h:66
double cosOmega
Definition FilterComponents.h:67
static JPL_INLINE BiquadShelf From(double normalizedFrequency)
Definition FilterComponents.h:495
double sqrt2SinOmega
Definition FilterComponents.h:68
Definition FilterComponents.h:59
float gain
Definition FilterComponents.h:62
float sqrtK
Definition FilterComponents.h:61
float t
Definition FilterComponents.h:60
Definition FilterComponents.h:196
std::array< State::DirectFormI, N > States
Definition FilterComponents.h:199
JPL_INLINE Sample Process(float x, const Topology::Cascade< N > &topo)
Definition FilterComponents.h:201
float Sample
Definition FilterComponents.h:197
Definition FilterComponents.h:137
Sample y1
Definition FilterComponents.h:139
JPL_INLINE void Reset()
Definition FilterComponents.h:141
Sample x1
Definition FilterComponents.h:139
float Sample
Definition FilterComponents.h:138
JPL_INLINE Sample Process(Sample x, const Topology::OnePole &onePole)
Definition FilterComponents.h:143
Definition FilterComponents.h:156
Sample y1
Definition FilterComponents.h:158
float Sample
Definition FilterComponents.h:157
JPL_INLINE Sample Process(Sample x, const Topology::Biquad &biquad)
Definition FilterComponents.h:162
Sample x2
Definition FilterComponents.h:158
JPL_INLINE void Reset()
Definition FilterComponents.h:160
Sample x1
Definition FilterComponents.h:158
Sample y2
Definition FilterComponents.h:158
Definition FilterComponents.h:179
float Sample
Definition FilterComponents.h:180
Sample s2
Definition FilterComponents.h:181
JPL_INLINE void Reset()
Definition FilterComponents.h:183
Sample s1
Definition FilterComponents.h:181
JPL_INLINE Sample Process(Sample x, const Topology::Biquad &biquad)
Definition FilterComponents.h:185
Definition FilterComponents.h:101
Sample a2
Definition FilterComponents.h:105
float Sample
Definition FilterComponents.h:102
Sample b1
Definition FilterComponents.h:104
static JPL_INLINE Biquad MakeHighShelf(double normalizedFrequency, double gainDb)
Definition FilterComponents.h:490
Sample a1
Definition FilterComponents.h:105
Sample b0
Definition FilterComponents.h:104
JPL_INLINE void SetCoefficients(const Topology::Biquad &other)
Definition FilterComponents.h:586
Sample b2
Definition FilterComponents.h:104
std::complex< double > CalculateResponse(float normalizedFrequency) const
Definition FilterComponents.h:457
void ProcessInterpolating(std::span< Sample > samples, StateType &state, Biquad &previous) const
Definition FilterComponents.h:534
static Biquad Combine(const OnePole &low, const OnePole &high, float broadbandGain)
Definition FilterComponents.h:479
Definition FilterComponents.h:126
JPL_INLINE std::complex< double > CalculateResponse(double normalizedFrequency) const
Definition FilterComponents.h:593
std::array< Biquad, N > Stages
Definition FilterComponents.h:127
Definition FilterComponents.h:81
Sample b0
Definition FilterComponents.h:84
static JPL_INLINE OnePole MakeHighShelf(float frequency, float sampleRate, float gain)
Definition FilterComponents.h:428
Sample a1
Definition FilterComponents.h:85
float Sample
Definition FilterComponents.h:82
Sample CalculateResponse(float normalizedFrequency) const
Definition FilterComponents.h:390
static JPL_INLINE OnePole MakeLowShelf(float frequency, float sampleRate, float gain)
Definition FilterComponents.h:399
Sample b1
Definition FilterComponents.h:84