121#include "../Core/RingBuffer.h"
122#include "../Core/DryWetMixer.h"
123#include "../Core/DspMath.h"
124#include "../Core/AudioSpec.h"
125#include "../Core/AudioBuffer.h"
126#include "../Core/DenormalGuard.h"
127#include "../Core/Biquad.h"
128#include "../Core/StateBlob.h"
147template <FloatType T>
199 static_cast<T
>(sr) *
preDelayMs_.load(std::memory_order_relaxed) / T(1000)),
200 std::memory_order_relaxed);
202 static_cast<T
>(sr) *
erToLateMs_.load(std::memory_order_relaxed) / T(1000)),
203 std::memory_order_relaxed);
206 erBuf_.prepare(
static_cast<int>(sr * 0.2) + 1);
209 int maxDiff =
static_cast<int>(sr * 0.012) + 1;
211 buf.prepare(maxDiff);
213 int maxFDN =
static_cast<int>(sr * 0.5) + 1;
218 int maxParAP =
static_cast<int>(sr * 0.021) + 1;
219 for (
auto& buf :
parAPBufs_) buf.prepare(maxParAP);
222 int maxDiffS2 =
static_cast<int>(sr * 0.047) + 1;
226 int maxFbAP =
static_cast<int>(sr * 0.06) + 1;
227 for (
auto& buf :
fbAPBufsA_) buf.prepare(maxFbAP);
228 for (
auto& buf :
fbAPBufsB_) buf.prepare(maxFbAP);
231 int maxIntAP =
static_cast<int>(sr * 0.035) + 1;
232 for (
auto& buf :
intAPBufsA_) buf.prepare(maxIntAP);
233 for (
auto& buf :
intAPBufsB_) buf.prepare(maxIntAP);
236 int maxOutDiff =
static_cast<int>(sr * 0.003) + 1;
238 buf.prepare(maxOutDiff);
240 buf.prepare(maxOutDiff);
243 T rate =
modRate_.load(std::memory_order_relaxed);
246 modLFOA_[i].prepare(sr, rate * (T(0.7) + T(0.05) *
static_cast<T
>(i)),
247 static_cast<uint32_t
>(i * 7919 + 1));
248 modLFOB_[i].prepare(sr, rate * (T(1.8) + T(0.11) *
static_cast<T
>(i)),
249 static_cast<uint32_t
>(i * 6271 + 31337));
253 dcCoeff_ = T(1) - std::exp(T(-6.283185307179586) * T(23)
254 /
static_cast<T
>(sr));
257 noiseCoeff_ = T(1) - std::exp(T(-6.283185307179586) * T(3)
258 /
static_cast<T
>(sr));
263 /
static_cast<T
>(sr));
276 toneDirty_.store(
false, std::memory_order_relaxed);
292 const int nCh = std::min(buffer.getNumChannels(), 2);
293 const int nS = buffer.getNumSamples();
294 if (nCh == 0 || nS == 0)
return;
301 for (
int ch = 0; ch < nCh; ++ch)
303 T* d = buffer.getChannel(ch);
304 for (
int i = 0; i < nS; ++i)
305 if (!std::isfinite(d[i])) d[i] = T(0);
313 for (
int i = 0; i < nS; ++i)
318 T L = buffer.getChannel(0)[i];
319 T R = buffer.getChannel(1)[i];
321 T env = std::abs(L) + std::abs(R);
323 if (std::abs(sum) < T(1e-5) * env)
327 monoIn = (L - R) * T(0.5);
331 monoIn = sum * T(0.5);
336 monoIn = buffer.getChannel(0)[i];
343 buffer.getChannel(0)[i] = outL;
344 buffer.getChannel(1)[i] = outR;
348 buffer.getChannel(0)[i] = (outL + outR) * T(0.5);
352 mixer_.mixWet(buffer,
mix_.load(std::memory_order_relaxed));
371 if (!std::isfinite(input)) input = T(0);
410 for (
auto& lfo :
modLFOA_) lfo.reset();
411 for (
auto& lfo :
modLFOB_) lfo.reset();
428 const int t = std::clamp(
static_cast<int>(type), 0,
430 type_.store(
static_cast<Type>(t), std::memory_order_relaxed);
469 std::memory_order_relaxed);
475 if (!std::isfinite(seconds))
return;
476 decayTime_.store(std::clamp(seconds, T(0.1), T(30)),
477 std::memory_order_relaxed);
484 if (!std::isfinite(dryWet))
return;
485 mix_.store(std::clamp(dryWet, T(0), T(1)), std::memory_order_relaxed);
494 if (!std::isfinite(size))
return;
495 size_.store(std::clamp(size, T(0.01), T(1)), std::memory_order_relaxed);
508 if (!std::isfinite(amount))
return;
509 T clamped = std::clamp(amount, T(0), T(1));
510 damping_.store(clamped, std::memory_order_relaxed);
511 highDecayMult_.store(T(1) - clamped * T(0.9), std::memory_order_relaxed);
518 if (!std::isfinite(ms))
return;
519 T clamped = std::clamp(ms, T(0), T(200));
520 preDelayMs_.store(clamped, std::memory_order_relaxed);
526 std::memory_order_relaxed);
531 if (!std::isfinite(amount))
return;
532 diffusion_.store(std::clamp(amount, T(0), T(1)), std::memory_order_relaxed);
539 if (!std::isfinite(amount))
return;
540 T clamped = std::clamp(amount, T(0), T(1));
541 modDepth_.store(clamped, std::memory_order_relaxed);
542 modDepthA_.store(clamped * T(30), std::memory_order_relaxed);
543 modDepthB_.store(clamped * T(15), std::memory_order_relaxed);
557 if (!std::isfinite(width))
return;
558 width_.store(std::clamp(width, T(0), T(2)), std::memory_order_relaxed);
563 if (!std::isfinite(ms))
return;
564 T clamped = std::clamp(ms, T(0), T(200));
565 erToLateMs_.store(clamped, std::memory_order_relaxed);
570 std::memory_order_relaxed);
588 if (!std::isfinite(mult))
return;
589 T clamped = std::clamp(mult, T(0.05), T(1));
591 damping_.store(std::clamp((T(1) - clamped) / T(0.9), T(0), T(1)),
592 std::memory_order_relaxed);
609 if (!std::isfinite(mult))
return;
611 std::memory_order_relaxed);
631 if (!std::isfinite(hz))
return;
633 std::memory_order_relaxed);
644 if (!std::isfinite(hz))
return;
646 std::memory_order_relaxed);
657 if (!std::isfinite(dB))
return;
664 if (!std::isfinite(dB))
return;
671 if (!std::isfinite(hz))
return;
672 modRate_.store(std::clamp(hz, T(0.1), T(5)), std::memory_order_relaxed);
683 if (!std::isfinite(hz))
return;
686 toneDirty_.store(
true, std::memory_order_release);
695 if (!std::isfinite(hz))
return;
697 toneDirty_.store(
true, std::memory_order_release);
704 [[nodiscard]]
Type getType() const noexcept {
return type_.load(std::memory_order_relaxed); }
707 [[nodiscard]] T
getMix() const noexcept {
return mix_.load(std::memory_order_relaxed); }
710 [[nodiscard]] T
getWidth() const noexcept {
return width_.load(std::memory_order_relaxed); }
716 [[nodiscard]] std::vector<uint8_t>
getState()
const
719 w.
write(
"type",
static_cast<int32_t
>(
type_.load(std::memory_order_relaxed)));
720 w.
write(
"quality",
static_cast<int32_t
>(
quality_.load(std::memory_order_relaxed)));
722 w.
write(
"size",
size_.load(std::memory_order_relaxed));
723 w.
write(
"damping",
damping_.load(std::memory_order_relaxed));
726 w.
write(
"modRate",
modRate_.load(std::memory_order_relaxed));
729 w.
write(
"mix",
mix_.load(std::memory_order_relaxed));
730 w.
write(
"width",
width_.load(std::memory_order_relaxed));
735 w.
write(
"earlyDb",
static_cast<float>(20.0 * std::log10(std::max(
736 static_cast<double>(
earlyLevel_.load(std::memory_order_relaxed)), 1e-6))));
737 w.
write(
"lateDb",
static_cast<float>(20.0 * std::log10(std::max(
738 static_cast<double>(
lateLevel_.load(std::memory_order_relaxed)), 1e-6))));
768 const float lo = r.
read(
"toneLowCut", -1.0f);
769 const float hi = r.
read(
"toneHighCut", -1.0f);
800 1, -1, 1, 1, -1, 1, -1, -1,
801 1, -1, -1, 1, -1, 1, 1, -1
804 1, 1, -1, 1, 1, -1, -1, -1,
805 -1, 1, -1, 1, -1, -1, 1, 1
818 29.7, 34.1, 39.3, 45.2, 52.0, 58.1, 64.9, 72.3,
819 80.4, 89.0, 98.3, 108.7, 119.9, 132.3, 145.7, 160.1
824 1.03, 1.47, 2.19, 3.13, 4.23, 5.59, 7.19, 9.47
828 0.75, 0.75, 0.72, 0.72, 0.70, 0.70, 0.68, 0.68
840 5.3, 6.1, 7.1, 7.9, 8.9, 9.7, 10.7, 11.7,
841 12.3, 13.3, 14.3, 15.1, 16.1, 17.1, 18.1, 19.1
846 15.7, 17.9, 20.1, 22.3, 24.7, 26.9, 29.3, 31.1,
847 33.7, 35.3, 37.1, 39.3, 41.1, 42.9, 44.3, 45.7
872 void prepare(
double sr, T rate, uint32_t seed)
noexcept
876 h0_ = nextRandom();
h1_ = nextRandom();
877 h2_ = nextRandom();
h3_ = nextRandom();
908 T c1 = T(0.5) * (
h2_ -
h0_);
911 return ((c3 * d + c2) * d + c1) * d + c0;
917 T nextRandom() noexcept
922 return static_cast<T
>(
state_)
923 /
static_cast<T
>(0xFFFFFFFFu) * T(2) - T(1);
950 std::array<T, kFDNSize>
dcZ_ {};
1116 T white =
static_cast<T
>(
static_cast<int32_t
>(
noiseState_))
1127 T white =
static_cast<T
>(
static_cast<int32_t
>(
noiseState_))
1135 T delayed = buf.read(delay);
1136 T temp = input + coeff * delayed;
1137 T output = delayed - coeff * temp;
1143 T coeff, T input,
bool linearInterp)
noexcept
1145 T readPos =
static_cast<T
>(baseDelay) + modAmount;
1146 readPos = std::max(readPos, T(1));
1150 T delayed = linearInterp
1151 ? buf.template readInterpolated<InterpMethod::Linear>(readPos)
1152 : buf.readInterpolated(readPos);
1153 T temp = input + coeff * delayed;
1154 T output = delayed - coeff * temp;
1167 for (
int i = 0; i <
kFDNSize; i += 2)
1168 { T a = x[i], b = x[i+1]; x[i] = a + b; x[i+1] = a - b; }
1170 for (
int i = 0; i <
kFDNSize; i += 4)
1171 for (
int j = 0; j < 2; ++j)
1172 { T a = x[i+j], b = x[i+j+2]; x[i+j] = a + b; x[i+j+2] = a - b; }
1174 for (
int i = 0; i <
kFDNSize; i += 8)
1175 for (
int j = 0; j < 4; ++j)
1176 { T a = x[i+j], b = x[i+j+4]; x[i+j] = a + b; x[i+j+4] = a - b; }
1178 for (
int j = 0; j < 8; ++j)
1179 { T a = x[j], b = x[j+8]; x[j] = a + b; x[j+8] = a - b; }
1181 for (
auto& v : x) v *= T(0.25);
1191 for (
int i = 0; i < 8; i += 2)
1192 { T a = x[i], b = x[i+1]; x[i] = a + b; x[i+1] = a - b; }
1194 for (
int i = 0; i < 8; i += 4)
1195 for (
int j = 0; j < 2; ++j)
1196 { T a = x[i+j], b = x[i+j+2]; x[i+j] = a + b; x[i+j+2] = a - b; }
1198 for (
int j = 0; j < 4; ++j)
1199 { T a = x[j], b = x[j+4]; x[j] = a + b; x[j+4] = a - b; }
1201 constexpr T norm = T(0.35355339059327373);
1202 for (
int i = 0; i < 8; ++i) x[i] *= norm;
1215 for (
int i = 0; i < n; ++i) sum += x[i];
1216 T factor = sum * T(2) /
static_cast<T
>(n);
1217 for (
int i = 0; i < n; ++i) x[i] -= factor;
1249 &&
qualityDirty_.exchange(
false, std::memory_order_acquire))
1268 &&
presetDirty_.exchange(
false, std::memory_order_acquire))
1270 Type t =
type_.load(std::memory_order_relaxed);
1281 &&
paramsDirty_.exchange(
false, std::memory_order_acquire))
1291 T md =
modDepth_.load(std::memory_order_relaxed);
1292 modDepthA_.store(md * T(30), std::memory_order_relaxed);
1293 modDepthB_.store(md * T(15), std::memory_order_relaxed);
1296 T damp = std::clamp((T(1) - hd) / T(0.9), T(0), T(1));
1297 damping_.store(damp, std::memory_order_relaxed);
1301 if (
toneDirty_.load(std::memory_order_acquire)
1302 &&
toneDirty_.exchange(
false, std::memory_order_acquire))
1315 static_cast<double>(std::clamp(hpHz, T(20), T(500)))));
1326 static_cast<double>(std::clamp(lpHz, T(2000), T(16000)))));
1357 T delayed = (preDelSamp > 0)
1361 T diffused = delayed;
1365 T diffPol = (d & 1) ? T(-1) : T(1);
1366 T diffMod = diffNoise * T(2) * diffPol;
1374 T earlyL = T(0), earlyR = T(0);
1390 T fdnInputRaw = (erToLateSamp > 0)
1397 std::array<T, kFDNSize> diffCh {};
1398 for (
int d = 0; d < n; ++d)
1429 for (
int d = 0; d < n; ++d)
1440 for (
int d = 0; d < n; ++d)
1450 std::array<T, kFDNSize> reads {};
1451 for (
int d = 0; d < n; ++d)
1454 T polarity = (d & 1) ? T(-1) : T(1);
1458 readPos = std::max(readPos, T(1));
1463 int readInt =
static_cast<int>(readPos);
1464 T frac = readPos -
static_cast<T
>(readInt);
1476 std::array<T, kFDNSize> mixed = reads;
1480 for (
int d = 0; d < n; ++d)
1516 T exceed = std::max(T(0), val - T(1)) - std::max(T(0), T(-1) - val);
1523 val += exceed / (T(1) + std::abs(exceed));
1542 T lateL = T(0), lateR = T(0);
1543 for (
int d = 0; d < n; ++d)
1545 lateL += reads[d] *
static_cast<T
>(
kOutSignL_[d]);
1546 lateR += reads[d] *
static_cast<T
>(
kOutSignR_[d]);
1554 int posL = std::max(1,
static_cast<int>(
1556 int posR = std::max(1,
static_cast<int>(
1565 for (
int d = 0; d <
kFDNSize; d += 2)
1572 for (
int d = 1; d <
kFDNSize; d += 2)
1581 for (
int d = 0; d <
kFDNSize; d += 2)
1588 for (
int d = 1; d <
kFDNSize; d += 2)
1598 lateL *= lateLvl * outNorm;
1599 lateR *= lateLvl * outNorm;
1612 T mid = (lateL + lateR) * T(0.5);
1613 T side = (lateL - lateR) * T(0.5);
1620 T outL = earlyL + lateL;
1621 T outR = earlyR + lateR;
1635 return { outL, outR };
1657 T decay =
decayTime_.load(std::memory_order_relaxed);
1664 T t60High = decay * hdMult;
1665 T t60Bass = decay * bdMult;
1667 t60High = std::max(t60High, T(0.05));
1668 t60Bass = std::max(t60Bass, T(0.05));
1680 const double fc = std::clamp(
1681 static_cast<double>(
highCrossover_.load(std::memory_order_relaxed)),
1683 const double K = std::tan(3.14159265358979323846 * fc / fsD);
1690 if (M < T(1)) M = T(1);
1693 T gMid = std::pow(T(0.001), M / (t60Mid * sr));
1694 T gHigh = std::pow(T(0.001), M / (t60High * sr));
1695 T gBass = std::pow(T(0.001), M / (t60Bass * sr));
1701 const double gM = std::max(
static_cast<double>(gMid), 1e-12);
1702 const double gH = std::clamp(
static_cast<double>(gHigh), 1e-12, gM);
1703 const double ratio = std::sqrt(gM / gH);
1704 const double w1 = K * ratio;
1705 const double w2 = K / ratio;
1706 const double inv = 1.0 / (1.0 + w2);
1707 absB0_[d] =
static_cast<T
>(gH * (1.0 + w1) * inv);
1708 absB1_[d] =
static_cast<T
>(gH * (w1 - 1.0) * inv);
1709 absA1_[d] =
static_cast<T
>((w2 - 1.0) * inv);
1717 const T maxBassRatio = T(0.98) / std::max(gMid, T(1e-6));
1723 bassLPCoeff_ = T(1) - std::exp(T(-6.283185307179586) * bassCut / sr);
1730 double sz = 0.35 + 0.65 *
static_cast<double>(
1731 size_.load(std::memory_order_relaxed));
1737 const int srcIdx =
eco_ ? std::min(d * 2,
kFDNSize - 1) : d;
1738 int raw = std::max(1,
static_cast<int>(
1782 T diff =
diffusion_.load(std::memory_order_relaxed);
1794 T rate =
modRate_.load(std::memory_order_relaxed);
1797 modLFOA_[i].setRate(rate * (T(0.7) + T(0.05) *
static_cast<T
>(i)),
1799 modLFOB_[i].setRate(rate * (T(1.8) + T(0.11) *
static_cast<T
>(i)),
1832 double ratio = (maxMs > minMs) ? maxMs / minMs : 1.0;
1833 double sqrtN = std::sqrt(
static_cast<double>(
numERTaps_));
1838 ?
static_cast<double>(t) /
static_cast<double>(
numERTaps_ - 1) : 0.0;
1840 double msL = minMs * std::pow(ratio, frac);
1841 erTapsL_[t] = std::max(1,
static_cast<int>(msL * sr / 1000.0));
1843 double jitter = 1.0 + 0.13 * std::sin(
static_cast<double>(t) * 2.39996323);
1844 double msR = msL * jitter;
1845 msR = std::clamp(msR, minMs * 0.8, maxMs * 1.15);
1846 erTapsR_[t] = std::max(1,
static_cast<int>(msR * sr / 1000.0));
1851 double rawGain = std::pow(0.92,
static_cast<double>(t));
1852 erGainsL_[t] =
static_cast<T
>(rawGain / sqrtN);
1853 erGainsR_[t] =
static_cast<T
>(rawGain / sqrtN * 0.95);
1860 T f =
static_cast<T
>(t) /
static_cast<T
>(std::max(1,
numERTaps_ - 1));
1861 T cutoff = T(15000) * std::pow(T(3000) / T(15000), f);
1862 erAbsCoeffs_[t] = T(1) - std::exp(T(-6.283185307179586) * cutoff
1863 /
static_cast<T
>(sr));
1876 static const std::vector<uint8_t> sieve = []
1882 if (s[
static_cast<size_t>(i)])
1884 s[
static_cast<size_t>(j)] = 0;
1892 if (n <= 2)
return 2;
1901 if (n % 2 == 0) ++n;
1904 bool isPrime =
true;
1905 for (
int d = 3; d * d <= n; d += 2)
1906 if (n % d == 0) { isPrime =
false;
break; }
1907 if (isPrime)
return n;
1927 const uint32_t m =
userParamMask_.load(std::memory_order_acquire);
1928 if (!(m &
kUserSize))
size_.store(p.size, std::memory_order_relaxed);
1950 T(5000), T(250), T(0.72), T(0.14), T(1.2),
1951 T(1), T(0.8), T(0),
false,
false});
1956 T(4500), T(200), T(0.84), T(0.22), T(0.55),
1957 T(0.7), T(1), T(15),
false,
false});
1962 T(5000), T(250), T(0.78), T(0.18), T(0.8),
1963 T(0.9), T(0.9), T(8),
false,
false});
1968 T(7000), T(150), T(0.94), T(0.32), T(1.4),
1969 T(0), T(1), T(0),
false,
false});
1975 T(4000), T(200), T(0.38), T(0.08), T(0.35),
1976 T(0.5), T(1), T(0),
false,
false});
1981 T(3500), T(150), T(0.91), T(0.18), T(0.35),
1982 T(0.5), T(1), T(25),
false,
false});
1988 T damp = std::clamp((T(1) - hd) / T(0.9), T(0), T(1));
1989 damping_.store(damp, std::memory_order_relaxed);
1992 T md =
modDepth_.load(std::memory_order_relaxed);
1993 modDepthA_.store(md * T(30), std::memory_order_relaxed);
1994 modDepthB_.store(md * T(15), std::memory_order_relaxed);
2001 T er =
erToLateMs_.load(std::memory_order_relaxed);
2004 std::memory_order_relaxed);
2006 T rate =
modRate_.load(std::memory_order_relaxed);
2010 rate * (T(0.7) + T(0.05) *
static_cast<T
>(i)),
2011 static_cast<uint32_t
>(i * 7919 + 1));
2013 rate * (T(1.8) + T(0.11) *
static_cast<T
>(i)),
2014 static_cast<uint32_t
>(i * 6271 + 31337));
16-line FDN reverb with Jot absorption and 6 presets.
std::array< int, kFDNSize > fbAPDelaysB_
std::array< T, kFDNSize > modACache_
std::array< T, kFDNSize > absState_
static constexpr double kMultiTapFracR_[kNumMultiTaps]
std::array< RingBuffer< T >, kOutDiffStages > outDiffBufsL_
std::array< RingBuffer< T >, kOutDiffStages > outDiffBufsR_
std::array< int, kMaxERTaps > erTapsL_
Quality
Engine quality / CPU cost trade-off (see setQuality()).
@ Eco
Reduced engine, roughly 3-4x cheaper. For constrained targets.
@ Full
Complete 16-line engine. Default; bit-identical to previous releases.
T getHighDecayMultiplier() const noexcept
std::atomic< T > modDepth_
static void householderInPlace(std::array< T, kFDNSize > &x, int n) noexcept
In-place Householder reflection for 16 channels.
T getWidth() const noexcept
static constexpr double kIntAPRatioA_
std::atomic< int > erToLateSamples_
std::array< T, kMaxERTaps > erGainsL_
std::array< RingBuffer< T >, kFDNSize > intAPBufsA_
std::array< T, kFDNSize > bassRatio_
std::array< int, kFDNSize > intAPDelaysA_
std::atomic< T > toneHighCutHz_
static constexpr int kMultiTapLineR_[kNumMultiTaps]
std::atomic< T > modDepthA_
std::pair< T, T > processSampleInternal(T input) noexcept
Core per-sample processing - returns wet {L, R}.
static constexpr double kOutDiffDelaysMsL_[kOutDiffStages]
std::atomic< T > lateLevel_
std::array< RingBuffer< T >, kFDNSize > fdnDelays_
void setQuality(Quality q) noexcept
Selects the engine quality / CPU cost trade-off.
Type getType() const noexcept
std::array< RingBuffer< T >, kFDNSize > parAPBufs_
static constexpr int kMultiTapSignR_[kNumMultiTaps]
std::atomic< T > modRate_
static constexpr T kOutputNormEco
std::array< T, kFDNSize > modBCache_
std::array< int, kFDNSize > fdnDelayLens_
std::array< RingBuffer< T >, kFDNSize > fbAPBufsA_
T nextFilteredNoiseEco() noexcept
T nextFilteredNoise() noexcept
Lowpass-filtered white noise for modulation randomization.
void setToneHighCut(T hz) noexcept
Sets a post-reverb high-cut filter on the wet signal (12 dB/oct).
std::atomic< T > highDecayMult_
static const std::vector< uint8_t > & getPrimeSieve() noexcept
std::pair< T, T > processSample(T input) noexcept
Processes a single sample and returns a stereo pair.
void generateERTapsForType(Type type) noexcept
static constexpr int kMultiTapLineL_[kNumMultiTaps]
void refreshCachedParams() noexcept
Pulls the atomic parameters into the block-local cache (audio thread).
static constexpr int kPrimeTableMax
void setDiffusion(T amount) noexcept
void setType(Type type) noexcept
void applyPreset(Type type)
std::array< T, kMaxERTaps > erGainsR_
RingBuffer< T > preDelayBuf_
void setEarlyLevel(T dB) noexcept
static constexpr int kMaxERTaps
Quality getQuality() const noexcept
Biquad< T, 2 > toneLPBiquad_
static constexpr int kEcoCtrlInterval
modulation refresh period (samples)
std::array< T, kMaxERTaps > erLPStateL_
static constexpr double kDiffDelaysMs_[kDiffStages]
static constexpr double kBaseDelaysMs_[kFDNSize]
RingBuffer< T > erToLateBuf_
std::array< RingBuffer< T >, kFDNSize > fbAPBufsB_
T getDecay() const noexcept
static constexpr int kOutSignL_[kFDNSize]
std::atomic< bool > paramsDirty_
std::array< int, kFDNSize > intAPDelaysB_
std::atomic< T > toneLowCutHz_
static int nearestPrime(int n) noexcept
std::array< T, kFDNSize > absX1_
T getBassDecayMultiplier() const noexcept
static constexpr int kOutSignR_[kFDNSize]
void setModulation(T amount) noexcept
T getHighCrossover() const noexcept
std::array< T, kFDNSize > absB1_
std::array< int, kOutDiffStages > outDiffDelaysL_
std::atomic< T > erToLateMs_
void setBassCrossover(T hz) noexcept
Frequency below which bass decay multiplier applies.
void setPreDelay(T ms) noexcept
static constexpr int kDiffStages
void updateDelayLengths() noexcept
std::array< T, kFDNSize > absB0_
void setMix(T dryWet) noexcept
void drainPendingChanges() noexcept
Drains deferred parameter changes on the audio thread.
void updateDiffCoeffs() noexcept
std::array< T, kFDNSize > apInterpState_
void reset() noexcept
Resets all internal delay buffers, filters, and LFO phases.
std::atomic< T > earlyLevel_
static void hadamard8InPlace(std::array< T, kFDNSize > &x) noexcept
In-place Hadamard 8x8 on the first 8 elements (Eco quality).
std::array< int, kDiffStages > diffDelays_
bool setState(const uint8_t *data, size_t size)
Restores parameters from a blob (tolerant; rejects foreign ids).
T getBassCrossover() const noexcept
void updateModulation() noexcept
Biquad< T, 2 > toneHPBiquad_
void setLateLevel(T dB) noexcept
std::array< RingBuffer< T >, kFDNSize > intAPBufsB_
T getMix() const noexcept
std::array< SmoothRandomLFO, kFDNSize > modLFOA_
void setToneLowCut(T hz) noexcept
Sets a post-reverb low-cut filter on the wet signal (12 dB/oct).
std::vector< uint8_t > getState() const
Serializes the parameter state (setup/UI threads; allocates).
static constexpr double kIntAPRatioB_
CachedParams cachedParams_
std::atomic< T > bassDecayMult_
std::atomic< bool > toneDirty_
std::atomic< T > diffusion_
void setDamping(T amount) noexcept
Sets high-frequency damping (0 = bright, 1 = dark).
static constexpr int kMultiTapSignL_[kNumMultiTaps]
static constexpr T kInputGain
std::array< T, kMaxERTaps > erAbsCoeffs_
static constexpr T kOutputNorm
T processAllpassModulated(RingBuffer< T > &buf, int baseDelay, T modAmount, T coeff, T input, bool linearInterp) noexcept
void setErToLateDelay(T ms) noexcept
void processBlock(AudioBufferView< T > buffer) noexcept
Processes an audio block in-place with zero allocations.
void markUserParam(uint32_t bit) noexcept
std::atomic< uint32_t > userParamMask_
void setSize(T size) noexcept
std::atomic< bool > presetDirty_
std::array< T, kDiffStages > diffCoeffs_
void commitPreset(const PresetValues &p) noexcept
std::array< T, kFDNSize > dcZ_
std::atomic< T > decayTime_
void setModRate(T hz) noexcept
std::array< int, kFDNSize > parAPDelays_
std::array< int, kFDNSize > fbAPDelaysA_
static constexpr int kNumMultiTaps
static constexpr int kOutDiffStages
void updateDecayParams() noexcept
Computes per-line Jot absorption filter coefficients.
void setWidth(T width) noexcept
Sets stereo width of the late reverb tail.
std::array< T, kFDNSize > absA1_
@ Chamber
Recording studio chamber, warm, balanced.
@ Spring
Spring reverb, bouncy vintage character.
@ Cathedral
Large cathedral, immense decay, vast space.
@ Hall
Concert hall, spacious, long smooth tail.
@ Plate
Metal plate, dense shimmer, no early reflections.
@ Room
Small room, short decay, dense close reflections.
std::array< RingBuffer< T >, kDiffStages > diffBufs_
std::array< int, kOutDiffStages > outDiffDelaysR_
std::atomic< T > bassCrossover_
static constexpr double kParAPDelaysMs_[kFDNSize]
~AlgorithmicReverb()=default
std::array< SmoothRandomLFO, kFDNSize > modLFOB_
std::atomic< T > damping_
void setHighDecayMultiplier(T mult) noexcept
Sets HF decay as a multiplier of mid decay time.
void generateERTaps(double minMs, double maxMs, int numTaps) noexcept
std::array< T, kFDNSize > bassState_
static constexpr double kMultiTapFracL_[kNumMultiTaps]
std::atomic< Quality > quality_
static void hadamardInPlace(std::array< T, kFDNSize > &x) noexcept
In-place Hadamard 16x16 via Fast Walsh-Hadamard butterfly.
static constexpr double kDiffBaseCoeffs_[kDiffStages]
void setHighCrossover(T hz) noexcept
Frequency where the HF decay transition is centered.
void prepare(const AudioSpec &spec)
Prepares the reverberation engine and allocates required memory.
void setDecay(T seconds) noexcept
static constexpr T intAPCoeff_
static constexpr int kEcoLines
FDN lines in Eco quality.
std::atomic< T > modDepthB_
static constexpr double kDiffuserStep2Ms_[kFDNSize]
std::atomic< T > highCrossover_
std::array< T, kMaxERTaps > erLPStateR_
std::atomic< Type > type_
std::array< RingBuffer< T >, kFDNSize > diffuserStep2_
T processAllpass(RingBuffer< T > &buf, int delay, T coeff, T input) noexcept
std::array< int, kMaxERTaps > erTapsR_
std::array< T, kFDNSize > prevFeedback_
static constexpr double kOutDiffDelaysMsR_[kOutDiffStages]
void setBassDecayMultiplier(T mult) noexcept
Sets bass decay as a multiplier of mid decay time.
std::atomic< bool > qualityDirty_
static constexpr double kFbAPRatioB_
std::atomic< int > preDelaySamples_
static constexpr int kFbAPStages
std::array< int, kFDNSize > diffuserStep2Delays_
std::atomic< T > preDelayMs_
static constexpr double kFbAPRatioA_
static constexpr int kFDNSize
static constexpr int kEcoERTaps
early-reflection tap cap in Eco
Non-owning view over audio channel data.
Biquad filter using Transposed Direct Form II (TDF-II) with thread-safe updates.
void setCoeffs(const BiquadCoeffs &c) noexcept
Sets the filter coefficients asynchronously.
void reset() noexcept
Resets all per-channel filter states to zero to avoid ringing/clicks.
T processSample(T input, int channel) noexcept
Processes a single sample for a specific channel.
RAII scope guard to disable denormalised (subnormal) floating-point numbers.
Pre-allocated, SIMD-friendly dry/wet blender for real-time audio.
Power-of-two circular buffer with compile-time interpolated read access.
Tolerant reader: missing keys yield defaults, unknown keys are skipped.
float read(const char *key, float defaultValue) const
Reads a float, or defaultValue when the key is absent.
bool isValid() const noexcept
uint32_t processorId() const noexcept
Serializes key/value parameters into a versioned blob.
std::vector< uint8_t > blob() const
Finalizes and returns the blob.
void write(const char *key, float value)
Writes a float parameter.
Main namespace for the DSPark framework.
T decibelsToGain(T dB, T minusInfinityDb=T(-100)) noexcept
Converts a value in decibels to linear gain.
constexpr uint32_t stateId(const char(&tag)[5]) noexcept
Builds a FOURCC processor id, e.g. dspark::stateId("COMP").
Hermite-interpolated random noise generator for organic modulation.
void setRate(T rate, double sr) noexcept
T nextStride(int stride) noexcept
Advances the LFO by stride samples in one call.
void prepare(double sr, T rate, uint32_t seed) noexcept
Describes the audio environment for a DSP processor.
constexpr bool isValid() const noexcept
Checks if the specification contains valid, processable parameters.
double sampleRate
Sample rate in Hz.
static BiquadCoeffs makeHighPass(double sampleRate, double freq, double Q=0.7071067811865476) noexcept
High-pass filter.
static BiquadCoeffs makeLowPass(double sampleRate, double freq, double Q=0.7071067811865476) noexcept
Low-pass filter.