15#include <pulp/signal/detail/simd_lanes.hpp>
27namespace lanes = signal::detail::lanes;
55 fast_ =
snap_to_zero(fast_ + (magnitude - fast_) * fast_coeff_);
56 slow_ =
snap_to_zero(slow_ + (magnitude - slow_) * slow_coeff_);
59 return gain_for(fast_, slow_);
67 template <
typename SampleType>
double process_block(SampleType* l, SampleType* r,
int n) {
71 for (
int k = 0; k < n; ++k) {
72 const double magnitude =
73 0.5 * (std::abs(
static_cast<double>(l[k])) + std::abs(
static_cast<double>(r[k])));
74 fast += (magnitude - fast) * fast_coeff_;
75 slow += (magnitude - slow) * slow_coeff_;
76 gain = gain_for(fast, slow);
77 l[k] =
static_cast<SampleType
>(
static_cast<double>(l[k]) * gain);
78 r[k] =
static_cast<SampleType
>(
static_cast<double>(r[k]) * gain);
95 static double gain_for(
double fast,
double slow) {
96 const double ratio = (fast - slow) / std::max(slow, 1
e-9);
98 const double smooth = t * t * (3.0 - 2.0 * t);
102 static double coeff_for(
double ms,
double sample_rate) {
103 const double n = std::max(1.0, ms * 0.001 * sample_rate);
104 return 1.0 - std::exp(-1.0 / n);
107 double fast_coeff_ = 0.0;
108 double slow_coeff_ = 0.0;
122template <
typename SampleType =
float>
137 const double t =
static_cast<double>(
i) /
141 lfo_.set_rate(
i, hz, sample_rate);
164 static_assert(
kNumEnsembleLfos == 6,
"the tap grouping below is for six LFOs");
168 left_.push_block(l, n);
169 right_.push_block(r, n);
170 const SampleType* rails[2] = {left_.taps_at(0), right_.taps_at(0)};
171 const I2 mask = lanes::splat_i(
static_cast<std::int64_t
>(left_.mask()));
172 const std::size_t end = left_.write_index();
173 const D2 base = lanes::splat(base_samples_);
174 const D2 depth = lanes::splat(depth_samples_);
175 const D2 lowest = lanes::splat(2.0);
176 const D2 highest = lanes::splat(
static_cast<double>(left_.max_delay()));
177 const F4 half = lanes::splat(0.5f);
178 const F4 two_and_half = lanes::splat(2.5f);
179 const F4 two = lanes::splat(2.0f);
180 const F4 one_and_half = lanes::splat(1.5f);
181 const F4 one = lanes::splat(1.0f);
184 for (
int k = 0; k < n; ++k) {
188 const I2 origin = lanes::splat_i(
189 static_cast<std::int64_t
>(end -
static_cast<std::size_t
>(n - 1 - k)) - 2);
190 alignas(16) std::int64_t start[12];
192 for (
int p = 0; p < 6; ++p) {
194 const D2 phase = p < 3 ? lanes::make(lfo_.sin(
i), lfo_.sin(
i + 1))
195 : lanes::make(lfo_.cos(
i), lfo_.cos(
i + 1));
196 const D2 d = lanes::clamp(base + depth * phase, lowest, highest);
197 const I2 whole = lanes::to_int(d);
198 f[p] = d - lanes::to_double(whole);
199 const I2 at = (origin - whole) & mask;
200 start[2 * p] = lanes::lane(at, 0);
201 start[2 * p + 1] = lanes::lane(at, 1);
204 for (
int g = 0; g < 3; ++g) {
205 F4 ym1 = lanes::load(rails[(4 * g) / 6] + start[4 * g]);
206 F4 y0 = lanes::load(rails[(4 * g + 1) / 6] + start[4 * g + 1]);
207 F4 y1 = lanes::load(rails[(4 * g + 2) / 6] + start[4 * g + 2]);
208 F4 y2 = lanes::load(rails[(4 * g + 3) / 6] + start[4 * g + 3]);
209 lanes::transpose(ym1, y0, y1, y2);
210 const F4 t = one - lanes::narrow(f[2 * g], f[2 * g + 1]);
211 const F4
c1 = half * (y1 - ym1);
212 const F4
c2 = ym1 - two_and_half * y0 + two * y1 - half * y2;
213 const F4
c3 = half * (y2 - ym1) + one_and_half * (y0 - y1);
214 out[g] = ((
c3 * t +
c2) * t +
c1) * t + y0;
216 SampleType sum_l = 0;
217 SampleType sum_r = 0;
219 sum_l += lanes::lane(out[tap / 4], tap % 4);
221 sum_r += lanes::lane(out[tap / 4], tap % 4);
222 l[k] = l[k] * dry + sum_l * wet;
223 r[k] = r[k] * dry + sum_r * wet;
231 double base_samples_ = 0.0;
232 double depth_samples_ = 0.0;
239 const double mid = 0.5 * (l + r);
240 const double side = 0.5 * (l - r) *
width;
260 const float c = std::clamp(x, -9.0f, 9.0f);
261 const float s =
c *
c;
263 (((1.3810137e-08f * s + 2.0946818e-05f) * s + 3.5165233e-03f) * s + 1.3398798e-01f) * s +
266 (((7.9656114e-07f * s + 3.3195784e-04f) * s + 2.5957071e-02f) * s + 4.6732125e-01f) * s +
268 return std::clamp(
c * p / q, -1.0f, 1.0f);
Definition stages.hpp:123
void prepare(double max_sample_rate)
Definition stages.hpp:125
void process_block(SampleType *l, SampleType *r, int n)
Definition stages.hpp:163
void process(SampleType &l, SampleType &r)
Definition stages.hpp:153
void configure(double sample_rate)
Definition stages.hpp:133
void reset()
Definition stages.hpp:145
Definition frac_delay.hpp:36
Definition modulation.hpp:103
void reset()
Definition stages.hpp:47
double slow_envelope() const
Definition stages.hpp:92
double process_block(SampleType *l, SampleType *r, int n)
Definition stages.hpp:67
void configure(double sample_rate)
Definition stages.hpp:42
double fast_envelope() const
Definition stages.hpp:91
double process(double magnitude)
Definition stages.hpp:53
void heal()
Definition stages.hpp:86
Scalar math helpers for real-time audio DSP.
constexpr double kDuckFastMs
Definition config.hpp:149
constexpr double kWetLimiterHeadroom
Definition config.hpp:164
constexpr double kEnsembleRateMaxHz
Definition config.hpp:158
T finite_or_zero(T value)
Definition sanitize.hpp:12
constexpr double kDuckKnee
Definition config.hpp:152
constexpr double kEnsembleRateMinHz
Definition config.hpp:157
constexpr int kNumEnsembleLfos
Definition config.hpp:22
void apply_width(double &l, double &r, double width)
Definition stages.hpp:238
constexpr double kEnsembleWetMix
Definition config.hpp:159
@ width
Definition config.hpp:225
constexpr double kEnsembleBaseMs
Definition config.hpp:155
constexpr double kDuckAmount
Definition config.hpp:151
constexpr int kHermiteGuard
Definition config.hpp:171
constexpr int kMaxBlockSamples
Definition config.hpp:190
constexpr double kEnsembleDepthMs
Definition config.hpp:156
constexpr double kDuckSlowMs
Definition config.hpp:150
float tanh_rational(float x)
Definition stages.hpp:259
double soft_limit(double x)
Definition stages.hpp:251
double lipschitz_tanh(double v) noexcept
A [3/2] Padé tanh that is 1-LIPSCHITZ and bounded — the saturator to reach for inside a feedback loop...
Definition fast_math.hpp:710
@ c
Definition spectrum_trace.hpp:35
@ c3
Full chorus. The lush one.
Definition scanner_vibrato.hpp:28
@ c2
Medium chorus.
Definition scanner_vibrato.hpp:27
@ c1
Shallow chorus — scanner plus dry.
Definition scanner_vibrato.hpp:26
T snap_to_zero(T x)
Returns 0 if |x| < snap_threshold, else x.
Definition denormal.hpp:45
@ ratio
base_frequency_hz * ratio.
Definition fm_operator_engine.hpp:20
@ i
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20