Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
two_pole_resonator.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <algorithm>
6#include <cmath>
7
8namespace pulp::signal {
9
44template <typename SampleType = float>
46public:
47 TwoPoleResonatorT() { update(); }
48
49 void set_sample_rate(double sr) {
50 sample_rate_ = sr > 0.0 ? sr : sample_rate_;
51 update();
52 }
53
54 void set_frequency(double hz) {
55 // Above Nyquist the recurrence is still stable but the resonance folds,
56 // so clamp rather than let a pitch envelope sweep into nonsense.
57 frequency_ = std::clamp(hz, 0.0, 0.499 * sample_rate_);
58 update();
59 }
60
62 void set_t60_ms(double ms) {
63 t60_ms_ = std::max(ms, 0.01);
64 update();
65 }
66
67 double frequency() const { return frequency_; }
68 double t60_ms() const { return t60_ms_; }
69
70 void reset() {
71 y1_ = 0;
72 y2_ = 0;
73 }
74
77 bool is_ringing() const {
78 return std::fabs(static_cast<double>(y1_)) > 1e-7 ||
79 std::fabs(static_cast<double>(y2_)) > 1e-7;
80 }
81
82 SampleType process(SampleType excitation) {
83 const SampleType y = static_cast<SampleType>(a1_) * y1_ -
84 static_cast<SampleType>(a2_) * y2_ +
85 static_cast<SampleType>(gain_) * excitation;
86 y2_ = y1_;
87 y1_ = snap_recursion_state(y);
88 return y;
89 }
90
91private:
92 void update() {
93 // T60 in samples -> pole radius: the radius raised to that many samples
94 // must equal -60 dB.
95 const double samples = std::max(0.001 * t60_ms_ * sample_rate_, 1.0);
96 radius_ = std::pow(10.0, -3.0 / samples);
97 const double theta = 2.0 * 3.14159265358979323846 * frequency_ / sample_rate_;
98 a1_ = 2.0 * radius_ * std::cos(theta);
99 a2_ = radius_ * radius_;
100 // Floor the gain so a body tuned to DC still passes its excitation
101 // rather than muting it.
102 gain_ = std::max(std::sin(theta), 1e-4);
103 }
104
105 double sample_rate_ = 44100.0;
106 double frequency_ = 100.0;
107 double t60_ms_ = 300.0;
108
109 double radius_ = 0.0;
110 double a1_ = 0.0;
111 double a2_ = 0.0;
112 double gain_ = 1.0;
113
114 SampleType y1_{};
115 SampleType y2_{};
116};
117
120
121} // namespace pulp::signal
A single decaying resonance: one complex pole pair, excited by whatever the caller feeds it.
Definition two_pole_resonator.hpp:45
double frequency() const
Definition two_pole_resonator.hpp:67
TwoPoleResonatorT()
Definition two_pole_resonator.hpp:47
void set_t60_ms(double ms)
Time for the ring to fall by 60 dB.
Definition two_pole_resonator.hpp:62
void reset()
Definition two_pole_resonator.hpp:70
SampleType process(SampleType excitation)
Definition two_pole_resonator.hpp:82
void set_sample_rate(double sr)
Definition two_pole_resonator.hpp:49
bool is_ringing() const
Whether the ring is still above the denormal floor.
Definition two_pole_resonator.hpp:77
void set_frequency(double hz)
Definition two_pole_resonator.hpp:54
double t60_ms() const
Definition two_pole_resonator.hpp:68
Definition additive_bank.hpp:218
TwoPoleResonatorT< float > TwoPoleResonator
Definition two_pole_resonator.hpp:118
TwoPoleResonatorT< double > TwoPoleResonator64
Definition two_pole_resonator.hpp:119
@ e
Definition formant_filter_bank.hpp:20
T snap_recursion_state(T x)
Snap a per-sample recursion's state: snap_to_zero() where subnormals are slow, the identity where kSu...
Definition denormal.hpp:84