56 void configure(std::uint32_t seed,
double flutter_phase_offset)
noexcept {
58 flutter_phase_offset_ = flutter_phase_offset;
63 innovation_filter_.reset();
67 drift_previous_ = 0.0;
75 if (wow_phase_ >= 1.0) wow_phase_ -= 1.0;
77 if (flutter_phase_ >= 1.0) flutter_phase_ -= 1.0;
79 if (--control_counter_ <= 0) {
83 drift_interpolation_ =
84 1.0 -
static_cast<double>(control_counter_) /
static_cast<double>(
kControlInterval);
88 double offset_ms(
double wow_depth_ms,
double flutter_depth_ms)
const noexcept {
90 drift_previous_ + drift_interpolation_ * (drift_ - drift_previous_);
91 const double wow = wow_depth_ms * (std::cos(2.0 *
kPi * wow_phase_) + drift);
95 const double harmonic =
static_cast<double>(k + 1);
98 flutter_phase_offset_));
100 return wow + flutter_depth_ms * flutter;
109 void step_drift() noexcept {
112 drift_previous_ = drift_;
118 OnePole innovation_filter_;
119 double sample_rate_ = 48000.0;
120 double control_rate_ = 1500.0;
121 double wow_phase_ = 0.0;
122 double flutter_phase_ = 0.0;
123 double flutter_phase_offset_ = 0.0;
125 double drift_previous_ = 0.0;
126 double drift_interpolation_ = 0.0;
127 int control_counter_ = 0;
146 playback_eq_.reset();
147 loss_filter_.reset();
152 void update(
double character_amount)
noexcept {
157 loss_filter_.set_cutoff(loss_hz, sample_rate_);
161 const double tanh_bias = std::tanh(bias_);
162 compensation_ = drive_ * (1.0 - tanh_bias * tanh_bias);
163 tanh_bias_ = tanh_bias;
168 return playback_eq_.process(saturate(record_eq_.process(x)));
173 const double lossy = head_bump_.process(loss_filter_.lowpass(x));
174 return dc_blocker_.highpass(lossy);
182 double saturate(
double x)
const noexcept {
183 if (compensation_ <= 1
e-12)
return x;
184 return (std::tanh(drive_ * x + bias_) - tanh_bias_) / compensation_;
187 FirstOrderShelf record_eq_;
188 FirstOrderShelf playback_eq_;
189 OnePole loss_filter_;
193 double sample_rate_ = 48000.0;
196 double tanh_bias_ = 0.0;
197 double compensation_ = 1.0;
Xorshift32 (Marsaglia, "Xorshift RNGs", J.
Definition rng.hpp:33
double lowpass(double x) noexcept
Definition primitives.hpp:257
Wow + flutter generator for one channel. Shared by both tape tiers.
Definition tape.hpp:44
void reset() noexcept
Definition tape.hpp:61
void tick() noexcept
Advance one sample.
Definition tape.hpp:73
void prepare(double fs)
Definition tape.hpp:46
void configure(std::uint32_t seed, double flutter_phase_offset) noexcept
flutter_phase_offset is in cycles; the right channel starts at a fixed nonzero fraction so the two ch...
Definition tape.hpp:56
double offset_ms(double wow_depth_ms, double flutter_depth_ms) const noexcept
Zero-mean read-position offset, in milliseconds.
Definition tape.hpp:88
Standard-tier tape coloration for one channel: the pre-line record EQ + saturation + playback EQ,...
Definition tape.hpp:132
void prepare(double fs)
Definition tape.hpp:134
void update(double character_amount) noexcept
Definition tape.hpp:152
double pre_process(double x) noexcept
Record EQ → saturation → playback EQ. Runs before the delay line.
Definition tape.hpp:167
void reset() noexcept
Definition tape.hpp:144
double post_process(double x) noexcept
Loss stand-in + head bump + DC block. Runs after the delay line.
Definition tape.hpp:172
double gaussian() noexcept
Standard normal via Box-Muller (Box & Muller 1958).
Definition primitives.hpp:98
constexpr double kTapeEmphasisDb
Record/playback EQ bracketing the saturator: boost HF into saturation, cut it after,...
Definition tables.hpp:158
constexpr std::array< double, 4 > kTapeBias
Definition tables.hpp:141
constexpr std::array< double, 3 > kFlutterAmplitudes
Definition tables.hpp:182
double flush_denormal(double v) noexcept
Definition primitives.hpp:45
constexpr std::array< double, 4 > kTapeBumpDb
Definition tables.hpp:145
constexpr std::array< double, 4 > kTapeLossLpHz
Definition tables.hpp:144
constexpr double kPi
Definition primitives.hpp:38
constexpr int kControlInterval
Coefficient recomputation and stochastic-walk stepping cadence, in samples.
Definition tables.hpp:40
constexpr double kWowOuSigma
Definition tables.hpp:175
constexpr std::array< double, 3 > kFlutterPhases
Definition tables.hpp:183
constexpr double kTapeDcBlockHz
In-loop DC blocker.
Definition tables.hpp:168
constexpr double kWowOuNoiseLpHz
Definition tables.hpp:176
constexpr double kWowOuTheta
Definition tables.hpp:174
constexpr double kTapeBumpQ
Definition tables.hpp:164
constexpr std::uint32_t kPrngSeed
PRNG seed.
Definition tables.hpp:92
constexpr double kTapeEmphasisHz
Definition tables.hpp:159
constexpr std::array< double, 4 > kTapeDrive
Definition tables.hpp:140
double interpolate_knots(const std::array< double, N > &axis, const std::array< double, N > &values, double c) noexcept
Definition primitives.hpp:125
constexpr std::array< double, 4 > kTapeAxis
Definition tables.hpp:139
constexpr double kFlutterBaseHz
Flutter: published band 5–100 Hz.
Definition tables.hpp:181
constexpr double kWowRateHz
Wow: periodic + stochastic.
Definition tables.hpp:173
constexpr double kTapeBumpHz
Loss stand-in head bump (the physical tier replaces this with a speed-dependent center; see kHeadBump...
Definition tables.hpp:163
@ e
Definition formant_filter_bank.hpp:20