138 prepared_.store(
false, std::memory_order_relaxed);
141 mixMaxStep_ =
static_cast<T
>(1.0 / std::max(1.0, sampleRate_ * 0.02));
149 const double osRate = sampleRate_ *
static_cast<double>(osFactor_);
152 oversampler_ = std::make_unique<Oversampling<T>>(
154 oversampler_->prepare(spec);
158 oversampler_.reset();
165 hysteresis_.assign(
static_cast<size_t>(numChannels_), {});
166 hysteresisN_.assign(
static_cast<size_t>(numChannels_), {});
167 for (
auto& h : hysteresis_)
170 h.setParameters(3.5e5, 2.2e4, 1.6e-3, 2.7e4, 0.17);
172 for (
auto& h : hysteresisN_)
175 h.setParameters(3.5e5, 2.2e4, 1.6e-3, 2.7e4, 0.17);
178 recordHF_.assign(
static_cast<size_t>(numChannels_), {});
179 recordLF_.assign(
static_cast<size_t>(numChannels_), {});
180 playHF_.assign(
static_cast<size_t>(numChannels_), {});
181 playLF_.assign(
static_cast<size_t>(numChannels_), {});
182 headBump_.assign(
static_cast<size_t>(numChannels_), {});
183 overBiasLp_.assign(
static_cast<size_t>(numChannels_), 0.0);
187 outHp_.assign(
static_cast<size_t>(numChannels_), {});
190 for (
auto& h : outHp_)
191 h = PeakSection { hc.b0, hc.b1, hc.b2, hc.a1, hc.a2, 0.0, 0.0 };
194 firState_.assign(
static_cast<size_t>(numChannels_),
195 std::vector<T>(
static_cast<size_t>(kFirRing), T(0)));
197 firTaps_.assign(
static_cast<size_t>(kFirLen), T(0));
201 delayCenter_ = std::max(96,
static_cast<int>(96.0 * sampleRate_ / 48000.0));
203 while (dsz < 2 * delayCenter_ + 8) dsz <<= 1;
204 delayMask_ = dsz - 1;
205 delayRing_.assign(
static_cast<size_t>(numChannels_),
206 std::vector<T>(
static_cast<size_t>(dsz), T(0)));
210 const double dt = 1.0 / sampleRate_;
211 driftA_ = std::exp(-2.0 * std::numbers::pi * 0.10 * dt);
212 scrapeA1_ = std::exp(-2.0 * std::numbers::pi * 90.0 * dt);
213 scrapeA2_ = std::exp(-2.0 * std::numbers::pi * 40.0 * dt);
215 latency_ = (oversampler_ ? oversampler_->getLatency() : 0) + kFirCenter + delayCenter_;
217 while (drySize_ < latency_ + maxBlock_ + 1) drySize_ <<= 1;
218 dryRing_.assign(
static_cast<size_t>(numChannels_),
219 std::vector<T>(
static_cast<size_t>(drySize_), T(0)));
222 prepared_.store(
true, std::memory_order_relaxed);
223 dirty_.store(
true, std::memory_order_release);
230 if (!prepared_.load(std::memory_order_relaxed))
return;
231 for (
auto& h : hysteresis_) h.reset();
232 for (
auto& h : hysteresisN_) h.reset();
233 for (
auto& f : firState_) std::fill(f.begin(), f.end(), T(0));
234 for (
auto& d : delayRing_) std::fill(d.begin(), d.end(), T(0));
235 for (
auto& d : dryRing_) std::fill(d.begin(), d.end(), T(0));
236 for (
auto& s : recordHF_) s.clear();
237 for (
auto& s : recordLF_) s.clear();
238 for (
auto& s : playHF_) s.clear();
239 for (
auto& s : playLF_) s.clear();
240 for (
auto& v : overBiasLp_) v = 0.0;
243 for (
auto& b : headBump_) b.clear();
244 for (
auto& h : outHp_) h.clear();
248 currentMix_ = mix_.load(std::memory_order_relaxed);
252 modPhaseFlut2_ = 0.0;
254 scrapeLp1_ = scrapeLp2_ = 0.0;
256 rngNoise_ = 0x2468beefu;
257 if (oversampler_) oversampler_->reset();
266 if (!std::isfinite(driveDb))
return;
267 driveDb_.store(std::clamp(driveDb, T(-12), T(24)), std::memory_order_relaxed);
268 dirty_.store(
true, std::memory_order_release);
279 if (!std::isfinite(bias))
return;
280 bias_.store(std::clamp(bias, T(0), T(1)), std::memory_order_relaxed);
281 dirty_.store(
true, std::memory_order_release);
299 if (factor < 1 || factor > 16 || (factor & (factor - 1)) != 0)
return;
300 if (factor == osFactor_)
return;
302 if (prepared_.load(std::memory_order_relaxed))
313 const int v = std::clamp(
static_cast<int>(s),
316 speed_.store(v, std::memory_order_relaxed);
317 dirty_.store(
true, std::memory_order_release);
324 const int v = std::clamp(
static_cast<int>(s),
327 standard_.store(v, std::memory_order_relaxed);
328 dirty_.store(
true, std::memory_order_release);
335 if (!std::isfinite(amount))
return;
336 loss_.store(std::clamp(amount, T(0), T(1)), std::memory_order_relaxed);
337 dirty_.store(
true, std::memory_order_release);
344 if (!std::isfinite(amount))
return;
345 headBumpAmt_.store(std::clamp(amount, T(0), T(1)), std::memory_order_relaxed);
346 dirty_.store(
true, std::memory_order_release);
353 if (!std::isfinite(amount))
return;
354 wowFlutter_.store(std::clamp(amount, T(0), T(1)), std::memory_order_relaxed);
361 if (!std::isfinite(dbfs))
return;
362 noiseDb_.store(std::clamp(dbfs, T(-200), T(-20)), std::memory_order_relaxed);
369 if (!std::isfinite(mix))
return;
370 mix_.store(std::clamp(mix, T(0), T(1)), std::memory_order_relaxed);
373 [[nodiscard]] T
getDrive() const noexcept {
return driveDb_.load(std::memory_order_relaxed); }
374 [[nodiscard]] T
getBias() const noexcept {
return bias_.load(std::memory_order_relaxed); }
377 return static_cast<Speed>(speed_.load(std::memory_order_relaxed));
381 return static_cast<Standard>(standard_.load(std::memory_order_relaxed));
383 [[nodiscard]] T
getLossEffects() const noexcept {
return loss_.load(std::memory_order_relaxed); }
384 [[nodiscard]] T
getHeadBump() const noexcept {
return headBumpAmt_.load(std::memory_order_relaxed); }
385 [[nodiscard]] T
getWowFlutter() const noexcept {
return wowFlutter_.load(std::memory_order_relaxed); }
386 [[nodiscard]] T
getNoise() const noexcept {
return noiseDb_.load(std::memory_order_relaxed); }
387 [[nodiscard]] T
getMix() const noexcept {
return mix_.load(std::memory_order_relaxed); }
391 [[nodiscard]]
int getLatency() const noexcept {
return latency_; }
397 [[nodiscard]] std::vector<uint8_t>
getState()
const
402 w.
write(
"drive",
static_cast<float>(driveDb_.load(std::memory_order_relaxed)));
403 w.
write(
"bias",
static_cast<float>(bias_.load(std::memory_order_relaxed)));
404 w.
write(
"speed", speed_.load(std::memory_order_relaxed));
405 w.
write(
"standard", standard_.load(std::memory_order_relaxed));
406 w.
write(
"loss",
static_cast<float>(loss_.load(std::memory_order_relaxed)));
407 w.
write(
"headBump",
static_cast<float>(headBumpAmt_.load(std::memory_order_relaxed)));
408 w.
write(
"wowFlutter",
static_cast<float>(wowFlutter_.load(std::memory_order_relaxed)));
409 w.
write(
"noise",
static_cast<float>(noiseDb_.load(std::memory_order_relaxed)));
410 w.
write(
"mix",
static_cast<float>(mix_.load(std::memory_order_relaxed)));
411 w.
write(
"oversampling", osFactor_);
439 if (!prepared_.load(std::memory_order_relaxed))
return;
442 const int nCh = std::min(buffer.getNumChannels(), numChannels_);
443 const int nS = buffer.getNumSamples();
444 if (nCh == 0 || nS == 0)
return;
453 for (
int ch = 0; ch < nCh; ++ch)
455 T* d = buffer.getChannel(ch);
456 for (
int i = 0; i < nS; ++i)
457 if (!std::isfinite(d[i])) d[i] = T(0);
462 if (dirty_.load(std::memory_order_relaxed)
463 && dirty_.exchange(
false, std::memory_order_acquire))
466 const T mixTarget = mix_.load(std::memory_order_relaxed);
467 const T mixStart = currentMix_;
468 const double noiseAmp = std::pow(10.0,
static_cast<double>(
469 noiseDb_.load(std::memory_order_relaxed)) / 20.0);
470 const bool noiseOn = noiseDb_.load(std::memory_order_relaxed) > T(-120);
471 const double wfDepth =
static_cast<double>(wowFlutter_.load(std::memory_order_relaxed));
474 for (
int ch = 0; ch < nCh; ++ch)
476 const T* in = buffer.getChannel(ch);
477 auto& dry = dryRing_[
static_cast<size_t>(ch)];
479 for (
int i = 0; i < nS; ++i)
481 dry[
static_cast<size_t>(dp)] = in[i];
482 dp = (dp + 1) & (drySize_ - 1);
487 for (
int ch = 0; ch < nCh; ++ch)
489 T* d = buffer.getChannel(ch);
490 auto& hf = recordHF_[
static_cast<size_t>(ch)];
491 auto& lf = recordLF_[
static_cast<size_t>(ch)];
492 for (
int i = 0; i < nS; ++i)
493 d[i] =
static_cast<T
>(hf.process(lf.process(
static_cast<double>(d[i]))));
502 const bool osOn = (oversampler_ !=
nullptr);
503 auto osView = osOn ? oversampler_->upsample(buffer) : buffer;
504 const int osN = osView.getNumSamples();
505 const int phaseStart = biasPhase_;
506 for (
int ch = 0; ch < nCh; ++ch)
508 T* d = osView.getChannel(ch);
509 auto& hp = hysteresis_[
static_cast<size_t>(ch)];
510 auto& hn = hysteresisN_[
static_cast<size_t>(ch)];
511 const double inScale = hScale_;
512 const double outScale = mScale_ * 0.5;
513 const double B = biasAmp_;
514 int phase = phaseStart;
515 for (
int i = 0; i < osN; ++i)
517 const double x = inScale *
static_cast<double>(d[i]);
518 const double c = B * kBiasTable[
static_cast<size_t>(phase)];
519 phase = (phase + 1) & 7;
520 const double mp =
static_cast<double>(hp.processSample(
static_cast<T
>(x + c)));
521 const double mn =
static_cast<double>(hn.processSample(
static_cast<T
>(x - c)));
522 d[i] =
static_cast<T
>(outScale * (mp + mn));
525 biasPhase_ = (phaseStart + osN) & 7;
526 if (osOn) oversampler_->downsample(buffer);
530 for (
int i = 0; i < nS; ++i)
534 const double mod = nextTransportMod(wfDepth);
535 const double readPos =
static_cast<double>(delayCenter_) + mod;
536 const auto readInt =
static_cast<int>(std::floor(readPos));
537 const T frac =
static_cast<T
>(readPos - readInt);
539 for (
int ch = 0; ch < nCh; ++ch)
541 T* d = buffer.getChannel(ch);
544 auto& fir = firState_[
static_cast<size_t>(ch)];
545 fir[
static_cast<size_t>(firPos_)] = d[i];
546 fir[
static_cast<size_t>(firPos_ + kFirRing / 2)] = d[i];
547 const T* win = &fir[
static_cast<size_t>(firPos_ + kFirRing / 2 - (kFirLen - 1))];
551 y = headBump_[
static_cast<size_t>(ch)].process(y);
556 auto& dl = delayRing_[
static_cast<size_t>(ch)];
557 dl[
static_cast<size_t>(delayPos_)] = y;
558 const int base = delayPos_ - readInt;
559 const int dm = delayMask_;
560 const T p0 = dl[
static_cast<size_t>((base + 1) & dm)];
561 const T p1 = dl[
static_cast<size_t>(base & dm)];
562 const T p2 = dl[
static_cast<size_t>((base - 1) & dm)];
563 const T p3 = dl[
static_cast<size_t>((base - 2) & dm)];
564 T w = p1 + T(0.5) * frac * (p2 - p0
565 + frac * (T(2) * p0 - T(5) * p1 + T(4) * p2 - p3
566 + frac * (T(3) * (p1 - p2) + p3 - p0)));
569 w =
static_cast<T
>(playLF_[
static_cast<size_t>(ch)].process(
570 playHF_[
static_cast<size_t>(ch)].process(
static_cast<double>(w))));
575 if (overBiasA_ > 0.0)
577 auto& lp = overBiasLp_[
static_cast<size_t>(ch)];
578 lp += overBiasA_ * (
static_cast<double>(w) - lp);
579 w =
static_cast<T
>(lp);
584 w = outHp_[
static_cast<size_t>(ch)].process(w);
590 rngNoise_ = rngNoise_ * 1664525u + 1013904223u;
591 const double n1 =
static_cast<double>(rngNoise_ >> 8) / 8388608.0 - 1.0;
592 w +=
static_cast<T
>(noiseAmp * n1 * 0.35);
596 const auto& dry = dryRing_[
static_cast<size_t>(ch)];
597 const int dryIdx = (dryPos_ + i - latency_) & (drySize_ - 1);
598 const T drySample = dry[
static_cast<size_t>(dryIdx)];
599 const T mixVal =
moveTowards(mixStart, mixTarget, mixMaxStep_ *
static_cast<T
>(i + 1));
600 d[i] = drySample + (w - drySample) * mixVal;
603 firPos_ = (firPos_ + 1) & (kFirRing / 2 - 1);
604 delayPos_ = (delayPos_ + 1) & delayMask_;
606 dryPos_ = (dryPos_ + nS) & (drySize_ - 1);
607 currentMix_ =
moveTowards(mixStart, mixTarget, mixMaxStep_ *
static_cast<T
>(nS));
616 double b0 = 1.0, b1 = 0.0, a1 = 0.0;
617 double x1 = 0.0, y1 = 0.0;
619 void clear() noexcept { x1 = 0.0; y1 = 0.0; }
620 [[nodiscard]]
double process(
double x)
noexcept
622 const double y = b0 * x + b1 * x1 - a1 * y1;
632 double b0 = 1.0, b1 = 0.0, b2 = 0.0, a1 = 0.0, a2 = 0.0;
633 double z1 = 0.0, z2 = 0.0;
635 void clear() noexcept { z1 = 0.0; z2 = 0.0; }
636 [[nodiscard]] T process(T x)
noexcept
638 const double in =
static_cast<double>(x);
639 const double y = b0 * in + z1;
640 z1 = b1 * in - a1 * y + z2;
641 z2 = b2 * in - a2 * y;
642 return static_cast<T
>(y);
648 static ShelfSection makeHFShelf(
double t,
double kHi,
double fs,
bool inverse)
noexcept
650 const double kt = 2.0 * fs * t;
651 double n0 = kHi * kt + 1.0, n1 = 1.0 - kHi * kt;
652 double d0 = kt + 1.0, d1 = 1.0 - kt;
653 if (inverse) { std::swap(n0, d0); std::swap(n1, d1); }
654 return { n0 / d0, n1 / d0, d1 / d0, 0.0, 0.0 };
657 static ShelfSection makeLFShelf(
double t,
double kLo,
double fs,
bool inverse)
noexcept
659 const double kt = 2.0 * fs * t;
660 double n0 = kt + kLo, n1 = kLo - kt;
661 double d0 = kt + 1.0, d1 = 1.0 - kt;
662 if (inverse) { std::swap(n0, d0); std::swap(n1, d1); }
663 return { n0 / d0, n1 / d0, d1 / d0, 0.0, 0.0 };
667 void recompute() noexcept
669 const double driveDbV =
static_cast<double>(driveDb_.load(std::memory_order_relaxed));
670 const double drive = std::pow(10.0, driveDbV / 20.0);
671 const double bias =
static_cast<double>(bias_.load(std::memory_order_relaxed));
672 const auto speed =
static_cast<Speed>(speed_.load(std::memory_order_relaxed));
673 const auto standard =
static_cast<Standard>(standard_.load(std::memory_order_relaxed));
674 const double lossAmt =
static_cast<double>(loss_.load(std::memory_order_relaxed));
675 const double bumpAmt =
static_cast<double>(headBumpAmt_.load(std::memory_order_relaxed));
687 const double biasB = std::min(6.0, 3.0 * std::pow(9.0, bias - 0.5));
688 biasAmp_ = biasB * 2.2e4;
693 const double fc = 22000.0 * (3.5 / biasB);
694 overBiasA_ = 1.0 - std::exp(-2.0 * std::numbers::pi * fc / sampleRate_);
716 constexpr double kHiCap = 4.0;
717 constexpr double kLoBoost = 2.0;
718 const double tLo = 3180e-6;
728 hScale_ = drive * 1.2 * 2.2e4;
736 const double w1 = 2.0 * std::numbers::pi * 1000.0 * t2;
737 const double emph1k = std::sqrt((1.0 + kHiCap * kHiCap * w1 * w1)
739 const double kCalAmp = 0.25 * emph1k;
741 const double fs4 = sampleRate_ *
static_cast<double>(osFactor_);
744 const int calN =
static_cast<int>(0.024 * fs4);
745 const int calFrom = (calN * 2) / 3;
747 calib_.setParameters(3.5e5, 2.2e4, 1.6e-3, 2.7e4, 0.17);
748 calib2_.prepare(fs4);
749 calib2_.setParameters(3.5e5, 2.2e4, 1.6e-3, 2.7e4, 0.17);
754 const double wCal = 2.0 * std::numbers::pi * 1000.0 / fs4;
755 double outRe = 0.0, outIm = 0.0;
757 for (
int i = 0; i < calN; ++i)
759 const double s = std::sin(wCal * i);
760 const double x = hScale_ * kCalAmp * s;
761 const double c = biasAmp_ * kBiasTable[
static_cast<size_t>(i & 7)];
763 * (
static_cast<double>(calib_.processSample(
static_cast<T
>(x + c)))
764 +
static_cast<double>(calib2_.processSample(
static_cast<T
>(x - c))));
767 outRe += m * std::cos(wCal * i);
768 outIm += m * std::sin(wCal * i);
772 const double fund = 2.0 * std::sqrt(outRe * outRe + outIm * outIm)
774 mScale_ = (fund > 0.0) ? kCalAmp / fund : 1.0;
782 mScale_ *= std::pow(drive, 0.25);
785 for (
int ch = 0; ch < numChannels_; ++ch)
787 auto& rhf = recordHF_[
static_cast<size_t>(ch)];
788 auto& rlf = recordLF_[
static_cast<size_t>(ch)];
789 auto& phf = playHF_[
static_cast<size_t>(ch)];
790 auto& plf = playLF_[
static_cast<size_t>(ch)];
792 const double sx1 = rhf.x1, sy1 = rhf.y1;
793 rhf = makeHFShelf(t2, kHiCap, sampleRate_,
false);
794 rhf.x1 = sx1; rhf.y1 = sy1;
796 const double lx1 = rlf.x1, ly1 = rlf.y1;
797 rlf = useLF ? makeLFShelf(tLo, kLoBoost, sampleRate_,
false) : ShelfSection {};
798 rlf.x1 = lx1; rlf.y1 = ly1;
800 const double px1 = phf.x1, py1 = phf.y1;
801 phf = makeHFShelf(t2, kHiCap, sampleRate_,
true);
802 phf.x1 = px1; phf.y1 = py1;
804 const double qx1 = plf.x1, qy1 = plf.y1;
805 plf = useLF ? makeLFShelf(tLo, kLoBoost, sampleRate_,
true) : ShelfSection {};
806 plf.x1 = qx1; plf.y1 = qy1;
812 const double v = ips * 0.0254;
813 constexpr double gap = 3.0e-6;
814 constexpr double spacing = 0.5e-6;
815 constexpr double thickness = 1.0e-6;
817 constexpr int kGrid = kFirLen + 1;
818 double mags[kGrid / 2 + 1];
819 for (
int kBin = 0; kBin <= kGrid / 2; ++kBin)
821 const double f = kBin * sampleRate_ / kGrid;
825 const double lambda = v / f;
826 const double spacingLoss = std::pow(10.0, -54.6 * (spacing / lambda) / 20.0);
827 const double gx = std::numbers::pi * gap / lambda;
828 const double gapLoss = (gx < 1e-9) ? 1.0
829 : std::abs(std::sin(gx) / gx);
830 const double tx = 4.0 * std::numbers::pi * thickness / lambda;
831 const double thickLoss = (tx < 1e-9) ? 1.0 : (1.0 - std::exp(-tx)) / tx;
832 mag = spacingLoss * gapLoss * thickLoss;
834 mags[kBin] = (1.0 - lossAmt) + lossAmt * mag;
843 else if (f > 19500.0)
844 mags[kBin] *= 0.5 + 0.5 * std::cos(std::numbers::pi * (f - 19500.0) / 2000.0);
846 double taps[kFirLen];
847 for (
int n = 0; n < kFirLen; ++n)
849 double acc = mags[0];
850 for (
int kBin = 1; kBin < kGrid / 2; ++kBin)
851 acc += 2.0 * mags[kBin]
852 * std::cos(2.0 * std::numbers::pi * kBin * (n - kFirCenter)
853 /
static_cast<double>(kGrid));
854 acc += mags[kGrid / 2] * std::cos(std::numbers::pi * (n - kFirCenter));
855 const double hann = 0.5 - 0.5 * std::cos(2.0 * std::numbers::pi * (n + 1)
857 taps[n] = acc * hann / kGrid;
862 const double w1k = 2.0 * std::numbers::pi * 1000.0 / sampleRate_;
863 double re = 0.0, im = 0.0;
864 for (
int n = 0; n < kFirLen; ++n)
866 re += taps[n] * std::cos(w1k * n);
867 im -= taps[n] * std::sin(w1k * n);
869 const double g = std::sqrt(re * re + im * im);
870 const double norm = (g > 1e-9) ? 1.0 / g : 1.0;
871 for (
int n = 0; n < kFirLen; ++n) taps[n] *= norm;
873 for (
int n = 0; n < kFirLen; ++n)
874 firTaps_[
static_cast<size_t>(kFirLen - 1 - n)] =
875 static_cast<T
>(taps[n]);
880 const double bumpDb = 2.5 * bumpAmt;
882 for (
auto& b : headBump_)
884 const double pz1 = b.z1, pz2 = b.z2;
885 b = PeakSection { bc.b0, bc.b1, bc.b2, bc.a1, bc.a2, 0.0, 0.0 };
892 [[nodiscard]]
double nextTransportMod(
double depth)
noexcept
896 const double dt = 1.0 / sampleRate_;
897 modPhaseWow_ += 0.55 * dt;
898 if (modPhaseWow_ >= 1.0) modPhaseWow_ -= 1.0;
899 modPhaseFlut_ += 8.3 * dt;
900 if (modPhaseFlut_ >= 1.0) modPhaseFlut_ -= 1.0;
901 modPhaseFlut2_ += 23.0 * dt;
902 if (modPhaseFlut2_ >= 1.0) modPhaseFlut2_ -= 1.0;
904 rng_ = rng_ * 1664525u + 1013904223u;
905 const double n =
static_cast<double>(rng_ >> 8) / 8388608.0 - 1.0;
908 driftState_ = driftA_ * driftState_ + (1.0 - driftA_) * n * 600.0;
909 const double drift = std::clamp(driftState_, -18.0, 18.0);
912 scrapeLp1_ = scrapeA1_ * scrapeLp1_ + (1.0 - scrapeA1_) * n;
913 scrapeLp2_ = scrapeA2_ * scrapeLp2_ + (1.0 - scrapeA2_) * n;
914 const double scrape = (scrapeLp1_ - scrapeLp2_) * 0.6;
919 const double twoPi = 2.0 * std::numbers::pi;
924 const double rateScale = sampleRate_ / 48000.0;
925 const double wow = 27.8 * std::sin(
twoPi * modPhaseWow_);
926 const double flut = 0.55 * std::sin(
twoPi * modPhaseFlut_);
927 const double flut2 = 0.066 * std::sin(
twoPi * modPhaseFlut2_);
929 return 0.4 * depth * rateScale * (drift + wow + flut + flut2 + scrape);
933 static constexpr int kFirLen = 63;
934 static constexpr int kFirCenter = 31;
935 static constexpr int kFirRing = 128;
938 double sampleRate_ = 48000.0;
939 int numChannels_ = 0;
941 std::atomic<bool> prepared_ {
false };
946 std::unique_ptr<Oversampling<T>> oversampler_;
947 std::vector<Hysteresis<T>> hysteresis_;
948 std::vector<Hysteresis<T>> hysteresisN_;
949 Hysteresis<T> calib_;
950 Hysteresis<T> calib2_;
964 static constexpr double kBiasTable[8] = {
965 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0
968 std::vector<ShelfSection> recordHF_, recordLF_, playHF_, playLF_;
969 std::vector<PeakSection> headBump_;
970 std::vector<PeakSection> outHp_;
971 std::vector<double> overBiasLp_;
973 std::vector<T> firTaps_;
974 std::vector<std::vector<T>> firState_;
977 std::vector<std::vector<T>> delayRing_;
979 int delayCenter_ = 96;
980 int delayMask_ = 255;
981 double driftA_ = 0.0, scrapeA1_ = 0.0, scrapeA2_ = 0.0;
983 std::vector<std::vector<T>> dryRing_;
986 double hScale_ = 1.0, mScale_ = 1.0;
987 double biasAmp_ = 3.0 * 2.2e4;
988 double overBiasA_ = 0.0;
991 double modPhaseWow_ = 0.0, modPhaseFlut_ = 0.0, modPhaseFlut2_ = 0.0;
992 double driftState_ = 0.0, scrapeLp1_ = 0.0, scrapeLp2_ = 0.0;
993 uint32_t rng_ = 0x1357feedu;
994 uint32_t rngNoise_ = 0x2468beefu;
996 std::atomic<T> driveDb_ { T(0) };
997 std::atomic<T> bias_ { T(0.5) };
999 std::atomic<int> standard_ {
static_cast<int>(
Standard::NAB) };
1000 std::atomic<T> loss_ { T(0.5) };
1001 std::atomic<T> headBumpAmt_ { T(0.5) };
1002 std::atomic<T> wowFlutter_ { T(0.15) };
1003 std::atomic<T> noiseDb_ { T(-200) };
1004 std::atomic<T> mix_ { T(1) };
1005 T currentMix_ = T(1);
1006 T mixMaxStep_ = T(1.0 / 960.0);
1007 std::atomic<bool> dirty_ {
true };